2
votes

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.

2
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?Vince Bowdren
What's your MongoDB server version?chridam
I have version MongoDB Version 3.4Josch

2 Answers

1
votes

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"]
    }
}]);
0
votes

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.