I have below code
Users.aggregate([{
$match: dt //working fine
}, {
$lookup: { //not working returns []
from: "points",
localField: "_id", //field in user collection
foreignField: "user", //field in points collection
as: "points"
}
}, {
$lookup: {
from: "orders", //not working returns []
localField: "_id", //field in user collection
foreignField: "user_id", //field in orders collection
as: "orders"
}
}], (err, data) => {
return res.status(200).json({
message: "success",
data: data ? data : [],
status: true
})
})
there are 16 records in points and orders collections but it always returns empty array.
I am unable to find the issue. Please help.
in points and orders collections user id is added.
Is there any other way to find data in these collections with user table? I didn't find anything except populate to do so.
MongoDB server version: 3.6.9
"mongoose": "^5.3.4"
Related
I need an endpoint that returns the user's info, amount of post's they've submitted and the sum of all likes their post's have received.
I'm using MongoDB for my database along with Mongoose for my Node server.
These are my models:
var userSchema = new Schema({
'username' : String,
'email' : String,
'password' : String
});
var photoSchema = new Schema({
'text' : String,
'path' : String,
'timestamp': Number,
'postedBy' : {type: Schema.Types.ObjectId, ref: "user"}
});
var likeSchema = new Schema({
'timestamp' : Number,
'photo' : {type: Schema.Types.ObjectId, ref: 'photo'},
'user' : {type: Schema.Types.ObjectId, ref: 'user'}
});
I've already accomplished this by using Javascript and multiple queries:
// Get user
let user = await userModel.findOne({_id: mongoose.Types.ObjectId(id)})
.select("_id, username")
.lean()
.exec();
// Get all users photos
let photos = await photoModel.find({postedBy: mongoose.Types.ObjectId(user._id)})
.select("_id")
.exec();
// Sum likes for all photos
let likes = 0;
for (let i = 0; i < photos.length; i++)
likes += (await likeModel.find({photo: photos[i]._id}).exec()).length;
But I want to try it with the aggregate pipeline. This is my failed attempt:
let user = await userModel.aggregate(
[
{
$match: {_id: mongoose.Types.ObjectId(id)}
},
{
$unset: ["email", "password", "__v"]
},
{
$lookup: {
from: "photos",
localField: "_id",
foreignField: "postedBy",
as: "photos"
}
},
{
$lookup: {
from: "likes",
localField: "photos._id",
foreignField: "photo",
as: "likes"
}
},
{
$unwind: "$photos"
},
{
$group: {
_id: "$_id",
username: {$first: "$username"},
postCount: {$sum: 1},
likeCount: {$sum: 1}
}
}
]);
I don't know how to get the number of likes for each photo post. Is it possible in a single aggregation pipeline?
Would it make more sense to build it with multiple aggegation pipelines or even just with multiple queries?
If you use $size to get the size of each array, you won't need the unwind or group stages.
[
{$match: {_id: mongoose.Types.ObjectId(id)}},
{$lookup: {
from: "photos",
localField: "_id",
foreignField: "postedBy",
as: "photos"
}},
{$lookup: {
from: "likes",
localField: "photos._id",
foreignField: "photo",
as: "likes"
}},
{$project: {
likeCount: {$size:"$likes"},
postCount: {$size:"$photos"}
}}
]
Playground
Alternately, since the only field you are getting from the user collection is _id, which you already have, you can skip the match and lookup, and just aggregate the photos collection directly:
photoModel.aggregate([
{$match: {postedBy: mongoose.Types.ObjectId(id)}},
{$lookup: {
from: "likes",
localField: "_id",
foreignField: "photo",
as: "likes"
}},
{$group: {
_id: "$postedBy",
likeCount: {$sum:{$size:"$likes"}},
postCount: {$sum:1}
}}
])
Playground
I have two collection ProductInventories and ProductDetails. The ProductInventories has a prodId that is connected to the id of ProductDetails. I want to get all the product that is Active only.
const products = await ProductInventory.find().populate({
path: 'prodId',
model: ProductDetails,
populate: { path: 'category', select: 'category_name', model: Category },
});
I'm using this code to get all the data, but I want to have a condition that it will return the item that is isActicve = True. I'm just using a filter function to filter all active data. I want to learn how to use aggregate and match.
You can first use the lookup aggregation from ProductInventories to ProductDetails based on the prodId field, then do a match aggregation with field isActive = true
db.ProductInventories.aggregate([
{
$lookup: {
from: "ProductDetails",
localField: "prodId",
foreignField: "id",
as: "product_details"
},
$match: {
isActive: true
}
}
])
Refer https://docs.mongodb.com/manual/reference/operator/aggregation/match/
https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/
This is my updated code and its working now. Thanks also for helping #smitha t m.
const products = await ProductInventory.aggregate([
{
$lookup: {
from: 'productdetails',
localField: 'prodId',
foreignField: '_id',
as: 'product',
},
},
{
$unwind: '$product',
},
{
$match: { 'product.isActive': true },
},
]);
im new on mongo and want to get data for a chat, let me explain.
i have a colection of messages:
_id:id
viewed:boolean
created_at:date
text:String
receiver:ObjectId
emitter:ObjectId
i want all the list of messager for a certain emitter and receiver order by the date (like a normal chat)
i have tryed aggregation like this:
db.messages.aggregate(
[
{
$lookup: {
from: "users",
localField: "emitter", // field in the orders collection
foreignField: "_id", // field in the items collection
as: "fromItems"
}
},
{
$match: {
'emitter':ObjectId("5c8917b4ef9ebf2e608c68dc")
}
}
,
{
$addFields: {
ids: { _id: "$_id" } ,
created: { created_at: "$created_at" }
}
},
{
$group:
{
_id: { tot:["$emitter", "$receiver"] },
text: { $addToSet:"$text"},
}
},
{
$sort: {created_at: 1}
}
]
)
But this gives me an array of messages only of a certain emitter and dont give me the date or the viewed data.
Im really new on mongo and node so if someone can help me with a explain will be great.
Thanks for reading and sory for the bad english
You must include date or the viewed data in $group stage.
Try with this.
{
$group:
{
_id: { tot:["$emitter", "$receiver"] },
text: { $addToSet:{text:"$text",created:"$created.created_at"}},
created_at:{$last:"$created.created_at"}
}
},
Why there is ids and need for tot fields and created as a object ??
I am trying to use $match to find items with a specific _id in a double embedded document.
I have a document called users which contains information such as name, email, and it also contains an embedded document which has the business this user is with.
I also have a document called businesses, which contains an embedded document which has the building that this business is in.
I also have a document called building.
I am trying to have a mongo query which returns all of the users with a business at a certain building ID.
I have an aggregate function which uses $lookup to match the users to the building they are in. and this does work. However now I am trying to use $match to only return the documents with a specific building id.
Here is an example of my user, business and building documents:
_id: 5ca487c0eeedbe8ab59d7a7a
name: "John Smith"
email: "jsmith9#gmail.com"
business: Object
_id: 5ca48481eeedbe8ab59d7a38
name: "Visitors"
_id: 5ca48481eeedbe8ab59d7a38
name: "Visitors"
building: Object
_id: 5ca48481eeedbe8ab59d7a36
name: "Building1"
_id: 5ca48481eeedbe8ab59d7a36
name: "Building1"
When I return the aggregated query it returns documents in the following format:
{
"_id": "5ca487c0eeedbe8ab59d7a7a",
"name": "John Smith",
"email": "jsmith9#gmail.com",
"business": {
"_id": "5ca48481eeedbe8ab59d7a38",
"name": "Visitors"
},
"__v": 0,
"user_building": {
"_id": "5ca48481eeedbe8ab59d7a38",
"name": "Visitors",
"building": {
"_id": "5ca48481eeedbe8ab59d7a36",
"name": "Building1"
},
"__v": 0
}
},
However when I add the match in, it returns []. What am i doing wrong here?
router.get("/:id", async (req, res) => {
const users_buildings = await User.aggregate([
{
$lookup: {
from: "businesses",
localField: "business._id",
foreignField: "_id",
as: "user_building"
}
},
{ $unwind: "$user_building" },
{
$match: {
"user_building.building": { _id: req.params.id }
}
}
]);
You need to match _id inside the building object. Try with this
{
$match: {
"user_building.building._id": req.params.id
}
}
if not working
{
$match: {
"user_building.building._id": ObjectId(req.params.id)
}
}
op edit: I imported ObjectId with:
var ObjectId = require('mongodb').ObjectID;
and used the second solution and it worked correctly.
I am attempting to use a query that will search my DB for characters who currently have a specific item and then console.log the characters.
The items are actually referenced models.
Ex:
Character
var characterSchema = new mongoose.Schema({
name: String,
items: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Item"
}
]
});
module.exports = mongoose.model("Character", characterSchema);
Item
var itemSchema = new mongoose.Schema({
name: String,
owners: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Character"
}
]
});
module.exports = mongoose.model("Item", itemSchema);
So far I have tried this:
Character.find({"items": "Greatsword"}, function(err, characters) {
if(err) {
console.log(err);
} else {
console.log(characters);
}
});
I figured this wouldn't work since "items" within the character is just an array referencing items, but I am not sure where to go from here..
You need to use aggregation to join the referenced documents and then search on them.
Character.aggregate([
// deconstruct array field (as you can't join on an array field)
{
$unwind: '$items'
},
// join items collection
{
$lookup: {
from: 'items', // collection to join
localField: 'items', // field from the input documents
foreignField: '_id', // field from the documents of the "from" collection
as: 'item' // output array field
}
},
// deconstruct array field
{
$unwind: '$item'
},
// filter
{
$match: {
'item.name': 'Greatsword'
}
},
// rebuild
{
_id: '_id',
name: { $first: '$name' },
items: { $push: '$item' }
}
], function (err, characters) {
});
For more information, read
$unwind array
$lookup with an Array
$match