【问题标题】:Mongo db - How perform unwind and match with conditionMongodb - 如何执行展开并匹配条件
【发布时间】:2022-11-26 05:59:46
【问题描述】:

假设我的产品集合包括每个产品都有如下数组项的产品。

 [
      {
        "_id": "1",
        "score": 200,
        "items": [
          {
            "_id": "1",
            "title": "title1",
            "category": "sport"
          },
          {
            "_id": "2",
            "title": "title2",
            "category": "sport"
          },
          {
            "_id": "3",
            "title": "title3",
            "category": "tv"
          },
          {
            "_id": "4",
            "title": "title4",
            "category": "movies"
          }
        ]
      },
      {
        "_id": "2",
        "score": 1000000000,
        "items": [
          {
            "_id": "9",
            "title": "titleBoo",
            "category": "food"
          },
          {
            "title": "title4",
            "category": "movies"
          },
          {
            "title": "titlexx",
            "category": "food"
          },
          {
            "title": "titl113",
            "category": "sport"
          }
        ]
      },
      {
        "_id": "3",
        "score": 500,
        "items": [
          {
            "title": "title3",
            "category": "movies"
          },
          {
            "title": "title3",
            "category": "food"
          },
          {
            "title": "title3",
            "category": "sport"
          },
          {
            "title": "title3",
            "category": "sport"
          }
        ]
      }
    ]

我想按类别返回具有最高得分的类别的单个项目,如果没有匹配的类别,则只返回具有最高得分的随机/第一个产品。

类别“食品”的示例,结果应为:

   {
     "_id" : "9",
      "title": "titleBoo",
      "category": "food"
    }

因为它的最高分是1000000000

对于其他不存在的类别“Foo”,结果应该是从得分最高的产品项目中随机抽取的,比方说

{
      "title": "titlexx",
      "category": "food"
    },

基本上我使用 java spring 数据聚合管道所做的

Aggregation agg1 = newAggregation(
            unwind("items"),
            match(Criteria.where("items.category").is(category)),
            group().max("score").as("score")
    );

 BasicDBObject result =    mongoTemplate.aggregate(
            agg1, "products", BasicDBObject.class).getUniqueMappedResult();


 if (result empty) { // didn't find any matched category so without match step !

   Aggregation agg2 = newAggregation(
                unwind("items"),
                group().max("score").as("score")
        );

    // take some item inside max  "score"
     BasicDBObject res2 =    mongoTemplate.aggregate(
                agg2, "products", BasicDBObject.class).getUniqueMappedResult();

    System.out.print(res2);

}

这段代码不理想,因为我需要执行两次“unwind”(如果不匹配)再做一次。我知道有 $cond / switch 函数,我想知道我是否可以在 unwind 之后使用一些 switch case 操作像这儿:

 Aggregation agg = newAggregation(
                unwind("items"),
               // switch-case {
                      a. match(Criteria.where("items.category").is(category)),
                          if (result or size > 0) {
                               group().max("score").as("score") // max on matched result by category
                           } 
                 
                      b. group().max("score").as("score"). // max on random unwind score
                 }
        );

     BasicDBObject result =    mongoTemplate.aggregate(
                agg, "products", BasicDBObject.class).getUniqueMappedResult();

有什么提示吗?

【问题讨论】:

  • 考虑做一个 $sort$limit 而不是分组以获得最大值。还要确保 score 是一个数字而不是真实数据集中的字符串。
  • 谢谢@user20042973,编辑我的问题,确定分数是数字(长),放松和匹配怎么样?如果没有匹配项,我如何减少两次 unwind 调用 ...

标签: mongodb mongodb-query spring-data spring-data-mongodb


【解决方案1】:

一种选择是使用$setWindowFields$facet

db.collection.aggregate([
  {$unwind: "$items"},
  {$setWindowFields: {
      sortBy: {score: -1},
      output: {bestScore: {$max: "$score"}}
  }},
  {$match: {$expr: {
        $or: [
          {$eq: ["$score", "$bestScore"]}, 
          {$eq: ["$items.category", "food"]}
        ]
  }}},
  {$facet: {
      category: [{$match: {"items.category": "food"}}, {$limit: 1}],
      other: [{$limit: 1}]
  }},
  {$replaceRoot: {newRoot: {
        $cond: [
          {$eq: [{$size: "$category"}, 1]},
          {$first: "$category"},
          {$first: "$other"}
        ]
  }}}
])

查看它在playground example 上的工作原理

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-25
    • 1970-01-01
    • 2012-11-30
    • 1970-01-01
    相关资源
    最近更新 更多