首页  编辑  

MongoDB复杂查询示例和Expression的使用

Tags: /Java/   Date Created:
Spring Data MongoDb - Criteria equivalent to a given query that uses $expr - Stack Overflow

MongoDB数据库,有以下实体
  1. Demo {
  2.  String id; // PK
  3.  Long eid;
  4.  Long recordId;
  5.  String recordStatus; // "A""P""V""R""T""E"
  6.  String refNo;
  7. }
可能存在refNo相同但是recordId和recordStatus不同的记录。
现在需要查询所有数据,根据 eid进行过滤,若 refNo 相同的情况下只返回 recordId 最大的记录。
然后对查询的数据进行过滤,例如返回所有 recordStatus 或者只返回给定 recordStatus(例如 "T")的数据,并进行refNo倒序排序和分页处理,然后返回结果。例如返回下图中,打勾✔行的数据:
类似的SQL语句如下:
  1. select A.*
  2. from Demo A, (
  3.   select id, refNo, max(recordId) as recordId
  4.   from Demo
  5.   where eid=111111 and A.refNo = "abcd123456" -- other filters...
  6.   group by refNo
  7. ) B
  8. where A.id = B.id and recordStatus in ("D""R""P""V""E""T")
  9. order by enrolmentRefNo desc
  10. limit 010
等价的 Mongodb查询是什么?如何用Java实现相关查询功能?

等价的MongoDB查询是:
  1. db.getCollection('Demo').aggregate([
  2.  {
  3.   $match: {
  4.    eid: 12345678,
  5.    refNo: "ABCD123456" // other filters...
  6.   }
  7.  },
  8.  {
  9.   $group: {
  10.    _id: "$refNo",
  11.    maxRecordId: { $max"$recordId" },
  12.    docs: { $push"$$ROOT" }
  13.   }
  14.  },
  15.  { $unwind"$docs" },
  16.  {
  17.   $match: {
  18.    $expr: {
  19.     $and: [
  20.      { $eq: ["$docs.recordId""$maxRecordId"] },
  21.      { $in: ["$docs.recordStatus", ["D""R""P""V""E""T"]] }
  22.     ]
  23.    }
  24.   }
  25.  },
  26.  { $sort: { "docs.refNo": -1 } },
  27.  { $skip: 0 },
  28.  { $limit: 100 },
  29.  { $replaceRoot: { newRoot: "$docs" } }
  30. ])
这个聚合管道执行了以下步骤:
  1. 匹配指定条件的文档。
  2. 使用 $group 阶段按照 refNo 分组,同时计算每个分组中 recordId 的最大值。
  3. 使用 $unwind 阶段展开分组后的文档数组。
  4. 再次进行匹配,选择具有最大 recordId 且 recordStatus 在指定范围内的文档。
  5. 使用 $sort 阶段按照 refNo 进行倒序排序。
  6. 使用 $skip 和 $limit 阶段进行分页。
  7. 最后,使用 $replaceRoot 阶段将文档结构恢复为原始的 Demo 文档结构。
转换成Java/Spring代码:
  1. public Page<Demo> findPageList(Integer page, Integer size, Demo dto) {
  2.  Criteria matchCriteria = new Criteria();
  3.  if (StringUtils.isNotBlank(dto.getRefNo())) {
  4.    matchCriteria.and("refNo").is(dto.getRefNo());
  5.  }
  6.  if (!isEmpty(dto.geteid())) {
  7.    matchCriteria.and("eid").is(dto.geteid());
  8.  }
  9.  AggregationOperation matchOperation = Aggregation.match(matchCriteria);
  10.  AggregationOperation groupOperation = Aggregation.group("$refNo")
  11.   .max("recordId").as("maxRecordId")
  12.   .push("$$ROOT").as("docs");
  13.  AggregationOperation unwindOperation = Aggregation.unwind("docs");
  14.  // Parse recordStatus filter
  15.  List<String> recordStatus;
  16.  if (isEmpty(dto.getRecordStatus())) {
  17.    recordStatus = "['A', 'P', 'V', 'R', 'T', 'E']";
  18.  } else {
  19.    recordStatus = "['" + dto.getRecordStatus + "']";
  20.  }
  21.  AggregationExpression expression = AggregationExpression.from(MongoExpression.create(("$expr: {\n" +
  22.   "\t$and: [\n" +
  23.   "\t\t{ $eq: ['$docs.recordId', '$maxRecordId'] },\n" +
  24.   "\t\t{ $in: ['$docs.recordStatus', $PLACE] }\n" +
  25.   "\t]\n" +
  26.   "}").replace("$PLACE", recordStatus)));
  27.  AggregationOperation filterOperation = Aggregation.match(expression);
  28.  AggregationOperation sortOperation = Aggregation.sort(Sort.Direction.DESC, "docs.refNo");
  29.  AggregationOperation skipOperation = Aggregation.skip(page * size);
  30.  AggregationOperation limitOperation = Aggregation.limit(size);
  31.  AggregationOperation replaceRootOperation = Aggregation.replaceRoot("docs");
  32.  AggregationOperation countOperation = Aggregation.count().as("totalCount");
  33.  Aggregation count = Aggregation.newAggregation(
  34.   matchOperation, groupOperation, unwindOperation, filterOperation, countOperation
  35.  );
  36.  CountResult queryResult = mongoTemplate.aggregate(count, Demo.classCountResult.class).getUniqueMappedResult();
  37.  long totalRecord = queryResult == null ? 0 : queryResult.getTotalCount();
  38.  if (totalRecord == 0) {
  39.    return new PageImpl<>(new ArrayList<>(0), PageRequest.of(page, size), 0);
  40.  }
  41.  Aggregation aggregation = Aggregation.newAggregation(
  42.   matchOperation,
  43.   groupOperation,
  44.   unwindOperation,
  45.   filterOperation,
  46.   sortOperation,
  47.   skipOperation,
  48.   limitOperation,
  49.   replaceRootOperation
  50.  );
  51.  List<Demo> pageResult = mongoTemplate.aggregate(aggregation, Demo.classDemo.class).getMappedResults();
  52.  return new PageImpl<>(list, PageRequest.of(page, size), totalRecord);
  53. }
  54. @Data
  55. private static class CountResult {
  56.  private long totalCount;
  57. }