Mongoose: Update does not work in nested array object - node.js

I have a document with the array of objects and one object contains multiple objects I want to update inner object with $set but didn't get any luck.
can anybody give me any hint so that I can resolve it?.
This is my object:
{
"_id": ObjectId("56fbfafdf86fa6161911d104"),
"site": "xyz",
"adsPerCategory": NumberInt(2),
"sampledAt": ISODate("2016-03-30T16:12:45.138+0000"),
"items": [
{
"id": "4563873",
"content": {
"title": "WATER DISTILLERS",
"body": "Perfect to save money.",
}
},
{
"id": "4563s23232873",
"content": {
"title": "Cola water",
"body": "Perfect for body.",
}
}
]
}
I want to update body.
for now, I have given single object but it can be multiple.
Here what I tried
models.Sample.update(
{
_id: samples._id
},
'$set': {
'items.0.content.body': body.description
},
function(err, numAffected) {
console.log(err);
console.log('Affected....', numAffected);
}
);
It's working fine if I put 0 but I want to make it dynamic.
Like 'items.index.content.body': body.description
Thank You.

I think you can do something like this.
models.Sample.find({ _id: ObjectId(samples._id) })
.forEach(function (doc) {
doc.items.forEach(function (element, index, array) {
items[index].content.body = body.description;
});
models.Sample.save(doc);
});

Related

mongoose find in document object

Lets say I have a mongoose schema, something like:
mongoose.Schema({
website_id:mongoose.SchemaTypes.ObjectId,
data:Object
})
where data field contains JSON Object. Something like:
{
"actions":[
{
"action":"pageChange",
"url":"http://localhost:3000/login",
"dom":"",
"timestamp":1653341614846
},
{
"action":"pageChange",
"url":"http://localhost:3000/signup",
"dom":"",
"timestamp":1653341626442
},
{
"action":"pageChange",
"url":"http://localhost:3000/view",
"dom":"",
"timestamp":1653341626442
},
{
"action":"pageChange",
"url":"http://localhost:3000/login",
"dom":"",
"timestamp":1653341626442
}
]
}
Is there any way I can get all documents, where data field object contains http://localhost:3000/login as url, without getting all the documents first and looping them through.
Object is going to be dynamic generated, and items will repeat themselves
Of course, there are several ways and in this case, one of the best ways is to use "aggregate"
db.collection.aggregate([
{$unwind: "$actions"},
{ $match: {"actions.url": "http://localhost:3000/login"}},
{$group: {
_id: "$_id",
actions: {$push: "$actions"}
}
}
])
return Response :
{
"actions": [
{
"action": "pageChange",
"dom": "",
"timestamp": 1.653341614846e+12,
"url": "http://localhost:3000/login"
},
{
"action": "pageChange",
"dom": "",
"timestamp": 1.653341626442e+12,
"url": "http://localhost:3000/login"
}
]
}
If i find other or better methods, I'll definitely share..
I hope this solution helps you.
Sure you can do that. You can specify the object nesting in form of string in the query.
await MyModel.find({ 'data.objectKey.items.item': 'text I want to find' }).exec();

Return Array from Embedded document

I have a data like in mongodb collection name property.
{
"_id": "593a3d828e2ef100d1496e77",
"feature_type": "house",
"features": [
{
"name": "h1"
},
{
"name": "h2"
}
]
}
I want only
[
{
"name": "h1"
},
{
"name": "h2"
}
]
as result i tried this by
req.db.collection('FeatureSettings').findOne({feature_type: req.params.feature_type}, {features: 1});
this give
{
"_id": "593a3d828e2ef100d1496e77",
"features": [
{
"name": "Hotel"
},
{
"name": "Apartment"
}
]
}
what can i do to get result as given above.
You have to exclude the _id from your projection like here:
req.db.collection('FeatureSettings').findOne({feature_type: req.params.feature_type}, {features: 1, _id:0});
Assuming you're using something like mongoose that returns promises, you could use the result of the query
return req.db.collection('FeatureSettings')
.findOne({feature_type: req.params.feature_type}, {features: 1})
.then((result) => {
return result.features;
});
You can try this to return only features array values.
req.db.collection('FeatureSettings')
.findOne({feature_type: req.params.feature_type}, {features:1, _id:0}).features;
OR
// if you use mongoose can use
return req.db.collection('FeatureSettings')
.findOne({feature_type: req.params.feature_type}, {features:1, _id:0})
.exec()
.then((result) => {
return result.features;
});

