Fail to PUT second data in mongodb via Angularjs - angularjs

I am using Angularjs to update the following expressjs Team model, but one weird thing is that when I PUT the first one to the database, it works.
var TeamSchema = new Schema({
title: {
type: String,
unique: true
},
image: String,
homeColor: String,
guestColor: String,
thirdColor: String,
created: {
type: Date,
default: Date.now
},
});
If the database consists of exactly one data, it will have the following errors:
{ [MongoError: E11000 duplicate key error index: acleague.teams.$prefix_1 dup key: { : null }]
name: 'MongoError',
err: 'E11000 duplicate key error index: acleague.teams.$prefix_1 dup key: { : null }',
code: 11000,
n: 0,
lastOp: { _bsontype: 'Timestamp', low_: 1, high_: 1399820159 },
connectionId: 69575,
ok: 1 }
I can sure that I don't have an $prefix_1 attr for team model.
Anyone can help me solve this problem? It already spends me lots of time of it.

Related

How to find a particular sub-document in MongoDB?

This thread states that it is not a very good idea to create _id in a sub-document of MongoDB.
Mongo _id for subdocument array
I have locations and their reviews in a collection:
So, what would be the way to find a unique sub-document?
What can be set as a primary key for review sub-document?
var mongoose = require('mongoose')
var openingClosingTimeSchema = new mongoose.Schema(
{
days: {type: String, required: true},
opening: String,
closing: String,
closed: {type: String, required: true}
}
)
var reviewSchema = new mongoose.Schema(
{
author: String,
rating: {type: Number, required: true, min: 0, max: 5},
reviewText: String,
createdOn: {type: Date, default: Date.now}
}
)
var locationSchema = new mongoose.Schema(
{
// 'required' keyword is for validation.
name: {type: String, required: true},
address: String,
// 'default' keyword can be with or without quotes.
// When defining multiple properties for a field, {} are required.
rating: {type: Number, default: 0, min: 0, max: 5},
facilities: [String],
// Nest 'openingClosingTimeSchema' under 'locationSchema'.
openingTimes: [openingClosingTimeSchema],
// Nest 'reviewSchema' under 'locationSchema'.
reviews: [reviewSchema]
}
)
it is not a very good idea to create _id in a sub-document of MongoDB
This is not a universally applicable guideline.
If you need to identify subdocuments in an array, an _id field would be helpful (or any other field that would serve the same function).
If you don't need to identify subdocuments, you don't need an identification field.
Since you are in the first category it's perfectly ok to have an _id field in your use case.

How to add value to array element withing collection using mongoose?

I have written the following mongoose function to create new document in mongodb
createdata: (body) => {
let sEntry = new SData(Object.assign({}, {
dataId: body.DataId
//,
//notes.message: body.message
}));
return sEntry.save();
}
Here sData schema includes notes array schema within it.
I am not able to add value to message within notes [] using notes.message: body.message
My schema definition is as follows:
var nSchema = new Schema({
_id: {type:ObjectId, auto: true },
message: String
});
var sSchema = new Schema({
_id: {type:ObjectId, auto: true },
dataId: { type:String, unique: true },
notes: [nSchema]
}
I also want to mention that for every dataId there can be multiple notes [] entries. However, SData can have only unique row entry for every dataId.
I want notes to be an array within SData collection. How it can be achieved without creating separate notes collection? How should i modify createdata to accommodate all the given requirements.
Use references for other collection mapping and use populate when fetching
Schema Design
var sSchema = new Schema({
_id: {type:ObjectId, auto: true },
dataId: { type:String, unique: true },
notes: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'nSchema',
}]
}
Adding Data
createdata: (body) => {
let sEntry = new SData({
dataId: body.DataId,
notes: [nSchemaIds]
});
return sEntry.save();
}

Using `filter`, `some`, and `includes` Not Working as Expected in Filtering Docs

