【问题标题】:MongoDB / MongoEngine - Query for Item in ListMongoDB / MongoEngine - 查询列表中的项目
【发布时间】:2014-06-12 04:51:50
【问题描述】:

我有一系列这样的 MongoDB 文档:

{
 "_id" : ObjectId("53992c7d02b8756437f81cba"),
 "account_id" : ObjectId("5397929402b8751ae8a32349"),
 "event_history" : [ 
                     [ 0, ISODate("2014-06-12T04:28:45.684Z") ],
                     [ 0, ISODate("2014-06-11T04:28:45.684Z") ],
                     [ 1, ISODate("2014-05-12T04:28:45.684Z") ] 
                   ],
 "status" : 1
}

是否可以返回具有0 作为第一个元素的event_history(在 MongoEngine 中)?

我想得到这个:

[ 0, ISODate("2014-06-12T04:28:45.684Z") ],
[ 0, ISODate("2014-06-11T04:28:45.684Z") ],

这是我尝试过的查询,但它不起作用:

Record.objects(id=ObjectId("53992c7d02b8756437f81cba"), event_history__in=[0])

【问题讨论】:

    标签: python mongodb aggregation-framework mongoengine


    【解决方案1】:

    好问题。这里有一些事情让这并不简单。

    首先要提到的是嵌套数组/列表不是标准的 django 公平,但您可以深入了解 MongoEngine 提供的 __raw__ 语法,因为这确实需要更特定于 MongoDB。

    也就是说,即便如此,您想要在此处执行的过滤类型甚至超出了标准 MongoDB .find() 查询类型的范围,因为您希望匹配多个内部数组成员,然后“过滤”结果中返回的成员。

    这实际上非常棘手,不仅对于嵌套数组,而且对于只能使用聚合框架才能完成的事情。尽管有任何明确的文档,但您可以通过使用类上的 ._get_collection() 方法访问集合的“原始”pymongo 驱动程序函数来使用 MongoEngine。

    评论解释:

    Record._get_collection().aggregate([
        # Match only those documents that would meet the condition
        { "$match": { 
            "event_history": { 
                "$elemMatch": { 
                    "$elemMatch": {"$in": [0] }
                }
            }
        }},
    
        # Unwind the event history array, top level
        { "$unwind": "$event_history" },
    
        # Make a copy of the inner array
        { "$project": {
            "account_id": 1,
            "event_history": 1,
            "status": 1,
            "copy": "$event_history"
        }},
    
        # Unwind that copy
        { "$unwind": "$copy" },
    
        # Match the numeric 0 elements only, filters non-matches
        { "$match": { "copy": 0 } },
    
        # Group back to the original document
        { "$group": {
            "_id": "$_id",
            "account_id": { "$first": "$account_id" },
            "event_history": { "$push": "$event_history" },
            "status": { "$first": "$status" }
        }}
    ])
    

    如果其中有多个可能为 0 的数值,那么您需要知道它是第一个。所以多一点参与:

    Record._get_collection().aggregate([
        # Match only those documents that would meet the condition
        { "$match": { 
            "event_history": { 
                "$elemMatch": { 
                    "$elemMatch": {"$in": [0] }
                }
            }
        }},
    
        # Unwind the event history array, top level
        { "$unwind": "$event_history" },
    
        # Make a copy of the inner array
        { "$project": {
            "account_id": 1,
            "event_history": 1,
            "status": 1,
            "copy": "$event_history"
        }},
    
        # Unwind that copy
        { "$unwind": "$copy" },
    
        # Group back the document keeping the "first" inner element only
        { "$group": {
            "_id": {
                "_id": "$_id",
                "account_id": "$account_id",
                "event_history": "$event_history",
                "status": "$status"
            },
            "copy": { "$first": "$copy" }
        }},
    
        # Match only where 0 to filter
        { "$match": { "copy": 0 } },
    
        # Group back to the original document
        { "$group": {
            "_id": "$_id._id",
            "account_id": { "$first": "$_id.account_id" },
            "event_history": { "$push": "$_id.event_history" },
            "status": { "$first": "$status" }
        }}
    ])
    

    以及每种情况下的结果:

    {
        "_id" : ObjectId("53992c7d02b8756437f81cba"),
        "account_id" : ObjectId("5397929402b8751ae8a32349"),
        "event_history" : [
                [ 0, ISODate("2014-06-11T04:28:45.684Z") ],
                [ 0, ISODate("2014-06-12T04:28:45.684Z") ]
        ],
        "status": 1
    }
    

    所以是的,这个过程看起来有点复杂,并不像您想象的那样简单。但幸运的是,有一种方法可以访问原始收集方法并使用aggregate 来解决这个问题。

    【讨论】:

      猜你喜欢
      • 2013-07-29
      • 2020-01-10
      • 1970-01-01
      • 1970-01-01
      • 2019-05-05
      • 1970-01-01
      • 2011-08-24
      • 2016-08-02
      • 2022-10-21
      相关资源
      最近更新 更多