geoNear not returning any results

I'm trying to use geoNear to return documents in my collection that have latitude and longitude coordinates, but not getting any results even though there are documents in the collection that have latitude and longitude coordinates. I'm using mlab to store my data, for example, here is a document in my collection.
var trucker = db.collection('trucker');
{
"_id": {
"$oid": "581e82d00f192a694bb56679"
},
"latitude": 77.0551642746301,
"longitude": 10.825842664395019,
"loc": [
77.0551642746301,
10.825842664395019
],
}
I have the coordinates in two different ways, because I was trying to see if geoNear would pick up the location, and return the result, but still can't find it. This is how I'm using geoNear currently, to find results
exports.geoNear = function (lat, lon ,res, next)
{
console.log("Inside geoNear");
var job = {};
job.lon = JSON.parse(lon);
job.lat = JSON.parse(lat);
console.log(job.lon);
console.log(job.lat);
trucker.geoNear([job.lon, job.lat], function(err, success){
if(success) {
res(success);
} else {
console.log('Response error' + err);
}
});
};
app.get('/geoFind', function(req, res) {
AM.geoNear('48.99759239999999', '-123.0683089', function(o){
res.send(200,o);
});
});
I am getting this result when visiting localhost:8000/geoFind:
{
"waitedMS": 0,
"results": [],
"stats": {
"nscanned": 0,
"objectsLoaded": 0,
"maxDistance": 0,
"time": 0
},
"ok": 1
}
Now, I have tried many different ways such as the following but get, Response errorMongoError: 'near' field must be point with the following:
trucker.createIndex( { loc : "2dsphere" } )
trucker.geoNear({loc: {type: "Point", coordinates: [job.lon,job.lat]}}, {spherical: true}, function(err, success) {
if(success){
res(success);
} else {
console.log('Response error '+err + job.lon +':' + job.lat);
}
});
Now, I'm wondering how do I use geoNear to return documents with latitude and longitude coordinates? Does latitude and longitude need to be stored in the database a specific way? I tried db.command() as well with geoNear and I always get db.command is not a function, same with trucker.command or trucker.db.db.command or trucker.db.command (anything with command to get data from the database which I think is because I'm using mongoose not mongoclient, because my database it set up with mlab). Nothing I've tried has worked which is why I'm now asking this question as I've tried just about every way imaginable to get results from my database using geoNear.
I got back at it this morning and did some more testing because it didn't make sense to me why I was not getting any results, so I created a brand new collection called location, and inserted 1 document to test and it worked, then 2 documents that look like the following and that worked too:
{
"_id": {
"$oid": "581cc2f430c34502e36eb148"
},
"truckerID": "b233a9eaaedc63730e71a8b542606ee82e0aa5e5",
"name": "Justin",
"email": "Justin#gmail.com",
"company": "Justins Shipping",
"user": "justin1",
"pass": "D6Mvu6rUur758f37eac7010958c14557bb4df9871a",
"phone": "1234567890",
"location": [
-73.9928,
40.7193
]
}
{
"_id": {
"$oid": "581cc2f530c34502e36eb158"
},
"truckerID": "b233a9eaaedc63731e72a8b542606ee82e0aa7a6",
"name": "Alan",
"email": "Alan#gmail.com",
"company": "Alans Shipping",
"user": "alan1",
"pass": "D6Mvu6fUur758f37eac7010958c14557bb4df9872c",
"phone": "1234567890",
"location": [
-122.4194155,
37.7749295
]
}
Apparently, location does have to be in the format,
location: [longitude, latitude]
or geoNear will not be able to find your document.
The really interesting finding to me, was that if you have a collection, with 10 documents, and there are documents in your collection that don't have:
location: [longitude, latitude]
geoNear will not get any results either. If even one document does not have that field, location: [longitude, latitude], geoNear will also not be able to find anything, or return any results after having done some more testing.
The following worked for me after creating those 2 new documents in a new collection for testing purposes using mongoose and mlab.
exports.geoNear = function (lon, lat ,res, next)
{
console.log("Inside geoNear");
var job = {};
job.lon = JSON.parse(lon);
job.lat = JSON.parse(lat);
console.log(job.lon);
console.log(job.lat);
locations.geoNear([job.lon, job.lat], {spherical: true}, function(err, success){
if(success) {
res(success);
} else {
console.log('Response error' + err);
}
});
}
var AM = require('./modules/account-manager');
app.get('/geoFind', function(req, res) {
AM.geoNear('-73.99279', '40.719296', function(o){
res.send(200,o);
});
});
and I got the following result:
{
"waitedMS": 0,
"results": [
{
"dis": 1.4957325341976439e-7,
"obj": {
"_id": "581cc2f430c34502e36eb148",
"truckerID": "b233a9eaaedc63730e71a8b542606ee82e0aa5e5",
"name": "Justin",
"email": "Justin#gmail.com",
"company": "Justins Shipping",
"user": "justin1",
"pass": "D6Mvu6rUur758f37eac7010958c14557bb4df9871a",
"phone": "1234567890",
"location": [
-73.9928,
40.7193
]
}
},
{
"dis": 0.6482546796756842,
"obj": {
"_id": "581cc2f530c34502e36eb158",
"truckerID": "b233a9eaaedc63731e72a8b542606ee82e0aa7a6",
"name": "Alan",
"email": "Alan#gmail.com",
"company": "Alans Shipping",
"user": "alan1",
"pass": "D6Mvu6fUur758f37eac7010958c14557bb4df9872c",
"phone": "1234567890",
"location": [
-122.4194155,
37.7749295
]
}
}
],
"stats": {
"nscanned": 24,
"objectsLoaded": 2,
"avgDistance": 0.3241274146244688,
"maxDistance": 0.6482546796756842,
"time": 6
},
"ok": 1
}
I hope this answer helps someone else down the line who is also wondering why geoNear isn't returning any results.
EDIT: After doing even more research, it was actually because the collection needs to have a geospatial index created from the get-go. I tried adding the location field to all documents in the collection but still could not get any results back. Once I removed my collection entirely in mlab called "trucker" and re-added it, I was finally able to search that index and get results.
This line is needed initially:
trucker.createIndex( { location : "2dsphere" } )
Then you can use geoNear to find users near your location such as the following:
trucker.geoNear([job.lon, job.lat], {maxDistance:5000, distanceMultiplier: 6378137, spherical: true}, function(err, success){
if(success) {
res(success);
} else {
console.log('Response error' + err);
}
});

