Mongoose why would you use populate over another find? - mongodb

I'm guessing because you save resources by making 1 request instead of 2 to the database. Is this significant? Should I care to use populate if I'm populating only 1 field (the advantage is clearer when you populate more than 1)?

You don't save resources by using populate. Under the hood mongoose calls the database as many times as required. Consider the example:
module.exports = function(){
var UserSchema = new Schema({
email : {type : String, required: true},
password: {type: String, required: true}
});
return mongoose.model("User", UserSchema);
};
module.exports = function(){
var AccountSchema = new Schema({
number : {type : String, required: true},
user: {type: Schema.Types.ObjectId, ref: 'User'}
});
return mongoose.model("Account", AccountSchema);
};
mongoose.set('debug', true); //to see queries mongoose is using behind the scenes
Account.find({}, function(err, res){
console.log(res)
}).populate("user")
Apart from the results, you'll see something like this on console:
Mongoose: accounts.find({}, { fields: undefined })
Mongoose: users.find({ _id: { '$in': [ ObjectId("5807d6d6aa66d7633a5d7025"), ObjectId("5807d6d6aa66d7633a5d7026"), ObjectId("5807d709aa66d7633a5d7027") ] } }, { fields: undefined })
That's mongoose finding account documents and then user for each one of them.
It's saving you a lot of code and I don't see why you should not use it irrespective of the number of fields you're populating.

Related

MongoDB - is there a better way to store a list of ObjectIDs?

Say I have a User schema/model and the user has a list of friends. Mongoose wants you to store your list of friends (foreign key / ObjectID type) as an array, right? Which means if I want to find my friend by ID, Mongoose will search the array until it finds the first instance of a friend with the ID I want. That seems really time inefficient no? Is there a better way?
const FriendSchema = new Schema({
username: { type: String, required: true, unique: true },
});
const UserSchema = new Schema({
username: { type: String, required: true, unique: true },
friends: [FriendSchema],
});
Part of what I was looking for is this:
Indexes are what allows you to "iterate" through an array or a field in a collection without having to look at every single one. So to make sure you don't waste time iterating, you can create an "index" on any field and it makes it searchable in a binary-tree structure.
https://docs.mongodb.com/manual/indexes/
Arrays already are made to have the field be a "key" so you wouldn't need to worry about the time complexity of searching an array by the field name of one of its elements.
https://docs.mongodb.com/manual/core/index-multikey/
Use ref to refer to the documents in another schema and call populate to get the referred doc.
// friend.model.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const FriendSchema = new Schema({
username: { type: String, required: true, unique: true },
});
module.exports = mongoose.model('Friend', FriendSchema);
// user.model.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const UserSchema = new Schema({
username: { type: String, required: true, unique: true },
friends: [{ type: Schema.Types.ObjectId, ref: 'Friend' }],
});
module.exports = mongoose.model('User', UserSchema);
const User = require('user.model.js');
User.find(...)
.populate('friends')
.exec()

findOneAndUpdate doesn't create ObjectId

I need to make a patch request to update only one (or several) field(s) at the same time.
I've got a big object which is my document, and inside nested array of objects.
For example, for my car array, this is the schema :
const carSchema = new Schema({
owner: [{value: {type: String}, label: {type: String}}],
carPlate: {type: String},
carColor: {type: String},
carBrand: {type: String},
carStatus: {type: String}
});
const myObject = new Schema({
...
cars: [carSchema]
...
});
When I send my changes, I do it this way :
let dynamicVar = 'cars.'+i+'.'+myfield;
this.props.updateGeneral({_id: this.props.general._id, [dynamicVar ]: [myfield.value]});
I'm on redux, so my action looks like :
export function updateGeneral(data) {
let _id = data._id;
delete data._id;
return {
type: 'UPDATE_GENERAL',
payload: client.patch(`${url}/${_id}`, data)
}
}
And my PATCH request is like :
router.patch('/url/:id', async (req, res, next) => {
myObject.findOneAndUpdate({_id: req.params.id}, {$set: req.body }, {upsert: true, new: true}, function (err, objectReturn) {
if (err) return next(err);
cn = cn.substr(0, cn.indexOf(' {'));
res.json(objectReturn);
});
});
My BIG issue is that my field is update or inserted, but if it's inserted and it creates a new array it won't create the objectId linked. It won't even create the array of object,just an object with a property.
How can I make mongoose initiates ObjectId??
Per the reply to this SO post it looks like you cannot update object IDs. When doing so, you are effectively "deleting" the object and creating a new one.

