MongoDB: Update nested data, but avoid multiple objects - javascript

I try to insert some data into an existing document:
Graph.update(
{ id: id },
{
$push: {
tooltips: {
element: Session.get('tooltipID'),
text: text
}
}
}
);
This is working quite well, but if there is already data in tooltips, this one should be updated instead of adding a new object, as there can only be a unique object for a unique element (tooltipID).
I want to avoid these multiple entries for the same element-value in tooltips.
{
"_id" : "c4bKur6TKcgFHGLZZ",
"data" : "[]",
"tooltips" : [
{
"element" : "2d4edaaf",
"text" : "Lorem"
},
{
"element" : "2d4edaaf",
"text" : "ipsum"
}
]
}
But it should be possible to have more then one object in tooltips, if element is really unique...
I tried to add a upsert:true to the update(), but that doesn't work.

Definitely upsert won't work with embedded document.
One approach can be
Graph.update({id:id},
{
$addToSet: {
'tooltips': {
element: Session.get('tooltipID'),
text: text
}
}
})
It will ensure no duplicate on tooltips;
similarly you can use $set
Graph.update({
id:id,
'tooltips.element': Session.get('tooltipID')
},
{
$set: {
'tooltips.$.text':text
}
})
or you can pull before push
Graph.update({
"id":id
}, {
$pull: {
'tooltips': {
"element": Session.get('tooltipID')
}
}
})
Graph.update(
{ id: id },
{
$push: {
tooltips: {
element: Session.get('tooltipID'),
text: text
}
}
}
);

Related

Is it possible to update multiple documents with different values using mongo? [duplicate]

I have the following documents:
[{
"_id":1,
"name":"john",
"position":1
},
{"_id":2,
"name":"bob",
"position":2
},
{"_id":3,
"name":"tom",
"position":3
}]
In the UI a user can change position of items(eg moving Bob to first position, john gets position 2, tom - position 3).
Is there any way to update all positions in all documents at once?
You can not update two documents at once with a MongoDB query. You will always have to do that in two queries. You can of course set a value of a field to the same value, or increment with the same number, but you can not do two distinct updates in MongoDB with the same query.
You can use db.collection.bulkWrite() to perform multiple operations in bulk. It has been available since 3.2.
It is possible to perform operations out of order to increase performance.
From mongodb 4.2 you can do using pipeline in update using $set operator
there are many ways possible now due to many operators in aggregation pipeline though I am providing one of them
exports.updateDisplayOrder = async keyValPairArr => {
try {
let data = await ContestModel.collection.update(
{ _id: { $in: keyValPairArr.map(o => o.id) } },
[{
$set: {
displayOrder: {
$let: {
vars: { obj: { $arrayElemAt: [{ $filter: { input: keyValPairArr, as: "kvpa", cond: { $eq: ["$$kvpa.id", "$_id"] } } }, 0] } },
in:"$$obj.displayOrder"
}
}
}
}],
{ runValidators: true, multi: true }
)
return data;
} catch (error) {
throw error;
}
}
example key val pair is: [{"id":"5e7643d436963c21f14582ee","displayOrder":9}, {"id":"5e7643e736963c21f14582ef","displayOrder":4}]
Since MongoDB 4.2 update can accept aggregation pipeline as second argument, allowing modification of multiple documents based on their data.
See https://docs.mongodb.com/manual/reference/method/db.collection.update/#modify-a-field-using-the-values-of-the-other-fields-in-the-document
Excerpt from documentation:
Modify a Field Using the Values of the Other Fields in the Document
Create a members collection with the following documents:
db.members.insertMany([
{ "_id" : 1, "member" : "abc123", "status" : "A", "points" : 2, "misc1" : "note to self: confirm status", "misc2" : "Need to activate", "lastUpdate" : ISODate("2019-01-01T00:00:00Z") },
{ "_id" : 2, "member" : "xyz123", "status" : "A", "points" : 60, "misc1" : "reminder: ping me at 100pts", "misc2" : "Some random comment", "lastUpdate" : ISODate("2019-01-01T00:00:00Z") }
])
Assume that instead of separate misc1 and misc2 fields, you want to gather these into a new comments field. The following update operation uses an aggregation pipeline to:
add the new comments field and set the lastUpdate field.
remove the misc1 and misc2 fields for all documents in the collection.
db.members.update(
{ },
[
{ $set: { status: "Modified", comments: [ "$misc1", "$misc2" ], lastUpdate: "$$NOW" } },
{ $unset: [ "misc1", "misc2" ] }
],
{ multi: true }
)
Suppose after updating your position your array will looks like
const objectToUpdate = [{
"_id":1,
"name":"john",
"position":2
},
{
"_id":2,
"name":"bob",
"position":1
},
{
"_id":3,
"name":"tom",
"position":3
}].map( eachObj => {
return {
updateOne: {
filter: { _id: eachObj._id },
update: { name: eachObj.name, position: eachObj.position }
}
}
})
YourModelName.bulkWrite(objectToUpdate,
{ ordered: false }
).then((result) => {
console.log(result);
}).catch(err=>{
console.log(err.result.result.writeErrors[0].err.op.q);
})
It will update all position with different value.
Note : I have used here ordered : false for better performance.

