MongoDB / Mongoose $pull (remove) Sub Document not working - node.js

Smashing my head into the keyboard over this.
Simply need to remove subdocument. Example below only has one item in OnCommands but there could be a many items there. I have tried find, findbyid, updatebyId, pull, one things after another. Tried by _id of subdoc and by generic searchinMost simple run without doing anything no errors.
I would be so greatful if you can show me what I am doing wrong, it's the last part of my code that isn't work.
Sample Data:
> db.EntryPoints.find({_id: ObjectId("569e4fabf1e4464495ebf652")}).pretty()
{
"__v" : 0,
"_id" : ObjectId("569e4fabf1e4464495ebf652"),
"name" : "bbbb",
"offCommands" : [ ],
"onCommands" : [
{
"data" : "11111",
"operation" : "on",
"command" : "ISY-HTTPGet",
"_id" : ObjectId("569e4faff1e4464495ebf653")
}
]
Model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var onCommandsSchema = new Schema({
command: String
,operation: String
,data: String
})
var offCommandsSchema = new Schema({
command: String
,operation: String
,data: String
})
mongoose.model('onCommands', onCommandsSchema);
mongoose.model('offCommands', offCommandsSchema);
// create a schema
var EntryPointsSchema = new Schema({
name: String
,onCommands: [onCommandsSchema]
,offCommands: [offCommandsSchema]
,description: String
}, { collection: 'EntryPoints' });
mongoose.model('EntryPoints', EntryPointsSchema);
var EntryPoints = mongoose.model('EntryPoints');
module.exports = EntryPoints;
Node Post Code:
router.post('/webservices/removeCommand', function (req, res) {
var EntryPoints = require('../data_models/automate_entrypoints.js');
EntryPoints.update(
{ _id: ObjectId(req.body._id) }
, {
$pull: {
onCommands: { id_: req.body._id }
}
}
, function (err, ouput) { console.log("data:", numAffected) }
);
});

Your code won't work because of the query part of your update: you want to match on the embedded document's _id, not on the main document. So change it to
var EntryPoints = require('../data_models/automate_entrypoints.js');
EntryPoints.update(
{ "onCommands._id": req.body._id },
{
"$pull": {
"onCommands": { "_id": req.body._id }
}
},
function (err, numAffected) { console.log("data:", numAffected) }
);

Related

Weird behavior with mongoose Schema $inc operator

I'm working a todo app, it has projects and each project has todolistsand todoListsCount .
When user creates a new todoList I want to increment the project's todoListsCount, the problem I found is that somehow after creating the first todoList the project's todoListsCount successfully increment but the new value is 2 I suppose to what I was expecting which is 2 :
let project= null
await Project.findOneAndUpdate(
{_id:projectId},
{ $inc:{ todoListsCount:1 }},
{ new: true},
(err, doc) => {
if (err)console.log("Something wrong when updating data!");
project =doc
}
);
const todoList = new TodoList({
title,
owner,
projectId,
orderInProject : project.todoListsCount,
created_at : new Date(),
progress : 0,
done_at : new Date(),
todos : [],
done : false,
})
await todoList.save()
as you can see in the Project schema default value of todoListsCount is 0
import mongoose from 'mongoose'
const Schema = mongoose.Schema;
const ProjectSchema = new Schema(
{
/..rest of fields
todosCount : {
type : Number ,
required :false ,
default: 0
},
todoListsCount : {
type : Number ,
required :false ,
default: 0
},
}
)
export const Project = mongoose.model('Project', ProjectSchema);
I guess you are using both await and callback for same function, can be the culprit. Try:
let project = await Project.findOneAndUpdate(
{_id:projectId},
{ $inc:{ todoListsCount:1 }},
{ new: true}).exec();

insert and insertOne not a function and update not creating mongo ID

I thought I could read my way to this solution, but I cant see what im doing wrong.
Here is my model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var inspectSchema = new Schema({
_id: Object, // Mongo ID
property: String, // Property ID
room: String, // The room Name
item: Array // The Items text
});
module.exports = mongoose.model('inspectModel', inspectSchema, 'inspect');
And here is where I try to insert or insertOne
var inspectModel = require('../../models/inspectModel');
var inspectTable = mongoose.model('inspectModel');
inspectTable.insert(
{
"property" : inspectRecord.property,
"room" : inspectRecord.room,
"item" : inspectRecord.item
},
function (err, res) {
if (err) { return reject({err:true, err:"addInspect ERROR" + err}) }
else {
show("=====RESOLVE addInspect=====")
return resolve();
}
})
I tried
inspectTable.insert
inspectModel.insert
inspectTable.insertOne
inspectModel.insertOne
No matter what I always get
TypeError: inspectTable.insert is not a function
I also tried just update with { upsert: true } but then the mongo ID becomes null.
Any ideas?
The method you're looking for is create:
inspectTable.create(
{
"property" : inspectRecord.property,
"room" : inspectRecord.room,
"item" : inspectRecord.item
}, ...
However, your schema definition of _id: Object is likely wrong. Just leave any definition of _id out of your schema and it will use the default ObjectId, which is likely what you want.
You can try this
var insert_table = new inspectTable(
{
"property" : inspectRecord.property,
"room" : inspectRecord.room,
"item" : inspectRecord.item
});
insert_table.save(function (err, res) {
if (err) { return reject({err:true, err:"addInspect ERROR" + err}) }
else {
show("=====RESOLVE addInspect=====")
return resolve();
}
});

MongoDB and Nodejs insert ID with auto increment

I am new to NodeJs and MongoDB, i want to insert row with auto increment primary key 'id'. also defined a function called getNextSequence on mongo server.
this is working perfect on Mongodb server
> db.user.insert({
"id" : getNextSequence('user_id'),
"username" : "test",
"email" : "test#test.com",
"password" : "test123"
})
now i want to insert from NodeJs.I have tried this but not working
db.collection('user').insertOne({
id : "getNextSequence('user_id')",
username : query.name,
email: query.email,
password: query.pass
}, function(err, result) {
assert.equal(err, null);
console.log("row insterted ");
callback();
});
Assuming that getNextSequence is a server-script function (i.e. a method you defined and saved via db.system.js.save), it is not callable outside of the server. One way to go is to use eval, which forces the server to evaluate a string as a js code, even though it is not a good practice. Here is an example:
db.eval('getNextSequence(\'user_id\')', function(err, result) {
db.collection('users').insert({
"id" : result,
"username" : "test",
"email" : "test#test.com",
"password" : "test123"
});
});
Another way is to follow the mongo tutorial and to implement the getNextSequence directly in NodeJS. The syntax is pretty much the same:
function getNextSequence(db, name, callback) {
db.collection("counters").findAndModify( { _id: name }, null, { $inc: { seq: 1 } }, function(err, result){
if(err) callback(err, result);
callback(err, result.value.seq);
} );
}
You then use it in your nodeJS code like:
getNextSequence(db, "user_id", function(err, result){
if(!err){
db.collection('users').insert({
"_id": result,
// ...
});
}
});
Note: of course, you need to have set the counters collection as explained in the docs.
You can also use "mongoose-auto-increment".
The code has just 4 lines
var mongoose = require('mongoose');
var autoIncrement = require('mongoose-auto-increment');
autoIncrement.initialize(mongoose.connection);
userSchema.plugin(autoIncrement.plugin, 'user');
example :
npm i mongoose-auto-increment
connections.js :
const mongoose = require('mongoose');
require("dotenv").config;
const uri = process.env.MONGOURL;
mongoose.connect(uri, { useNewUrlParser: true }, (err) => {
if (!err) { console.log('MongoDB Connection Succeeded.') }
else { console.log('Error in DB connection : ' + err) }
});
require('../schema/userSchema');
userSchema.js :
var mongoose = require('mongoose'); // 1. require mongoose
var autoIncrement = require('mongoose-auto-increment'); // 2. require mongoose-auto-increment
var userSchema = new mongoose.Schema({
name: { type: String },
password: { type: String },
email: { type: String, unique: true, required: 'This field is required.' },
});
autoIncrement.initialize(mongoose.connection); // 3. initialize autoIncrement
userSchema.plugin(autoIncrement.plugin, 'user'); // 4. use autoIncrement
mongoose.model('user', userSchema);
To accomplish this, we will create a function that will keep trying to save the document untill it will have been saved with incremented _id
async function retryUntilSave(db, task) {
try {
const index = await db.collection('tasks').find().count() + 1;
const result = await db.collection('tasks').insertOne(Object.assign(task, { _id: index }))
} catch (error) {
if (error.message.includes("_id_ dup key")) {
console.log("ID already exists!")
console.log("Retrying...");
retryUntilSave(db, task)
} else {
console.log(error.message);
}
}
}
We can use task._id: index instead of Object.assign()
finally you can test this by making some concurrent requests
for (let index = 0; index < 20; index++) {
setTimeout(async () => {
await retryUntilSave(db, { title: "Some Task" })
}, 1000);
}
This function will handle easily if two or more tasks submitted at the same time because mogod throws error when we try to insert a document with duplicate _id, then we will retry saving the document again with incremented _id and this process will run until we save the document successfully !
You can also use "mongodb-autoincrement" module of node js. For example:
var autoIncrement = require("mongodb-autoincrement");
exports.yourMethod = function(newData, callback) {
autoIncrement.getNextSequence(db, your-collection-name, function (err, autoIndex) {
newData.id = autoIndex;
//save your code with this autogenerated id
});
}
You can use the below package on a model schema to auto-increment your collection field.
mongoose-auto-increment //you can download it from npm
Here I am not focusing on how to connect MongoDB. I just focus on how you can integrate auto increment in your model/collection/table.
const mongoose = require("mongoose"); //
const autoIncrement = require("mongoose-auto-increment");
const post_schema = new mongoose.Schema({
title: {
type: String,
required: true,
min: 3,
max: 225,
},
slug: {
type: String,
required: true,
},
});
autoIncrement.initialize(mongoose.connection);
post_schema.plugin(autoIncrement.plugin, {
model: "post", // collection or table name in which you want to apply auto increment
field: "_id", // field of model which you want to auto increment
startAt: 1, // start your auto increment value from 1
incrementBy: 1, // incremented by 1
});
module.exports = mongoose.model("post", post_schema);

How to use $in in mongoose reference schema

I have 2 schemas 1st is city and second is pincode. Pincode having reference of city. They both look like this
CITY schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
// create a all city list
var allCitySchema = new Schema({
cities: {
type: String
}
}, {collection: 'allcities'});
var allcities = mongoose.model('allcities', allCitySchema);
module.exports = allcities;
Pincode schemas
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var allPincode = new Schema({
city_id: {
type: Schema.ObjectId,
ref: 'allcities'
},
pincode: {
type: String
}
}, {collection: 'pincode'});
var allPincode = mongoose.model('pincode', allPincode);
module.exports = allPincode;
Now the problem is when i tried to fetch all pincode based upon city id for that i tries like this
app.post('/api/getPincodeByCity', function(req, res) {
console.log("In pincode");
var cities_id = [];
cities_id = req.body.cities_id;
console.log(req.body); // { cities_id: '["5597aa08c0a0beb40be128d4","5597aa2bbb18fefc142b6915"]' }
console.log(cities_id);
pincodes.findById( {city_id: { $in: cities_id }}, function(err,pincodeIds){
if(err) res.send(err.message);
res.send(pincodeIds);
res.end('{"success" : "Recieved Successfully", "status" : 200}');
});
});
But it's not working its giving me this error
Cast to ObjectId failed for value "[object Object]" at path "_id"
I also try with find() instead of findById() method but it giving me this error
undefined is not a function
The $in operator is not just "strictly" for querying arrays as that can be done with basically any operator for a singular value.
It's actually a "list of arguments" which evaluates to an $or condition, but with shorter syntax:
var idList = ["559e0dbd045ac712fa1f19fa","559e0dbe045ac712fa1f19fb"];
var pincode = mongoose.model('pincode');
pincode.find({ "city_id": { "$in": idList } },function(err,docs) {
// do something here
});
Which as mentioned is short form for this:
pincode.find(
{
"$or": [
{ "city_id": "559e0dbd045ac712fa1f19fa" },
{ "city_id": "559e0dbe045ac712fa1f19fb" }
]
},
function(err,docs) {
// do something here
}
)
You are getting an error because you are overwriting the "array" definition with a "string" which is what all "request" objects are unless parsed otherwise.
The other reason for the error is you are calling the wrong method. .findById() expects a single argument of the _id for the document. To query other fields use .findOne() or in this case .find() since an $in will possibly match more than one document.

mongoose "_id" vanishing in collection created with map/reduce

I done a very simple map/reduce in mongo console.
var mapState = function () {
emit(this.state, 1);
};
var sumState = function (keyState, valuesCount) {
return Array.sum(valuesCount);
};
db.FooBar.mapReduce(
mapState,
sumState,
{out: "state_counts"}
);
var sc = {};
db.state_counts.find(
{_id: {$exists: true}}).forEach(
function(o){
sc[o._id]=o.value;
}
);
> sc
{
"ak" : 29,
"al" : 5832,
"ar" : 2798,
...
}
> db.state_counts.find().limit(3)
{ "_id" : "ak", "value" : 29 }
{ "_id" : "al", "value" : 5832 }
{ "_id" : "ar", "value" : 2798 }
So far so good. I have the expected state abbreviations and counts in the "sc" object. Oddness occurs when I'm attempting to pull data from state_counts prior to converting it to the equivalent of the "sc" object using mongoose.
#!/usr/bin/env node
mongoose = require("mongoose");
mongoose.connect("mongodb://localhost/thedb");
var schema = new mongoose.Schema({});
schema.collection = 'state_counts';
console.log(schema.collection);
var cur = mongoose.model(schema.collection, schema);
cur.find({}).exec(
function(err, data) {
if (err) {
console.log(err);
mongoose.disconnect();
}
console.log(data);
mongoose.disconnect();
}
);
$ ./test.js
state_counts
[ { value: 29 },
{value: 5832 },
{ value: 2798 },
...
]
This is surprising to me. Why is the "_id" value not showing up in my script when using mongoose?
_id isn't showing up because you haven't defined a schema and mongoose is all about adding schemas to mongodb. So given a completely empty schema, mongoose probably assumes _id will be of type ObjectId (which is conventional for mongodb) and when casting the data in mongodb to that type fails, as it will always do given your data, mongoose omits the value, which makes sense given the majority of mongoose's job is to enforce a consistent schema. This will "fix" it.
var schema = new mongoose.Schema({_id: String, value: Number});

Resources