create a new object Id in mongoDB using node js

I am using the below code to insert data to mongodb
router.post('/NewStory', function (req, res) {
var currentObject = { user: userId , story : story , _id:new ObjectID().toHexString() };
req.db.get('clnTemple').findAndModify({
query: { _id: req.body.postId },
update: { $addToSet: { Stories: currentObject } },
upsert: true
});
});
This code is working fine if i remove the _id:new ObjectID().toHexString()
What i want to achieve here is that for every new story i want a unique _id object to be attached to it
What am i doing wrong?
{
"_id": {
"$oid": "55ae24016fb73f6ac7c2d640"
},
"Name": "some name",
...... some other details
"Stories": [
{
"userId": "105304831528398207103",
"story": "some story"
},
{
"userId": "105304831528398207103",
"story": "some story"
}
]
}
This is the document model, the _id that i am trying to create is for the stories
You should not be calling .toHexString() on this as you would be getting a "string" and not an ObjectID. A string takes more space than the bytes of an ObjectId.
var async = require('async'),
mongo = require('mongodb'),
db = require('monk')('localhost/test'),
ObjectID = mongo.ObjectID;
var coll = db.get('junk');
var obj = { "_id": new ObjectID(), "name": "Bill" };
coll.findAndModify(
{ "_id": new ObjectID() },
{ "$addToSet": { "stories": obj } },
{
"upsert": true,
"new": true
},
function(err,doc) {
if (err) throw err;
console.log(doc);
}
)
So that works perfectly for me. Noting the "new" option there as well so the modified document is returned, rather than the original form of the document which is the default.
{ _id: 55c04b5b52d0ec940694f819,
stories: [ { _id: 55c04b5b52d0ec940694f818, name: 'Bill' } ] }
There is however a catch here, and that is that if you are using $addToSet and generating a new ObjectId for every item, then that new ObjectId makes everything "unique". So you would keep adding things into the "set". This may as well be $push if that is what you want to do.
So if userId and story in combination already make this "unique", then do this way instead:
coll.findAndModify(
{
"_id": docId,
"stories": {
"$not": { "$elemMatch": { "userId": userId, "story": story } }
}
},
{ "$push": {
"stories": {
"userId": userId, "story": story, "_id": new ObjectID()
}
}},
{
"new": true
},
function(err,doc) {
if (err) throw err;
console.log(doc);
}
)
So test for the presence of the unique elements in the array, and where they do not exist then append them to the array. Also noting there that you cannot do an "inequality match" on the array element while mixing with "upserts". Your test to "upsert" the document should be on the primary "_id" value only. Managing array entries and document "upserts" need to be in separate update operations. Do not try an mix the two, otherwise you will end up creating new documents when you did not intend to.
By the way, you can generate an ObjectID just using monk.
var db = monk(credentials.database);
var ObjectID = db.helper.id.ObjectID
console.log(ObjectID()) // generates an ObjectID

