Here is the collection.
{{
"id" : "123",
"likes" : [ {
"member" : "3041"
},
{
"member" : "3141"
}]
},
{
"id" : "124",
"likes" : [ {
"member" : "3241"
},
{
"member" : "3241"
},
{
"member" : "3341"
}]
}}
How to retrieve the count of number of objects of likes key for each document?
In this format:
[{
"id" : "123",
"likesCount" : 2
},
{
"id" : "124",
"likesCount" : 3
}]
This should do it for you:
db.collection.aggregate([
{
$project: {
_id: 0,
id: 1,
likesCount: {
$size: "$likes"
}
}
}
])
You are using an aggregation and in the $project part of it use $size to get the length of the array.
You can see it working here
I think you have to map the collection to transform the objects within it into the shape you want.
In this case we get the object and extract the id and instead of all the likes we just get the length of them.
let newCollection = collection.map(obj => ({
id: obj.id,
likesCount: obj.likes.length
}));
I have a basic document that I am performing an aggregate on to search for the combination of a first and and last name combined:
My document:
{
_id: dmw9294r94
firstName: "John",
lastName: "Smith",
friends: [28enw93hr, 29nwgkn38]
}
My aggregate pipline:
User.aggregate([
{
$project: {
"name" : { $concat : [ "$firstName", " ", "$lastName" ] },
"firstName": "$firstName",
"lastName": "$lastName",
"username": "$username",
"friends": "$friends"}
},{
$match: {
"name": {$regex: new RegExp(req.query.query, "i")
}
}}]).exec(function(err, results){
res.json(results);
}
);
How can I add to this to check if a specific user ID exists within that users friends array?
I tried the following, although had no luck:
$match: {
"name": {$regex: new RegExp(req.query.query, "i"),
"friends" {$eq: myVarHere}
}
As #profesor79 said, you just have to $match for the userid you are interested in:
{$match: {"friends": "user_id_to_find")}
Unless I don't understand the question, it seems to me that $regex is not needed here.
My application has "Posts" which will have stories
Now I want to select one post and only one of its stories from multiple stories in the story array
var db = req.db;
var mongo = require('mongodb');
var collection = db.get('clnPost');
var userId = req.body.userId;
var postId = req.body.postId;
var storyId = req.body.storyId;
var ObjectID = mongo.ObjectID;
var mongo = require('mongodb');
var ObjectID = mongo.ObjectID;
collection.find({ _id: ObjectId(postId), "Stories._id": ObjectID(req.body.storyId)}, function (e, docs) {
//some processing on the docs
}
I want this to return the post along with only story which has the story Id that is sent through the request, but its returning all the stories in the stories array
Here I am getting all the stories for that post, where as i need only the story with Id that matches req.body.storyId
I also tried using $elemMatch after checking this question but still got the same results
collection.find({ _id: postId}, {Stories: { $elemMatch: { _id: req.body.storyId } } }, function (e, docs) {
I also tried
collection.find({ "Stories._id":ObjectID(storyId)}, {"Stories.$":1,Stories: {$elemMatch: { "Stories._id" :ObjectID(storyId) }} } , function (e, docs) {
The structure of the post document is as follows
{
"_id": {
"$oid": "55a7847ee4b07e03cc6acd21"
},
"Stories": [
{
"userId": "743439369097985",
"story": "some story goes on here",
"_id": {
"$oid": "55c0b9367d1619a81daaefa0"
},
"Views": 29,
"Likes": []
},
{
"userId": "743439369097985",
"story": "some story goes on here",
"_id": {
"$oid": "55c0bbd97abf0b941aafa169"
},
"Views": 23,
"Likes": []
}
]
}
I also tried using $unwind
var test= collection.aggregate( //where collection =db.get('clnPost')
{ $match: { "Stories._id": ObjectID(storyId) } },
{ $project : { Stories : 1 } },
{ $unwind : "$Stories" }
);
Update 1: i am using monk and hence aggregate is not working
I can not copy your example document, so I used similar one:
{
"_id" : "55a7847ee4b07e03cc6acd21",
"Stories" : [{
"userId" : "743439369097985",
"story" : "some story goes on here",
"_id" : "55c0b9367d1619a81daaefa0",
"Views" : 29,
"Likes" : [ ]
}, {
"userId" : "743439369097985",
"story" : "some story goes on here",
"_id" : "55c0bbd97abf0b941aafa169",
"Views" : 23,
"Likes" : [ ]
}]
}
To get what you want, you need to use dollar projection operator in a way I used it here.
db.collection.find({
"Stories._id": "55c0bbd97abf0b941aafa169"
}, {
"Stories.$": 1
}).pretty()
This is my appointment collection:
{ _id: ObjectId("518ee0bc9be1909012000002"), date: ISODate("2013-05-13T22:00:00Z"), patient:ObjectId("518ee0bc9be1909012000002") }
{ _id: ObjectId("518ee0bc9be1909012000002"), date: ISODate("2013-05-13T22:00:00Z"), patient:ObjectId("518ee0bc9be1909012000002") }
{ _id: ObjectId("518ee0bc9be1909012000002"), date: ISODate("2013-05-13T22:00:00Z"), patient:ObjectId("518ee0bc9be1909012000002") }
I used aggregate to get the following result
{date: ISODate("2013-05-13T22:00:00Z"),
patients:[ObjectId("518ee0bc9be1909012000002"),ObjectId("518ee0bc9be1909012000002"),ObjectId("518ee0bc9be1909012000002")] }
like this:
Appointments.aggregate([
{$group: {_id: '$date', patients: {$push: '$patient'}}},
{$project: {date: '$_id', patients: 1, _id: 0}}
], ...)
How can I populate the patient document
I trued this but it doesn't work ... Appointments.find({}).populate("patient").aggregate....
In other words, can i use populate and aggregate at the same statement
any help please
With the latest version of mongoose (mongoose >= 3.6), you can but it requires a second query, and using populate differently. After your aggregation, do this:
Patients.populate(result, {path: "patient"}, callback);
See more at the Mongoose API and the Mongoose docs.
Edit: Looks like there's a new way to do it in the latest Mongoose API (see the above answer here: https://stackoverflow.com/a/23142503/293492)
Old answer below
You can use $lookup which is similar to populate.
In an unrelated example, I use $match to query for records and $lookup to populate a foreign model as a sub-property of these records:
Invite.aggregate(
{ $match: {interview: req.params.interview}},
{ $lookup: {from: 'users', localField: 'email', foreignField: 'email', as: 'user'} }
).exec( function (err, invites) {
if (err) {
next(err);
}
res.json(invites);
}
);
You have to do it in two, not in one statement.
In async await scenario, make sure await until populate.
const appointments = await Appointments.aggregate([...]);
await Patients.populate(appointments, {path: "patient"});
return appointments;
or (if you want to limit)
await Patients.populate(appointments, {path: "patient", select: {_id: 1, fullname: 1}});
You can do it in one query like this:
Appointments.aggregate([{
$group: {
_id: '$date',
patients: {
$push: '$patient'
}
}
},
{
$project: {
date: '$_id',
patients: 1,
_id: 0
}
},
{
$lookup: {
from: "patients",
localField: "patient",
foreignField: "_id",
as: "patient_doc"
}
}
])
populate basically uses $lookup under the hood.
in this case no need for a second query.
for more details check MongoDB aggregation lookup
Perform a Join with $lookup
A collection orders contains the following documents:
{ "_id" : 1, "item" : "abc", "price" : 12, "quantity" : 2 }
{ "_id" : 2, "item" : "jkl", "price" : 20, "quantity" : 1 }
{ "_id" : 3 }
Another collection inventory contains the following documents:
{ "_id" : 1, "sku" : "abc", description: "product 1", "instock" : 120 }
{ "_id" : 2, "sku" : "def", description: "product 2", "instock" : 80 }
{ "_id" : 3, "sku" : "ijk", description: "product 3", "instock" : 60 }
{ "_id" : 4, "sku" : "jkl", description: "product 4", "instock" : 70 }
{ "_id" : 5, "sku": null, description: "Incomplete" }
{ "_id" : 6 }
The following aggregation operation on the orders collection joins the documents from orders with the documents from the inventory collection using the fields item from the orders collection and the sku field from the inventory collection:
db.orders.aggregate([
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
}
])
The operation returns the following documents:
{
"_id" : 1,
"item" : "abc",
"price" : 12,
"quantity" : 2,
"inventory_docs" : [
{ "_id" : 1, "sku" : "abc", description: "product 1", "instock" : 120 }
]
}
{
"_id" : 2,
"item" : "jkl",
"price" : 20,
"quantity" : 1,
"inventory_docs" : [
{ "_id" : 4, "sku" : "jkl", "description" : "product 4", "instock" : 70 }
]
}
{
"_id" : 3,
"inventory_docs" : [
{ "_id" : 5, "sku" : null, "description" : "Incomplete" },
{ "_id" : 6 }
]
}
Reference $lookup
Short answer:
You can't.
Long answer:
In the Aggregation Framework, the returned fields are built by you, and you're able to "rename" document properties.
What this means is that Mongoose can't identify that your referenced documents will be available in the final result.
The best thing you can do in such a situation is populate the field you want after the query has returned. Yes, that would result in two DB calls, but it's what MongoDB allows us to do.
Somewhat like this:
Appointments.aggregate([ ... ], function( e, result ) {
if ( e ) return;
// You would probably have to do some loop here, as probably 'result' is array
Patients.findOneById( result.patient, function( e, patient ) {
if ( e ) return;
result.patient = patient;
});
});
domain.Farm.aggregate({
$match: {
"_id": mongoose.Types.ObjectId(farmId)
}
}, {
$unwind: "$SelfAssessment"
}, {
$match: {
"SelfAssessment.questionCategoryID": QuesCategoryId,
"SelfAssessment.questionID": quesId
}
},function(err, docs) {
var options = {
path: 'SelfAssessment.actions',
model: 'FarmAction'
};
domain.Farm.populate(docs, options, function (err, projects) {
callback(err,projects);
});
});
results i got action model populate
{ "error": false, "object": [
{
"_id": "57750cf6197f0b5137d259a0",
"createdAt": "2016-06-30T12:13:42.299Z",
"updatedAt": "2016-06-30T12:13:42.299Z",
"farmName": "abb",
"userId": "57750ce2197f0b5137d2599e",
"SelfAssessment": {
"questionName": "Aquatic biodiversity",
"questionID": "3kGTBsESPeYQoA8ae2Ocoy",
"questionCategoryID": "5aBe7kuYWIEoyqWCWcAEe0",
"question": "Waterways protected from nutrient runoff and stock access through fencing, buffer strips and off stream watering points",
"questionImage": "http://images.contentful.com/vkfoa0gk73be/4pGLv16BziYYSe2ageCK04/6a04041ab3344ec18fb2ecaba3bb26d5/thumb1_home.png",
"_id": "57750cf6197f0b5137d259a1",
"actions": [
{
"_id": "577512c6af3a87543932e675",
"createdAt": "2016-06-30T12:38:30.314Z",
"updatedAt": "2016-06-30T12:38:30.314Z",
"__v": 0,
"Evidence": [],
"setReminder": "",
"description": "sdsdsd",
"priority": "High",
"created": "2016-06-30T12:38:30.312Z",
"actionTitle": "sdsd"
}
],
"answer": "Relevant"
},
"locations": []
} ], "message": "", "extendedMessage": "", "timeStamp": 1467351827979 }
I see that there are many answers, I am new to mongoldb and I would like to share my answer too.
I am using aggregate function along with lookup to populate the patients.
To make it easy to read I have changed the names of the collections and fields.
Hope it's helpful.
DB:
db={
"appointmentCol": [
{
_id: ObjectId("518ee0bc9be1909012000001"),
date: ISODate("2013-05-13T22:00:00Z"),
patientId: ObjectId("518ee0bc9be1909012000001")
},
{
_id: ObjectId("518ee0bc9be1909012000002"),
date: ISODate("2013-05-13T22:00:00Z"),
patientId: ObjectId("518ee0bc9be1909012000002")
},
{
_id: ObjectId("518ee0bc9be1909012000003"),
date: ISODate("2013-05-13T22:00:00Z"),
patientId: ObjectId("518ee0bc9be1909012000003")
}
],
"patientCol": [
{
"_id": ObjectId("518ee0bc9be1909012000001"),
"name": "P1"
},
{
"_id": ObjectId("518ee0bc9be1909012000002"),
"name": "P2"
},
{
"_id": ObjectId("518ee0bc9be1909012000003"),
"name": "P3"
},
]
}
Aggregate Query using lookup:
db.appointmentCol.aggregate([
{
"$lookup": {
"from": "patientCol",
"localField": "patientId",
"foreignField": "_id",
"as": "patient"
}
}
])
Output:
[
{
"_id": ObjectId("518ee0bc9be1909012000001"),
"date": ISODate("2013-05-13T22:00:00Z"),
"patient": [
{
"_id": ObjectId("518ee0bc9be1909012000001"),
"name": "P1"
}
],
"patientId": ObjectId("518ee0bc9be1909012000001")
},
{
"_id": ObjectId("518ee0bc9be1909012000002"),
"date": ISODate("2013-05-13T22:00:00Z"),
"patient": [
{
"_id": ObjectId("518ee0bc9be1909012000002"),
"name": "P2"
}
],
"patientId": ObjectId("518ee0bc9be1909012000002")
},
{
"_id": ObjectId("518ee0bc9be1909012000003"),
"date": ISODate("2013-05-13T22:00:00Z"),
"patient": [
{
"_id": ObjectId("518ee0bc9be1909012000003"),
"name": "P3"
}
],
"patientId": ObjectId("518ee0bc9be1909012000003")
}
]
Playground:
mongoplayground.net
I used lookup instead, and it worked well. See the code snipped below.
Post.aggregate([
{
$group: {
// Each `_id` must be unique, so if there are multiple
// posts with the same category, MongoDB will increment `count`.
_id: '$category',
count: { $sum: 1 }
}
},
//from: is collection name in MongoDB, localField are primary and foreign keys in Model.
{$lookup: {from: 'categories', localField: '_id', foreignField:'_id', as: 'category'}}
]).then(categoryCount => {
console.log(categoryCount);
let json = [];
categoryCount.forEach(cat => {
console.log(json);
});
I have a MongoDB collection [Users] each with an Array of embedded documents [Photos].
I would like to be able to have a page which lists recent photos, for example - the last 10 photos uploaded by any user.
Users
[
{
_id: ObjectID
name: "Matthew"
Photos: [
{
_id: ObjectID
url: "http://www.example.com/photo1.jpg"
created: Date("2013-02-01")
},
{
_id: ObjectID
url: "http://www.example.com/photo3.jpg"
created: Date("2013-02-03")
}
]
},
{
_id: ObjectID
name: "Bob"
Photos: [
{
_id: ObjectID
url: "http://www.example.com/photo2.jpg"
created: Date("2013-02-02")
},
{
_id: ObjectID
url: "http://www.example.com/photo4.jpg"
created: Date("2013-02-04")
}
]
}
]
My first attempt at solving this was to find all Users where Photos is not null, and sort it by the created date:
User.find({images:{$ne:[]}}).sort({"images.created":-1})
Unfortunately, this doesn't work as I need it to. It sorts Users by the most recent images, but still returns all images, and there is no way to limit the number of images returned by the function.
I started looking into aggregate, and it seems like it might be the solution I'm looking for, but I'm having trouble finding out how to get it to work.
Ideally, the type of result I would like returned would be like this:
results: [
{
_id: ObjectID (from Photo)
name: "Bob"
url: "http://www.example.com/photo4.jpg"
created: Date("2013-02-04")
}
{
_id: ObjectID (from Photo)
name: "Matthew"
url: "http://www.example.com/photo3.jpg"
created: Date("2013-02-03")
}
]
Each result should be a Photo, and I should be able to limit the results to a specific amount, and skip results for paged viewing.
Please let me know if you need any more information, and thank you for your responses.
You need aggregation framework:
db.users.aggregate(
{ $project: {"Photos" : 1, _id: 0, name:1 }},
{ $unwind: "$Photos" },
{ $sort: {"Photos.created" : -1} },
{ $skip: 1 },
{ $limit: 2 }
)
And result would be like this:
{
"result" : [
{
"name" : "Matthew",
"Photos" : {
"_id" : 2,
"url" : "http://www.example.com/photo3.jpg",
"created" : "2013-02-03"
}
},
{
"name" : "Bob",
"Photos" : {
"_id" : 3,
"url" : "http://www.example.com/photo2.jpg",
"created" : "2013-02-02"
}
}
],
"ok" : 1
}
I think you want something like this:
db.Users.aggregate( [
{$unwind:"$Photos"},
{$sort: {"Photos.created":-1},
{$limit: 10}
] );