Mongo DB按日期排序并按标签分组

问题描述

我有一个与此类似的Mongo DB集合:

{
    "_id": "...","name": "name 1","created": "2020-07-21T08:11:18.414+00:00","tags": ["red","green"]
}
{
    "_id": "...","name": "name 2","created": "2020-07-20T08:11:18.414+00:00","blue"]
}
{
    "_id": "...","name": "name 3","created": "2020-07-19T08:11:18.414+00:00","tags": ["green","name": "name 4","created": "2020-07-18T08:11:18.414+00:00","tags": ["white","name": "name 5","created": "2020-07-17T08:11:18.414+00:00","green"]
}

我需要能够指定标签列表,并且对于列表中的每个标签,我都需要获取最新创建的文档。在上面的示例中,如果我指定["red","green","blue"],则应该得到类似于以下的结果:

{
    "red": {
        "_id": "...","green"]
    },"green": {
        "_id": "...","blue": {
        "_id": "...","blue"]
    }
}

我的问题是:

  1. 获得以上结果的最佳Mongo数据库查询是什么?
  2. 如果周围有任何熟练的Spring Data专家,那么如何使用Aggregationoperation将其转换为Spring Data查询

我对问题1的答案很满意,我可以分别寻找问题2的答案。

解决方法

如前所述,您可以通过聚合来实现

  1. $sort 创建的按降序排列
  2. $facet有助于对内部文档进行分类,在其中我们使用$match来过滤 tags
  3. $project有助于投影。我们已经按创建的进行了排序。因此,我们将通过$arrayElemAt获得第一个元素,有时,如果没有元素,它将引发异常,因此,为了返回空对象,我们使用$ifNull

Mongo脚本

[
  {
    $sort: {
      created: -1
    }
  },{
    $facet: {
      red: [
        {
          $match: {
            tags: "red"
          }
        }
      ],green: [
        {
          $match: {
            tags: "green"
          }
        }
      ],blue: [
        {
          $match: {
            tags: "blue"
          }
        }
      ]
    }
  },{
    $project: {
      red: {
        $ifNull: [
          {
            "$arrayElemAt": [
              "$red",0
            ]
          },{}
        ]
      },blue: {
        $ifNull: [
          {
            "$arrayElemAt": [
              "$blue",green: {
        $ifNull: [
          {
            "$arrayElemAt": [
              "$green",{}
        ]
      }
    }
  }
]

工作Mongo playground

当我们将其转换为Spring数据时

public List<Object> test() {
    Aggregation aggregation = Aggregation.newAggregation(
            sort(Sort.Direction.DESC,"created"),facet(
                    match(Criteria.where("tags").is("red"))
            ).as("red")
                    .and(
                            match(Criteria.where("tags").is("green"))
                    ).as("green")
                    .and(
                            match(Criteria.where("tags").is("blue"))
                    ).as("blue"),project()
                    .and(ConditionalOperators.ifNull(ArrayOperators.arrayOf("red").elementAt(0)).then(new Document())).as("red")
                    .and(ConditionalOperators.ifNull(ArrayOperators.arrayOf("blue").elementAt(0)).then(new Document())).as("blue")
                    .and(ConditionalOperators.ifNull(ArrayOperators.arrayOf("green").elementAt(0)).then(new Document())).as("green")

    ).withOptions(AggregationOptions.builder().allowDiskUse(Boolean.TRUE).build());

    return mongoTemplate.aggregate(aggregation,mongoTemplate.getCollectionName(YOUR_COLLECTION.class),Object.class).getMappedResults();

}

注意:我没有在spring-boot中测试以上代码。但是它是基于有效的mongo脚本编写的。希望它能工作。