I have a mongoose schema that looks like this:
var mongoose = require('mongoose');
var _ = require('lodash');
var Schema = mongoose.Schema;
var shopSchema = new mongoose.Schema({
name: {type: String, trim: true},
description: {type: String, trim: true},
rating: Number,
ratingsData: [{type: Number}]
});
shopSchema.methods.rate = function(rating){
this.ratingsData.push(rating);
this.rating = _.mean(this.ratingsData);
return this.ratingsData;
}
module.exports = mongoose.model('Shop', shopSchema);
The rate method is supposed to append a value passed to it to the ratingsData field and then return the average of that array. The problem I'm having is that everytime the method is called, this.ratingsData is just an empty array. The previous values aren't saved for some reason. Therefore the average I get is always the rating passed to the method.
What am I doing wrong?
You just didn't save the schema object, you need something like this:
shopSchema.methods.rate = function(rating, clb){
this.ratingsData.push(rating);
this.rating = _.mean(this.ratingsData);
this.save(function(err, result){
if(err) throw err;
return clb(result.ratingsData);
})
};
or if you use Promise:
shopSchema.methods.rate = function(rating){
this.ratingsData.push(rating);
this.rating = _.mean(this.ratingsData);
return this.save()
.then(result=>{
return result.ratingsData
})
.catch(err=>{
throw err;
})
};
Related
I'm trying to implement an autoicremental user_key field. Looking on this site I came across two questions relevant for my problem but I don't clearly understand what I should do. This is the main one
I have two Mongoose models, this is my ProductsCounterModel.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var Counter = new Schema({
_id: {type: String, required: true},
sequence_value: {type: Number, default: 0}
});
module.exports = mongoose.model('products_counter', Counter);
and this is the Mongoose model where I try to implement the auto-increment field:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var products_counter = require('./ProductsCounterModel.js');
var HistoricalProduct = new Schema({
product_key: { type: Number },
class: { type: String },
brand: { type: String },
model: { type: String },
description: { type: String }
});
HistoricalProduct.pre("save", function (next) {
console.log("first console log:",products_counter);
var doc = this;
products_counter.findOneAndUpdate(
{ "_id": "product_key" },
{ "$inc": { "sequence_value": 1 } },
function(error, products_counter) {
if(error) return next(error);
console.log("second console log",products_counter);
doc.product_key = products_counter.sequence_value;
next();
});
});
module.exports = mongoose.model('HistoricalProduct', HistoricalProduct);
Following the steps provided in the above SO answer I created the collection products_counter and inserted one document.
The thing is that I'm getting this error when I try to insert a new product:
"TypeError: Cannot read property 'sequence_value' of null"
This are the outputs of the above console logs.
first console log output:
function model (doc, fields, skipId) {
if (!(this instanceof model))
return new model(doc, fields, skipId);
Model.call(this, doc, fields, skipId);
}
second console log:
Null
can you see what I'm doing wrong?
You can run following line in your middleware:
console.log(products_counter.collection.collectionName);
that line will print products_counters while you expect that your code will hit products_counter. According to the docs:
Mongoose by default produces a collection name by passing the model name to the utils.toCollectionName method. This method pluralizes the name. Set this option if you need a different name for your collection.
So you should either rename collection products_counter to products_counters or explicitly configure collection name in your schema definition:
var Counter = new Schema({
_id: {type: String, required: true},
sequence_value: {type: Number, default: 0}
}, { collection: "products_counter" });
Schema :
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Config = require('../Config');
var serviceAvailability = new Schema({
agentId: {type: Schema.ObjectId, ref: 'agentProfile', required: true},
availabilityDate: {type: Date, required: true},
availabilityTime: {type: Array, required: true}
});
serviceAvailability.index({agentId:1 , availabilityDate:1},{unique:true});
module.exports = mongoose.model('serviceAvailability', serviceAvailability);
Controller :
Models.serviceAvailability.find({'agentId':'abcd'}, function (err, service) {
console.log(service);
if(service) {
callback(err , service);
}
else {
callback(err);
}
});
I am trying to get all data with some criteria like if agentId is equal to some value but whenever i am using any criteria to find data i am getting empty array while if i remove the criteria and find all data then i am getting data, why is this ?
I think, you try to find a mongoDB document with a request on ObjectId Field, but, in your example, you don't use a correct ObjectId String Value.
ObjectId is a 12-byte BSON type, constructed using:
So, this is a correct way to request your serviceAbility with a correct ObjectId :
Models.serviceAvailability.find({
'agentId':'507f1f77bcf86cd799439011'
}, function (err, service) {
if (err) {
callback(err);
return;
}
callback(null, service);
});
In this case, you should have an agentProfile with the _id equals to 507f1f77bcf86cd799439011
I'm trying to import an Object into Mongo. But when I try to use a value of the Object as _id it fails. The error i.e: "[CastError: Cast to ObjectID failed for value "11563195" at path "_id"]" and later "[Error: document must have an _id before saving]"
What am I doing wrong ?
// Read and import the CSV file.
csv.fromPath(filePath, {
objectMode: true,
headers: keys
})
.on('data', function (data) {
setTimeout(function(){
var Obj = new models[fileName](data);
Obj._id = Obj.SOME_ID;
Obj.save(function (err, importedObj) {
if (err) {
console.log(err);
} else {
console.log('Import: OK');
}
});
}, 500);
})
Here is the used Schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var SomeSchema = new Schema(
{
SOME_ID: String,
FIELD01: String,
FIELD02: String,
FIELD03: String,
FIELD04: String,
FIELD05: String,
FIELD06: String,
FIELD07: String,
FIELD08: String,
FIELD09: String,
FIELD10: String,
FIELD11: String,
FIELD12: String,
FIELD13: String
},
{
collection: 'SomeCollection'
});
module.exports = mongoose.model('SomeCollection', SomeSchema);
Many thanks for your time and help.
By default mongoose is validating that the _id field is a MongoId. If you want to store something other than a MongoId in the _id field you will need to give the _id field a different type.
var SomeSchema = new Schema({
_id: { type: String, required: true }
}
My goal is to insert a new country (with incremented country_id) into the db if it doesn't exist. In that case I try to get the max country_id and insert a new country with country_id + 1. Otherwise I don't do anything.
readFile is a promise to readfile,
filetoArray changes that file content to an array,
processMap processes each array element and decide if we store the info to mongodb or not
The problem is:
promise.promisifyAll(Country.findOne({}).sort({'zid' : -1}).exec()
always gives me the same result even when some data are already inserted into the database...
Any suggestions are greatly appreciated. Thanks.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var CountrySchema = new Schema({
zn: {type: String, required: true},
zid: {type: Number, required: true}
});
var promise = require('bluebird');
function processMap(data){
return promise.bind(data).then(insertCountry);
}
var insertCountry = function() {
var googledata = this; // from bind promise
return promise.promisifyAll(Country.findOne({zn: googledata.country}).exec())
.then(function(dbdata){ return {dbdata: dbdata, googledata: googledata}; })
.then(insertCountryIfNotExist)
}
var insertCountryIfNotExist = function(data){
return promise.promisifyAll(Country.findOne({}).sort({'zid' : -1}).exec())
.then(function(d){
var newc = new Country({zn: data.googledata.country, zid: d.zid + 1});
return promise.promisifyAll(newc.saveAsync())
});
}
// main code is here
readFile(file)
.then(filetoArray)
.map(processMap, {concurrency: 1}) // end of then
.then(function(data){
console.log('done');
})
Actually Exec returns a promise inherited from mpromise, there's no need to use bluebird on your case or if you want to use bluebird, then don't mix the mongoose promises with blue bird.
some example:
var insertCountry = function() {
var googledata = this;
return Country.findOne({zn: googledata.country}).exec()
.then(function(dbdata){
return {dbdata: dbdata, googledata: googledata};
})
.then(function(data){
return Country.findOne({}).sort({'zid' : -1}).exec()
.then(function(d){
var newc = new Country({zn: data.googledata.country, zid: d.zid + 1});
return newc.save();
})
})
}
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')
})