I work with nodejs/express/mongoose/angularjs. I'd like to update a collection named Lists which has several properties, one of which is an array of items. In the following code, I'm pushing a new task items in the items array. Everything works fine, however the update function does not sends back the updated collection, then I must perform another query on the database. Is there a more efficient way to do this ?
The nodejs/express code :
exports.addTaskToList = function(req, res) {
var listId = req.params.Id;
var taskId = req.params.TaskId;
Lists.update({_id: listId}, {$push: {items: taskId}}, {safe:true, upsert: true}, function(err, result){
if(err) {
console.log('Error updating todo list. ' + err);
}
else{
console.log(result + ' todo list entry updated - New task added');
Lists.findById(listId).populate('items').exec(function (err, updatedEntry) {
if (err) {
console.log('Unable to retrieve todo list entry.');
}
res.send(JSON.stringify(updatedEntry));
});
}
});
};
Furthermore, the array items is an array of ObjectIds. Those items are in a separate schema so in a separate collection. Is it possible to push the whole object and not only its _id so that there is not another collection created ?
use findOneAndUpdate() method and in query parameter use option as { "new": true}
return this.sessionModel
.findOneAndUpdate({user_id: data.user_id}, {$set:{session_id: suuid}}, { "new": true})
.exec()
.then(data=>{
return {
sid: data.session_id
}
})
The update method doesn't return the updated document:
However, if we don't need the document returned in our application and
merely want to update a property in the database directly,
Model#update is right for us.
If you need to update and return the document, please consider one of the following options:
Traditional approach:
Lists.findById(listId, function(err, list) {
if (err) {
...
} else {
list.items.push(taskId)
list.save(function(err, list) {
...
});
}
});
Shorter approach:
Lists.findByIdAndUpdate(listId, {$push: {items: taskId}}, function(err, list) {
...
});
Regarding your last question:
Is it possible to push the whole object and not only its _id so that
there is not another collection created ?
The answer is yes. You can store sub-documents within documents quite easily with Mongoose (documentation on sub-documents here). By changing your schema a little, you can just push your whole item object (not just item _id) into an array of items defined in your List schema. But you'll need to modify your schema, for example:
var itemSchema = new Schema({
// Your Item schema goes here
task: 'string' // For example
});
var listSchema = new Schema({
// Your list schema goes here
listName: String, // For example...
items: [itemSchema] // Finally include an array of items
});
By adding an item object to the items property of a list, and then saving that list - your new item will be persisted to the List collection. For example,
var list = new List({
listName: "Things to do"
});
list.items.push({
task: "Mow the lawn"
});
list.save(function(error, result) {
if (error) // Handle error
console.log(result.list) // Will contain your item instance
});
So when you load your list, the items property will come pre-populated with your array of items.
This is because Items will no longer persist it a separate collection. It will be persisted to the List collection as a sub-document of a List.
Related
I am creating an app where a job is created and that job's id is added to another collection (client) so the job can be referenced from the client itself. I have been able to add the job's id to the client's collection so far, but I am having trouble figuring out how to remove the job's id from the client's collection if the job is deleted. This is because the id is stored as a sub-collection within the client. The code I am trying to get to work is below:
// delete
app.delete("/jobs/:id", function(req, res){
Client.find({jobs._id: req.params.id}, function (err, foundClient){ //This part doesn't work
if (err) {
console.log(err);
} else {
// Add id identifier to Client
foundClient.jobs.pull(req.params.id);
foundClient.save();
}
});
// Delete Job
Job.findByIdAndRemove(req.params.id, function(err, deletedJob){
if (err){
console.log(err)
} else {
// Redirect
res.redirect("/jobs");
}
});
});
I am trying to get the logic of this part to work:
Client.find({jobs._id: req.params.id},
Here is the Client Schema
// =======================Client Schema
var clientSchema = new mongoose.Schema({
organization_name: String,
first_name: String,
middle_name: String,
last_name: String,
email_address: String,
phone_number: String,
street: String,
city: String,
state: String,
zip: String,
description: String,
active: {type: Boolean, deafult: true},
date_added: {type: Date, default: Date.now},
transactions: [{type: mongoose.Schema.Types.ObjectID, ref: "Transaction"}],
jobs: [{type: mongoose.Schema.Types.ObjectID, ref: "Job"}]
});
module.exports = mongoose.model("Client", clientSchema);
Basically, what I am trying to tell it to do is find the Client where the client's jobs array contains an id equal to the id of the job being deleted. Of course, this syntax is incorrect, so it does not work. I have not been able to find documentation that explains how I would be able to do this. Is there a more straightforward way of doing this, the way I am writing it out here? I know that I can query the db this way if the job itself was not an array and only contained one singular variable. Is there a way to do this or do I need to write a completely separate looping function to get this to work? Thank you.
jobs is an array of ids, so to find some documents in Client collection that have req.params.id in the jobs array, the query should be something like this
Client.find({jobs: req.params.id})
this will return an array of documents, each document has an array of jobs Ids
If you are sure that the req.params.id exists only in one document, you can use findOne instead of find, and this will return only one document with an array of jobs Ids
this is regarding the find part
regarding the remove job Id from jobs array, we can use one of the following methods
1- as you suggested, we can find the clients documents that have this job Id first, then remove this id from all the jobs arrays in all matching documents
like this
Client.find({ jobs: req.params.id }, async function (err, foundClients) {
if (err) {
console.log(err);
} else {
// loop over the foundClients array then update the jobs array
for (let i = 0; i < foundClients.length; i++) {
// filter the jobs array in each client
foundClients[i].jobs = foundClients[i].jobs || []; // double check the jobs array
foundClients[i].jobs = foundClients[i].jobs.filter(jobId => jobId.toString() !== req.params.id.toString());
// return all the jobs Ids that not equal to req.params.id
// convert both jobId and req.params.id to string for full matching (type and value)
await foundClients[i].save(); // save the document
}
}
});
2- we can use $pull array update operator to update the jobs array directly
something like this
Client.updateMany(
{ jobs: req.params.id }, // filter part
{
$pull: { jobs: { $in: [req.params.id] } } // update part
},
function (err) {
if (err) {
console.log(err);
} else {
console.log('job id removed from client documents successfully');
}
}
);
hope it helps
I am working on a MEAN stack application in which i defined a model using following schema:
var mappingSchema = new mongoose.Schema({
MainName: String,
Addr: String,
Mapping1: [Schema1],
Mappings2: [Schema2]
},
{collection : 'Mappings'}
);
I am displaying all this data on UI and Mapping1 & Mapping2 are displayed in the 2 tables where I can edit the values. What I am trying to do is once I update the values in table I should update them in database. I wrote put() api where I am getting these two updated mappings in the form of object but not able to update it in database. I tried using findAndModify() & findOneAndUpdate() but failed.
Here are the Schema1 & Schema2:
const Schema1 = new mongoose.Schema({
Name: String,
Variable: String
});
const Schema2 = new mongoose.Schema({
SName: String,
Provider: String
});
and my put api:
.put(function(req, res){
var query = {MainName: req.params.mainname};
var mapp = {Mapping1: req.params.mapping1, Mapping2: req.params.mapping2};
Mappings.findOneAndUpdate(
query,
{$set:mapp},
{},
function(err, object) {
if (err){
console.warn(err.message); // returns error if no matching object found
}else{
console.log(object);
}
});
});
Please suggest the best to way update those two arrays.
UPDATE :
I tried this
var mapp = {'Mapping2': req.params.mapping2};
Mappings.update( query ,
mapp ,
{ },
function (err, object) {
if (err || !object) {
console.log(err);
res.json({
status: 400,
message: "Unable to update" + err
});
} else {
return res.json(object);
}
});
what I got is
My array with size 3 is saved as String in Mapping2 array.
Please help. Stuck badly. :(
From Mongoose's documentation I believe there's no need to use $set. Just pass an object with the properties to update :
Mappings.findOneAndUpdate(
query,
mapp, // Object containing the keys to update
function(err, object) {...}
);
I'm trying to replace an array of sub-documents with a new copy of the array.
Something like...
var products = productUrlsData; //new array of documents
var srid = the_correct_id;
StoreRequest.findOneAndUpdate({_id: srid}, {$set: {products: products}}, {returnNewDocument : true}).then(function(sr) {
return res.json({ sr: sr}); //is not modified
}).catch(function(err) {
return res.json({err: err});
})
The products var has the correct modifications, but the returned object, as well as the document in the db, are not being modified. Is this not the correct way to replace a field which is an array of subdocuments? If not, what is?
I am a bit late to the party plus I am really in a hurry -- but should be:
StoreRequest.updateOne(
{ _id: srid },
{ $set: { 'products.$': products }},
{ new: true });
I couldn't make it work with findOneAndUpdate but the above does work.
Today i'm in front of a problem. I'm using express, mongodb with mongoose to handle the back-end of my application.
I got one model with a ObjectId reference to his parent. I want to retrieve every documents that contain this parent. But i'm receiving only the name of the parent not the id.
The only solution that i found it's to do a first query to find the id then one more to find my documents. I want to know if that's possible to do that in one query ?
My Model :
const childSchema = new Schema({
name: {
type: String,
required: true
},
_exampleParent: {
type: Schema.Types.ObjectId, ref: 'parents',
}
});
My query :
Parent.findOne({name:req.query.parent}, function(err, values){
if(err) return next(err);
Child.find({_exampleParent:values.id},
'name',
function(err, values){
if(err) return next(err);
res.send(values);
}
);
});
Thanks guys !
You can populate "child" and filter with parent's name afterwards.
You Can Do This, By Changing The Schema:
Pass Reference Of Child To Parent Instead of parent reference to the child.
Below I am just showing an example of how to do this, do consider your variable names as mine can differ:
var parentSchema = new Schema({name:{
type:'string'
},
children:[{type:Schema.Types.ObjectId,
ref:"child"}]
});
var childSchema = new Schema({name:{type:'string'}});?
var child = mongoose.model('child',childSchema);
var parent = mongoose.model('parent',parentSchema);
Now To Retrieve:
parent.findOne({name:req.query.name}).populate('child').then((result)=>{
console.log(result.children) //will be an array of all the children having the same parent.
})
I'm new to MongoDB, and trying to reorder an array in a db.
Here's the schema:
headline: String,
Galleryslides: [{type: ObjectId, ref: 'Galleryslide'}],
Here's the logic I'm using. By the way, correctOrder is an array with the new order of ids for the DB.
Gallery.findById(req.params.galleryId, function(err, gallery) {
var newArr = [];
req.body.ids.forEach(function(id, index) {
newArr[index] = Galleryslides.find({"_id" : id});
});
gallery.Galleryslides = newArr;
gallery.save(function() {
res.json({status: 'ok'});
});
});
When this runs, nothing happens - the order of the array in the DB does not change. D'you know a better way to do this?
In mongodb, the records are sorted in natural order. You should get them in the same order you inserted but that's not guaranteed.
Like the docs say :
This ordering is an internal implementation feature, and you should
not rely on any particular structure within it.
If you want to sort by the _id field, you can do that(it will sort by the _id index) :
Gallery.find().sort({ "_id": 1 })