how to populate to an array inside another shema using mongoose - node.js

There are two collections. Catalog collection and order Collection. Catalog collection contains seller id , and compelete product details.
Order collection contains order details like ids of ordered products and buyer details.
My problem is how can I get the complete product details when fetching an order .
order collection is given below
this is the order collection. It contains product Id. I need to get the details of products when fetching orders. Problem is that there is no seperate collection for products. Products are present inside 'catalog' collection
Catalog Collection is given below:
this is the catalog collection.It contains an array of products. so the products are present there
I need to get product details when fetching order details

const order = await Order.find().populate("products");

I am not sure I understand your question entirely but hope should help you:
Plan
unwind products
lookup product with _id of product in catalog.products
unwind the lookup result because result of lookup stage is an array
group this back together
Implementation
const result = await orderModel.aggregate()
.unwind('products')
.lookup({
from: 'catalogs',
let: { product_id: '$products' }, // in order
pipeline: [
{
$unwind: '$products' // $expr cannot digest arrays so we need to unwind which hurts performance...
},
{
$match: { $expr: { $eq: ['$products._id', '$$product_id'] } }
},
{
$project: { _id: 0, products: 1 } // only care products in catelog
}
],
as: 'productDetail'
})
.unwind('productDetail')
.group({
_id: '$_id',
seller: { '$first': '$seller' },
buyer: { '$first': '$buyer' },
products: {
$push: '$productDetail.products'
}
});
However, I don't recommend doing it this way as it can be resource intensive... I suggest you change your database design eg separating product into a separate collection for easier querying.

Related

MongoDB: How to perform a second match using the results (an array of ObjectIds) of the previous match in aggregation pipeline

I have a MongoDB collection called users with documents that look like:
{
_id: ObjectId('123'),
username: "abc",
avatar: "avatar/long-unique-random-string.jpg",
connections: [ObjectId('abc'), ObjectId('xyz'), ObjectId('lmn'), ObjectId('efg')]
}
This document belongs to the users collection.
What I want to do:
First, find one document from the users' collection that matches _id -> '123'.
Project the connections field received from step 1, which is an array of ObjectIds of other users within the same collection.
Find all documents of users from the array field projected in step 2.
Project and return an array of only the username and avatar of all those users from step 3.
While I know that I can do this in two separate queries. First using findOne which returns the friends array. Then, using find with the results of findOne to get all the corresponding usernames and avatars.
But, I would like to do this in one single query, using the aggregation pipeline.
What I want to know, is it even possible to do this in one query using aggregation?
If so, what would the query look like?
What, I currently have:
await usersCollection
.aggregate([
{ $match: { _id: new ObjectId(userId) } },
{ $project: { ids: "$connections" } },
{ $match: { _id: { $in: "ids" } } },
{
$project: {
username: "$username",
avatar: { $ifNull: ["$avatar", "$$REMOVE"] },
},
},
])
.toArray()
I know this is wrong because each aggregation stage receives the results from the previous stage. So, the second match cannot query on the entire users' collection, as far as I know.
I'm using MongoDB drivers for nodeJS. And I would like to avoid $lookup for possible solutions.

Nestjs- How to sum up all the number values of a particular collection of mongodb

I am working on nestjs and I have a collection where 5 documents are saved and each document has a property name 'price'. I want to fetch price property from every document and need to sum up all and shows that sum as an output. How do I achieve this?
Use aggregate
.aggregate([
{
$group: {
_id: '',
sumPrices: { $sum: '$price' }
}
])

how can i agregate datas from different collections in mongodb using nodejs?

I am using mongoDB and nodejs
I have 4 collections
collection 1 is teacher with fields teacher_id, teacher_name
collection 2 is subject with fieldssubject _id, subject_name
collection 3 is book with fields book_id, book_name
collection is student which have fields -- _id, student_name, teacher_id, subject_id, book_id
how can I fetch ids from 1, 2, 3 collections simultaneously and insert to corresponding id in collection
I have tried some which always ask for a matching field... is ther any function which returns data from collection even though no match field?
can someone please help
Well, in that case, you need to fetch all the documents from those collections. That will be a bit costly aggregation but I'm adding here in code:
Firstly, I'm grouping on null, to avoid to attach lookup value to every single document in teacher collection.
db.teacher.aggregate([
{
$group:{
"_id":null,
"root":{
$push:"$$ROOT"
}
}
},
{
$lookup:
{
from:"subject",
pipeline: [],
as: "subjectLookup"
}
},
{
$lookup:
{
from:"book",
pipeline: [],
as: "bookLookup"
}
},
{
$lookup:
{
from:"student",
pipeline: [],
as: "studentLookup"
}
}
]).pretty()
These lookups will give the array which contains all the documents from respective collections, you can limit the documents by adding $match stage in the pipeline of lookup stage.
Hope this will solve your problem :)

mongoose find x docs for specific filed

I have a collection of videos, each video doc has user_id.
I need to get the last 5 videos of each user.
don't know where to start... tried grouping without any success.
any help would be appreciated.
give this pipeline a try:
db.collection.aggregate([
{
$sort: { _id: -1 }
},
{
$group: {
_id: "$user_id",
videos: { $push: "$$ROOT" }
}
},
{
$project: {
_id: 0,
user_id: "$_id",
videos: { $slice: ["$videos", 5] }
}
}
])
https://mongoplayground.net/p/6NOOJDnfIhk
explanation:
with the first stage, you $sort the whole collection in descending order on the _id field. sorting initially is necessary because there's no operator in mongo that can sort the grouped items afaik.
then you group by user_id field. we $push all documents of each group to a new field called videos. $$ROOT variable gives you access to all docs of each group.
the final $project stage uses $slice to pick only the top 5 items in the videos array from the previous stage and sets it on a field of the same name.

Aggregate and flatten an array field in MongoDB

I have a Schema:
var ProjectSchema = new Schema({
name: {
type: String,
default: ''
},
topics: [{
type: Schema.ObjectId,
ref: 'Topic'
}],
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
What I want to do is get an array with all topics from all projects. I cannot query Topic directly and get a full list because some topics are unassigned and they do not hold a reference back to a Project (for reasons of avoiding two way references). So I need to query Project and aggregate some how. I am doing something like:
Project.aggregate([{$project:{topics:1}}]);
But this is giving me an array of Project objects with the topics field. What I want is an array with topic objects.
How can I do this?
When dealing with arrays you typically want to use $unwind on the array members first and then $group to find the distinct entries:
Project.aggregate(
[
{ "$unwind": "$topics" },
{ "$group": { "_id": "$topics._id" } }
],
function(err,docs) {
}
)
But for this case, it is probably simplier to just use .distinct() which will do the same as above, but with just an array of results rather than documents:
Project.distinct("topics._id",function(err,topics) {
});
But wait there a minute because I know what you are really asking here. It's not the _id values you want but your Topic data has a property on it like "name".
Since your items are "referenced" and in another collection, you cannot do an aggregation pipeline or .distinct() operation on the property of a document in another collection. Put basically "MongoDB does not perform Joins" and mongoose .populate() is not a join, just something that "emulates" that with additional query(ies).
But you can of course just find the "distinct" values from "Project" and then fetch the information from "Topic". As in:
Project.distinct("topics._id",function(err,topics) {
Topic.find({ "_id": { "$in": topics } },function(err,topics) {
});
});
Which is handy because the .distinct() function already returned an array suitable for use with $in.

Resources