Create GeoJson from coordinates array in the same Mongodb collection - node.js

I want to modify my mongodb collection from 2d to 2dsphere.
I have this structure in my db.users:
{
"_id" : "1c6930387123e960eecd65e8ade28488",
"interests" : [
{
"_id" : ObjectId("56a8b2a72f2c2a4c0250e896"),
"coordinates" : [-3.6833, 40.4]
}
],
}
I would like to have something like this:
{
"_id" : "1c6930387123e960eecd65e8ade28488",
"interests" : [
{
"_id" : ObjectId("56a8b2a72f2c2a4c0250e896"),
"loc":{
"type":"Point",
"coordinates" : [-3.6833, 40.4]
}
}
],
}
I tried this:
db.users.update( {interests:{$elemMatch:{coordinates : { $exists: true } }}}, { $set: { 'interests.$.place':{"type":"Point", "coordinates":'interests.$.coordinates'} } }, { upsert: false, multi: false });
Obviously, it insert literally "'interests.$.coordinates'
And tried this in Node:
users.find({interests:{$elemMatch:
{coordinates :
{ $exists: true } }}} ,
{"interests.coordinates":1,"interests._id":1,"_id":0 }).forEach( function( doc ){
users.update( {interests:
{$elemMatch:
{_id : doc.interests[0]['_id'] }}},
{ $set: { 'interests.$.loc':{"type":"Point", "coordinates":doc.interests[0]['coordinates']} } },
{ upsert: false, multi: false },function(err, numberAffected, rawResponse) {
var modified=numberAffected.result.nModified;
console.log(modified)
});
});
But coordinates were inserted with mixed values.
Thoughts?
Thanks!!

I didn't test this piece of code but I think this would shed light on this issue.
users.find(
{interests:{ $elemMatch: {coordinates : { $exists: true } }}}).forEach( function( doc ){
for(var c = 0; c < doc.interests.length; c++) {
var orig = doc.interests[c].coordinates;
doc.interests[c].loc: { type:"Point", coordinates: orig };
//delete doc.interests[c].coordinates;
};
users.update({ _id: doc._id }, doc);
});
Try to iterate over all documents in your collection modifying the whole document and then update it in a single line.
Warning: If you have a huge quantity of documents in your collection that match the 'find' condition, you should use pagination (skip/limit) to avoid performance and memory issues in the first load.

Related

findOneAndUpdate is not working inside array

I'm trying to update the document in the MongoDB collection but it's not working for me. Here is the function all fields come to the backend.
{
"_id" : ObjectId("59a6b381c13a090c70fc21b6"),
"processNumber" : "FEE 082517",
"System" : "abc",
"TaxAmount" : 0,
"TaxPercent" : 0,
"Currency" : "USD",
"ProcessData" : [
{
"_id" : ObjectId("59ee2873b1621419a03fba6c"),
"KDSID" : "1db1d4b8-61bc-45eb-bf6d-15af1e391df5"
},
{
"_id" : ObjectId("59ee2873b1621419a03fba6d"),
"KDSID" : "aa9ccaf3-a638-4013-afdc-ccf0a39361e8"
},
{
"_id" : ObjectId("59ee2873b1621419a03fba6e"),
"KDSID" : "4c5e32a7-e2fb-4fe9-998f-e22602e46dba"
}
{
"Name" : "2017 Calc.xlsx",
"FileID" : "59ee2873b1621419a03fb9b7",
"_id" : ObjectId("59ee2873b1621419a03fba75")
}
]
}
Query:
db.process.findOneAndUpdate(
{ 'ProcessData._id': ObjectId("59ee2873b1621419a03fba75"),'ProcessData.FileID': { '$exists': true, '$ne': null }},
{ $set: { 'ProcessData.$.IsFailed': "Yes" } }
)
When I run the above query IsFailed is not updating. can you please advise?
I have tried in node and MongoDB and it's not working.
If ProcessData._id matches with the given id and ProcessData.FileID exist we have to set IsFailed Yes
here's my version with $elemMatch
db.process.updateOne(
{
ProcessData: {
$elemMatch: {
_id: ObjectId("59ee2873b1621419a03fba75"),
FileID: {
$exists: true,
$ne: null
}
}
}
},
{
$set: { "ProcessData.$.IsFaild": "Yes" }
})
https://mongoplayground.net/p/3LBlw9kA6Gl
You need to use arrayFilter when you modify the array.
db.collectionName.updateOne(
{
'ProcessData._id': ObjectId("59ee2873b1621419a03fba75"),
'ProcessData.FileID': { '$exists': true, '$ne': null }
},
{
$set:{"ProcessData.$[p].isFailed": "Yes"}},
{
arrayFilters:[{"p._id":ObjectId("59ee2873b1621419a03fba75")}]
}
)
You're querying through an array of embedded documents, and hence by using elemMatch to find the specific id and perform needed operation on that document should be implemented for example as below:
db.process.updateOne(
{
ProcessData: {
$elemMatch: {
_id:
ObjectId("59ee2873b1621419a03fba75"),
}
}
},
{
"ProcessData.FileID": {
$exists: true,
$ne: null
}
},
{
$set: {
"ProcessData.$.IsFailed": "Yes"
}
})

