Mongo按数组中的匹配计数排序

Rak*_*ddy 2 mongodb mongodb-query aggregation-framework

可以说我的测试数据是

db.multiArr.insert({"ID" : "fruit1","Keys" : ["apple", "orange", "banana"]})
db.multiArr.insert({"ID" : "fruit2","Keys" : ["apple", "carrot", "banana"]})
Run Code Online (Sandbox Code Playgroud)

得到像胡萝卜一样的水果

db.multiArr.find({'Keys':{$in:['carrot']}})
Run Code Online (Sandbox Code Playgroud)

当我做一个或查询橙色和香蕉时,我同时看到了水果1和水果2的记录

db.multiArr.find({ $or: [{'Keys':{$in:['carrot']}}, {'Keys':{$in:['banana']}}]})
Run Code Online (Sandbox Code Playgroud)

输出的结果应该是fruit2,然后是fruit1,因为fruit2既有胡萝卜又有香蕉

Nei*_*unn 5

要首先实际回答此问题,您需要“计算”与给定条件的匹配数,以便对结果进行“排序”,以返回最优先匹配的结果。

为此,您需要一个聚合框架,该框架用于在MongoDB中“计算”和“操纵”数据:

db.multiArr.aggregate([
  { "$match": { "Keys": { "$in": [ "carrot", "banana" ] } } },
  { "$project": {
    "ID": 1,
    "Keys": 1,
    "order": {
      "$size": {
        "$setIntersection": [ ["carrot", "banana"], "$Keys" ]
      }
    }
  }},
  { "$sort": { "order": -1 } }
])
Run Code Online (Sandbox Code Playgroud)

在版本3之前的MongoDB上,您可以执行更长的形式:

db.multiArr.aggregate([
  { "$match": { "Keys": { "$in": [ "carrot", "banana" ] } } },
  { "$unwind": "$Keys" },
  { "$group": {
    "_id": "$_id",
    "ID": { "$first": "$ID" },
    "Keys": { "$push": "$Keys" },
    "order": {
      "$sum": {
        { "$cond": [
          { "$or": [
           { "$eq": [ "$Keys", "carrot" ] },
           { "$eq": [ "$Keys", "banana" ] }
         ]},
         1,
         0
        ]}
      }
    }
  }},
  { "$sort": { "order": -1 } }
])
Run Code Online (Sandbox Code Playgroud)

无论哪种情况,此处的功能都是通过提供带有参数的“列表”,首先将可能的文档与条件匹配$in。获得结果后,您想将数组中匹配元素的数量“计数”到提供的可能值的“列表”中。

在现代形式中,$setIntersection运算符比较两个“列表”,返回一个仅包含“唯一”匹配成员的新数组。由于我们想知道那有多少个匹配项,因此我们只返回该$size列表的。

在较旧的版本中,您将文档数组拆开$unwind以对其执行操作,因为较旧的版本缺少无需更改即可使用数组的较新的运算符。然后,该过程将分别查看每个值,如果两个表达式中的任何一个都$or与可能的值匹配,则$cond三进制1会向$sum累加器返回一个值,否则0。最终结果是与现代版本相同的“匹配数”。

最后一件事只是$sort根据返回的“匹配数”得出的结果,因此,大多数匹配项位于“顶部”。这是“降序”,因此您提供-1来表示。


关于$ in和数组的附录

您对入门的MongoDB查询有几处误解。该$in运营商实际上是用于一个像这样的参数“列表”:

{ "Keys": { "$in": [ "carrot", "banana" ] } }
Run Code Online (Sandbox Code Playgroud)

这实际上是“匹配属性'Keys'中的'carrot' 'banana'的简写方式。甚至可以这样写成很长的形式:

{ "$or": [{ "Keys": "carrot" }, { "Keys": "banana" }] }
Run Code Online (Sandbox Code Playgroud)

如果这是一个“奇异”的匹配条件,则应该真正带您去,那么您只需提供要匹配的值即可:

{ "Keys": "carrot" }
Run Code Online (Sandbox Code Playgroud)

因此,这应该涵盖误解,即您$in用来匹配作为文档内数组的属性的误解。相反,“反向”情况是预期的用法,在这种情况下,您将提供“参数列表”以匹配给定的属性,该属性可以是数组,也可以是单个值。

MongoDB查询引擎在相等或类似操作中不区分单个值或值数组。