Why are mongoose references turning into IDs? - node.js

According to http://mongoosejs.com/docs/populate.html, if I set a ref property to an object, and not an ID, when getting it, I should get back an object and not an ID. I'm referring to this part of the page:
var guille = new Person({ name: 'Guillermo' });
guille.save(function (err) {
if (err) return handleError(err);
story._creator = guille;
console.log(story._creator.name);
// prints "Guillermo" in mongoose >= 3.6
// see https://github.com/LearnBoost/mongoose/wiki/3.6-release-notes
Here's my sample code:
var T1schema = new mongoose.Schema({
otherModel:{type:mongoose.Schema.Types.ObjectId, ref:"T2"}
});
var T1 = mongoose.model('T1', T1schema);
var T2schema = new mongoose.Schema({
email: {type: String},
});
var T2 = mongoose.model('T2', T2schema);
var t1 = new T1();
var t2 = new T2({email:"foo#bar.com"});
t1.otherModel = t2;
Now when I refer to t1.otherModel, it's just an ObjectId, and not a T2. For example:
console.log(t1.otherModel.email);
prints undefined instead of "foo#bar.com". Any ideas why? Note I'm using Mongoose 3.6.18 according to it's package.json.
Thanks!

I think your expectations here just don't match what mongoose does. The schema is a way of saying "model M's property P will always be of type T". So when you set a value, mongoose uses the schema definitions to cast the set value to the type the schema requires. Here's a little REPL session. Note setting a number property with a string value casts it to a number, but trying to store a boolean in a number field just ignores the errant value.
> var mongoose = require('mongoose')
> var schema = new mongoose.Schema({prop1: Number})
> var Model = mongoose.model('Model', schema)
> var m = new Model
> m.prop1 = 42
42
> m.prop1
42
> m.prop1 = "42"
'42'
> m.prop1
42
> m.prop1 = false
false
> m.prop1
42
So when your schema says something is going to be an ObjectId, if you give it a model instance, mongoose immediately converts it to an ObjectId in preparation for a write to the database, which is the common case. Normally if you just set that model instance, you don't need to get it back out of the parent model before saving the parent model to the database.
So the model instance getter defined properties are always going to return something compatible with the schema, and populate has to do with loading refs from the DB, but for whatever reason mongoose just doesn't work the same comparing a .populated instance with a non-populated instance. I can see why this is confusing and perhaps unexpected/disappointing though.

Mongoose is normalizing the instance to match the Schema, which specifies that otherModel is an ObjectId.
otherModel:{type:mongoose.Schema.Types.ObjectId, ref:"T2"}
So, Mongoose treats t1.otherModel = t2; the same as:
t1.otherModel = t2._id;
The ref isn't used until .populate() is called (either directly on the document or in a query), which needs both objects to be saved:
t2.save(function (err) {
t1.save(function (err) {
console.log(t1.otherModel);
// 7890ABCD...
t1.populate('otherModel', function () {
console.log(t1.otherModel);
// { email: 'foo#bar.com', _id: 7890ABCD..., __v: 0 }
});
});
});

Related

how to query mongoDb with array of boolean fields?

My mongoose model schema looks like -
{
email: { type: String},
Date: {type: Date},
isOnboarded: {type: Boolean},
isVip: {type: Boolean},
isAdult: {type: Boolean}
}
In my frontend I have 3 checkboxes for "isVip", "isOnboarded" and "isAdult" options. If they are checked I'm adding them to an array, which I'll pass to the server. Let's say if "isVip" and "isAdult" are checked, I will pass [isVip, isAdult] in post api to server. Now how can I write a query to get all the documents with the fields in array as true i.e in above example how can I retrieve all docs with {isVip: true, isAdult:true}
I'm having trouble because the array values keep changing, it can be only one field or 3 fields. I couldn't find a way to give condition inside mongoose query.
User.find(
{ [req.query.array]: true},
{ projection: { _id: 0 } }
)
User is my mongoose model.
I want something like this (documents with the value 'true' for the fields given in the array) and 'req.query.array' is the array with field names I passed from frontend.
You have to create your object in JS and pass then to mongo in this way:
var query = {}
if(isVip) query["isVip"] = true;
if(isOnboarded) query["isOnboarded"] = true;
if(isAdult) query["isAdult"] = true;
And then you can use the mongoose method you want, for example:
var found = await model.find(query)
And this will return the document that matches the elements.
Also, note that this is to create and object to be read by the query, so you can use this into an aggregation pipeline or whatever you vant
Check the output:
var query = {}
query["isVip"] = true;
query["isOnboarded"] = true;
query["isAdult"] = true;
console.log(query)
Is the same object that is used to do the query here
{
"isVip": true,
"isOnboarded": true,
"isAdult": true
}
Also, to know if your post contains "isVip", "isOnboarded" or "isAdult" is a javascript question, not a mongo one, but you can use something like this (I assume you pass a string array):
var apiArray = ["isVip","isAdult"]
var isVip = apiArray.includes("isVip")
var isAdult = apiArray.includes("isAdult")
var isOnboarded = apiArray.includes("isOnboarded")
console.log("isVip: "+isVip)
console.log("isAdult: "+isAdult)
console.log("isOnboarded: "+isOnboarded)

Inserting into array error in Mongoose

This is my MongoDB schema, Seems like push operator is not working
locationCoordinate : {
type : [Number],
index: '2d'
},
i am getting Post data from frontend in my Node.js server which is having Longitude and Latitude
var event = new Events({})
(Events is the name of Schema )
I want to push into this array,
so this seems not to be working
Error on this line
event.locationCoordinate.push(req.body.longitude);
event.locationCoordinate.push(req.body.latitude);
And then saving this by
event.save(function(err,result)){
}
The result may be something like
locationCoordinate[1,2]
var array = [];
array.push({"lng":req.body.longitude},{"lat":req.body.latitude}); // or
array.push(req.body.longitude,req.body.latitude); //just try mybe work
var Evnt = new Events()
Evnt.type = array;
Evnt.index = req.body.index;
Evnt.save(function(...){ ... });

Mongoose Bulk upsert not working

Hi i am new to nodejs and mongoose. Just trying to update mongo data with bulk inserts with following methods.
"use strict";
var mongo = require('../models/tracking_mongo');
var Schema = mongo.trackingMasterMongoosePoc.Schema;
//create a schema
var userwise_tracking_events = new Schema({
activities : {},
summary : {},
userId : Number,
counter : Number,
created : Date,
modified : Date
});
let collection = 'userwise_tracking_events';
let UserwiseTrackingEvents = mongo.trackingMasterConnPoc.model(collection, userwise_tracking_events);
UserwiseTrackingEvents.updateCollectionStream = function(condition, params, options, callback){
var Bulk = UserwiseTrackingEvents.collection.initializeUnorderedBulkOp();
Bulk.find(condition).upsert().update(params);
Bulk.execute(callback);
};
module.exports = UserwiseTrackingEvents;
Upper code works fine but this didn't solved my problem for using bulk inserts.
Then i just made a change by making the bulk variable global.
"use strict";
var mongo = require('../models/tracking_mongo');
var Schema = mongo.trackingMasterMongoosePoc.Schema;
//create a schema
var userwise_tracking_events = new Schema({
activities : {},
summary : {},
userId : Number,
counter : Number,
created : Date,
modified : Date
});
let collection = 'userwise_tracking_events';
let UserwiseTrackingEvents = mongo.trackingMasterConnPoc.model(collection, userwise_tracking_events);
var Bulk = UserwiseTrackingEvents.collection.initializeUnorderedBulkOp();
UserwiseTrackingEvents.updateCollectionStream = function(condition, params, options, callback){
Bulk.find(condition).upsert().update(params);
Bulk.execute(callback);
};
module.exports = UserwiseTrackingEvents;
Now getting the error find of undefined is not a function.
When i checked
console.log(UserwiseTrackingEvents.collection);
Gave me result with NativeCollection.collection is null.
Do let me know what i am doing wrong.
I have another methods to work on this but i specifically want what i am doing wrong in this question.
Thanks :)
I would venture to say that your declaration of Bulk is not outside of any function declaration when moved out of the function call. Thus when the exposed function is called , Bulk was not initialized.

Mongoose Plugin - schema.add to Subschema

I am writing a mongoose plugin that involves adding a elements to the subschema. I was thinking it would be quite straightforward, but I've been trying unsuccessfully for a few days now.
Below is a simplification of the schema:
var inheritables = require('./inheritables.server.module.js');
var OuFieldTypeSchema = new Schema({
name: String,
desc: String
});
var OuFieldType = mongoose.model('OuFieldType', OuFieldTypeSchema);
var OuSchema = new Schema({
name: String,
fieldTypes: [OuFieldTypeSchema],
});
OuSchema.plugin(inheritables, {inherit: ['fieldTypes']});
mongoose.model('Ou', OuSchema);
Within the body of my Mongoose Plugin, I'd like to iterate over the "inherit" array and add the following to the schemas that represent each of these elements in the Ou schema;
A simplification of my plugin is as follows:
var _ = require('lodash');
module.exports = exports = function(schema, options) {
_.each(options.inherit, function(currItemSchema){
var addToSchema = {inheritedFrom: {name: String}};
//Really want to add this to the OuFieldTypeSchema represented by the fieldTypes element array.
schema.add(addToSchema, currItemSchema + ".");
});
});
Note that the second parameter of add() adds a prefix, but this doesn't work.
I've also tried:
_.each(options.inherit, function(currItemSchema){
var addToSchema = {};
addToSchema[currItemSchema] = {inheritedFrom: {name: String}};
schema.add(addToSchema);
});
The expected behaviour here is that elsewhere in my plugin, I have a method that sets the inheritedFrom schema. No error is received when the item is set, but the resulting output never sees the inheritedFrom elements.
I am currently adding inheritedFrom to each subschema that is used by OuFieldType, but the above is a simplification and is not very DRY.
I'm new to Node.JS - any pointers in the right direction would be greatly appreciated.