Deep Population of an object that belongs to a Schema that is a value of an object in an array in Mongoose.

I have the following Schema:
const userSchema = new Schema({
name: String,
favoriteStreams: [
{
stream: {
type: Schema.Types.ObjectId,
ref: "Stream",
unique: true
},
param: Number
}
]
});
I can't for the life of me figure out how to populate each stream. This code, and many variations that I've tried do not work.
User.findOne({_id: req.user._id})
.populate({path: "favoriteStreams", populate: {path: "stream", model:"Stream"}})
The added level of separation has made it difficult for me. How would I get at each streams properties?
What about this:
User.findOne({_id: req.user._id})
.populate(path: "favoriteStreams.stream")

Mongoose Populate a field

I have two mongoose schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var itemSchema = new Schema({
name: {type: String, required: true, max: 25, trim: true},
price: {type: Number, required: true, trim: true, default: 0},
tax: {
type: Schema.Types.ObjectId,
ref: "Store"
}
});
module.exports = mongoose.model('Item', itemSchema);
The second Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var storeSchema = new Schema({
name: {type: String, required: true, trim: true},
taxes: [
{
name: String,
rate: Number
}
]
});
module.exports = mongoose.model('Store', storeSchema);
What I want to do is populate the itemSchema tax object with the storeSchema taxes array of object. every time I pushed a new tax object to the taxes array mongoose created an ObjectId. I stored that ObjectId in my itemSchema Tax. I want to use that _id to retrieve the store taxes that matches the itemSchema _id.
I have tried this so far, but I get no in the tax attribute.
Item.find().populate("tax", "taxes").exec(function (err, docs) {
if (err) return console.error(err);
console.log(items);
});
Try this query
Item.find().populate({
path: 'tax',
select: 'taxes'
}).exec(function (err, docs) {
if (err) {
console.error(err);
} else {
console.log(docs);
}
});
Item.find().populate(path:"tax", model: )
Mention your item model file name... don't use "" or '' for the file name, simply add the file name.
Use Item.find().populate("Store", "taxes") instead of Item.find().populate("tax", "taxes").

MongoDB $pull value from array of ObjectIDs

I have this document in my collection:
{
"_id" : ObjectId("52718433e18a711923000005"),
"owners" : [
ObjectId("52718433e18a711923000004"),
ObjectId("52ed40dccc5bc50000000003"),
ObjectId("52ed4171abe2780000000003")
]
}
I have the following statement, where I am trying to remove one of the values in owners field:
Business.update({_id:req.body._id}, {$pull:{"owners":req.body.userid}}, function(err){
if(err){
res.json(500, {message:"Could not remove user from admin list"});
}else{
res.json(200);
}
});
I know that req.body._id and req.body.userid have valid values:
{ _id: '52718433e18a711923000005',
userid: '52ed4171abe2780000000003' }
Other operations, such as finding business by ID, etc, work, so it's not an ObjectId format issue. What else could it be?
--
Edit: here is my (abbreviated) schema definition:
var BusinessSchema = new Schema({
business_name: {type: String, required: true},
owners: {type: Array}
});
Your current schema doesn't provide any direction to Mongoose regarding the data type contained within the owners array field. If you want Mongoose to cast your string to an ObjectID you need to provide type information in your schema:
var BusinessSchema = new Schema({
business_name: {type: String, required: true},
owners: [{type: Schema.ObjectId}]
});
It looks like a conversion to ObjectId is required when trying to match values to pull. But not to search. So this works:
var ObjectId = require('mongoose').Types.ObjectId;
Business.update({_id:req.body._id}, {$pull:{"owners": new ObjectId(req.body.userid)}}, function(err){
if(err){
res.json(500, {message:"Could not remove user from admin list"});
}else{
res.json(200);
}
});
--
Edit: So, if I change my schema from owners: {type: Array} to owners: [Schema.Types.ObjectId], I can skip the conversion, and my original statement works.