I am using a combination of filter, some, and includes to return a filtered set of documents in my MongoDB/Node back-end environment.
While I can get this to work in a mock example, when I plug it in to my actual data, I get an error.
This is the key problematic piece of code:
let filteredDocs = docs.filter(doc => doc.branches._id.some(branch => mongoArrBranchID.includes(branch._id)));
When I console.log this out with:
console.log("filteredDocs: ", filteredDocs);
I get:
Reason: TypeError: Cannot read property 'some' of undefined
I've been scratching my head trying to figure out what the issue is here. Why is my mock example working, but not this?
One thought I had was that maybe the issue is that the comparison is made with different types. So then I checked with these two lines of code to make sure the comparison is using Mongo ObjectIDs in both cases (both return true):
console.log("is param value valid: ", mongoose.Types.ObjectId.isValid(mongoArrBranchID[0])); // returns true
console.log("is doc value valid: ", mongoose.Types.ObjectId.isValid(docs[0].branches[0]._id)); // returns true
So why am I getting the TypeError: Cannot read property 'some' of undefined error here?
By the way, just so you know what the data looks like, my passed-in filter values when consoled out look like this :
console.log("mongoArrBranchID: ", mongoArrBranchID); // result below
mongoArrBranchID: [ 5ac26645121f0613be08185d, 5ac26645121f0613be08185a ]
And again, this check returns true:
console.log("is param value valid: ", mongoose.Types.ObjectId.isValid(mongoArrBranchID[0])); // returns true
My docs data looks like this when I console out the first of the docs:
console.log("doc branches: ", docs[0].branches); // result below
doc branches: [{"_id":"5ac26645121f0613be08185a","name":"New York"},{"_id":"5ac26645121f0613be08185d","name":"Los Angeles"},{"_id":"5ac26648121f0613be081862","name":"Charlotte"},{"_id":"5ac2664a121f0613be081869","name":"Chicago"},{"_id":"5ac2664a121f0613be08186e","name":"Seattle"}]
When I console out just the first branches._id value, like so:
console.log("doc branch: ", docs[0].branches[0]._id);
I get:
doc branch: 5ac26645121f0613be08185a
And again, this check on the whether the value is a valid Mongo Object ID returns true:
console.log("is doc value valid: ", mongoose.Types.ObjectId.isValid(docs[0].branches[0]._id)); // returns true
So what's the problem here? Why am I getting this error:
Reason: TypeError: Cannot read property 'some' of undefined
When I do:
let filteredDocs = docs.filter(doc => doc.branches._id.some(branch => mongoArrBranchID.includes(branch._id)));
console.log("filteredDocs: ", filteredDocs);
And for extra clarification, when I use mock data in ScratchJS in Chrome, this works for me:
let docs = [
{
_id: "5ba39a12179b771820413ad8",
name: "Samson",
branches: [{ _id: "3nc26645121f0613be08167r", name: "New York" }, { _id: "3fc26645121f0613be08185d", name: "Los Angeles" }, { _id: "2hc26648121f0613be081862", name: "Seattle" }, { _id: "7jc2664a121f0613be081869", name: "Chicago" }, { _id: "7ju2664a121f0613be08186e", name: "Charlotte" }],
updatedAt: "2018-09-20T13:01:06.709Z",
createdAt: "2018-09-20T13:01:06.709Z"
},
{ _id: "3ya39a12179b771820413af5", name: "Sarah", branches: [{ _id: "3nc26645121f0613be08167r", name: "New York" }, { _id: "5ac26645121f0613be08145d", name: "Miami" }, { _id: "5ac2664a121f0613be08154s", name: "Sacramento" }], updatedAt: "2018-09-20T13:01:06.709Z", createdAt: "2018-09-20T13:01:06.709Z" },
{ _id: "2sa39a12179b771820413gy4", name: "Tim", branches: [{ _id: "1rd26645121d5613be08167h", name: "Denver" }, { _id: "5ac2664a121f0613be08154s", name: "Sacramento" }], updatedAt: "2018-09-20T13:01:06.709Z", createdAt: "2018-09-20T13:01:06.709Z" }
];
let filterValues = ["5ac26645121f0613be08145d", "7ju2664a121f0613be08186e"];
let filteredDocs = docs.filter(doc => doc.branches.some(branch => filterValues.includes(branch._id)));
console.log(filteredDocs);
So what's the difference? Why does it work in the mock example but not with my actual data?
It is because docs.branches is an array, and therefore does not have the _id attribute you have accessed on it. You should revise your code to the following:
let filteredDocs = docs.filter(doc => doc.branches.some(branch => mongoArrBranchID.includes(branch._id)));
The error you received occurred because accessing an non-existent attribute of an object returns undefined, so doc.branches._id returned undefined, and trying to access an attribute of undefined, some in this case, throws an error.
EDIT:
I want to clarify that the mistake is you wrote doc.branches._id.some instead of doc.branches.some in your code. The issue is the _id part.