MongoDB Node check if objectid is valid

How can I check whether an ObjectID is valid using Node's driver
I tried :
var BSON = mongo.BSONPure;
console.log("Validity: " + BSON.ObjectID.isValid('ddsd'))
But I keep getting an exception instead of a true or false. (The exception is just a 'throw e; // process.nextTick error, or 'error' event on first tick'
This is a simple check - is not 100% foolproof
You can use this Regular Expression if you want to check for a string of 24 hex characters.
var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$")
checkForHexRegExp.test("i am a bad boy")
// false
checkForHexRegExp.test("5e63c3a5e4232e4cd0274ac2")
// true
Regex taken from github.com/mongodb/js-bson/.../objectid.ts
For a better check use:
var ObjectID = require("mongodb").ObjectID
ObjectID.isValid("i am a bad boy")
// false
ObjectID.isValid("5e63c3a5e4232e4cd0274ac2")
// true
isValid code github.com/mongodb/js-bson/.../objectid.ts
isValid() is in the js-bson (objectid.ts) library, which is a dependency of node-mongodb-native.
For whoever finds this question, I don't recommend recreating this method as recommend in other answers. Instead, continue using node-mongodb-native like the original poster was using, the following example will access the isValid() method in js-bson.
var mongodb = require("mongodb");
var objectid = mongodb.BSONPure.ObjectID;
console.log(objectid.isValid('53fbf4615c3b9f41c381b6a3'));
July 2018 update: The current way to do this is:
var mongodb = require("mongodb")
console.log(mongodb.ObjectID.isValid(id))
As an extension of Eat at Joes answer... This is valid in node-mongodb-native 2.0
var objectID = require('mongodb').ObjectID
objectID.isValid('54edb381a13ec9142b9bb3537') - false
objectID.isValid('54edb381a13ec9142b9bb353') - true
objectID.isValid('54edb381a13ec9142b9bb35') - false
If you are using mongoose then you can use mongoose for validation rather than depending on any other library.
if (!mongoose.Types.ObjectId.isValid(req.id)) {
return res.status(400).send("Invalid object id");
}
#GianPaJ's snippet is great but it needs to be extended slightly to cover non hex objectID's. Line 32 of the same file indicates objectID's can also be 12 characters in length. These keys are converted to a 24 character hex ObjectID by the mongodb driver.
function isValidObjectID(str) {
// coerce to string so the function can be generically used to test both strings and native objectIds created by the driver
str = str + '';
var len = str.length, valid = false;
if (len == 12 || len == 24) {
valid = /^[0-9a-fA-F]+$/.test(str);
}
return valid;
}
Below is my model where I am trying to validate subject id that is of type objectId data using JOI (Joi.objectId().required()):
const Joi = require('joi');
const mongoose = require('mongoose');
const Category = mongoose.model('Category', new mongoose.Schema({
name: String
}));
function validateCategory(category) {
const schema = {
name: Joi.string().min(5).max(50).required(),
subject_id: Joi.objectId().required(),
};
return Joi.validate(category, schema);
}
exports.Category = Category;
exports.validate = validateCategory;
joi-objectid validates that the value is an alphanumeric string of 24 characters in length.
MongoDB ObjectId validator for Joi.
Follow this regular expression :
in js
new RegExp("^[0-9a-fA-F]{23}$").test("5e79d319ab5bfb2a9ea4239")
in java
Pattern.compile("^[0-9a-fA-F]{23}$").matcher(sanitizeText(value)).matches()
You can use Cerberus and create a custom function to validate and ObjectId
from cerberus import Validator
import re
class CustomValidator(Validator):
def _validate_type_objectid(self, field, value):
"""
Validation for `objectid` schema attribute.
:param field: field name.
:param value: field value.
"""
if not re.match('[a-f0-9]{24}', str(value)):
self._error(field, ERROR_BAD_TYPE % 'ObjectId')
## Initiate the class and validate the information
v = CustomValidator()
schema = {
'value': {'type': 'objectid'}
}
document = {
'value': ObjectId('5565d8adba02d54a4a78be95')
}
if not v(document, schema):
print 'Error'

Resources