How to get data from array in mongoose? - arrays

I am new to mongoose node.js and mongoDB, I have a db Schema like
Project:{
projectName:"String",
projectManager:"String",
task:[{
taskName:"String",
timetakeninhrs:"String"
}]
};
So what I want is to get only the details of task with particular task name.
I am writing sql script so that you can know what I want :
Select taskname,timetakeninhrs from project where taskName ='DB create';

The $elemMatch projection operator would come in handy for this:
Project
.where('task.taskName', 'DB create') // or where('task.taskName').equals('DB create').
.select({_id: 0, task: {$elemMatch: {'taskName': 'DB create'}})
.exec(function(err, docs){
var tasks = docs.map(function(doc){ return doc.task[0]; });
console.log(tasks[0].taskName); // 'DB create'
console.log(tasks[0].timetakeninhrs); // '3'
});
In the above, the where() method acts as a static helper method of the Mongoose model that builds up a query using chaining syntax, rather than specifying a JSON object. So
// instead of writing:
Project.find({ 'task.taskName': 'DB create' }, callback);
// you can instead write:
Project.where('task.taskName', 'DB create');
// or
Project.where('task.taskName').equals('DB create');
and then chain the select() method to project the 'task' array field using $elemMatch. In the exec() method (which executes the query asynchronously), you need to pass in a callback which follows the pattern callback(error, results). What results is depends on the operation: For findOne() it is a potentially-null single document, find() a list of documents, count() the number of documents, update() the number of documents affected, etc. In this case this returns an array of documents in the format:
[
/* 0 */
{
"task" : [
{
"taskName" : "DB create",
"timetakeninhrs" : "3"
}
]
},
/* 1 */
{
"task" : [
{
"taskName" : "DB create",
"timetakeninhrs" : "9"
}
]
}
/* etc */
]
In your callback you can do a bit of data manipulation to get an object that only has those properties you specified, hence the use of the native JavaScript map() function to create a new array of objects with those fields

i create this example that can help you:
var async=require('async');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var uri = 'mongodb://localhost/myDb';
mongoose.connect(uri);
// define a schema
var ProjectSchema = new Schema({
projectName: "String",
projectManager: "String",
task: [{
taskName: "String",
timetakeninhrs: "String"
}]
});
// compile our model
var Project = mongoose.model('Project', ProjectSchema);
// create a documents
var Project01 = new Project({
projectName: "Project01",
projectManager: "Manager01",
task: [{
taskName: "tsk01_Project01",
timetakeninhrs: "1111-1111"
}, {
taskName: "tsk02_Project01",
timetakeninhrs: "1111-2222"
}, {
taskName: "tsk03_Project01",
timetakeninhrs: "1111-3333"
}, {
taskName: "tsk04_Project01",
timetakeninhrs: "1111-4444"
}]
});
var Project02 = new Project({
projectName: "Project02",
projectManager: "Manager02",
task: [{
taskName: "tsk01_Project02",
timetakeninhrs: "2222-1111"
}, {
taskName: "tsk02_Project02",
timetakeninhrs: "2222-2222"
}, {
taskName: "tsk03_Project02",
timetakeninhrs: "2222-3333"
}, {
taskName: "tsk04_Project02",
timetakeninhrs: "2222-4444"
}]
});
//delete existing documents and create them again
Project.remove({}, function() {
Project01.save(function() {
Project02.save(function() {
//for example we find taskName: "tsk03_Project02"
Project.find({'task': {$elemMatch: {taskName: "tsk03_Project02"}}},'task.taskname task.timetakeninhrs',function(err, docs) {
if (!err) {
console.log(docs);
}
});
});
});
});

Related

mongoose + nodejs push array to database

I'm new to NodeJS + Mongoose and having trouble pushing an array to my database via mongoose.
I have the following schema:
const StudentSchema = mongoose.Schema({
name: {
type: String
},
quizzes: [{
quiz: String,
answers: []
}]
});
What I'm trying to do is have an array of quiz objects in which the quiz number is shown and the array of answers for that particular quiz.
When I create a new user, the student object looks like this in mongoose:
{
"_id" : ObjectId(id here),
"name" : "John",
"quizzes" : [ ],
"__v" : 0
}
The function I'm using to update the quiz array:
module.exports.addQuiz = function(student_id, answers, callback){
Student.findByIdAndUpdate(
student_id,
{$push: {"quizzes": {answers: answers}}},
{safe: true, upsert: true},
function(err, model) {
console.log(err);
}
);
}
And this is my route which calls the function whenever the endpoint is hit with a student_id, which will then be used to find the student and push to the array
router.post('/quiz/:student_id', (req, res, next) => {
var student_id = req.params.id;
var answers = req.body.answers;
Student.addQuiz(student_id, answers, (err, answers) => {
//error handling
})
});
I'm trying to test this by sending a post request to /quiz/:student_id with an id of a student in my database with the following JSON sent in the body:
[{
"quiz": "Quiz 1",
"answers": ["Answer 1", "Answer 2"]
}]
Although when I try this it ends up getting hung somewhere and the request never completes - I also get a "null" in the console.
Can anyone help me out? Thank you.

$push to an array in MongoDB with mongoose doesn't work

errmsg: 'The field \'weight\' must be an array but is of type int in
document
My Schema:
weight: [{
type: Number
}]
and my post request:
app.post('/edit', function(req, res){
var update = { $push: {"weight": req.body.weight}};
User.findOneAndUpdate(conditions, update, options, function (err)
{
if (err)
{
console.log(err);
}
else
{
console.log('yep');
}
})
});
If there are multiple documents in the collection that match your conditions, you can update only suitable one by adding { weight: { $type: 4 } } to your conditions.
Otherwise your application's schema doesn't match data in the database.
This might work.
//Schema
weight: [Number]
http://mongoosejs.com/docs/schematypes.html
//Or this way too if pushing objects into array
//Schema
weight: [{
weight: {
type: Number
}
}]
//Then in API
var update = { $push: {"weight": { "weight": req.body.weight }}};

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

Merge local Backbone collection with server

I have a local Backbone collection:
var collection = new Backbone.Collection([ { greeting: "hi" }, { greeting: "bye" } ]);
I understand that when I run collection.fetch, Backbone will run collection.set on the results. I need to merge in the response from the server, however. Say the response is:
[ { id: "2", greeting: "hi", name: "Bob" } ]
I would like the resulting collection, after the merge, to be:
[ { id: "2", greeting: "hi", name: "Bob" }, { greeting: "bye" } ]
I understand Backbone already attempts to do some merging here, but if I set the example response above, no merge happens and a new model gets added instead. I assume this is because it merges by id, and here we do not have any ids (in the local collection). In this case, greeting is my unique identifier / key.
The reason I am trying to do this is because I have a local collection and I simply want to see what already exists from that collection (using the key greeting) and merge any findings in.
My solution:
feeds.fetch({
add: false,
remove: false,
merge: false,
data: params,
success: function (feeds, response) {
// Merge any matches
_.each(response.results, function (result) {
_.each(feeds.models, function (feed) {
// We have to `parse` the result before setting it, as Model#set does
// not automatically run `parse` (Collection#set does).
result = feed.parse(result)
if (feed.get('rssUrl') === result.rssUrl) feed.set(result)
})
})
cb(feeds)
}
})
You can tell backbone to use a different key for the id attribute on your model:
GreetingModel = Backbone.Model.extend({
idAttribute: "greeting"
});
GreetingCollection = Backbone.Collection.extend({
model: GreetingModel
});
http://backbonejs.org/#Model-idAttribute
Edit: I suppose you could use two separate collections for local and server side.
var localCollection = new Backbone.Collection([ { greeting: "hi" }, { greeting: "bye" } ]);
ServerCollection = Backbone.Collection.extend({
url: "/api/"
...
});
var serverCollection = new ServerCollection({});
serverCollection.on("reset", function() {
localCollection.each(function(localModel) {
var greeting = localModel.get("greeting");
serverModel = serverCollection.findWhere({greeting: greeting});
if(serverModel) {
localModel.set(serverModel.attributes);
}
});
});
serverCollection.fetch();

Resources