MongoDb: 通过 $in 排序

7 投票
3 回答
1955 浏览
提问于 2025-04-17 14:27

我正在使用mongodb执行一个find查询,并且使用了$in操作符:

collection.find({name: {$in: [name1, name2, ...]}})

我希望结果能够按照我的名字数组的顺序排序:[name1, name2, ...]。我该怎么做呢?

注意:我通过pymongo访问MongoDb,但我觉得这并不重要。

补充说明:因为在MongoDb中无法直接实现这个功能,所以我最后使用了一个典型的Python解决方案:

names = [name1, name2, ...]
results = list(collection.find({"name": {"$in": names}}))
results.sort(key=lambda x: names.index(x["name"]))

3 个回答

2

Vitaly说得对,使用find是无法做到这一点的,但可以通过聚合来实现:

db.collection.aggregate([
      { $match: { name: { $in: [name1, name2, /* ... */] } } },
      {
        $project: {
          name: 1,
          name1: { $eq: ['name1', '$name'] },
          name2: { $eq: ['name2', '$name'] },
        },
      },
      { $sort: { name1: 1, name2: 1 } },
    ])

这个是在2.6.5版本上测试过的

我希望这能给其他人一些正确的方向。

7

从即将发布的3.4版本(2016年11月)开始,你可以通过聚合框架来实现这个功能。

假设你想要的顺序是数组 order=["David", "Charlie", "Tess"],你可以通过以下的处理流程来实现:

m = { "$match" : { "name" : { "$in" : order } } };
a = { "$addFields" : { "__order" : { "$indexOfArray" : [ order, "$name" ] } } };
s = { "$sort" : { "__order" : 1 } };
db.collection.aggregate( m, a, s );

在3.4版本中新增的 "$addFields" 阶段可以让你在已有的文档中添加新的字段,而不需要知道所有其他字段的情况。新的 "$indexOfArray" 表达式可以返回给定数组中特定元素的位置。

这个聚合的结果将是符合你条件的文档,按照输入数组 order 中指定的顺序排列,并且这些文档会包含所有原始字段,还会多一个叫 __order 的字段。

2

不可能。$in操作符是用来检查某个值是否存在的。这里的列表被当作集合来处理。

选项:

  • 可以把查询分开,针对每个名字(name1到nameN)进行多次查询,或者用同样的方法过滤结果。名字越多,查询次数就越多。
  • 使用itertools库中的groupby或ifilter。在这种情况下,需要给每个文档添加一个“排序优先级”的标记,把name1匹配到PREC1,name2匹配到PREC2,依此类推,然后先按PREC排序,再按PREC分组。

如果你的数据集合在“name”字段上有索引,那么第一种选项更好。如果没有索引,或者因为读写比例太高而无法创建索引,那么第二种选项更适合你。

撰写回答