Mongoose - Retreive ObjectId with attribute and pass it to an insert - node.js

With mongoose 4.9.4, I have two models, a User model and a Customer model.
A customer is always attached to a user (user 1 - n customer).
Now to create a Customer in the front, I use a dropdown in a form to select a unique attribute from a user (which is called "trigramUser"), retreive the user objectId with this attribute and pass it to an attribute for the new cutomer.
the createCustomer method :
Find a user objectId with the attribute "trigramUser"
Convert this objectId to string (id.str)
Replace the req.body.userCustomer attribute to create a new customer with the objectId as a string
customerController.js
exports.createCustomer = (req, res) => {
console.log('Creation of the customer '+req.body.siretCustomer+' in progress.');
console.log('Worker attached to the customer '+req.body.userCustomer+' .');
database.getConnection();
user.findOne( { trigramUser: req.body.userCustomer } , '_id', (err, id) => {
var newCustomer = {
sireCustomer : req.body.siretCustomer,
// attributes...
userCustomer : id.str
};
console.log('ID worker : '+id.str+' .');
customer.create(newCustomer, (err) => {
if (err) return res.status(500).json(err.stack);
res.status(200).json(
{ message: 'Customer '+req.body.siretCustomer+'
has been successfully created.' }
);
});
});
};
customer.js
var mongoose = require('mongoose'), Schema = mongoose.Schema;
var customerSchema = new Schema({
siretCustomer:
{ type: String, required: true, minlength: 15, maxlength: 15 },
// attributes ...
userCustomer:
[{ type: Schema.Types.ObjectId, ref: 'User' }],
}, { collection: 'customer' });
module.exports = mongoose.model('Customer', customerSchema, 'customer');
user.js
var mongoose = require('mongoose'), Schema = mongoose.Schema;
var userSchema = new Schema({
trigramUser:
{ type: String, required: true },
// attributes...
customersUser:
[{ type: String, ref: 'Customer'}]
}, { collection: 'user'});
module.exports = mongoose.model('User', userSchema, 'user');
However, for the createCustomer method, the console shows when I submit the form :
Creation of the customer 987987987987987 in progress.
Worker attached to the customer PLD .
ID worker : undefined .
So the req.body.userCustomer reach the user.find({}, '_id', method.
Now if I write in Mongo :
db.user.findOne({trigramUser:'PDL'})._id.str;
The output is 5988897d7b55ba0548d95da6.
Why when I print req.body.userCustomer (which is the string 'PDL') in the method user.findOne({trigramUser: req.body.userCustomer}, '_id', the string is null and the query doesn't work?

Related

Is it possible to populate data with respect to other fields not _id in mongoose

All of the time populating data booking_unique_id is giving me null.
Here is the schema:
const chat = require('../models/chat.model')
const booking_details = new Schema({
booking_unique_id:{type:Object,ref:chat,field:'chat_screen_id'}
});
const chat_details = new Schema({
...
receiver_public_name:{type:String}
chat_screen_id:{type:Object}
});
Booking.find({booking_status:'e'}).populate('booking_unique_id'))
Currently it is not supporting in ref populate, there are issues in Mongoose Issue-3225, and Issue-1888,
For the alternative they have published populate-virtuals,
chat schema
const chat_details = new Schema({
...
receiver_public_name: { type: String }
chat_screen_id: { type: Object }
});
booking schema
const chat = require('../models/chat.model');
const booking_details = new Schema({
booking_unique_id: { type: Object }
});
booking virtual
booking_details.virtual('bookings', {
ref: chat, // The model to use
localField: 'booking_unique_id', // Find booking where `localField`
foreignField: 'chat_screen_id', // is equal to `foreignField`
// Query options, see /mongoose-query-options
// options: { sort: { name: -1 }, limit: 5 }
});
booking model
const Booking = mongoose.model('Booking', booking_details);
booking find query with populate
Booking.find({ booking_status: 'e' }).populate('bookings').exec(function(error, result) {
console.log(result);
});

Autoincrement with Mongoose

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" });

Mongoose remove document with references

I have two Schemas, eventSchema and personSchema as shown below:
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var eventSchema = Schema({
title : String,
location : String,
startDate : Date,
endDate : Date
});
var personSchema = Schema({
firstname: String,
lastname: String,
email: String,
dob: Date,
city: String,
eventsAttended: [{ type: Schema.Types.ObjectId, ref: 'Event' }]
});
var Event = mongoose.model('Event', eventSchema);
var Person = mongoose.model('Person', personSchema);
How can I remove all eventsAttended from a deleted Person?
For example, if I remove a Person then I expect that all events assigned to that Person will be removed.
This is my code:
Person.findOneAndRemove({_id: req.body._id}, (err, response) => {
// remove the events assigned to this person
})
With mongoose you can use pre and post middleware on your schemas:
personSchema.post('remove', removeLinkedDocuments);
Then in the removeLinkedDocuments callback, you can remove all linked documents:
function removeLinkedDocuments(doc) {
// doc will be the removed Person document
Event.remove({_id: { $in: doc.eventsAttended }})
}
Note the middleware is only called for the following methods (refer to the linked documentation for details):
count
find
findOne
findOneAndRemove
findOneAndUpdate
update
To remove the documents 'manually' in your callback, you might do
Person.findOneAndRemove({_id: req.body._id}, (err, response) => {
// note that if you have populated the Event documents to
// the person documents, you have to extract the id from the
// req.body.eventsAttended object
Event.remove({_id: { $in: req.body.eventsAttended }}, (err, res) => {
...
})
})

Mongoose Populate Cast Error

I have the follwing schemas:
// online.js
var mongoose = require('mongoose');
// Online Friends
var onlineSchema = mongoose.Schema({
userid:{ type: Number, ref:'Player' }
},{timestamps : true});
// Export
module.exports = mongoose.model('Online', onlineSchema);
//player.js
var mongoose = require('mongoose');
// define the schema for player data
var playerSchema = mongoose.Schema({
userid : { type: Number, required: true,unique:true, default: 0 },
firstname : { type: String },
nick : {type: String, required: true},
lastname : {type: String},
lastupdate: {type: Date, default: Date.now}
},{timestamps : true});
// create the model and expose it to our app
module.exports = mongoose.model('Player', playerSchema);
//main.js
...
const Online = require('./config/models/online.js');
const Player = require('./config/models/player.js');
Online.find({userid:3441341}).populate('userid').exec(function(err,result){
if (err) {
console.log(err);
} else {
console.log(result);
}
});
...
I want to search Online for a userid and then print the name of the user, not exactly sure what I am doing wrong.
This is the error I'm getting:
MongooseError: Cast to ObjectId failed for value "3441341" at path "_id"
What is the proper way to achieve this ?
Change the userid type from Number to mongoose.Schema.ObjectId and it should work. It basically says you are trying to cast a number to Object Id. Pass the ids as strings.
In NoSQL DBS your documents must have _id field. Do you have a User model or do you use Player for the job? If you use Player as User model so change user_id to _id

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