I have a mongoose model that looks like this:
var ModuleSchema = new Schema({
systems: [{
system: {
type: Schema.ObjectId,
ref: 'System'
},
quantity: {
type: Number
}
}]
});
mongoose.model('Module', ModuleSchema);
Basically the ModuleSchema.systems.$.system property will not get populated.
The property belongs to an object in the array of objects. I have tried everything to get it to populate but it just won't happen.
I tried the following syntax for populating but not sure what might be wrong because I still don't get back the populated System property.
Module.findOne({project: pId}).sort('-created')
.populate('systems.system')
It wont work as system is not a property of systems. You need to populate it like
Module.findOne({project: pId}).sort('-created')
.populate('systems.0.system').exec(function (err, doc){})
Module.findOne({project: pId}).sort('-created')
.populate('systems.1.system').exec(function (err, doc){})
So you should have a for loop and iterate over it to populate all the documents. Else you should modify your model to make it work better.
var ModuleSchema = new Schema({
systems: [{
system: {
type: Schema.ObjectId,
ref: 'System'
},
quantity: {
type: Number
}
}]
});
Change your model to this and it will make easy for you.
var ModuleSchema = new Schema({
systems: {
system: [{
type: Schema.ObjectId,
ref: 'System'
}],
quantity: {
type: Number
}
}
});
Related
I have two mongodb model as following.
const CompanySchema = new Schema(
{
sections: [{
name: { type: String },
budgets: [{ // indicates from CalcSchema
index: { type: Number },
title: { type: String },
values: [Number],
sum: { type: Number, default: 0 },
}],
}]
},
{ timestamps: true }
);
const CalcSchema = new Schema({
budget: {
type: Schema.Types.ObjectId, // I want to populate this field. this indicates budget document in Company model
ref: "Company.sections.budgets" //it's possible in mongoose?
},
expense: {
type: Number,
default: 0
}
});
budget field indicate one of budgets field in CompanySchema.
So I want to populate when get Calc data.
But I don't how to populate embedded document.
I tried set ref value to ref: "Company.sections.budgets". but it's not working.
Please anyone help.
Finally, I found answer myself.
There is useful plugin for it.
https://github.com/QuantumGlitch/mongoose-sub-references-populate#readme
And I learned that my schema structure was wrong. It's anti-pattern in mongodb.
I am a little stuck with trying to figure out the best way to populate a virtual schema in my db. In this instance, I want to check against multiple fields but I do not know any way to do this or alternative ways.
I had hoped I could just use some string arrays in the 'localField' & 'foreignField' keys of the virtual schema but this was not the case.
The user has a 'Series' _id saved, and the virtual schema 'leagues' gets all leagues that the user is entered into too, the catch is a league belongs to different Series. I want to only retrieve the leagues that the user is entered into & that match up with the user's series _id also...
As you can see currently this virtual schema just returns all the leagues the user is entered into regardless of the series. :(
Any ideas? I have been very confused about how to achieve this.
const schema: mongoose.Schema = new mongoose.Schema({
_id: {
type: mongoose.Schema.Types.ObjectId,
auto: true
},
username: {
type: String,
unique: true,
},
series: {
ref: 'Serie',
type: mongoose.Schema.Types.ObjectId,
autopopulate: {
maxDepth: 1,
select: ['title']
}
}
});
schema.virtual('leagues', {
ref: 'League',
localField: '_id',
foreignField: 'users',
autopopulate: {
maxDepth: 1,
select: ['title', 'engineSize', 'host', 'series']
}
});
The league schema looks like this
const schema: mongoose.Schema = new mongoose.Schema({
_id: {
type: mongoose.Schema.Types.ObjectId,
auto: true
},
title: String,
series: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Serie',
},
users: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
}]
});
This was my solution to checking against multiple fields in mongoose using a getter.
schema.virtual('motoduel').get(function () {
return motoduelModel.findOne({
event: this.series.upcomingEvent._id,
riderGroup: this.riderGroup._id
});
});
I have a schema which looks like this:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const timestamps = require('mongoose-timestamps');
const BidSchema = new Schema({
bidder: {
type: Schema.ObjectId,
ref: 'Buyer'
},
counterOffer: {
type: Schema.ObjectId,
ref: 'Counter_Offer'
},
box: {
type: Schema.ObjectId,
ref: 'Box'
},
status: {
type: String,
enum: ['Pending', 'Approved', 'Disapproved']
},
bidPrice: Number,
bidTimeout: {
type: Date,
default: Date.now
},
isAnonymous: Boolean,
});
BidSchema.plugin(timestamps);
module.exports = mongoose.model('Bid', BidSchema);
I want to store multiple data types in the Box field. For example, I have a Box model and also I have a GroupedBox model. I want to be able to store id for both of these schemas and to be able to populate this field accordingly. For instance, if the document will store an ObjectId that references a GroupedBox document, the populate method will fetch the right GroupedBox document. Same goes for a Box document id.
I read about Mixed field types but I don't see how that can help me.
replace box field in your code with this:
box: {
box: { type: Schema.Types.ObjectId, ref: 'Box' },
groupedBox : { type: Schema.Types.ObjectId, ref: 'GroupedBox' }
}
To populate box you need to access box with a dot populate("box.box") similar to groupedBox you populate it this way populate("box.groupedBox")
I'm learning mongoose and need some help. I have 3 collections, and on a single API call, I want to create 3 documents that reference each other; "joins" below:
Users - need to reference chirps
Videos - need to reference chirps
Chirps - need to reference users & chirps
Question: I know that I can do a model.create() and pass in the new document in each callback, and then update to the respective docs, but I was wondering if there's a cleaner way of doing it?
Sorry if I'm not clear on the question. Please ask me if something doesn't make sense.
Code
var chirpSchema = new mongoose.Schema({
date_created: { type: Date, default: Date.now }
, content: { post : String }
, _video: { type: $oid, ref: "video" }
, _author: { type: $oid, ref: "user" }
});
var chirp = mongoose.model('chirp', chirpSchema);
var userSchema = new mongoose.Schema({
date_joined: { type : Date, default: Date.now }
, cookie_id: String,
chirp_library: [{type: $oid, ref: "chirp"}]
})
var user = mongoose.model('user', userSchema);
var videoSchema = new mongoose.Schema({
date_tagged: { type : Date, default: Date.now }
, thumbnail_url : String
, _chirps: [{type: $oid, ref: "chirp" }]
});
var video = mongoose.model('video', videoSchema);
Mongo and other NoSQL databases aren't just an interchangeable alternative to a SQL database. It forces you to rethink your design in a different way. The concept isn't to define a relationship. The idea is to make information available in less queries. Arrays are generally a thing to avoid in Mongo, especially if they have the potential to grow infinitely. Based on your naming, that seems like a strong possibility. If you keep the rest of your schema and just delete those two arrays off of your user and video schemas:
chirp.find({_author: yourUserId}).populate("_author") gives you the same information as user.findOne({_id: yourUserId}) in your current design.
similarly,
chirp.find({_video: yourVideoId}).populate("_video") and video.findOne({_id: yourVideoId})
The only issue with this is that the .populate() is running on every single chirp you are pulling. A way around this is to denormalize some (or all) of your author and video documents on the chirp document. How I would likely design this is:
var chirpSchema = new mongoose.Schema({
date_created: { type: Date, default: Date.now },
content: {
post : String
},
_author: {
_id: { type: $oid, ref: "video" },
date_joined: { type: Date, default: Date.now }
},
_video: {
_id: { type: $oid, ref: "user" },
date_tagged: { type: Date, default: Date.now },
thumbnail_url: String
}
});
var userSchema = new mongoose.Schema({
date_joined: { type : Date, default: Date.now }
, cookie_id: String
})
var videoSchema = new mongoose.Schema({
date_tagged: { type : Date, default: Date.now }
, thumbnail_url : String
});
It's perfectly OK to have data repeated, as long as it makes your queries more efficient. That being said, you need to strike a balance between reading and writing. If your user information or video information changes regularly, you'll have to update that data on each chirp document. If there is a specific field on your video/author that is changing regularly, you can just leave that field off the chirp document if it's not necessary in queries.
var SecuritySchema = new Mongoose.Schema({
_bids: [{
type: Mongoose.Schema.Types.ObjectId,
ref: 'BuyOrder'
}],
_asks: [{
type: Mongoose.Schema.Types.ObjectId,
ref: 'SellOrder'
}]
});
var OrdersSchema = new Mongoose.Schema({
_security: {
type: Mongoose.Schema.Types.ObjectId,
ref: 'Security'
},
price: {
type: Number,
required: true
},
quantity: {
type: Number,
required: true
}
});
// declare seat covers here too
var models = {
Security: Mongoose.model('Security', SecuritySchema),
BuyOrder: Mongoose.model('BuyOrder', OrdersSchema),
SellOrder: Mongoose.model('SellOrder', OrdersSchema)
};
return models;
And than when I save a new BuyOrder for example:
// I put the 'id' of the security: order.__security = security._id on the client-side
var order = new models.BuyOrder(req.body.order);
order.save(function(err) {
if (err) return console.log(err);
});
And attempt to re-retrieve the associated security:
models.Security.findById(req.params.id).populate({
path: '_bids'
}).exec(function(err, security) {
// the '_bids' array is empty.
});
I think this is some sort of naming issue, but I'm not sure, I've seen examples here and on the moongoose website that use Number as the Id type: http://mongoosejs.com/docs/populate.html
The ref field should use the singular model name
Also, just do:
models.Security.findById(req.params.id).populate('_bids').exec(...
My main suspicion given your snippet at the moment is your req.body.order has _security as a string instead of an array containing a string.
Also, you don't need an id property. Mongodb itself will automatically do the _id as a real BSON ObjectId, and mongoose will add id as a string representation of the same value, so don't worry about that.
While I don't understand your schema (and the circular nature of it?), this code works:
var order = new models.BuyOrder({ price: 100, quantity: 5});
order.save(function(err, orderDoc) {
var security = new models.Security();
security._bids.push(orderDoc);
security.save(function(err, doc) {
models.Security.findById({ _id: doc._id })
.populate("_bids").exec(function(err, security) {
console.log(security);
});
});
});
It:
creates a BuyOrder
saves it
creates a Security
adds to the array of _bids the new orderDoc's _id
saves it
searches for the match and populates
Note that there's not an automatic method for adding the document to the array of _bids, so I've done that manually.
Results:
{ _id: 5224e73af7c90a2017000002,
__v: 0,
_asks: [],
_bids: [ { price: 100,
quantity: 5,
_id: 5224e72ef7c90a2017000001, __v: 0 } ] }