问题描述:

My task is to find individual authors(comments.user_id) comment on the article (_id)

{

"_id" : ObjectId("56479d9c8510369a4ecea3a9"),

"comments" : [

{

"text" : "222",

"user_id" : ObjectId("563f2db0e2bf6c431b297d45"),

},

{

"text" : "333",

"user_id" : ObjectId("563f2db0e2bf6c431b297d45"),

},

{

"text" : "444",

"user_id" : ObjectId("563f2db0e2bf6c431b297d45"),

},

{

"text" : "55555",

"user_id" : ObjectId("563e3337e2bf6c431b297d41"),

},

{

"text" : "00000",

"user_id" : ObjectId("563f7c0a8db7963420cd5732"),

},

{

"text" : "00001",

"user_id" : ObjectId("563f7c0a8db7963420cd5732"),

}

]

}

My query looks as follows

db.getCollection('messages').find({

'_id': ObjectId("56479d9c8510369a4ecea3a9"),

'comments.user_id': {$in : [

ObjectId("563e3337e2bf6c431b297d41"),

ObjectId("563f7c0a8db7963420cd5732")

]}

})

It returns all comments. Please help to understand why it happens.

Expected Result

{

"_id" : ObjectId("56479d9c8510369a4ecea3a9"),

"comments" : [

{

"text" : "55555",

"user_id" : ObjectId("563e3337e2bf6c431b297d41"),

},

{

"text" : "00000",

"user_id" : ObjectId("563f7c0a8db7963420cd5732"),

},

{

"text" : "00001",

"user_id" : ObjectId("563f7c0a8db7963420cd5732"),

}

]

}

update query (hopelessness)

db.getCollection('messages').find(

{'_id': ObjectId("56479d9c8510369a4ecea3a9")},

{'comments.user_id': {$in: ["563f2db0e2bf6c431b297d45", "563e3337e2bf6c431b297d41"]}},

{'comments.user_id': {$elemMatch: {$in: ["563f2db0e2bf6c431b297d45", "563e3337e2bf6c431b297d41"]}}}

)

db.getCollection('messages').find(

{'_id': ObjectId("56479d9c8510369a4ecea3a9")},

{comments: {$elemMatch: {'user_id': {$in : [ObjectId("563f2db0e2bf6c431b297d45"), ObjectId("563f7c0a8db7963420cd5732")]}}}}

)

I return only 1 record, and I have all the records from these authors

网友答案:

As you've seen, the $ and $elemMatch projection operators only include the first matching element.

To include multiple, filtered array elements in your projection of the comment array, your can use aggregate with the $redact operator instead of find:

db.getCollection('test').aggregate([
    {$match: {
        '_id': ObjectId("56479d9c8510369a4ecea3a9"),
        'comments.user_id': {$in : [
            ObjectId("563e3337e2bf6c431b297d41"),
            ObjectId("563f7c0a8db7963420cd5732")
        ]},
    }},
    {$redact: {
        $cond: {
            if: {
                $or: [
                    {$eq: ['$user_id', ObjectId("563e3337e2bf6c431b297d41")]},
                    {$eq: ['$user_id', ObjectId("563f7c0a8db7963420cd5732")]},
                    {$not: '$user_id'}
                ]
            },
            then: '$$DESCEND',
            else: '$$PRUNE'
        }
    }}
])

$redact iterates over each doc like a tree, keeping or trimming the fields of each doc as it's $cond expression dictates.

It gets a bit tricky to wrap your head around $redact, but it's basically saying that if the level's user_id field matches either of the two ObjectIds in your $in, or it's not present (i.e. as it is in the top level of the doc), include the data, otherwise remove it.

相关阅读:
Top