Possible circular reference in mongoose

I have a problem with circular dependency when I need to SAVE a 'document'.
My application is all Restfull with the interface via AngularJS.
On the screen to create a COMPANY, you can create OBJECTS and SERVICES. In the creation of the SERVICES screen, it must associate a created OBJECT.
The problem is that the OBJECT created has not yet persisted, so I don't have a _id. Thus it is not possible to reference it in SERVICE. Only when I have an OBJECT persisted, I can associate it in company.services[0].object.
Any suggestion?
This is what I need to be saved in MongoDB. Look at the reference to the OBJECT "5779f75a27f9d259248211c7" in SERVICE.
/* 1 */
{
"_id" : ObjectId("5749bb92bf8145c97988e4a9"),
"name" : "company 1",
"services" : [
{
"_id" : ObjectId("5764cb2c00d00cf10c9c41c6"),
"description" : "service 1",
"object" : ObjectId("5779f75a27f9d259248211c7"),
}
],
"objects" : [
{
"_id" : ObjectId("5779f75a27f9d259248211c7"),
"description" : "object 1",
}
]
}
And this is my Schema:
var objectSchema = new mongoose.Schema({
description: {
type: String,
trim: true,
unique: true,
required: 'Description is required'
}
})
var serviceSchema = new mongoose.Schema({
description: {
type: String,
trim: true,
required: 'Description is required'
},
object: {
type: mongoose.Schema.ObjectId,
ref: 'Object'
},
})
var companySchema = new mongoose.Schema({
name: {
type: String,
default: '',
trim: true,
unique: true,
required: 'Name is required'
},
guarda_documentos: {
services: [serviceSchema],
objects: [objectSchema],
},
});
mongoose.model('Company', companySchema);
mongoose.model('Object', objectSchema);
You would need to persist your object first and once this is done, use the returned _id to then persist your service. This is an async process.
// Simplified code concept
// Create a new object
var o = new Object()
o.description = 'foo'
...
// Create a new service
var s = new Service()
// Save object and return it (will now have an _id)
o.save(function(err, doc) {
// Add object reference to service and save
s.object = doc._id
s.save(function(err, res) {
// Call your controller or whatever callback here
})
})
On another note don't use "Object" as the name of your doc. It already defined in Javascript.

NodeJS & Mongoose: Add element to existing empty array

