0

I am new in mongodb/

I need to get a document using mongo shell if its first emdedded document, which matches condition A, mathes codition B.

For examle, I have only one doc in collection:

{
"_id": "life",
"docs": [{
    "_id": "sex",
    "p": 2,
    "c": 2
}, {
    "_id": "drugs",
    "p": 1,
    "c ": 2
}, {
    "_id": "rock'n'roll",
    "p": 1,
    "c": 4
}]

}

If A condition is p=1, then

---- if B condition is с=2, I get "life" (embedded doc "drugs" matches the condtitions)

---- if B condition is c=4, I get empty result.

If A condition is c=2, then

---- if B condition is p=2, I get "life" (emdedded doc "sex" matches the conditions)

---- if B condition is p=1, I get empty result (

Tnx

Added:

The task is to filter the collection and get whole documents according to condtitions applied to emdedded document in specific way, so the question is not a duplicate I think.

But with help of that question I got the solution:

db.test.aggregation([
{
    $project:
    {
        docs:
        {
            $filter:
            {
                input:"$docs",
                as:"doc",
                cond:{$eq:['$$doc.p',1]}
            }
        }
    }
},
{
    $project:
    {
        docs:{$slice:["$docs",1]}
    }
},
{
    $project:
    {
        docs:
        {
            $filter:
            {
                input:"$docs",
                as:"doc",
                cond:{$eq:['$$doc.c',2]}
            }
        }
    }
},
{
    $project:
    {
        "docs":1, 
        "n":{"$size":"$docs"}
    }
},
{
    $match:{"n":{$gt:0}}
}])

Another solution:

db.c.aggregate([
{
    $project:
    {
        docs:
        {
            $filter:
            {
                input:"$docs",
                as:"doc",
                cond:{$eq:['$$doc.p',1]}
            }
        }
    },
},
{
    "$unwind":"$docs"
},
{ 
    "$group": 
    {
        "_id": "$_id",
        "doc": { "$first": "$docs" }
    }
},
{
    $match:{"doc.c":2}
}])
  • Just to note that the format for Stack Overflow is **"one question"**. If you feel tempted to write *"Also this..."* or *"Other variant(sic)"* then what you actually need to be doing is post "multiple" questions with the [Ask a New Question](https://stackoverflow.com/questions/ask) button. New accounts may experience a delay in doing this, but "them's the rules". You will however see that "first" is really quite simple and "last" would require some manipulation. Yet the basic process is explained in the answers already existing. – Neil Lunn Nov 16 '17 at 03:12
  • Thanks for your recommendation. By the way - question is not a duplicate. Either I explained poorly, or you misunderstood it – Алексей Шевченко Nov 16 '17 at 08:50
  • 1
    Think I understood it pretty well. First case is a plain `$elemMatch` and a positional projection. That's in the answers. Second case would use `$filter` with aggregate. Also in the answers. Guess you should read them and learn them. Feel free to [Ask a new Question](https://stackoverflow.com/questions/ask) showing actual application of one of the solutions if you still don't understand how to apply it. But the answers are there, which is why someone provided you with an answer here that's a direct rip from the answers there. Question gets asked a lot. – Neil Lunn Nov 16 '17 at 08:53
  • A similar question really helped, thank you. But the solution is not very elegant, maybe there is a better way? – Алексей Шевченко Nov 16 '17 at 11:30
  • Nope. That's the way. Learn from it. – Neil Lunn Nov 16 '17 at 11:34
  • Oh okay I see what you mean now. Anyway, good you found your solution, but I dont understand in your first solution why you do the `docs:{$slice:["$docs",1]}`. This way you remove documents that could fit the second condition. But if it works for you, it works for me – Alex P. Nov 16 '17 at 18:16

1 Answers1

0

So you mean you want to extract the values from an array and present it in a "flat" structure?

If this is what you mean you'd need something like this:

db.collectionName.aggregate([
{
  $match: {
    "docs.p": 1,
    "docs.c": 2
  }
},
{ $unwind: "$docs"},
{
  $match: {
    "docs.p": 1,
    "docs.c": 2
  }
}
])

So basically I would $match twice. In the first one you match all documents that contain these values. Then you $unwind to "flatten" your array structure. And last step I'd use $match again to only take the set that you actually need.

Alex P.
  • 2,651
  • 3
  • 15
  • 25