在 Mongoose/MongoDB 的文档中过滤数组、子文档、数组、文档

Posted

技术标签:

【中文标题】在 Mongoose/MongoDB 的文档中过滤数组、子文档、数组、文档【英文标题】:Filtering an array, in a subdocument, in an array, in a document in Mongoose/MongoDB 【发布时间】:2018-01-26 02:04:53 【问题描述】:

使用 Mongoose(甚至只是 MongoDB 查询),我想返回匹配所有文档的查询结果:

a) “units”字段(子文档数组)包含一个带有“unit”字段的子文档,该字段本身包含一个带有匹配给定字符串值的“_id”字段的子文档,

b) “units”字段包含一个带有“period”字段(Date 对象数组)的子文档,其中给定的日期介于数组的第一个和第二个元素之间。

数据结构如下:


  "_id" : ObjectId("5984bdfdb3ac279e39f156d4"),
  "surname" : "Dare",
  "firstname" : "Dan",
  "units" : [
               "period" : [
                            ISODate("2018-01-01T00:00:00.000Z"), 
                            ISODate("2019-12-31T00:00:00.000Z")
                          ],
               "unit" : 
                          "unit_name" : "My test unit",
                           "_id" : "5979437c6d346eb7d074497a"
                        
            ]

我尝试过使用 .find() 和 .aggregate() 的各种组合,例如在 unit._id 上的 $elemMatch 之后在 period 数组上使用 $project 和 $filter,但无济于事 - 我明白了诸如“can't use $filter on an Array”之类的错误。

任何指向正确方向的指针都将受到赞赏 - 至少在最合适的查询类型和组合运算符以生成我所追求的数据集的最有效方式方面。

架构(根据要求):


    surname: 
    type: String
  ,
    firstname: 
    type: String
  ,
  units: 
    type: [
      unit: 
        _id: String,
        unit_name: String,
      ,
      period: [Date]
    ]
  

【问题讨论】:

发布您的架构。 【参考方案1】:

认为我把问题复杂化了。不确定这是否是最有效的方法,但使用 $and 运算符似乎提供了我正在寻找的内容:

db.getCollection("records").find( "$and": [
"units.unit._id": "5979437c6d346eb7d074497a",
"units.unit_period.0": "$gte": new Date('2018-12-31T00:00:00.000Z') ,
"units.unit_period.1": "$lte": new Date('2020-12-31T00:00:00.000Z')  ]
)

更新

根据 Neil Lunn 在 cmets 中的建议,使用 $elemMatch 而不是 $and 显然是更好的选择,因为给出的原因。

因此,这是我将采用的解决方案;即使用#elemMatch 来匹配那些包含嵌入式子文档和数组的元素,通过键上的简单点符号可以访问和匹配值。

我因为试图使用 $filter 和 $aggregate 对解决方案进行过度编码而感到内疚,在检索我需要的数据集时实际上就像这样简单:

db.getCollection("records").find( "units": 
   "$elemMatch": 
     "unit._id": "5979437c6d346eb7d074497a", 
      "unit_period.0":  "$gte": new Date("2019-12-31") ,
      "unit_period.1":  "$lte": new Date("2020-12-31") 
     
  )

【讨论】:

跳过$and 并实际应用$elemMatch 而不是.find( "units": "$elemMatch": "unit._id": "5979437c6d346eb7d074497a", "unit_period.0": "$gte": new Date('2019-12-31") , "unit_period.1": "$lte": new Date("2020-12-31") ) $elemMatch 部分实际上对于确保所有这些条件实际上都在同一个元素上非常重要。不需要$and,因为所有 MongoDB 查询表达式基本上已经是 AND 条件。你很少需要写一个明确的$and。但在基本原则上,这“只是一个查询”,无需过于复杂。 非常感谢,尼尔。这完美地工作,我非常感谢有关确保条件在同一个元素上的提醒(很可能让我在调试过程中更加头疼!)。如果您将其作为答案,我将标记为已接受的解决方案。 评论在那里是因为您基本上自己回答了,您真正需要做的就是查询指定的数组索引。 $elemMatch 确保这些条件实际上应用于相同的元素,但在您问题的主要上下文中,它实际上是关于匹配那些“内部”元素值。您可能真的应该考虑使用“命名属性”而不是使用数组,因为这样做时它看起来确实更简洁明了。

以上是关于在 Mongoose/MongoDB 的文档中过滤数组、子文档、数组、文档的主要内容,如果未能解决你的问题,请参考以下文章

如何在 mongoose/mongodb 查询子文档中使用 mapreduce?

如何在 mongoose/mongodb 查询子文档中使用 mapreduce?

Mongoose/MongoDb 在数组中查找具有反向引用的文档

创建 Mongoose/MongoDB 常量文档

Mongoose/MongoDB:$in 和 .sort()

在 mongoose mongodb 中使用 $or 返回文档的一部分