I am building a NodeJS application for creating reports based on data from a MSQL database. All application relevant data is stored in a MongoDB using Mongoose. My mongoose model contains an empty array which is then filled by the user via a Rest-API.
I get an error when adding a new element to the array. I already tried it with model.array.push(object); model.save() and findByIdAndUpdate(...). Find my code including the two different attempts below:
Mongoose schema
var sqlSchema = mongoose.Schema({ // Schema to store information about SQL-procedures
'name': String,
'inputs': [
{
'name': String,
'type': String,
'value': String,
'adjustable': Boolean
}
]
});
REST API
My application accepts new elements for the 'inputs'-array via POST:
var mongoose = require('mongoose'),
SqlProcedure = mongoose.model('SqlProcedure');
// ...
router.post('/sql/:id/inputs', function(req, res) {
SqlProcedure.findById(req.params.id, function(err, sql) {
if(err) {
res.send(msg.error('error retrieving sql-procedure', err));
} else {
if(sql.inputs.length > 0) { // catch empty array
for(var key in sql.inputs) {
if(sql.inputs[key].name == req.body.name) {
return res.send(msg.error('input already in inputs', err));
}
}
}
var data = req.body;
var input = {
name: data.name,
type: data.type,
value: data.value,
adjustable: data.adjustable
};
// attempt one or two
}
});
});
attempt one:
sql.inputs.push(input); // EXCEPTION 1
sql.save(function(err) {
// fancy errorhandling
return res.send(msg.ok(sql));
});
attempt two:
SqlProcedure.findByIdAndUpdate(req.params.id,
{$push: {inputs: input}}, // EXCEPTION 2
{safe: true, upsert: true},
function(err, sql) {
// fancy errorhandling
res.send(msg.ok('input added to sql-procedure' + req.params.id));
}
);
Exceptions
Attempt one:
throw new CastError('string', value, this.path);
^
Error
at MongooseError.CastError (\node_modules\mongoose\lib\error\cast.js:18:16)
at SchemaString.cast (\node_modules\mongoose\lib\schema\string.js:434:9)
at Array.MongooseArray.mixin._cast (\node_modules\mongoose\lib\types\array.js:124:32)
at Array.MongooseArray.mixin._mapCast (\node_modules\mongoose\lib\types\array.js:295:17)
at Object.map (native)
at Array.MongooseArray.mixin.push (\node_modules\mongoose\lib\types\array.js:308:25)
at Query.<anonymous> (\app\api\sql_procedure.js:69:28)
at \node_modules\mongoose\node_modules\kareem\index.js:177:19
at \node_modules\mongoose\node_modules\kareem\index.js:109:16
at doNTCallback0 (node.js:417:9)
at process._tickCallback (node.js:346:13)
Attempt two:
"stack": "Error
at MongooseError.CastError (\\node_modules\\mongoose\\lib\\error\\cast.js:18:16)
at SchemaArray.cast (\\node_modules\\mongoose\\lib\\schema\\array.js:156:15)
at SchemaArray.cast (\\node_modules\\mongoose\\lib\\schema\\array.js:167:17)
at Query._castUpdateVal (\\node_modules\\mongoose\\lib\\query.js:2384:22)
at Query._walkUpdatePath (\\node_modules\\mongoose\\lib\\query.js:2298:27)
at Query._castUpdate (\\node_modules\\mongoose\\lib\\query.js:2227:23)
at castDoc (\\node_modules\\mongoose\\lib\\query.js:2430:18)
at Query._findAndModify (\\node_modules\\mongoose\\lib\\query.js:1752:17)
at Query._findOneAndUpdate (\\node_modules\\mongoose\\lib\\query.js:1620:8)
at \\ITZReport\\node_modules\\mongoose\\node_modules\\kareem\\index.js:156:8
at \\node_modules\\mongoose\\node_modules\\kareem\\index.js:18:7
at doNTCallback0 (node.js:417:9)\n at process._tickCallback (node.js:346:13)",
"message": "Cast to undefined failed for value \"[object Object]\" at path \"inputs\"",
"name": "CastError",
"value": [
{
"adjustable": "true",
"value": "Harry Potter",
"type": "String",
"name": "salesman"
}
],
"path": "inputs"
data to be inserted
{ name: 'salesman',
type: 'String',
value: 'Harry Potter',
adjustable: 'true' }
I am new to NodeJS and mongoose and tried to solve this on my own for many hours. It would be great if anyone out there could help me!
Thanks in advance,
dj2bee
Update
I think I should clarify the process of the user interacting with the REST-API:
The user creates a new record by passing over the value for the
name. At this point the name is set and the inputs-array
is empty.
In the next step, the user adds new records to the
inputs-array one by one. The name stays as it is and only
new inputs are added to the array.
The user should be able to edit or remove entries from the
inputs-array.
Changing the data-type of adjustable to String did not made any changes. I also tried hard coding attributes and not passing them via HTTP-POST - still the same exception.
After hours of searching the web and testing the weirdest things in my code I found the solution:
You can't name a field 'type' in the mongoose-schema. That's it.
The correct code looks like this:
Mongoose schema
var sqlSchema = mongoose.Schema({
'name': String,
'inputs': {
'name': String,
'datatype': String, // you can't name this field 'type'
'value': String,
'adjustable': Boolean
}
});
REST API
router.post('/sql/:id/inputs', function(req, res) {
SqlProcedure.findById(req.params.id, function(err, sql) {
if(err) {
res.send(msg.error('error retrieving sql-procedure', err));
} else {
if(!sql) {
return res.send(msg.error('no sql-procedure found with id '
+ req.params.id, null));
}
// check for duplicates
var data = req.body;
var input = {
'name': data.name,
'datatype': data.type,
'value': data.value,
'adjustable': data.adjustable
};
SqlProcedure.findByIdAndUpdate(req.params.id,
{$push: {inputs: input}},
{safe: true, upsert: true},
function(err, sql) {
// fancy error handling
}
);
}
});
});
You should make your data to be inserted as
{ name: 'salesman',
inputs: [{name: 'salesman',
type: 'String',
value: 'Harry Potter',
adjustable: true}]}
i.e. true without quotes and inputs as an array
or else in schema make adjustable as String and remove input as array in model definition
var sqlSchema = mongoose.Schema({ // Schema to store information about SQL-procedures
'name': String,
'type': String,
'value': String,
'adjustable': String
});

Resources