Create or update object if exists in a nested array

The following code works great in that it updates the object in the nested array.
However, I'm struggling to find a way to push a new object (Ex. {"locale" : "ar" , value:"مرحبا"}) if locale does not exist or update value if locale already exists (Ex. {"locale" : "en" , value:"hello"})
Update code:
Project.findOneAndUpdate(
{_id:projectId, 'sections._id': sectionId},
{ "$set": { "sections.$.subheader": {"locale":args.lang,"value":args.title} }},
{ upsert : true, new: true, useFindAndModify: false },
(err, section) => {
}
)
Object structure:
"project": {
"name": "project name",
"sections": [
{
"subheader": [{
'locale' : "en",
'value' : "Helle"
},
{
'locale' : "fr",
'value' : "salut"
}]
}
]
}
Unfortunately, this is not possible to do in one go. The upsert option only works on objects in the collection, not on nested objects.
You could solve this by first trying to update the element in the array, then check if the object in the nested array was matched. If there was no match, you can insert it into the nested array using $addToSet.
Additionally, you need to use positional operators to match the nested arrays:
Project.findOneAndUpdate(
// match item in subheader array
{ _id: projectId, 'sections._id': sectionId, 'sections.subheader.locale': args.lang },
// update existing item in subheader array
{ "$set": { "sections.$[section].subheader.$[subheader].value": args.title } },
// we use arrayFilters here, don't use upsert now
{ arrayFilters: [{ 'section._id': sectionId }, { 'subheader.locale': args.lang }], useFindAndModify: false },
(err, section) => {
// check if section was found
if (!section) {
// add new object to array if it wasn't found yet
Project.findOneAndUpdate(
// match section
{ _id: projectId, 'sections._id': sectionId},
// add new object to array
{ "$addToSet": { "sections.$.subheader": {"locale": args.lang,"value": args.title } }},
(err, section) => {
console.log('created new locale')
}
)
} else {
console.log('updated existing locale')
}
}
)

Meteor: How can i push items to users collections and create a list or array instead of replacing each item with the new one?

I'm trying to attach objects from another collection to the Meteor.user collection by a click event. I have a collection with a list of items called "categories" each category has a name field, its that name i want to push into the meteor.user.
Its supposed to work in a way that the user can push as many of these names as they want however its only accepting one entry, and when i click on another name, the new name replaces the old one, instead of being an array. how can i make it so that it can allow many entries?
client/users.js
Template.CategoriesMain.events({
'click .toggle-category': function(e){
//var id = $(e.target).attr('posts.name');
var id = $(e.target).parent().find("a").text();
console.log(id);
e.preventDefault();
Meteor.call('addingCategory', id, function(error, user){ console.log(id)});
}
});
server/users.js
Meteor.methods({
addingCategory: function(name) {
var cats = [{}];
cats.push(name);
console.log(Meteor.userId());
Meteor.users.update({
_id: Meteor.userId()
}, {
$set: {
name: name
}
});
}
});
and this is the user from db.user.find() as you can see with
"name" : "a-reece"
its clearly pushing the name but i cannot add more, i can only replace
{ "_id" : "4CHcZjSD4hCrqweGA", "createdAt" :
ISODate("2016-07-13T21:38:59.505Z"), "services" : { "password" : {
"bcrypt" :
"$2a$10$lKZtrYSMD4EGPj6eamgFDuPZ41Jw52DgivBly3lUYWbGDtfZBg1X." },
"resume" : { "loginTokens" : [ { "when" :
ISODate("2016-07-13T21:38:59.719Z"), "hashedToken" :
"BsqTGedB2FkmSPO3+5I31rOM2+MPtF97Zc9tRQ4pf8Y=" } ] } }, "emails" : [ {
"address" : "mun#les.com", "verified" : false } ], "roles" : [
"discoveror", "yes" ], "isAdmin" : true, "name" : "a-reece" }
how can i add more names instead of replacing?
EDIT
Meteor.methods({
addingCategory: function(name) {
//Meteor.users.update(Meteor.userId(), { $addToSet: { name: name} } );
console.log(Meteor.userId());
//Meteor.users.update(Meteor.userId(), { $set: { "categories": cats }} );
Meteor.users.update({
_id: Meteor.userId()
},
{
$unset: {
name: name
}
},
{
$addToSet: {
name: name
}
});
}
});
ANSWER
Template.CategoriesMain.events({
'click .toggle-category': function(e){
//var id = $(e.target).attr('posts.name');
var ob = $(e.target).parent().find("a").text();
var id = $.makeArray( ob );
console.log(id);
e.preventDefault();
Meteor.call('addingCategory', id, function(error, user){ console.log(id)});
}
});
You're currently doing:
Meteor.users.update({ _id: Meteor.userId() }, { $set: { name: name } });
You have two choices: $push or $addToSet:
Meteor.users.update({ _id: Meteor.userId() }, { $push: { name: name } });
or
Meteor.users.update({ _id: Meteor.userId() }, { $addToSet: { name: name } });
The former pushes onto an array, allowing duplicates, the latter avoids dupes.
You don't need:
var cats = [{}];
cats.push(name);

