How to populate the result of a $lookup - node.js

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

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

lookup array shows no result

i am building an apllication with mongoose and node.js that enables you to post, comment, and like both the posts and the comments.
those are my schemas and the query that i am trying to built to send the user using the apllication all the posts, comments, likes to both the comment section and the post section.
tried to use lookup with mongoose to get comments (and to them likes),but the the pogram shows me no result as can you see:
( i checked by the way .)
comment.js
const commentSchema=
new mongoose.Schema({
userID : {type: mongoose.Types.ObjectId, ref: "User",required: true },
content : {type: String,required: true },
date :{type : Date,default:()=>Date.now()} ,
})
module.exports=mongoose.model("comment",commentSchema)
likes.js
const LikesSchema= new mongoose.Schema({
userID : {type: mongoose.Types.ObjectId, ref: "user",required: true },
postCommentID : {type: mongoose.Types.ObjectId ,required: true } //can be both a comment or a post
})
module.exports=mongoose.model("likes",LikesSchema)
posts.js
const postSchema= new mongoose.Schema({
userID : {type: mongoose.Types.ObjectId, ref: "User",required: true },
content : {type: String,required: true },
date : {type : Date,default:()=>Date.now()},
commentsID :[{type:mongoose.Types.ObjectId, ref:'comment' }],
})
module.exports=mongoose.model("post",postSchema)
incomplete query
Query.js
post.aggregate(
[
{
$lookup: {
from: 'likes',
localField: '_id',
foreignField: 'postCommentID',
as: '_likes'
},
},
{
//NOT WORKING
$lookup: {
from: 'comment',
localField : 'commentsID',
foreignField : '_id',
as: 'comments'
},
},
{
$set: {
'likes': '$_likes.userID'
}
},
{
$project: {
'_likes': 0,
}
},
]
)
result:
as can you see the comment section is empty.
[{
_id: new ObjectId("621fdff521935fad17cc27b5"),
userID: new ObjectId("621ba7b84ade9cd7ebe7ef46"),
content: 'new post',
commentsID: [
new ObjectId("621fe07c21935fad17cc29f9"),
new ObjectId("621fe2b221935fad17cc2aef"),
new ObjectId("621fe2ff21935fad17cc2b45"),
new ObjectId("621fe83260288e8ee9db1d62"),
new ObjectId("621fe9361ab243a8c2ebb0eb"),
new ObjectId("621ff5d471b483d2f59ca493")
],
date: 2022-03-02T21:21:57.660Z,
__v: 0,
comments: [],
likes: [
new ObjectId("621fe07c21935fad17cc29f9"),
new ObjectId("621fe2b221935fad17cc2aef"),
new ObjectId("621fe2ff21935fad17cc2b45")
]
}
]

How to query documents with reference fields parameters in Mongoose?

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'
}
}]);

Include count of subdocuments in schema

In my NodeJS project, I'm creating Mongoose schemas as below:
//Likes Schema
var likesSchema = mongoose.Schema({
postId: { type: mongoose.Schema.Types.ObjectId, ref: "Post", required: 'Provide the news ID to which this comment belongs' },
});
module.exports = mongoose.model('Likes', likesSchema);
//Post schema
var postSchema = mongoose.Schema({
title: { type: String, required: 'Kindly enter the title' },
description: { type: String, required: 'Kindly enter the description of the news' }
});
module.exports = mongoose.model('Post', postSchema);
Post is a schema that has a title and a description. Like is a schema that tracks number of likes for a particular post. So it has just postID.
Now I want to include "count" of likes as a variable in "Post" schema. I don't want to count the likes during query execution.
Is there any simple way to achieve it?
I found a solution after doing some trial and errors:
db.Post.aggregate([
{
$lookup: {
from: "Likes",
localField:"_id",
foreignField: "postId",
as: "likes"
}
},
{
$project: {
title: 1,
description: 1,
count: { $size: "$likes" }
}
}
]).pretty()

How to name a foreign key different from db name in mongoose

Is there a way in mongoose + Node.js/Express to define the relation between the foreign key field and what I refer to that field in the model is? My issue is that I have a mongo database where my foreign keys are all formatted like 'exampleId' instead of 'example'. I could just call out 'exampleId' directly but then it leads to weird things like when I populate 'exampleId' instead of 'example' (which is confusing because once populated, it is now the 'example' itself instead of its id).
Here is how I do it now and it works with my graphQL server, but only if my field in the database is 'course' while my database's field is 'courseId'
const CourseSchema = new Schema({
_id: { type: String },
sections: [{
type: Schema.Types.String,
ref: 'Section'
}],
});
const SectionType = new GraphQLObjectType({
name: 'SectionType',
fields: () => ({
id: { type: GraphQLID },
courseId: {
type: require('./course_type'),
resolve(parentValue) {
return Section.findById(parentValue)
.populate('course')
.then(section => section.course);
}
},
}),
});
I figured it out! With the newest version of mongoose, you actually can use virtual fields to accomplish what I wanted to do and this technique allows for flexibility in laying out your schema. Say that my MongoDB collections look like the following:
Courses { _id, sectionIds }
Lectures { _id, courseId }
I can use the following schema in mongoose and it will allow me to refer to course.lectures or lecture.course instead of the usual course.lectureIds or section.courseId:
const CourseSchema = new Schema({
_id: { type: String },
});
CourseSchema.virtual('sections', {
type: Schema.Types.String,
ref: 'Section',
localField: 'sectionIds',
foreignField: '_id',
justOne: false,
});
CourseSchema.statics.findSections = function(id) {
return this.findById(id)
.populate('sections')
.then(course => course.sections);
}
const SectionSchema = new Schema({
_id: { type: String },
});
SectionSchema.virtual('course', {
type: Schema.Types.String,
ref: 'Course',
localField: 'courseId',
foreignField: '_id',
justOne: true,
});
Actually MongoDB isn't a relational database. You can alter the field and its name whatever you like. Ex I Have an Owner(Meteor.users) table and Patient Table with this column
ownerid : {type: String, min: 1},
firstname: {type: String, min: 1},
lastname: {type: String, min: 1},
middlename: {type: String, min: 1, optional: true},
createdbyid: { type: String },
createdbyname: { type: String },
createdat: { type: Date, defaultValue: new Date() },
updatedbyid: { type: String, optional: true },
updatedbyname : { type: String, optional: true },
updatedat: { type: Date, defaultValue: new Date() },
I can easily stamp the value of my {Meteor.Users()._id} to ownerid of my designated patient by just processing them at meteor.methods. You don't have to worry about foreign keys mongo doesn't do relational databases you can customize your database whatever you like. I Hope this helps ;)
Mongoose Documentation posits that _id has to be used in refs and that[i]t is important to match the type of _id to the type of ref. , e.g.:
var personSchema = Schema({
_id : Number, //it is `Number`
name : String,
age : Number,
stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});
var storySchema = Schema({
_creator : { type: Number, ref: 'Person' },
title : String,
fans : [{ type: Number, ref: 'Person' }] // use `Number` to match
});
I also wonder if by 'database' you mean 'collection'.

Resources