Importing a custom mongoose schema in another schema - node.js

My software has a mongoose Schema, let's call it carSchema, and I am exporting a model of the carSchema called Car. Example:
/* Car.js */
var carSchema= new Schema({
_id: Schema.Types.ObjectId,
type: String,
wheels: Number
});
carSchema.statics.drive = async function(){
...
}
module.exports = mongoose.model('Car', carSchema);
Now say my software has another schema called lotSchema in another file, that uses carSchema in it:
/* Lot.js */
var lotSchema = new Schema({
_id: Schema.Types.ObjectId,
carArr: [carSchema]
});
lotSchema.statics.getAllId = async function(){
return carSchema[0]['_id']
}
How can I properly import or export carSchema for use in lotSchema? Would a simple const carSchema = require('Car.js') suffice?

If you are not using not using es6/7 then you can do this
/* Car.js */
const carSchema= new Schema({
_id: Schema.Types.ObjectId,
type: String,
wheels: Number
});
carSchema.statics.drive = async function(){
...
}
module.exports.carSchema = carSchema;
module.exports.carModel = mongoose.model('Car', carSchema);
/* Lot.js */
const { carSchema , carModel } = require('./Car.js');
var lotSchema = new Schema({
_id: Schema.Types.ObjectId,
carArr: [carSchema]
});
lotSchema.statics.getAllId = async function(){
return carSchema[0]['_id']
}

You could do this in Car.js
module.exports = {
useCarSchema: function(myCollection) {
return mongoose.model('Car', carSchema, myCollection);
}
}
And this in Lot.js
var carSchema = require('./Car.js');
var Car = carSchema.useCarSchema(myCollection);

You can use Populate:
Population is the process of automatically replacing the specified
paths in the document with document(s) from other collection(s). We
may populate a single document, multiple documents, plain object,
multiple plain objects, or all objects returned from a query.
Example:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const carSchema= new Schema({
_id: Schema.Types.ObjectId,
type: String,
wheels: Number
});
const lotSchema = new Schema({
_id: Schema.Types.ObjectId,
cars: [{ type: Schema.Types.ObjectId, ref: 'Car' }]
});
mongoose.model('Car', carSchema);
mongoose.model('Lot', lotSchema);
Now, to populate works, you first need to create a car object car = new Car({}) and a lot object lot = new Lot({}), and then:
lot.cars.push(car);
lot.save(callback);

Related

Generate test data from mongoose model

I have this model in nodejs app
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ServiceSchema = new Schema({
Taxy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Taxy',
required: 'Taxy cannot be blank'
},
User: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: 'User cannot be blank'
},
Servicio: String,
Minutos: Number,
Costo: Number,
Desc: String,
From: String,
To: String,
created: {
type: Date,
default: Date.now
}
});
mongoose.model('Service', ServiceSchema);
I have surfed for a couple of days and I have not found a framework to generate random data from a model service, something like
var Service = require('mongoose').model('Admin').schema;
var jsondata = generateRandomeFromModel(Service)
You can use mongoose-fakery module to generate test data
Example:
var mongoose = require('mongoose')
, Schema = mongoose.Schema;
var UserSchema = new Schema({
name: String,
surname: String
});
mongoose.model('User', UserSchema);
In your tests or fixture files:
var fakery = require('mongoose-fakery');
fakery.fake('user', mongoose.model('User'), {
name: 'john',
surname: 'doe'
});
You can go with complete reference here
You can use a package called Faker that makes it easier.
npm install #faker-js/faker --save-dev
get relational data by $sample (aggregation)
const { faker } = require('#faker-js/faker');
const mongoose = require('mongoose');
const getFakeData = async () => {
const taxy=await Model.Taxy.aggregate([{ $sample: { size: 1 } }]); //Randomly select document from `Taxy` Model
const user=await Model.User.aggregate([{ $sample: { size: 1 } }]);
return ({
Taxy: taxy.length && taxy[0]._id,
User: user.length && user[0]._id,
Servicio: faker.commerce.productAdjective(),
Minutos: faker.datatype.number(),
Costo: faker.datatype.number(),
Desc: String,
From: faker.name.findName(),
To: faker.name.findName(),
created: Date.now().toString()
});
}
const fakeJsonData = await getFakeData();
I found a working solution it was created 2 years ago. I tested it too it is working.
https://www.npmjs.com/package/mongoose-dummy

Find all documents which have certain element is not undefined with Mongoose

This is a Event model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Event = new Schema({
eventname: String,
location: String,
note: String,
remind: String,
userid: mongoose.Schema.Types.ObjectId,
projectid: mongoose.Schema.Types.ObjectId,
});
module.exports = mongoose.model('Event', Event);
If event belongs to a project it will save the id of project in projectid, if not projectid will be undefined.
I wonder how to find all the documents which have projectid is not undefined with mongoose?
You can use Query#exists for that:
Event.where('projectid').exists().exec(function(err, docs) {
...
});

TypeError object is not a function

