How to query documents with reference fields parameters in Mongoose? - node.js

I have the following schemas (Product, ProductCategory):
const productSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
productCategory: {
type: mongoose.Schema.Types.ObjectId,
ref: 'ProductCategory'
}
})
const productCategorySchema = new mongoose.Schema({
name: {
type: String,
required: true
}
})
What I would like to do is to query all the Product documents who has a certain Product.productCategory.name = ?
I read about population in mongodb but can't really know how to apply it here.

You could use aggregation function '$lookup'
db.productCategory.aggregate([{
$match: {
name: "{category_name}"
}
}, {
$lookup: {
from: 'product',
localField: '_id', // ProductCategory._id
foreignField: 'type', // Product.product_category_id
as: 'products'
}
}]);

Related

Using lookup in aggregate

I have 2 collections: Order, OrderItem. I'm trying to use lookup to get the OrderItem, and then get the inside them. I followed the docs, and looked at other stackoverflow questions, but nothing worked!
Order schema:
const orderSchema = new mongoose.Schema({
order_items: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'OrderItem',
required: true
}],
total_price: {
type: Number
}
});
module.exports = mongoose.model('Order', orderSchema);
OrderItem schema:
const orderItemSchema = new mongoose.Schema({
product_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Product',
required: true
},
quantity: {
type: Number,
required: true
},
unit_price: {
type: Number,
required: true
}
});
module.exports = mongoose.model('OrderItem', orderItemSchema);
And here's my approach:
const totalOrders = await Order.aggregate([
{
$lookup: {
from: "OrderItem",
localField: "order_items",
foreignField: "_id",
as: "ordersItems"
}
}
]);
I'm getting ordersItems as an empty array!
Can anyone tell me what I'm wrong?
Thanks

mongoose $lookup always returning empty array

I have already looked up some questions related to this but with no luck at all. I have the following schemas
const Card= new mongoose.Schema(
{
name: { type: String },
collectionId: { type: mongoose.Schema.Types.ObjectId, ref: "Collection" },
price: { type: Number}
},
{ timestamps: true }
);
const Collection= new mongoose.Schema(
{
name: { type: String },
},
{ timestamps: true }
);
const Transactions = new mongoose.Schema(
{
amount: { type: Number},
cardId: { type: mongoose.Schema.Types.ObjectId, ref: "Card" },
collectionId: {type: mongoose.Schema.Types.ObjectId, ref: "Collection"},
userId: {type: mongoose.Schema.Types.ObjectId, ref: "User" }
},
{ timestamps: true }
);
lets say I want to aggregate transactions to get the user who paid most to buy cards and his info then I will do something like this
const T= require("transaction model")
const User = require("user model")
const res = await T.aggregate([
{
$group: {
_id: "$userId",
totalPaid: { $sum: "$amount" },
cardsBought: { $sum: 1 },
},
},
{
$lookup: {
from: "users" / User.document.name
localField: "userId",
foreignField: "_id",
as: "userInfo",
},
},
])
I followed similar questions answers and made sure that the collection name is correct in the "from" field (I tried both users and User.document.name) and made sure the localField and foreignField types are the same(they are both mongo ObjectId) but still I get userInfo as empty array and I am not really sure why.
A work around that I have tried is making two queries like the following
const res = await T.aggregate([])
await User.populate(res, {path: "_id", select: {...}})
but the issue is that I can't do multiple lookups (lets say I want to populate some other data as well)

How to populate the result of a $lookup

I have 3 collections, posts, comments and users, the schemas for posts and comments are below:
post schema
const PostSchema: Schema = new Schema({
author: { type: String, ref: 'User' },
postID: {type: String},
text: { type: String },
});
comment schema
const commentSchema: Schema = new Schema({
author: { type: Schema.Types.ObjectId, ref: 'User' },
parentPost: {type: Schema.Types.ObjectId, ref: 'Post'},
parentPostID: {type: String}, // Post that was commented on
});
To get the comment from each post, I do an aggregate query:
const aggregateQuery = [
{
$match:{postID:{ $in: postKeys},},
},
{
$lookup:
{
from: 'comments',
localField: '_id',
foreignField: 'parentPost',
as: 'Top_Comment',
},
},
{
$sort: {
createdAt: 1,
}
,
},
]
That query works, but I can't figure out how to populate the author property of the comment since its a ref to a user.
What I tried
I tried using pipeline, but I'm a new to aggregation so I didn't know how, any ideas and solutions would be really appreciated

Why mongoose return empty results when I specify the path?

I defined two schema in mongoose: DocSchema has DocTypeSchema reference.
const DocTypeSchema = new Schema({
name: { type: String, unique: true, index: true }
});
export const DocType = mongoose.model('Doc-Type', DocTypeSchema);
const DocSchema = new Schema(
{
name: { type: String },
type: { type: Schema.Types.ObjectId, ref: 'Doc-Type' },
description: { type: String },
}
);
When I try to get the docs with type by the name I gets empty results.
How can I solve this?
docs.find({ 'type.name': 'VideoBook' }, { limit: 30 })
I don't want to get the type object inside the docs array. just to gets the docs that match to the query.
You need tu user .aggregate
Specify the collection:
const DocTypeSchema = new Schema({
name: { type: String, unique: true, index: true }
},{ collection: 'docType' });
Simple example :
const docs = await Doc.aggregate([
{
$lookup: {
from: 'docType',
localField: 'type',
foreignField: 'name',
as: 'magic'
}
},
{$unwind: '$magic'},
{
$match: {
$and: {
"magic.name": 'VideoBook'
}
}
},
{ $limit : 30 }
])

MongoDB parent / child relationinship and fast retrieval of parents

I have the following models in mongoose, Book which belonds to a User.
When I Book.findAll(), I also want to retrieve information from the associated User. What is the best way to achieve this?
const user_schema = new mongoose.Schema({
fullname: { type: String },
avatar: { type: String }
});
module.exports.User = mongoose.model("User", user_schema);
const book_schema = new mongoose.Schema({
_uid: { type: ObjectId, ref: "User", required: true },
title: { type: String, required: true }
text: { type: String, required: true }
});
module.exports.Book = mongoose.model("Book", book_schema);
I need to be able to do something like:
Book.findOne({...}).user.avatar
You could use $lookup for this kind of operations.
db.books.aggregate([
{
$lookup:
{
from: "users",
localField: "user_id", //User ID in users collection
foreignField: "_uid", //User ID in books collection
as: "books_data"
}
}
])
Also, kindly have primary key values in both schemas:
const user_schema = new mongoose.Schema({
user_id : {type : String },
fullname: { type: String },
avatar: { type: String }
});

Resources