Mongoose $in not working in nodeJS - node.js

I've created a collection containing some operation details like below
{ "_id" : ObjectId("580776455ecd3b4352705ec4"), "operation_number" : 10, "operation_description" : "SHEARING", "machine" : "GAS CUTT" }
{ "_id" : ObjectId("580776455ecd3b4352705ec5"), "operation_number" : 50, "operation_description" : "EYE ROLLING -1", "machine" : "E-ROLL-1" }
{ "_id" : ObjectId("580776455ecd3b4352705ec6"), "operation_number" : 60, "operation_description" : "EYE ROLLING -2", "machine" : "E-ROLL-1" }
{ "_id" : ObjectId("580776455ecd3b4352705ec7"), "operation_number" : 70, "operation_description" : "EYE REAMING", "machine" : "E-REAM" }
{ "_id" : ObjectId("580776455ecd3b4352705ec8"), "operation_number" : 80, "operation_description" : "COLD CENTER HOLE PUNCHING", "machine" : "C-PNCH-1" }
{ "_id" : ObjectId("580776455ecd3b4352705ec9"), "operation_number" : 150, "operation_description" : "READY FOR HT", "machine" : "RHT" }
using mongoose model as below
var mongoose = require('mongoose');
var uniqueValidator = require('mongoose-unique-validator');
var Promise = require("bluebird");
mongoose.Promise = Promise;
var Schema = mongoose.Schema;
var operationSchema = new Schema({
operation_number: {
type: String,
required: [
true,
"Please select valid operation code"
]unique : true
},
operation_description: {
type: String,
required: [
true,
"Please select valid operation description"
]
}
}, { strict: false });
var operation = mongoose.model('operation', operationSchema);
operationSchema.plugin(uniqueValidator, { message: 'Error, {PATH} {VALUE} already exist.' });
// make this available to our users in our Node applications
module.exports = operation;
now if I query this collection operations using db.operations.find({operation_number : {$in : [10, 50, 60]}}) it works but when it comes to mongoose it's not working.
var mc = require("./data-models/operation")
var filter = {'operation_number':
{$in :
[10, 50, 60]
}
}
console.log(filter)
mc.find(filter, function(me, md){
console.log(me, md) // prints null []
})
Even I've tried removing single quotes around operation_number
Please help finding way !

Your schema says that operation_number is a string:
operation_number: {
type: String, <-- here
...
}
Therefore, Mongoose will cast the numbers in the $in array to strings.
However, the data in the database is numerical, which is a different type. You should change your schema so that operation_number becomes a Number:
operation_number: {
type: Number,
...
}

Sometimes it happens that you forget to define that attribute in the schema.

Related

How to improve mongoDb query performance?

