按数组匹配数量对Mongo进行排序

3
假设我的测试数据如下:
db.multiArr.insert({"ID" : "fruit1","Keys" : ["apple", "orange", "banana"]})
db.multiArr.insert({"ID" : "fruit2","Keys" : ["apple", "carrot", "banana"]})

要获取像胡萝卜这样的单个水果,我会执行以下操作:

db.multiArr.find({'Keys':{$in:['carrot']}})

当我对橘子和香蕉进行或查询时,我看到了水果1和水果2的记录。
db.multiArr.find({ $or: [{'Keys':{$in:['carrot']}}, {'Keys':{$in:['banana']}}]})

输出结果应该是水果2然后是水果1,因为水果2既有胡萝卜又有香蕉。

我看到了记录fruit1和fruit2,然后你说“应该是fruit2然后是fruit1”,你得到了你想要的结果吗?! - Alex
您想执行$and查询吗? - Sercan Ozdemir
1
@Alex 这个问题还说了“...输出应该是fruit2然后是fruit1,因为fruit2既有胡萝卜又有香蕉”,这就是关键,它要求对结果进行“加权排序”,而不仅仅是返回两个都匹配的文档。 - Neil Lunn
啊,现在我明白了。 - Alex
1个回答

10

要回答这个问题,你需要“计算”满足条件的匹配数,以便将结果“排序”并优先返回最多匹配项的结果。

为此,你需要使用聚合框架,在 MongoDB 中用于数据的“计算”和“操作”:

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

如果您使用的是 MongoDB 3 版本以下的较旧版本,则可以使用长格式:

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 } }
])
无论哪种情况,该函数的功能首先是通过提供一个带有$in的“列表”参数来匹配可能的文档与条件。一旦获取了结果,您就想要在数组中“计数”匹配元素的数量,以与提供的“列表”可能值进行匹配。
现代形式中,$setIntersection运算符比较两个“列表”,返回仅包含“唯一”匹配成员的新数组。由于我们想知道有多少匹配项,因此只需返回该列表的$size即可。
在旧版本中,您需要使用$unwind拆分文档数组,以便对其执行操作,因为旧版本缺乏可在不改变数组的情况下使用的新运算符。然后该过程逐个查看每个值,如果$or表达式中的任一表达式与可能的值相匹配,则$cond三元运算符将返回1的值,以便累加器$sum,否则返回0。净结果与现代版本所示的“匹配计数”相同。
最后一件事就是根据返回的“匹配计数”$sort结果,让最多的匹配在“顶部”。这是“降序”,因此您提供-1来指示。

关于$in和数组的补充说明

首先,您对MongoDB查询有一些误解。实际上,$in运算符是为像这样的“列表”参数而设计的:

{ "Keys": { "$in": [ "carrot", "banana" ] } }

这本质上是说:“在‘Keys’属性中匹配‘carrot’或‘banana’”,这相当于一种简写方式。

。甚至可以这样长形式书写:

“在‘Keys’属性中匹配‘carrot’或‘banana’”。

{ "$or": [{ "Keys": "carrot" }, { "Keys": "banana" }] }

如果它是一个"单个"匹配条件,那么你只需要提供要匹配的值到属性中:

{ "Keys": "carrot" }

因此,这应该涵盖了一个误解,即您使用$in来匹配文档内的数组属性。相反,"反向"情况是预期用法,您将提供"参数列表"来匹配给定属性,无论该属性是数组还是单个值。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接