How to match multiple subdocuments in MongoDB?

Assuming that my book collection has the following data:

[
    {
        name: "Animal Farm",
        readers: [
            {
                name: "Johny"
            },
            {
                name: "Lisa"
            }
        ],
        likes: [
            {
                name: "Johny"
            }
        ]
    },

    {
        name: "1984",
        readers: [
            {
                name: "Fred"
            },
            {
                name: "Johny"
            },
            {
                name: "Johny",
                type: "bot"
            }
        ],
        likes: [
            {
                name: "Fred"
            }
        ]
    }
]

How to get all the readers and like this coincidence name "Johny", with an end result of something like this:

[
    {
        name: "Animal Farm",
        readers: [
            {
                name: "Johny"
            }
        ],
        likes: [
            {
                name: "Johny"
            }
        ]
    },

    {
        name: "1984",
        readers: [
            {
                name: "Johny"
            },
            {
                name: "Johny",
                type: "bot"
            }
        ],
        likes: []
    }
]

The following query is not possible:

db.books.find(
    { $or: [{ "readers.name": "Johny" }, { "likes.name": "Johny" }] },
    { name: 1, "readers.$": 1, "likes.$": 1 })

MongoDB complains about the following error: Cannot specify more than one positional array element per query (currently unsupported).

I tried to use the aggregation structure, but could not. So is this possible with MongoDB or do I need to run two queries to get the necessary results?

+4
source share
2 answers

As Sammaye has already pointed out, no more than one element of a positional array is currently indicated.

$elemMatch . $elemMatch , $elemMatch:

db.books.find(
    { $or: [{ "readers.name": "Johny" }, { "likes.name": "Johny" }] }, 
    { 
        readers : { $elemMatch : { name : "Johny" }}, 
        likes : { $elemMatch : { name : "Johny" }}
    }
);

Edit

Altong MongoDB , , , , . , , :

db.books.aggregate([
    // find only documents that have correct "name"
    { $match: { $or: [{ "readers.name": "Johny" }, { "likes.name": "Johny" }]}},
    // unwind the documents so we can push them to a array
    { $unwind: '$likes' },
    // do a group to conditionally push the values into the array
    { $group : { 
        _id : '$_id', 
        likes : { 
            $push : { 
                $cond : [
                    { $eq : ["$likes.name", "Johny"]}, 
                    "$likes", 
                    null
                ]
            }
        },
        readers : { $first : "$readers" }, 
        name : { $first : "$name" }
    }},
    // the process is repeated for the readers array
    { $unwind: '$readers' },
    { $group : { 
        _id : '$_id', 
        readers : { 
            $push : { 
                $cond : [
                    { $eq : ["$readers.name", "Johny"]}, 
                    "$readers", 
                    null
                ]
            }
        },
        likes : { $first : "$likes" }, 
        name : { $first : "$name" }
    }},
    // final step: remove the null values from the arrays
    { $project : {
        name : 1,
        readers : { $setDifference : [ "$readers", [null] ] },
        likes : { $setDifference : [ "$likes", [null] ] },
    }}
]);

, "" $push $cond $push. null. , setDifference.

, / , , , .

+5

@ChristianP :

db.books.aggregate(

    // So we don't have to random do this to docs we don't need to
    {$match: { $or: [{ "readers.name": "Johny" }, { "likes.name": "Johny" }] }},

    {$unwind: '$readers'},
    {$match: { "readers.name": "Johny" }},

    {$unwind: '$likes'},
    {$match: { "likes.name": "Johny" }},

    {$group: {_id: '$_id', likes: {$push: '$likes'}, readers: {$push: '$readers'}}}
)

- , , .

+3

All Articles