Mongoose populate a field without ObjectId? - node.js

I have a schema:
var Schema = mongoose.Schema;
var TicketSchema = new Schema({
externalId: String,
name: String,
items: [{
externalId: String,
price: Number,
quantity: {
type: Number,
default: 1
},
comment: {
type: String,
default: '',
trim: true
},
entity: {
type: String,
ref: 'Entity'
}
}],
tableId: String
});
mongoose.model('Ticket', TicketSchema);
And I want to populate entity field with an unique field other than ObjectId.
How can I achieve that?

Though late answer. Please check Populate Virtuals for Mongoose 4.5.0
Click the link below
http://mongoosejs.com/docs/populate.html
And scroll down or search for Populate Virtuals you will see it does exactly what you want.

I found Views as one useful approach, though not sure it is the most efficient! For example, in movielens database, I wanted to refer 'movieId' in ratings collection to 'movieId' in the movies collection using 'movieId' as foreign key.
db.createView('rating-movie-view','ratings',[{$lookup:{from:"movies",localField:"movieId",foreignField:"movieId",as:"ratings_movie"}},{ $project:{'userId':1,'movieId':1,'rating':1,'timestamp':1,'ratings_movie.title':1,'ratings_movie.genres':1 } }])
New view "rating-movie-view" thus created has the required fields 'title and 'genres'.
db["rating-movie-view"].findOne()
{
"_id" : ObjectId("598747c28198f78eef1de7a3"),
"userId" : 1,
"movieId" : 1129,
"rating" : 2,
"timestamp" : 1260759185,
"ratings_movie" : [
{
"title" : "Escape from New York (1981)",
"genres" : "Action|Adventure|Sci-Fi|Thriller"
}
]
}
Hope this useful!
Those who are not familiar with movielens data here are the schema
var MovieSchema = new mongoose.Schema({
movieId: Number,
title: String,
genres: String,
});
var RatingSchema = new mongoose.Schema({
userid: Number,
movieId:Number,
rating: Number,
timestamp:Number,
});
//View schema
var RatingViewSchema = new mongoose.Schema({
userid: Number,
movieId:Number,
rating: Number,
timestamp:Number,
rating_movie:{title:String,genres:String}
});

const blogs = this.blogModel
.find(find)
.populate('blogCategory', 'name -_id');
Note -_id will exclude the object _id

I'm not sure if I understood your question correctly.
In Mongoose model, in case we do not specify a primary key, it automatically adds in an extra field called ObjectId and assigns a unique value for each object.
In case we need to specify our own key, we can do it by specifying the key property.
For example:
mongoose.model('Todo', {
todoID: {
type: String,
key: true
},
text: {
type: 'text'
},
done: {
type: Boolean,
default: false,
},
date: {
type: Date,
},
items: [{
entity: {
type: String,
ref: 'Entity'
}
}]
});
I hope, this is what you meant.
If you are asking about fetching objects based on Items -> entity's property,
Todo.find({'items.entity.type':required_type}, function(err, foundTodos){
// ---
});
Thanks,

Use crypto to hash something unique like the objectId , and then save it to your entities.
Var hash = crypto.createHmac('sha256', ticket.objectId).digest('hex');
Ticket.entities= hash;

Related

How to populate sub document of another model in mongoose?

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.

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'.

Mongoose dynamic sub document schema

I have an Inquiry schema:
const inquirySchema = new mongoose.Schema({
client: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Client' }],
data: dynamicSchema?
}, {
timestamps: true
});
I would like to populate the "data" property field with a sub-document, but I want it to accept different sub-document schemas. I have an "Event" and a "Property" child schema which can be inserted as "data". How do I allow this in my Inquiry schema? It seems I have to actually specify WHICH sub-document schema it expects...
My Child schemas:
const eventSchema = new mongoose.Schema({
name: { min: Number, max: Number },
date: { type: Date },
zone: { type: String }
});
const propertySchema = new mongoose.Schema({
price: { min: Number, max: Number },
status: { type: String },
zone: { type: String }
});
you can make your data as type : ObjectId without defining any reference in the schema, and when you want to populate them, use path and model in the populate to populate from different collection, but you must have a logic for choosing which collection to populate from.
Here is how you can do the same:
inquirySchema
const inquirySchema = new mongoose.Schema({
client: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Client' }],
data: { type: mongoose.Schema.Types.ObjectId }
}, {
timestamps: true
});
Populating data
if(isEvent)
{
//Populate using Event collection
Inquiry.find({_id : someID}).
populate({path : 'data' , model : Event}).
exec(function(err,docs){...});
}
else if(isProperty)
{
//Populate using Property collection
Inquiry.find({_id : someID}).
populate({path : 'data' , model : Property}).
exec(function(err,docs){...});
}

Mongoose Populate - array

can someone please help me with population of this schema? I need to populate array of Staff by their userId.
var PlaceSchema = new Schema ({
name: { type: String, required: true, trim: true },
permalink: { type: String },
country: { type: String, required: true },
...long story :D...
staff: [staffSchema],
admins: [adminSchema],
masterPlace:{ type: Boolean },
images: []
});
var staffSchema = new Schema ({
userId: { type: Schema.Types.ObjectId, ref: 'Account' },
role: { type: Number }
});
var adminSchema = new Schema ({
userId: { type: Schema.Types.ObjectId, ref: 'Account'}
})
var Places = mongoose.model('Places', PlaceSchema);
I tried to use this query, but without success.
Places.findOne({'_id' : placeId}).populate('staff.userId').exec(function(err, doc){
console.log(doc);
});
Polpulation is intended as a method for "pulling in" information from the related models in the collection. So rather than specifying a related field "directly", instead reference the related fields so the document appears to have all of those sub-documents embedded in the response:
Places.findOne({'_id' : placeId}).populate('staff','_id')
.exec(function(err, doc){
console.log(doc);
});
The second argument just returns the field that you want. So it "filters" the response.
There is more information on populate in the documentation.

Mongoose Relationship Populate Doesn't Return results

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 } ] }

Resources