Updating an array item using NodeJS, MongoDB & Monk - node.js

I have a data set like this:
{
name : 'Doc Name',
photos: [
{
name: 'photo1',
url: 'http://.....'
},
{
name: 'photo2',
url: 'http://......'
}
],
etc ...
Using Monk https://github.com/LearnBoost/monk how do I update photo2? I can use an index as I am iterating over the fields at the moment.
My current attempt below gives me an error, and I can't use a variable for the JSON selector (as in the index).
collection.update({_id: data._id}, {photos[i].data: filename}, function(err, updatedata) {
});

Updating items at a position in an array can be done using the positional $ operator
collection.update(
{ _id: data.id, "photos.name": "photo2" },
{ $set: { "photos.$.data": "yourdata" } }
)

So I found a solution to my problem but there may be some better options and I will leave it unanswered. But for anyone else with the same issue this is what I did:
I extracted the MongoDB document as an object in Node.js, manipulated the document, and then replaced the entire array in a single update statement. For example here is some pseudo code:
collection.find({id: 1}, function(err, doc){
for(i=0; i< doc.array.length; i++) {
//do what you gotta do
doc.array[i].property = 'new value';
}
collection.update({id: 1}, {$set : {"doc.array": doc.array}}, function(err,doc){
console.log(err);
}
})

Related

Cannot read property 'columnName' of undefined in sails Js Mongo DB

I am using sails JS with Mongo DB.
My model is:
module.exports = {
attributes: {
title:{type:"string",required:true},
content:{type:"string",required:true},
date:{type:"string",required:true},
filename:{type:"string",required:true},
},
};
My Controller is:
fetchposts:function(req,res){
console.log("in fetch posts")
mysort={$id:-1}
Cyberblog.find().sort(mysort).limit(5).exec(function(err, result) {
if (err || !result) {
message="no records fetched";
console.log(message);
res.redirect('/newpost');
}
else{
console.log(result)
}
I am facing an error saying that
"Warning: The sort clause in the provided criteria is specified as a dictionary (plain JS object),
meaning that it is presumably using Mongo-Esque semantics (something like { fullName: -1, rank: 1 }).
But as of Sails v1/Waterline 0.13, this is no longer the recommended usage. Instead, please use either
a string like 'fullName DESC', or an array-like [ { fullName: 'DESC' } ].
(Since I get what you mean, tolerating & remapping this usage for now...)
and I am unable to fetch any records. It is showing no records fetched.
So I have one warning on Sort and no records coming from DB. Please help me resolve the issue.
Sort clause allow send string:
var users = await User.find({ name: 'Jake'})
.sort('age ASC');
return res.json(users);
Or an array:
var users = await User.find({ name: 'Finn'})
.sort([
{ age: 'ASC' },
{ createdAt: 'ASC' },
]);
return res.json(users);
Check this out in the documentation:
https://sailsjs.com/documentation/reference/waterline-orm/queries/sort

FindOneAndUpdate not updating nested field with passed in parameters

I am trying to create a service that can be used to update nested fields in a Mongoose model. In the following example I am trying to set the field 'meta.status' to the value 2. This is the service:
angular.module('rooms').factory('UpdateSvc',['$http', function($http)
{
return function(model, id, args)
{
var url = '/roomieUpdate/' + id;
$http.put(url, args).then(function(response)
{
response = response.data;
console.log(response.data);
});
}
}]);
This is how it is called in the controller:
var newStatus = {'meta.$.status' : 2};
var update = UpdateSvc("roomie", sessionStorage.getItem('userID'), newStatus);
And this is the model:
var RoomieSchema = new Schema(
{
meta:
{
created:
{
type: Date,
default: Date.now
},
status:
{
type: Number,
default: '1',
}
}
}
And this is the route:
app.put('/roomieUpdate/:id', function(req,res)
{
var id = req.params.id;
Roomie.findOneAndUpdate(
{_id: mongoose.Types.ObjectId(id)},
req.body,
{ new : true },
function(err, doc)
{
if(err)
{
console.log(err);
}
res.json(doc);
console.log(doc);
});
});
The argument is received correctly, but I can't seem to get this to work. I am not even getting an error message. console.log(doc) simply prints out the object and the field meta.status remains '1'. I have done a direct Mongo search on the target object to make sure that I wasn't just reading the old document. I've tried a great many things like separating the key and value of req.body and use {$set:{key:value}}, but result is the same.
findOneAndUpdate() by default will return the old document, not the new (updated) document.
For that, you need to set the new option:
Roomie.findOneAndUpdate({
_id : mongoose.Types.ObjectId(id)
}, req.body, { new : true }, function(err, doc) {
...
});
As it turns out, var newStatus = {'meta.$.status' : 2}; should have been var newStatus = {'meta.status' : 2}; The document now updates correctly.
The reason the $ was there in the first place was probably based on this thread:
findOneAndUpdate - Update the first object in array that has specific attribute
or another of the many threads I read through about this issue. I had tried several solutions with and without it, but couldn't get anything to go right.

express/mongoose update query

I having problem wrapping my head around updating multiple values in my mongoDB using mongooseJS and ExpressJS.
Let say I submit an array of 2 or more objects from my frontend to "express routing" and there I get the req.body parameters to fetch it. My req.body looks like this:
[articles:
{ article: {
_id: '564209c66c23d5d20c37bd84',
quantity: 25,
},
{ article: {
_id: '564209c66c23d5d20c37bd83',
quantity: 51,
},
}]
I then need to loop? to find the specific article in the db to update and when that article is found I want to update the "quantity" value from the frontend to the correct article in db.
var id = [];
var body = {};
for (var i = req.body.length - 1; i >= 0; i--) {
id.push(req.body[i].article._id);
body[i] = req.body[i].article.quantity;
};
Articles.update(
{ _id: {$in: id} },
{ $set: {quantity: body[0].article.quantity} },
{multi: true},
function(err, response){
if(err)
console.log(err);
console.log(response);
});
The problem with this code is that I put in the first quantity value for all articles and I want it to be the correct one from the frontend. It feels like I'm on the right path but i pretty new to mongoDB and express so if there is a better solution or even a solution let me know.
Grahlie,
If you are having issues with queries, it's sometimes useful to test queries from the mongodb shell itself to workout the logic.
If your article documents are structured as such:
{
_id: ObjectId("564209c66c23d5d20c37bd84"),
quantity: 25
}
{
_id: ObjectId("564209c66c23d5d20c37bd83"),
quantity: 51
}
If you want to update the quantity of a unique document based on it's _id then you could so with this query.
db.articles.update(
{"_id": "564209c66c23d5d20c37bd84"},
{$set : { "quantity" : 25}}
)
If you wanted to update multiple documents with the same quantity you could use $in, but that's not what you want to do. You want to loop through your req.body array and update the quantity of each article.
So your code would be as such:
var articles = req.body;
var updateArticle = function(article) {
Articles.update(
{_id:article._id},
{$set:{ quantity: article.quantity}},
function(err, article){
...
);
}
for(var i = 0, n = articles.length; i < n; i++){
updateArticle(articles.[i].article);
}

Sails.js - Querying array size

// Item.js
schema: true,
attributes: {
testArray: {
type: 'array',
required: true,
array: true
}
}
I would like to find all items where the testArray attribute have a specific length.
I tried with this code below, but it doesn't work.
Item.find({testArray: {length: 2}}).exec(function (err, items) {
console.log(items);
});
I also tried with minLength, maxLength, size, but still no results.
Is there is a way to do that?
I'm using MongoDB via Sails.js/Waterline.
Actually this is in documentation
Model.where({ property: { '>': 100 }})
http://sailsjs.org/#!documentation/models
Another solution:
var query = { testArray: { $size: { $gt:2 } } };
Item.native(function(err, collection) {
collection.find(query).toArray(function(err, items) {
//items contains objects where testArray.length is greater than 2
});
});
You might run into problems depending on your mongodb version, then read http://www.mkyong.com/mongodb/mongodb-find-all-documents-where-an-array-list-size-is-greater-than-n/

Node.js How to republish the data.

We have published items based on list ids , we use myLists variable to filter out the List Id, but this variable is not reactive by nature ,when we try to add the new list , then items of new list are not publishing automatically.
Meteor.publish('subscription_items', function () {
var userName = this.userId ? Meteor.users.find({ _id: this.userId }).fetch()[0].username : null;
var myLists = [];
var sharedListIDs = [];
SharedLists.find({ shared_with: userName }).forEach(function (list) {
sharedListIDs.push(list.list_id);
});
Lists.find({ $or: [{ owner: userName }, { _id: { $in: sharedListIDs } }] }).forEach(function (list) {
myLists.push(list._id);
});
return Items.find({ list_id: { $in: Lists.find({ $or: [{ owner: userName }, { _id: { $in: sharedListIDs } }] }).fetch() } });.
Can we have any way to always publish fresh data. Please help me to resolve this issue. any help/suggestion would appreciate.
As David Weldon pointed out in this answer to my similar question, what you are looking for is a reactive join. By using the package publish-with-relations, I think the publish function you want looks something like this:
Meteor.publish('subscription_items', function() {
return Meteor.publishWithRelations({
handle: this,
collection: Lists,
filter: {owner: this.userId},
mappings: [{collection: SharedLists, key: 'list_id'}]
});
});
Alternatively, as a (dirty) workaround, you could call
Meteor.subscribe('subscription_items');
everywhere where you need the collection to be republished.

Resources