Using $in in MongooseJS with nested objects

I've been successfully using $in in my node webservice when my mongo arrays only held ids. Here is sample data.
{
"_id": {
"$oid": "52b1a60ce4b0f819260bc6e5"
},
"title": "Sample",
"team": [
{
"$oid": "52995b263e20c94167000001"
},
{
"$oid": "529bfa36c81735b802000001"
}
],
"tasks": [
{
"task": {
"$oid": "52af197ae4b07526a3ee6017"
},
"status": 0
},
{
"task": {
"$oid": "52af197ae4b07526a3ee6017"
},
"status": 1
}
]
}
Notice that tasks is an array, but the id is nested in "task", while in teams it is on the top level. Here is where my question is.
In my Node route, this is how I typically deal with calling a array of IDs in my project, this works fine in the team example, but obviously not for my task example.
app.get('/api/tasks/project/:id', function (req, res) {
var the_id = req.params.id;
var query = req.params.query;
models.Projects.findById(the_id, null, function (data) {
models.Tasks.findAllByIds({
ids: data._doc.tasks,
query: query
}, function(items) {
console.log(items);
res.send(items);
});
});
});
That communicates with my model which has a method called findAllByIds
module.exports = function (config, mongoose) {
var _TasksSchema = new mongoose.Schema({});
var _Tasks = mongoose.model('tasks', _TasksSchema);
/*****************
* Public API
*****************/
return {
Tasks: _Tasks,
findAllByIds: function(data, callback){
var query = data.query;
_Tasks.find({_id: { $in: data.ids} }, query, function(err, doc){
callback(doc);
});
}
}
}
In this call I have $in: data.ids which works in the simple array like the "teams" example above. Once I nest my object, as with "task" sample, this does not work anymore, and I am not sure how to specify $in to look at data.ids array, but use the "task" value.
I'd like to avoid having to iterate through the data to create an array with only id, and then repopulate the other values once the data is returned, unless that is the only option.
Update
I had a thought of setting up my mongo document like this, although I'd still like to know how to do it the other way, in the event this isn't possible in the future.
"tasks": {
"status0": [
{
"$oid": "52995b263e20c94167000001"
},
{
"$oid": "529bfa36c81735b802000001"
}
],
"status1": [
{
"$oid": "52995b263e20c94167000001"
},
{
"$oid": "529bfa36c81735b802000001"
}
]
}
You can call map on the tasks array to project it into a new array with just the ObjectId values:
models.Tasks.findAllByIds({
ids: data.tasks.map(function(value) { return value.task; }),
query: query
}, function(items) { ...
Have you try the $elemMatch option in find conditions ? http://docs.mongodb.org/manual/reference/operator/query/elemMatch/

Resources