I'm completely new to Mongoose.
I have this Schema:
const houseSchema = new mongoose.Schema({
_id: String,
housename: {type: String,unique: true},
adress: String,
people: [ {
name: String,
age: Number
} ],
});
And document:
let house = new House({
_id: 20,
housename: "white",
adress: "St1",
people:[
{name: "Jon", age: 23},
{name: "Ann", age:50},
{name: "Pat", age:20},
{name: "Helen", age:15}]
});
I want to find this document by id, and filter people array by age, then return full document without filtered objects. So expected output would be: (for age>21 ):
{
_id: 20,
housename: "white",
adress: "St1",
people:[
{_id:"65976faeaa644d02c4090826", name: "Jon", age: 23},
{_id:"65976faeaa644d02c4090827", name: "Ann", age:50}
]
}
My solution to this problem, after hours of trying is:
app.get("/api/test", (req, res) => {
var searchID = "20";
var minAge = 21;
House.aggregate([{$match: {_id: searchID}}])
.unwind("people").match({'people.age': {$gt: minAge}})
.group({
_id: "$_id",
people: {$push: "$people"}
})
.exec((err,data)=>{
res.json(data);
});
});
So first I match id, then rewind people array - so I can filter it out, and then I try to rejoin to the first form. However after grouping I lose housename and adress fields. Here is output:
{"_id":"20",
"people":[
{"_id":"65976faeaa644d02c4090826","name":"Jon","age":23},{"_id":"65976faeaa644d02c4090827","name":"Ann","age":50}]}
I have no idea how to keep housename and adress fields at the output. I've tried adding.projection({housename:1 ,adress:1 ... }); ,but it did nothing, I think those keys don't exist in pipe after group(). I also have been thinking about saving those values to vars after match() and then adding them at the end, but I don't know how to access them in the middle of the chain.
You can use a simple $filter
to filter the people
objects that match your filter condition like so:
app.get("/api/test", async (req, res) => { //< Mark as async
var searchID = "20";
var minAge = 21;
try{
const data = await House.aggregate([
{
$match: {
"_id": searchID
}
},
{
"$addFields": {
people: {
$filter: {
input: "$people",
as: "p",
cond: {
$gt: [
"$$p.age",
minAge
]
}
}
}
}
}
]);
res.json(data);
} catch(err){
console.log(err);
res.json({message: 'Error on server'});
}
});
See HERE for a working example.
The use of $addFields
is important here because:
Adds new fields to documents.
$addFields
outputs documents that contain all existing fields from the input documents and newly added fields. The$addFields
stage is equivalent to a$project
stage that explicitly specifies all existing fields in the input documents and adds the new fields.
which I think was where you were having trouble with the $project
. All I have done is use $addFields
to overwrite the existing people
property with the new filtered version.