MongoDB mongoose update elemMatch doesn't work but when query (find) it's ok

I'm having some problems on update using $elemMatch.
When I try to match or find, it shows the write result.
.updateOne(
{ _id: req.body.sid },
{ $pull: {comments: { $elemMatch: { fid: '5ea9c87d2d5c8f491cae8818' } }}}
).then((res)=>{
console.log(res)
}).catch((err)=>{
console.log(err)
})
It's resulting :
{ n: 1, nModified: 0, ok: 1 }
And it's the query result using find:
.find({
$and: [
{ _id: req.body.sid },
{comments: { $elemMatch: { fid: '5ea9c87d2d5c8f491cae8818' } }}
]
})
Sample doc result of find :
[
{
_id: 5ea88b12423e6e4b3ce01956,
comments: [{"fid":"5ea9c87d2d5c8f491cae8818","comment":"ok"}, {"fid":"5ea9c87d2d5c8f491cae8899","comment":"ok"}],
date: 2020-04-28T00:00:00.000Z,
title: 'Fotos para o Álbum de ensaios',
__v: 5
}
]
can anybody help me? Thank you!!
You can try like below :
If your documents look something like :
/* 1 */
{
"_id" : "5ea88b12423e6e4b3ce01956",
"comments" : [
{
"fid" : "5ea9c87d2d5c8f491cae8818"
},
{
"fid" : "5ea9c87d2d5c8f491cae8899"
}
],
"date" : "2020-04-28T00:00:00.000Z",
"title" : "Fotos para o Álbum de ensaios",
"__v" : 5.0
}
/* 2 */
{
"_id" : "5ea88b12423e6e4b3ce01951",
"comments" : [
{
"fid" : "5ea9c87d2d5c8f491cae8811"
},
{
"fid" : "5ea9c87d2d5c8f491cae8899"
}
],
"date" : "2020-04-28T00:00:00.000Z",
"title" : "Fotos para o Álbum de ensaios",
"__v" : 5.0
}
So $elemMatch is not needed on single query condition which in your case {comments: { $elemMatch: { fid: '5ea9c87d2d5c8f491cae8818' } }} can just be "comments.fid": "5ea9c87d2d5c8f491cae8818", also you don't need $and operator in your .find().
Query :
/** Here "comments.fid" helps to filter doc, even though we'll get single doc based on `_id` filter but
* still adding this check prevents unnecessary update operation if `5ea9c87d2d5c8f491cae8818` doesn't exists in fid of comments in doc */
.updateOne(
{ _id: req.body.sid, "comments.fid": "5ea9c87d2d5c8f491cae8818" },
{ $pull: { "comments" :{ "fid": "5ea9c87d2d5c8f491cae8818" } } }
)
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});

How to update multiple documents?

I want to update multiple documents.
My current Document,
Document Account
{
"_id" : "5cbd96aca1a6363473d4g8745",
"contact" : [
"5cbd96aca1a6363473d4a968",
]
},
{
"_id" : "5cbd96aca1a6363473d4g8746",
"contact" : [
"5cbd96aca1a6363473d4z7632",
]
}
I need below output,
update contact array with different _id.
Document Account
{
"_id" : "5cbd96aca1a6363473d4g8745",
"contact" : [
"5c98833f98770728a7047f1a",
"5cbd96aca1a6363473d4a968",
]
},
{
"_id" : "5cbd96aca1a6363473d4g8746",
"contact" : [
"5caddf78b8c0645402090536",
"5cbd96aca1a6363473d4z763",
]
}
Use $addToSet or $push to push id with bulk update.
You can use update with upsert. It will update the doc if exist and if not then it will create new one.
for example:
//Make a obj to set
var contacts = {
id: req.body.id,
contactIds: req.body.contactIds,
};
req.app.db.models.ModelsName.update(
{
//if you want multiple fields to be update
$and: [{ id: contacts.id }, { contactIds: { $in: contacts.contactIds } }]
},
//Set the above obj
{ $set: contacts },
{ upsert: true },
(err, result) => {
if (err) {
console.log(err.message)
}
console.log("Updated successfully")
})
This is just a reference. Modify accordingly your use.
You can use Bulk.find.update() method to update all matching documents.
example:
var bulk = db.items.initializeUnorderedBulkOp();
bulk.find( { status: "D" } ).update( { $set: { status: "I", points: "0" } } );
bulk.find( { item: null } ).update( { $set: { item: "TBD" } } );
bulk.execute();

If condition query for mongo db

