mongodb - 对结果进行排序,以便首先检索子集

时间:2015-11-19 12:38:20

标签: mongodb mongodb-query aggregation-framework

考虑以下集合,我们称之为things。它有很多文件

{ _id: 'aaa', foo: 82374 },
{ _id: 'bbb', foo: 32476 },
{ _id: 'ccc', foo: 25733 },
{ _id: 'ddd', foo: 45253 },
{ _id: 'eee', foo: 15545 }

我需要找到所有匹配_id的子集的文档,这些文档我已经知道并满足其他一些条件。

此外,我希望那些在_id上匹配的文档首先出现在排序顺序中,其余文档则由其他字段排序。

不幸的是,我不能简单地找到匹配的_id,因为things包含数以万计的文档,我需要skiplimit结果。这导致了一个丑陋,混乱和复杂的应用程序级别连接。

理想情况下,我想要的是:

var query = {
  $or: [
    { _id: { $in: ['bbb', 'ddd'] } },
    { foo: { $lt: 20000 } }
  ]
};

var sort = {
  // somehow make the _id take priority here
  foo: 1
};

db.things.find(query).sort(sort).limit(100).toArray(callback);

有谁知道我是否可以使用运营商来实现这一目标?

2 个答案:

答案 0 :(得分:2)

你想要做的是"加权"要做到这一点,您需要使用.aggregate()方法来提供对聚合管道的访问。管道中的第一个阶段是$match阶段,您可以在其中过滤以仅允许匹配的文档未经修改地传递到下一个管道阶段。然后您需要使用$project$sort管道运算符如图所示here

db.things.aggregate([
    { "$match": {
        "$or": [
            { "_id": { 
                "$in": ['bbb', 'ddd'] } 
            }, 
            { "foo": { "$lt": 20000 } } ] 
    }}, 
    { "$project": { 
        "foo": 1, 
        "weight": {
            "$cond": [
                { "$eq": [ "$_id", "bbb" ] }, 
                10, 
                { "$cond": [
                    { "$eq": [ "$_id", "ddd" ] }, 
                    8, 
                    0
                ]}
            ]
        }
    }}, 
    { "$sort": { "weight": -1 }}
])

哪个收益率:

{ "_id" : "bbb", "foo" : 32476, "weight" : 10 }
{ "_id" : "ddd", "foo" : 45253, "weight" : 8 }
{ "_id" : "eee", "foo" : 15545, "weight" : 0 }

由于$in中不允许$cond运算符,您可以使用$map返回一个元素数组数组,然后使用$setIsSubset运算符检查_id运算符是否$map 1}}出现在你的第二个数组中。当然,因为"输入" db.things.aggregate([ { "$match": { "$or": [ { _id: { "$in": ['bbb', 'ddd'] } }, { foo: { $lt: 20000 } } ] }}, { "$project": { "foo": 1, "weight": { "$cond": [ { "$setIsSubset": [ { "$map": { "input": { "$literal": ["id"] }, "as": "id", "in": "$_id" }}, [ "bbb","ddd" ] ]}, 4, 0 ] } }}, { "$sort": { "weight": -1 } } ]) 必须是您需要使用$literal表达式的数组。

{ "_id" : "bbb", "foo" : 32476, "weight" : 4 }
{ "_id" : "ddd", "foo" : 45253, "weight" : 4 }
{ "_id" : "eee", "foo" : 15545, "weight" : 0 }

返回:

com.google.android.gms:play-services-analytics

请注意,如果你有很多" _id"在你的阵列中。

答案 1 :(得分:0)

如果将聚合框架与$project一起使用,则可以部分实现此目的。在下面的代码中,仅在_id上匹配的记录首先出现,但是,如果满足两个过滤条件,则不适用。遗憾的是,您无法在$in子句中使用$project运算符。

var query = [{
  $match: {
    $or: [{
      _id: {
        $in: ['bbb', 'ddd']
      }
    }, {
      foo: {
        $lt: 20000
      }
    }]
  }
}, {
  $project: {
    "_id": 1,
    "foo": 1,
    "sort_flag": {
      "$or": [{
        "$gte": ["$foo", 20000]
      }]
    }
  }
}, {
  $sort: {
    "sort_flag": -1
  }
}]



db.things.aggregate(query)