I have a collection named Codes. This is how the Schema is defined:
import mongoose from 'mongoose'
import autoIncrement from 'mongoose-auto-increment';
const Schema = mongoose.Schema;
const CodesSchema = mongoose.Schema(
{
configId: { type: Number },
campaignId: { type: Number },
permissions: {
all: { type: Boolean, default: false },
users: { type: [Number], default: [] }
}
)
autoIncrement.initialize(mongoose.connection);
CodesSchema.plugin(autoIncrement.plugin, { model: 'Codes', field: 'campaignId' });
export default mongoose.model('Codes', CodesSchema)
There is one query that looks like this:
const query = {
$and:[
{$or: [
{'permissions.all': true},
{'permissions.users': 12}
]},
{configId: 3}
]
};
Codes.find(query, (err, res) => {
// do something with the result
})
This works fine, but if there is a huge number of documents in the database then this query is really slow.
Is there anything I can do to improve the performance of this specific query? I'm thinking that createIndex would help, but I'm not sure if that can be applied since there are $and and $or conditions.
UPDATE
I've added indexes this way:
CodesSchema.index({configId: 1, 'permissions.all': 1, 'permissions.users': 1});
But running the query with .explain('executionStats') option returns:
{
"executionSuccess" : true,
"nReturned" : 6,
"executionTimeMillis" : 0,
"totalKeysExamined" : 10,
"totalDocsExamined" : 10,
}
Which doesn't seems right because the number of docs examined is greater than the number of docs returned.
The index itself is correct.
It must be CodesSchema.index, not Code.index.
Ensure you call Code.syncIndexes to update indexes dbside.
The "explain" part - you should check winningPlan.
If no indexes are used by the query, it should be something like
"winningPlan" : {
"stage" : "COLLSCAN",
"filter" : {
When the index is being used it changes to
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "OR",
"inputStages" : [
{
"stage" : "IXSCAN",
"keyPattern" : {

Skip one nested level of subdocument in Mongoose

I have a parent schema with a subdocument. The subdocument has a property with an array of embedded objects:
Child schema
var snippetSchema = new Schema({
snippet: [
{
language: String,
text: String,
_id: false
}
]
});
Parent schema
var itemSchema = new Schema({
lsin: Number,
identifier: {
isbn13: Number,
},
title: snippetSchema,
});
Which upon Item.find returns an object like so:
[
{
_id: (...),
lsin: 676765,
identifier: {
isbn13: 8797734598763
},
title: {
_id: (...),
snippet: [
{
language: 'se',
text: 'Pippi Långstrump'
}
]
}
}
]
I would like to skip one nested level of the subdocument when the object is returned to the client:
[
{
_id: (...),
lsin: 676765,
identifier: {
isbn13: 8797734598763
},
title: {
language: 'se',
text: 'Pippi Långstrump'
}
}
]
So far I have tried:
#1 using a getter
function getter() {
return this.title.snippet[0];
}
var itemSchema = new Schema({
...
title: { type: snippetSchema, get: getter }
});
But it creates an infinite loop resulting in RangeError: Maximum call stack size exceeded.
#2 using a virtual attribute
var itemSchema = new Schema({
..., {
toObject: {
virtuals: true
}
});
itemSchema
.virtual('title2')
.get(function () {
return this.title.snippet[0];
});
Which will generate the desired nested level but under a new attribute, which is not acceptable. To my knowledge there is no way of overriding an attribute with an virtual attribute.
The question is: is there any other way to go about in getting the desired output? There will be several references to the snippetSchema across the application and a DRY method is preferred.
I am new to MongoDB and Mongoose.
You'll need to use the $project within an mongodb aggregation pipeline.
Within my database I have the following:
> db.items.find().pretty()
{
"_id" : 123,
"lsin" : 676765,
"identifier" : {
"isbn13" : 8797734598763
},
"title" : {
"_id" : 456,
"snippet" : [
{
"language" : "se",
"text" : "Pippi Långstrump"
}
]
}
}
Then we just need to create a simple aggregation query:
db.items.aggregate([
{$project: { lsin: 1, identifier: 1, title: { $arrayElemAt: [ '$title.snippet', 0 ] }}}
])
This just uses a $project (https://docs.mongodb.com/v3.2/reference/operator/aggregation/project/) and a $arrayElemAt (https://docs.mongodb.com/v3.2/reference/operator/aggregation/arrayElemAt/) to project the first item out of the array. If we execute that we will get the following:
{
"_id" : 123,
"lsin" : 676765,
"identifier" : {
"isbn13" : 8797734598763
},
"title" : {
"language" : "se",
"text" : "Pippi Långstrump"
}
}

MongoDB updating embedded document isn't working

I'm trying to update embedded document, but it is not working. This is what documents look like:
{
"_id" : ObjectId("577c71735d35de6371388efc"),
"category" : "A",
"title" : "Test",
"content" : "Test",
"tags" : "test",
"comments" : [
{
"_id" : ObjectId("57811681010bd12923eda0ca"),
"author" : "creator",
"email" : "creator#example.com",
"text" : "helloworld!"
},
{
"_id" : ObjectId("57811b17b667676126bde94e"),
"author" : "creator",
"email" : "creator#example.com",
"text" : "helloworld2!"
}
],
"createdAt" : ...,
"updatedAt" : ...
}
you can see the comments field is embedded document that contains comments. I want to update specific comment, so I made query like this(node.js):
db.update('posts', {
_id: new ObjectID(postId), // ID of the post
comments: {
$elemMatch: {
_id: new ObjectId(commentId)
}
}
}, {
$set: {
"comments.$.author": newComment.author,
"comments.$.email": newComment.email,
"comments.$.text": newComment.text,
"comments.$.updatedAt": new Date()
}
}) ...
when I run this query, no error was shown but update wasn't applied. I tried this query too:
{
_id: new ObjectId(postId),
"comments._id": new ObjectId(commentId)
}
but not worked either. Am I missing something? I'm using Mongo v3.2.7.
Please try the below code. I think the "ObjectId" (i.e. case) should be the problem. Just check how you defined the object id and keep it consistent in the two places that you have used (i.e. posts _id and comments _id -> both places).
ObjectID = require('mongodb').ObjectID
The below code works fine for me. Basically, your query seems to be correct.
var Db = require('mongodb').Db, MongoClient = require('mongodb').MongoClient, Server = require('mongodb').Server, ReplSetServers = require('mongodb').ReplSetServers, ObjectID = require('mongodb').ObjectID, Binary = require('mongodb').Binary, GridStore = require('mongodb').GridStore, Grid = require('mongodb').Grid, Code = require('mongodb').Code, assert = require('assert');
var db = new Db('localhost', new Server('localhost', 27017));
db.open(function(err, db) {
var collection = db.collection("posts");
var postId = '577c71735d35de6371388efc';
var commentId = '57811681010bd12923eda0ca';
var query = {
_id : new ObjectID(postId),
comments : {
$elemMatch : {
_id : new ObjectID(commentId)
}
}
};
collection.update(query, {
$set : {
"comments.$.author" : "new author",
"comments.$.email" : "newemail#gmail.com",
"comments.$.text" : "new email updated",
"comments.$.updatedAt" : new Date()
}
}, {
multi : false
}, function(err, item) {
assert.equal(null, err);
console.log("comments updated ..." + JSON.stringify(item));
});
});

Update existing items in nested array using mongoose

There is a schema as mentioned below. I'm trying to update the existing todo.task.
The problem is, I am storing the path as var done = 'todos.'+todoIndex+'.tasks.'+taskIndex+'.done' and it does not work. I was looking to update like todos.0.tasks.0.done:req.body.done, but it doen't work at all.
(todoIndex and taskIndex are in string which stores the index values)
What is the correct way of doing this?
var mongoose = require('mongoose');
var todoSchema = {
authorId : mongoose.Schema.Types.ObjectId,
date:{
type:Date
},
title : {
type : String
},
description : {
type : String
},
todos : [ {
created : {
type : Date
},
updated : {
type : Date
},
title : {
type : String
},
description : {
type : String
},
done:{
type:Boolean
},
deadline:{
type:Date
},
tasks : [ {
done : Boolean,
task : String
} ]
} ]
}
module.exports = new mongoose.Schema(todoSchema);
module.exports.todoSchema = todoSchema;
I was trying to build the Api like this:
api.put('/notebooks/todo/update/:pid',wagner.invoke(function(Todo,User){
return function(req,res){
var taskIndex=req.body.taskIndex;
var todoIndex=req.body.todoIndex;
var done = 'todos.'+todoIndex+'.tasks.'+taskIndex+'.done';
console.log(done);
Todo.update({_id:req.params.pid},{$set:{
done : req.body.done,
}}, function(err,done){
console.log( done);
})
}}));
If you're using a recent Node version, you can use a computed property name:
Todo.update({ _id : req.params.pid }, { $set : { [ done ] : req.body.done } }, ...
Otherwise, you need to use an intermediate object:
var done = 'todos.'+todoIndex+'.tasks.'+taskIndex+'.done';
var obj = {};
obj[done] = req.body.done;
Todo.update({ _id : req.params.pid }, { $set : obj }, ...

NodeJS + Mongoose + Backbone.Marionette nested Collection templating

all
I'm developing an application that store my multimedia catalog, I've JSON collection like this :
{ "_id" : ObjectId( "5142f55394474e2aac000001" ),
"contentType" : "binary/octet-stream",
"length" : 2732376,
"chunkSize" : 262144,
"uploadDate" : Date( 1363342677601 ),
"metadata" : {
"TIT2" : "Chase The Blues (Cameron McVey Remix)",
"TPE1" : "Terranova",
"TRCK" : "1/13",
"TALB" : "!K7",
"TPOS" : "1/1",
"TDRC" : "2000-06",
"TCON" : [
"Electronica",
"Trip-Hop" ],
"COMM" : [
"Chillout",
"Love",
"German",
"Berlin",
"2000s",
"Female Vocalists",
"Male Vocalists" ],
"TMED" : "CD",
"TMOO" : "Chill",
"TDOR" : "2000-06",
"TSO2" : "Various Artists",
"TPE2" : "Various Artists",
"TCMP" : "1",
"TSOP" : "Terranova",
"TIT1" : "Electronica",
"TPUB" : "Sinedín Music",
"TLAN" : "eng",
"TYER" : [
"2000" ],
},
"md5" : "617401af615ac0c6cb1dee9a3f1b99e6",
"origin" : "Chase The Blues.109eb5ab5105a1caa505a26657f7f9a8.mp3",
"evolution" : null,
"insertDate" : Date( 1336662308000 ),
"tagSource" : "MusicBrainz",
"mediainfo" :
{ "Format" : "MPEG Audio",
"Format version" : "Version 1",
"Format profile" : "Layer 3",
"Duration" : "3mn 47s",
"Bit rate mode" : "Constant",
"Bit rate" : "96.0 Kbps",
"Channel(s)" : "1 channel",
"Sampling rate" : "44.1 KHz",
"Compression mode" : "Lossy",
"Stream size" : "2.60 MiB (100%)",
"Language" : "English"
}
}
so, as you can see, there are "metadata" and "mediainfo" array in the document
in the models.js , in the client side, I've rewrite the model parse function like this
var Audio_Model = Backbone.Model.extend({
idAttribute: "_id",
url: 'AudioModel',
urlRoot: 'AudioModel' ,
parse: function(response) {
// Check if response includes some nested collection data...
if (_.has(response, 'metadata')){
// Check if this model has a property called metadata
if (!_.has(this, 'metadata')) { // It does not...
// So instantiate a collection and pass in raw data
this.metadata = new Audio_Collection_Metadata(response.metadata);
} else {
// It does, so just reset the collection
this.metadata.reset(response.metadata);
}
delete response.metadata;
}
// Check if response includes some nested collection data...
if (_.has(response, 'mediainfo')){
// Check if this model has a property called mediainfo
if (!_.has(this, 'mediainfo')) { // It does not...
// So instantiate a collection and pass in raw data
this.mediainfo = new Audio_Collection_Mediainfo(response.mediainfo);
} else {
// It does, so just reset the collection
this.mediainfo.reset(response.mediainfo);
}
delete response.mediainfo;
}
return response;
}
});
so I've created two separate collection of 'metadata' and 'mediainfo'
the problem that I've is how to render 'metadata' and 'mediainfo' in html template because in 'mediainfo' and 'metadata' collection the key, values are not fixed and in 'metadata' some keys are array of values and the number of item in the array are not fixed
I've created backbone.marionette.itemview and compositeview for these two collections but I don't know how to render
Plase, someone have a solutions ?
Best Regards
finally I've fixed the problem myself with a data normalization, this is the new mongoose schema adopted :
var TagSchema = new mongoose.Schema({
value : {type : String, default: '', required: true}
});
var MetadataSchema = new mongoose.Schema({
name : {type: String, default: '', required : true},
values: [TagSchema]
});
var MediainfoSchema = new mongoose.Schema({
name : {type: String, default: ''},
value: {type: String, default: ''}
});
var StreamSchema = new mongoose.Schema({
_id: mongoose.Schema.ObjectId,
TIT2: {type : String, default: '', required: true},
metadata: [MetadataSchema],
mediainfo:[MediainfoSchema]
});
so that with sequence of CollectionView and CompositeView I can browse the entire model
Hope this can help someone

Resources