2

how i can find the document with $match on position 3 (only last item in array "ndr"). It is necessary that the aggreation search only in the last array-item of ndr.

 {
    "_id" : ObjectId("58bd5c63a3d24b4a2e4cde03"),
    "name" : "great document",
    "country" : "us_us",
    "cdate" : ISODate("2017-03-06T12:56:03.405Z"),
    "nodes" : [ 
        {
            "node" : 3244343,
            "name" : "Best Node ever",
            "ndr" : [ 
                {
                    "position" : 7,
                    "cdate" : ISODate("2017-03-06T10:55:20.000Z")
                }, 
                {
                    "position" : 3,
                    "cdate" : ISODate("2017-03-06T10:55:20.000Z")
                }
            ]
        }
    ],
}

I need this result after aggregation

{
    "name" : "great document",
    "country" : "us_us",
    "cdate" : ISODate("2017-03-06T12:56:03.405Z"),
    "nodes" : [ 
        {
            "node" : 3244343,
            "name" : "Best Node ever",
            "ndr" : [ 
                {
                    "position" : 3,
                    "cdate" : ISODate("2017-03-06T10:55:20.000Z")
                }
            ]
        }
    ]
}

I hope anyone can help me.

3
  • Hi Josch, and welcome to Stack Overflow. Can you edit your question to show what you've tried so far, and what's going wrong with it? Commented Mar 6, 2017 at 15:34
  • What's your MongoDB server version? Commented Mar 6, 2017 at 16:08
  • I have version MongoDB Version 3.4 Commented Mar 6, 2017 at 16:32

2 Answers 2

1

You can try below aggregation with Mongo 3.4 version.

The below query finds the last item (-1) using $arrayElemAt operator in the ndr array and stores the variable in last using $let operator for each nodes and compare the last variable position value using $$ notation to 3 and wraps the nbr element within array [] if entry found and else returns empty array.

$map operator to reach nbr array inside the nodes array and project the updated nbr array while mapping the rest of nodes fields.

$addFields stage will overwrite the existing nodes with new nodes while keeping the all the other fields.

db.collection.aggregate([{
    $addFields: {
        nodes: {
            $map: {
                input: "$nodes",
                as: "value",
                in: {
                    node: "$$value.node",
                    name: "$$value.name",
                    ndr: {
                        $let: {
                            vars: {
                                last: {
                                    $arrayElemAt: ["$$value.ndr", -1]
                                }
                            },
                            in: {
                                $cond: [{
                                        $eq: ["$$last.position", 3]
                                    },
                                    ["$$last"],
                                    []
                                ]
                            }
                        }
                    }
                }
            }
        }
    }
}]);

Update:

You can try $redact which will keep the whole document if it finds the matching position from with the given filter.

$map to project the true, false values based on the filter for each of the nodes nbr position value and $anyElementTrue will inspect the previous boolean values for each doc and return a true or false value and $redact will use the booelan value from above comparison; true value to keep and false value to remove the document.

db.collection.aggregate([{
    $redact: {
        $cond: [{
            $anyElementTrue: {
                $map: {
                    input: "$nodes",
                    as: "value",
                    in: {
                        $let: {
                            vars: {
                                last: {
                                    $arrayElemAt: ["$$value.ndr", -1]
                                }
                            },
                            in: {
                                $cond: [{
                                        $eq: ["$$last.position", 3]
                                    },
                                    true,
                                    false
                                ]
                            }
                        }
                    }
                }
            }
        }, "$$KEEP", "$$PRUNE"]
    }
}]);
Sign up to request clarification or add additional context in comments.

2 Comments

Thx! how i can remove the whole document if not nodes.node.position matched with the given filter? At the moment the document would be in the pipeline.
Added a separate solution which will remove the document.
0

you will need to unwind both nested arrays.

 db.<collection>.aggregate([
      { $unwind: '$nodes' },
      { $unwind: '$nodes.ndr'},
      { $group: {'_id':{'_id':'$_id', 'nodeID', '$nodes.node' },
                 'name':{'$last':'$name'},
                 'country':{'$last':'$country'},
                 'cdate':{'$last':'$cdate'},
                 'nodes':{'$last':'$nodes'}
                }
      },
      { $match : { nodes.ndr.position: 3 } }
  ]);

From here you can reassemble the aggregate results with a $group on the and do a projection. I'm not sure what your ultimate end result should be.

8 Comments

and how i can search only in the last array item?
Sorry I thought you were looking for a specific value "position:3" if you need to find the last you will need to use a $group and $last docs.mongodb.com/manual/reference/operator/aggregation/last
Hi, i need to find the document which hast position 3 in the last array item
Ok I modified the answer to include a $group and $last, this groups by _id and nodes.node this should let you search just the last nodes.ndr off each nested set.
uhhh this works nearly perfect, but what when i have more than one node? at the moment their push the document twice with the different node matches. how i can group by document ID and push the second node into the node array?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.