Find element in an array that may or may not exist

I have a document that looks a bit like this:
> db.orders.find()
{
_id: ObjectId(),
_reminders: [{
notified: true,
timestamp: ISODate(),
completed: false
}]
}
{
_id: ObjectId(),
_reminders: []
}
What I am trying to find is a document in the orders collection where the "reminders" does not contain a reminder in a specific time range, and is not completed.
db.orders.find({
'_reminders': {
$elemMatch: {
completed: false,
timestamp: { $ne: time }
}
}
});
The problem is that this will not find an order which does not have any reminders at all.
How would one query this?
This should get you what you want
db.getCollection('Clock').find({
$or : [
{
_reminders : {
$elemMatch : {
timestamp : {
$lte : ISODate("2019-07-12T15:35:32.278Z"),
$gte : ISODate("2012-07-12T15:35:32.278Z")
},
completed : false
}
}
},
{
_reminders : {$size : 0}
},
{
_reminders : {$exists : false}
}
]
})
you should use $or query.
db.orders.find({$or: [ { _reminders: [] }, here_put_your_query_with_time_match ]}) - it will return both documents which match your query and these with empty _reminders

MongoDB, remove object from array

Doc:
{
_id: 5150a1199fac0e6910000002,
name: 'some name',
items: [{
id: 23,
name: 'item name 23'
},{
id: 24,
name: 'item name 24'
}]
}
Is there a way to pull a specific object from an array? I.E. how do I pull the entire item object with id 23 from the items array.
I have tried:
db.mycollection.update({'_id': ObjectId("5150a1199fac0e6910000002")}, {$pull: {id: 23}});
However I am pretty sure that I am not using 'pull' correctly. From what I understand pull will pull a field from an array but not an object.
Any ideas how to pull the entire object out of the array.
As a bonus I am trying to do this in mongoose/nodejs, as well not sure if this type of thing is in the mongoose API but I could not find it.
try..
db.mycollection.update(
{ '_id': ObjectId("5150a1199fac0e6910000002") },
{ $pull: { items: { id: 23 } } },
false, // Upsert
true, // Multi
);
I have a document like
I have to delete address from address array
After searching lots on internet I found the solution
Customer.findOneAndUpdate(query, { $pull: {address: addressId} }, (err, data) => {
if (err) {
return res.status(500).json({ error: 'error in deleting address' });
}
res.json(data);
});
my database:
{
"_id" : ObjectId("5806056dce046557874d3ab18"),
"data" : [
{ "id" : 1 },
{ "id" : 2 },
{ "id" : 3 }
]
}
my query:
db.getCollection('play_table').update({},{$pull:{"data":{"id":3}}},{multi:true}
output:
{
"_id" : ObjectId("5806056dce046557874d3ab18"),
"data" : [
{ "id" : 1 },
{ "id" : 2 }
]
}
You can try it also:
db.getCollection('docs').update({ },{'$pull':{ 'items':{'id': 3 }}},{multi:true})
For a single record in array:
db.getCollection('documents').update(
{ },
{'$pull':{ 'items':{'mobile': 1234567890 }}},
{new:true}
);
For a multiple records with same mobile number in array:
db.getCollection('documents').update(
{ },
{
$pull: {
items: { mobile: 1234567890 }
}
},
{ new:true, multi:true }
)
Use $pull to remove the data
return this.mobiledashboardModel
.update({"_id": args.dashboardId}, { $pull: {"viewData": { "_id": widgetId}}})
.exec()
.then(dashboardDoc => {
return {
result: dashboardDoc
}
});
Kishore Diyyana:
If you want to remove all elements including the key of the element attributes list.
Here is the example of mongoDB unset operator:
db.UM_PREAUTH_CASE.update(
{ 'Id' : 123}, { $unset: { dataElements: ""} } )
JSON Look like this:
{ "Id":123,"dataElements" : [ { "createdBy" : "Kishore Babu Diyyana", "createdByUserId" : 2020 }, { "createdBy" : "Diyyana Kishore", "createdByUserId" : 2021 } ] }

Categories

Resources