MongoDB / MongoEngine -查询List中的Item



我有一系列这样的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 's(在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])

问得好。这里有一些事情使它不简单。

首先要提到的是嵌套数组/列表的事情不是标准的django公平,但你可以潜入__raw__语法提供的MongoEngine,因为这确实需要更多的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
}

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

最新更新