I'm developing an application in node.js with MongoDB
I have to files:
product.js and displaycost.js
this is product .js:
var mongoose = require('mongoose');
var Category = require('./category');
var productSchema = {
name:{type: String, require: true},
//Pictures mus start with "http://"
pictures:[{type:String, match: /^http:\/\//i}],
price:{
amount:{type: Number, required: true},
//ONly 3 supported currencies for now
currency:{
type: String,
enum:['USD','EUR','GBP'],
required: true
}
},
category: Category.categorySchema
};
var schema = new mongoose.Schema(productSchema);
var currencySymbols ={
'USD': '$',
'EUR':'€',
'GBP':'£'
};
/*
*
*/
schema.virtual('displayPrice').get(function(){
return currencySymbols[this.price.currency] +
'' + this.price.amount;
});
schema.set('toObject', {virtuals:true});
schema.set('toJSON', {virtuals:true});
module.exports = schema;
What I need is create a record with "productSchema"
I tried with this:
var Product = require('./product.js');
var p = new Product({
name: 'test',
price:{
amount : 5,
currency: 'USD'
},
category: {
name: 'test'
}
});
console.log(p.displayPrice); // "$5"
p.price.amount = 20;
console.log(p.displayPrice); //" $20"
//{... "displayPrice: "$20",...}
console.log(JSON.stringify(p));
var obj = p.toObject();
But when I run the displaycost.js throw me an error at the word "new"
and writes "TypeError: object is not a function"
I don't know why is happening that. Thank you.
you missing export mongoose model with model name.
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
Category = require('./category');
var productSchema = new Schema({
name: {type: String, require: true},
pictures: [{type: String, match: /^http:\/\//i}],
price: {
amount: {type: Number, required: true},
currency: {
type: String,
enum: ['USD', 'EUR', 'GBP'],
required: true
}
},
category: Category
});
var currencySymbols = {
'USD': '$',
'EUR': '€',
'GBP': '£'
};
productSchema.virtual('displayPrice').get(function () {
return currencySymbols[this.price.currency] +
'' + this.price.amount;
});
productSchema.set('toObject', {virtuals: true});
productSchema.set('toJSON', {virtuals: true});
module.exports = mongoose.model('Product', productSchema);
"new" can only be called on a function. not an object.
In program.js you are creating a new instance of the mongoose schema.
var schema = new mongoose.Schema(productSchema);
... more code ...
return schema;
In your other js file you require product. At this point in time product.js has returned an object to you. A new mongoose schema. It is an object which you are refering to as Product.
You then try to create a new instance of it by calling new on the product OBJECT. New cannot be called on an object literal. It can only be called on a function.
I don't believe you need to make a new instance of schema within product.js, just return the function that creates the schema. Then call new on it when you require it like you are in your second file.

How to create interdependent schemas in Mongoose?

I have two Schemas and I want them to interact with eachother. For instance:
// calendar.js
var mongoose = require('mongoose');
var Scema = mongoose.Schema;
var Day = mongoose.model('Day');
var CalendarSchema = new Schema({
name: { type: String, required: true },
startYear: { type: Number, required: true }
});
CalendarSchema.methods.getDays = function(cb){
Day.find({ cal: this._id }, cb);
}
module.exports = mongoose.model('Calendar', CalendarSchema);
// day.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var Calendar = mongoose.model('Calendar');
var DaySchema = new Schema({
cal: { type: ObjectId, required: true },
date: { type: Number, required: true },
text: { type: String, default: 'hello' }
});
DaySchema.methods.getCal = function(cb){
Calendar.findById(this.cal, cb);
}
module.exports = mongoose.model('Day', DaySchema);
However, I get an error because each schema depends on the other one. Is there a way to get this working using Mongoose? I include them like this:
// app.js
require('./models/calendar');
require('./models/day');
I realize this is an ancient thread, but I'm sure posting the solution will help others down the road.
The solution is to export the module before requiring the interdependent schemas:
// calendar.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var CalendarSchema = new Schema({
name: { type: String, required: true },
startYear: { type: Number, required: true }
});
module.exports = mongoose.model('Calendar', CalendarSchema);
// now you can include the other model and finish definining calendar
var Day = mongoose.require('./day');
CalendarSchema.methods.getDays = function(cb){
Day.find({ cal: this._id }, cb);
}
// day.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var DaySchema = new Schema({
cal: { type: ObjectId, required: true },
date: { type: Number, required: true },
text: { type: String, default: 'hello' }
});
module.exports = mongoose.model('Day', DaySchema);
// same thing here. require after exporting
var Calendar = require('./calendar');
DaySchema.methods.getCal = function(cb){
Calendar.findById(this.cal, cb);
}
It really is that simple. Explanation by Brian Bickerton can be found here:
http://tauzero.roughdraft.io/3948969265a2a427cf83-requiring-interdependent-node-js-modules
It's nice to be able to use functions by name within a module instead of the lengthy module.exports.name. It's also nice to have a single place to look and see everything to be exported. Typically, the solution I've seen is to define functions and variables normally and then set module.exports to an object containing the desired properties at the end. This works in most cases. Where it breaks down is when two modules are inter-dependent and require each other. Setting the exports at the end leads to unexpected results. To get around this problem, simply assign module.exports at the top, before requiring the other module.
You need require the files. If they are in the same path do this:
//calendar.js
var Day = require('./day');
/* Other logic here */
var CalendarSchema = new Schema({
name: { type: String, required: true },
startYear: { type: Number, required: true }
})
, Calendar;
/* other logic here */
/* Export calendar Schema */
mongoose.model('Calendar', CalendarSchema);
Calendar = mongoose.model('Calendar');
exports = Calendar;
Do the same in day.js
EDIT: As JohnnyHK says this don`t work. Link to explanation

How to set ObjectId as a data type in mongoose

Using node.js, mongodb on mongoHQ and mongoose. I'm setting a schema for Categories. I would like to use the document ObjectId as my categoryId.
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var Schema_Category = new Schema({
categoryId : ObjectId,
title : String,
sortIndex : String
});
I then run
var Category = mongoose.model('Schema_Category');
var category = new Category();
category.title = "Bicycles";
category.sortIndex = "3";
category.save(function(err) {
if (err) { throw err; }
console.log('saved');
mongoose.disconnect();
});
Notice that I don't provide a value for categoryId. I assumed mongoose will use the schema to generate it but the document has the usual "_id" and not "categoryId". What am I doing wrong?
Unlike traditional RBDMs, mongoDB doesn't allow you to define any random field as the primary key, the _id field MUST exist for all standard documents.
For this reason, it doesn't make sense to create a separate uuid field.
In mongoose, the ObjectId type is used not to create a new uuid, rather it is mostly used to reference other documents.
Here is an example:
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var Schema_Product = new Schema({
categoryId : ObjectId, // a product references a category _id with type ObjectId
title : String,
price : Number
});
As you can see, it wouldn't make much sense to populate categoryId with a ObjectId.
However, if you do want a nicely named uuid field, mongoose provides virtual properties that allow you to proxy (reference) a field.
Check it out:
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var Schema_Category = new Schema({
title : String,
sortIndex : String
});
Schema_Category.virtual('categoryId').get(function() {
return this._id;
});
So now, whenever you call category.categoryId, mongoose just returns the _id instead.
You can also create a "set" method so that you can set virtual properties, check out this link
for more info
I was looking for a different answer for the question title, so maybe other people will be too.
To set type as an ObjectId (so you may reference author as the author of book, for example), you may do like:
const Book = mongoose.model('Book', {
author: {
type: mongoose.Schema.Types.ObjectId, // here you set the author ID
// from the Author colection,
// so you can reference it
required: true
},
title: {
type: String,
required: true
}
});
My solution on using ObjectId
// usermodel.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const ObjectId = Schema.Types.ObjectId
let UserSchema = new Schema({
username: {
type: String
},
events: [{
type: ObjectId,
ref: 'Event' // Reference to some EventSchema
}]
})
UserSchema.set('autoIndex', true)
module.exports = mongoose.model('User', UserSchema)
Using mongoose's populate method
// controller.js
const mongoose = require('mongoose')
const User = require('./usermodel.js')
let query = User.findOne({ name: "Person" })
query.exec((err, user) => {
if (err) {
console.log(err)
}
user.events = events
// user.events is now an array of events
})
The solution provided by #dex worked for me. But I want to add something else that also worked for me: Use
let UserSchema = new Schema({
username: {
type: String
},
events: [{
type: ObjectId,
ref: 'Event' // Reference to some EventSchema
}]
})
if what you want to create is an Array reference. But if what you want is an Object reference, which is what I think you might be looking for anyway, remove the brackets from the value prop, like this:
let UserSchema = new Schema({
username: {
type: String
},
events: {
type: ObjectId,
ref: 'Event' // Reference to some EventSchema
}
})
Look at the 2 snippets well. In the second case, the value prop of key events does not have brackets over the object def.
You can directly define the ObjectId
var Schema = new mongoose.Schema({
categoryId : mongoose.Schema.Types.ObjectId,
title : String,
sortIndex : String
})
Note: You need to import the mongoose module
Another possible way is to transform your _id to something you like.
Here's an example with a Page-Document that I implemented for a project:
interface PageAttrs {
label: string
// ...
}
const pageSchema = new mongoose.Schema<PageDoc>(
{
label: {
type: String,
required: true
}
// ...
},
{
toJSON: {
transform(doc, ret) {
// modify ret directly
ret.id = ret._id
delete ret._id
}
}
}
)
pageSchema.statics.build = (attrs: PageAttrs) => {
return new Page({
label: attrs.label,
// ...
})
}
const Page = mongoose.model<PageDoc, PageModel>('Page', pageSchema)
Now you can directly access the property 'id', e.g. in a unit test like so:
it('implements optimistic concurrency', async () => {
const page = Page.build({
label: 'Root Page'
// ...
})
await page.save()
const firstInstance = await Page.findById(page.id)
const secondInstance = await Page.findById(page.id)
firstInstance!.set({ label: 'Main Page' })
secondInstance!.set({ label: 'Home Page' })
await firstInstance!.save()
try {
await secondInstance!.save()
} catch (err) {
console.error('Error:', err)
return
}
throw new Error('Should not reach this point')
})

Resources