How to perform aggregation in mongoose on the fields that are based on ref's of other schema..
eg : below id the schema
var StorySchema = new Schema({
_creator : { type: Schema.ObjectId, ref: 'Person' }
, title : String
, fans : [{ type: Schema.ObjectId, ref: 'Person' }]
});
how to perform aggregation with creator and fans
thanks in advance
You can use populate function, document: http://mongoosejs.com/docs/populate.html
populate will help you lookup data base on the ObjectIds and the ref collections:
Story
.findOne({title: 'your title'})
.populate(['_creator', 'fans'])
.exec(function (err, doc) {
console.log(doc);
})
Output:
{ _id: 52b9768a8a770b383d000003,
title: 'your title',
_creator: { name: 'user1', _id: 52b975e13e7f2dec31000001, __v: 0 },
fans:
[ { name: 'fan1', _id: 52b975e13e7f2dec31000001, __v: 0 },
{ name: 'fan2', _id: 52b975e13e7f2dec31000002, __v: 0 },
{ name: 'fan3', _id: 52b975e13e7f2dec31000003, __v: 0 }] }
you can remove populate function to see the different on result
Related
I need a help on mongoose deep population. Basically, I have an ItemGroupSchema having a _id referenced to ItemSchema and items being an array of references to ItemSchema as well.
const ItemSchema = new mongoose.Schema<IItem>({
// information fields about item type, quantity, model, size,...
}); // its model is ItemModel
const ItemGroupSchema = new mongoose.Schema<IItemGroup>({
_id: { type: mongoose.Schema.Types.ObjectId, required: true, ref: ItemModel.modelName },
items: [{ type: mongoose.Schema.Types.ObjectId, ref: ItemModel.modelName }]
}, { _id: false }); // its model is ItemGroupModel
From a valid _id input, I do the following:
Get the items of it from ItemGroupModel; done.
With the items of _id I got, I populate it from ItemGroupSchema so I can have items of items of _id; done.
Now, I should have items of items inside items of _id. Next is to populate only on items of _id from ItemSchema to have some information to work on, which is not done yet.
The issue is that, populate() operation of the last step gives me an empty items array. Below is how each operation looks like step by step:
1.
ItemGroupModel.find({ _id: 'root' });
{
_id: 'root',
items: ['parent1', 'parent2']
}
2.
ItemGroupModel.find({ _id: 'root' }).populate({ path: 'items', model: ItemGroupModel.modelName });
{
_id: 'root',
items: [
{ _id: 'parent1', items: ['child1', 'child2'] },
{ _id: 'parent2', items: ['child3']
]
}
3.
ItemGroupModel.find({ _id: 'root' }).populate({ path: 'items', model: ItemGroupModel.modelName, populate: { path: '_id', model: ItemModel.modelName }});
{
_id: 'root',
items: [] // yes, empty
}
// I expect it to be
{
_id: 'root',
items: [
{ _id: { _id: 'parent1', type: 'gun', quantity: 1, size: 1 }, items: ['child1', 'child2'] },
{ _id: { _id: 'parent2', type: 'backpack', quantity: 1, size: 1 }, items: ['child3']
]
}
use this aggregation on ItemSchema collection
[{
from: 'ItemGroup',
localField: 'items',
foreignField: '_id',
as: 'items'
}]
I have this Model:
const cart = new mongoose.Schema(
{
products: [{
productId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Product",
},
quantity: {
type: Number,
required: true,
default: 1
},
title: String,
price: Number
}],
},
{ timestamps: true });
How I find all my products (from Model Product) using it.
cart = Cart.find(id);
// inside cart.products
[{productId: 'asvhbajAS13', quantity: 8 },{productId: 'asvhbajAS13', quantity: 2 }]
I want to modify all products after that, is this approach right?
What I've tried:
Product.find({
'_id': { $in: { cart.products } }
}, function(err, product) {
})
});
your code is correct but if you use findOne() .or you can use populate instead of query once more :
cart = Cart.find(id).populate("products")
I have two models Vote and Link,I am trying to populate the votes array in link model,The votes array contains id's that references to the collection Vote,which only contains two fields link and User which also refs to same link model mentioned below and a user model respectively
link Schema:-
const linkSchema = new mongoose.Schema(
{
description: {
type: String,
trim: true,
},
url: {
type: String,
trim: true,
},
postedBy: {
type: mongoose.Types.ObjectId,
ref: "User",
},
votes: [{ type: mongoose.Types.ObjectId, ref: "Vote" }],
},
{
timestamps: true,
}
);
linkSchema.index({ description: "text" });
linkSchema.index({ createdAt: -1 });
module.exports = mongoose.model("Link", linkSchema);
Vote schema:-
const mongoose = require("mongoose");
const voteSchema = new mongoose.Schema({
link: { type: mongoose.Types.ObjectId, ref: "Link" },
user: { type: mongoose.Types.ObjectId, ref: "User" },
});
module.exports = mongoose.model("Vote", voteSchema);
but when i try to get the votes of a link,it always return an empty array ,My function:-
const votes = async ({ id }) => {
const linkData = await Link.findById(id).populate("votes").exec();
console.log(linkData);
};
Output Data:-
{
votes: [], //empty always
_id: 5ecb21059a157117c03d4fac,
url: 'https://www.apollographql.com/docs/react/',
description: 'The best GraphQL client for React',
postedBy: 5ec92a58bf38c32b38400705,
createdAt: 2020-05-25T01:36:05.892Z,
updatedAt: 2020-05-25T01:37:52.266Z,
__v: 0
}
Instead of populate(), you can use aggregate() to get your desired output. This should probably work in your case:
Link.aggregate([
{
$match: {
_id: { $in: [mongoose.Types.ObjectId(id)] // as suggested by the questioner
}
},
{
$lookup: {
from: "vote", // collection to join
localField: "votes", // field from the input documents (filtered after _id is matched)
foreignField: "link", // field to compare with, from other collection
as: "linkData" // output array name
}
}
])
Let me know in the comments.
I have the following schema:
var userSchema = new mongoose.Schema({
username: {type: String, required: true},
favouriteItems: [{type: mongoose.Schema.Types.ObjectId, ref: 'Item'}],
userItems: [{type: mongoose.Schema.Types.ObjectId, ref: 'Item'}]
});
var itemSchema = new mongoose.Schema({
name: {type: String, required: true},
price: {type: Number, required: true}
});
mongoose.model('User', userSchema , 'Users');
mongoose.model('Item', itemSchema , 'Items');
When I delete the item, I want to remove its ObjectId from favouriteItems and userItems from all users whose array contains it.
Example:
Initial:
Users:
[
{
_id: "a",
username: "username1",
favouriteItems: ["id1"],
userItems: ["id2", "id3"]
},
{
_id: "b",
username: "username2",
favouriteItems: ["id2"],
userItems: ["id1", "id4"]
},
...
]
Items:
[
{
_id: "id1",
name: "item1",
price: 19
},
{
_id: "id2",
name: "item2",
price: 7
},
...
]
When I remove item with id id1 I want the following result (id1 must be removed from arrays favouriteItems and userItems of all users):
Users:
[
{
_id: "a",
username: "username1",
favouriteItems: [],
userItems: ["id2", "id3"]
},
{
_id: "b",
username: "username2",
favouriteItems: ["id2"],
userItems: ["id4"]
},
...
]
Items:
[
{
_id: "id2",
name: "item2",
price: 7
},
...
]
How do I achieve this using mongoose and node.js?
You should take a look at the post mongoose middleware hooks and specifically the hook on remove:
itemSchema.post('remove', async function(doc) {
// doc is removed ... execute query to remove from favorites using the `doc._id`
});
This is the relevant portion of my Schema:
var classroomSchema = mongoose.Schema({
students: [{
_id: mongoose.Schema.Types.ObjectId,
ref: 'User',
rate: {
type: Number,
default: 200,
},
referrals: [mongoose.Schema.Types.ObjectId],
}],
)};
Here rate and referrals are properties of the students which are valid in the context of that classroom, and cannot be populated from the Students model.
Is there any way to define my schema such that I can keep those fields(rate and referrals) and also use populate to link other fields such as name, age, etc of the student?
Change your schema to this:
var classroomSchema = mongoose.Schema({
students: [{
rate: { type: Number, default: 200 },
user: { ref: 'User', type: Schema.Types.ObjectId },
referrals: [mongoose.Schema.Types.ObjectId],
}],
});
Usage:
classroom
.findOne(...)
.populate('user')
.exec((err, classroom) => {
let student0 = classroom.students[0];
// student0.rate
// student0.user.name
});