I have an collection with documents that can be public or private. I am looking to have a single query that can get a document by an _id and check if it's private. If it is private it needs to check if the user requesting the document is a participant in the object.
Here is the object:
{
"_id" : ObjectId("5769ae620e80ea1c7f8a997f"),
"owner" : ObjectId("5740edae95a1b4c043d033df"),
"private" : false,
"participants" : [
{
"uid" : ObjectId("5740edae95a1b4c043d033df"),
"notify" : true
}
],
"messages" : [ ]
}
This is the 2 step query I wrote and wondering if I can simplify it
function getRoom(roomId, user){
db.rooms.findOne({ _id: pmongo.ObjectId(roomId) })
.then(room => {
if(room.private){
return db.rooms.findOne({
_id: pmongo.ObjectId(roomId),
participants: {$eleMatch: {uid: String(user._id)}}
})
} else {
return room
}
})
}
Any help would be great!
MongoDB has $or operator which can be used in this case:
db.rooms.findOne({ $or: [
{
_id: pmongo.ObjectId(roomId),
private: { $ne: true }
}, {
_id: pmongo.ObjectId(roomId),
participants: {$eleMatch: {uid: String(user._id)}}
}
]})

Sort Embedded Documents in Array and fetch specifics

I have a mongoose model like this:
var activityItem = mongoose.Schema({
timestampValue: Number,
xabc: String,
full: Boolean,
comp: Boolean
});
var ABC = mongoose.Schema({
activity: [activityItem],
user: {
type: mongoose.Schema.ObjectId,
ref: 'User'
},
username: String
});
I want to get the activityItem array elements that have a timestampValue less than a specific value. Also, I want to sort the activity array first according to the timestampValue
This is the code that I currently have. And it doesn't work.
UserActivity.findOne({
'user': current_user,
'activity' : {
$all: [
{
"$elemMatch": {
timestampValue: {
$lte: time
}
}
}
]
}
},
function(err, user){
})
Sample Document structure:
{
"_id" : ObjectId("56d5e88adfd14baf1848a7c6"),
"user" : ObjectId("56bf225342e662f4277ded73"),
"notifications" : [],
"completed" : [],
"activity" : [
{
"timestampValue": 1456902600000,
"xabc": "Some value",
"full": true,
"comp": false,
"_id" : ObjectId("56d5e88adfd14baf1848a7d2")
},
{
"timestampValue": 1456702600000,
"xabc": "Some other value",
"full": true,
"comp": false,
"_id" : ObjectId("56d5e88adfd14baf1848a7d3")
}
],
"__v" : 1
}
The POST call has the following params
hash: "2e74aaaf42aa5ea733be963cb61fc5ff"
time: 1457202600000
hash comes into the picture once i have the docs from mongo
time is a unix timestamp value.
Instead of returning only the elements that are less than the time value, it is returning all the array elements. I tried the aggregation framework to sort the array before querying, but couldn't get the hang of it.
Any help would be greatly appreciated.
Please try to do it through aggregation as below
ABS.aggregate([
// filter the document by current_user
{$match: {user: ObjectId(current_user)}},
// unwind the activity array
{$unwind: '$activity'},
// filter the timestampValue less than time
{$match: {'activity.timestampValue': {$lte: time}}},
// sort activity by timestampValue in ascending order
{$sort: {'activity.timestampValue': 1}},
// group by _id, and assemble the activity array.
{$group: {_id: '$_id', user: {$first: '$user'},activity: {$push: '$activity'}}}
], function(err, results){
if (err)
throw err;
// populate user to get details of user information if needed
//ABS.populate( results, { "path": "user" }, function(err, rets) {
//
//});
});
Well, it seems little bit tricky with MongoDb aggregation pipeline unless you have MongoDB 3.2, but you can definitely
achieve your result with help of map-reduce.
e.g.
MongoDB version < 3.2
var findActivities = function (time) {
db.col.mapReduce(function () {
var item = Object.assign({}, this);
delete item.activity;
item.activity = [];
for (var i = 0; i < this.activity.length; i++) {
if (this.activity[i].timestampValue <= time) {
item.activity.push(this.activity[i]);
}
}
emit(item._id, item);
}, function (k, v) {
return {items: v};
}, {
out: {"inline": true},
scope: {time: time}
}).results.forEach(function (o) {
printjson(o); // Or perform action as appropriate
});
};
Based your sample data when called findActivities(1456802600000), it will find and return only those documents matching criteria.
{
"_id" : ObjectId("56d5e88adfd14baf1848a7c6"),
"value" : {
"_id" : ObjectId("56d5e88adfd14baf1848a7c6"),
"user" : ObjectId("56bf225342e662f4277ded73"),
"notifications" : [
],
"completed" : [
],
"__v" : NumberInt(1),
"activity" : [
{
"timestampValue" : NumberLong(1456702600000),
"xabc" : "Some other value",
"full" : true,
"comp" : false,
"_id" : ObjectId("56d5e88adfd14baf1848a7d3")
}
]
}
}
MongoDB version 3.2+
db.col.aggregate([
{$project:{user:1, notifications:1, completed:1, activity:{
$filter:{input: "$activity", as: "activity", cond:{
$lte: ["$$activity.timestampValue", 1456802600000]}}}}}
])
Both solutions will have same output.

Resources