【问题标题】:How to filter array elements如何过滤数组元素
【发布时间】:2012-09-17 14:56:38
【问题描述】:

我有以下 MongoDB 文档:

{
 _id: ObjectId(5), 
 items: [1,2,3,45,4,67,9,4]
}

我需要获取包含过滤项 (1、9、4) 的文档

结果:

{
 _id: ObjectId(5), 
 items: [1,9,4]
}

我尝试了 $elemMatch 投影,但它只返回一个项目:

A.findById(ObjectId(5))
   .select({ items: { $elemMatch: {$in: [1, 9, 4]}}})
   .exec(function (err, docs) {
      console.log(doc); // { _id: ObjectId(5), items: [ 1 ] }
      done(err);
});

我如何才能获得包含项目的文档:仅 1、9、4?

【问题讨论】:

  • @dnickless 这与您标记的潜在重复问题并不完全相同。这个示例文档有一个简单的数值数组,而另一个问题有一个对象数组。 MongoDB 中肯定有更多可用的聚合选项,但如果你想指出这一点,你应该添加作为评论而不是编辑旧答案。 2012 年最后更新的任何答案都隐含地描述了当时可用的选项:)。我已经更新了我的答案,包括一些更现代的运营商的例子。
  • @Stennie:点了。请注意,我并没有试图以任何方式减少您的答案,而是避免重复描述我认为普通 MongoDB 用户应该能够从一大堆其他技术中抽象出来的技术(非常相似但不可否认不完全相同)示例,并且还可以防止用户遵循您在 6 年前建议的旧样式模式(这是正确的),因为现在这样做会不必要地缓慢。所以请接受我的道歉。
  • @dnickless 不用担心 :)。如果更新可能合适,我建议对旧答案发表评论。在过去的 6 年中发生了很大变化,因此许多旧答案不再适用或可以使用刷新。标记的可能重复问题甚至更早(2010 年),也可以使用更新的答案。

标签: node.js mongodb express mongoose


【解决方案1】:

在现代版本的 MongoDB (3.2+) 中,您可以使用 $filter operator 来选择要根据指定条件返回的数组字段的子集。返回的元素将按照字段数组中的原始顺序。

mongo shell 中的示例:

db.items.aggregate([
    { $match : {
        _id: 5
    }},
    { $project: {
        items: {
            $filter: {
                input: "$items",
                cond: {
                    "$in": ["$$this", [1, 9, 4]]
                }
            }
        }
     }
}])

注意:因为这个问题中的原始数组有两次值4$filter 命令将返回两次出现:

{ "_id" : 5, "items" : [ 1, 4, 9, 4 ] }

对于只返回唯一匹配项的替代方法,可以使用$setIntersection operator

db.items.aggregate([
    { $match : {
        _id: 5
    }},        
    { $project: {
        items: {
            $setIntersection: ['$items', [1,4,9]] 
        }
    }}
])

这将返回:{ "_id" : 5, "items" : [ 1, 4, 9 ] }

(下面是 2012 年 9 月的原始答案)

如果您希望在服务器端进行文档操作,您可以在 MongoDB 2.2 中使用 Aggregation Framework

db.items.aggregate(

  // Match the document(s) of interest
  { $match : {
     _id: 5
  }},

  // Separate the items array into a stream of documents
  { $unwind : "$items" },

  // Filter the array
  { $match : {
    items: { $in: [1, 9, 4] }
  }},

  // Group the results back into a result document
  { $group : {
     _id: "$_id",
     items: { $addToSet : "$items" }
  }}
)

结果:

{
    "result" : [
        {
            "_id" : 5,
            "items" : [
                9,
                4,
                1
            ]
        }
    ],
    "ok" : 1
}

【讨论】:

  • 注意:这里的例子是使用mongo shell .. 但类似的方法应该适用于猫鼬。 Mongoose 3.1.0 或更高版本有一个 aggregate() 助手。
  • 谢谢。你的aproatch的表现如何?我需要经常查询。
  • @Erik:$match 可以利用索引,其余的文档操作将在内存中进行(在mongod 服务器上)。聚合框架文档也有一些关于optimizing performance 的注释。如果您在客户端执行相同的操作,您将返回一些额外的数据以丢弃,但会在服务器上释放几个周期。不确定服务器端 C++ 代码与 node.js 的相对性能如何,但我希望 C++ 具有优势。
【解决方案2】:

在你的 node.js 应用中使用下划线:

npm install underscore

var _ = require('underscore');

可以使用数组的交集函数:

 intersection_.intersection(*arrays)

计算作为所有数组交集的值列表。结果中的每个值都存在于每个数组中。

_.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2]

http://documentcloud.github.com/underscore/#intersection

【讨论】:

    【解决方案3】:

    A.items = A.items.filter( function(i) {return i == 1 || i == 9 || i == 4} );

    【讨论】:

    • 可以用猫鼬制作吗?
    猜你喜欢
    • 2019-05-29
    • 2020-12-20
    • 2014-01-05
    • 2017-01-04
    • 2020-07-15
    • 1970-01-01
    • 2019-02-21
    • 2016-09-04
    • 1970-01-01
    相关资源
    最近更新 更多