How to upsert nested array object mongodb - mongodb

I have a document which looks like this
{
_id:'asasasasa23sdsdsd',
source:'page',
url:[]
}
I need to upsert some values/objects to the url array. the objects that need to be upserted looks like this.
{
"type" : "blog",
"value" : "hello blog",
"id" : "1815f620-b45c-4230-85bb-7ba90ac330ed",
"datetime" : "2019-12-26 15:58:33"
}
Then it would look like this
{
_id:'asasasasa23sdsdsd',
source:'page',
url:[{
"type" : "admin",
"value" : "hello admin",
"id" : "1815f620-b45c-4230-85bb-7ba90ac330ed",
"datetime" : "2019-12-26 15:58:33"
},
{
"type" : "blog",
"value" : "hello blog",
"id" : "1815f620-b45c-4230-85bb-7ba90ac330ed",
"datetime" : "2019-12-26 15:58:33"
}
]
}
Here the id and the type fields are unique. I need to insert them if they do not exist or update them if they do.
This is the code that I have tried
db.collection(TABLE_NAME).update(
{ source: data.source },
{
source: data.source,
url: [data.urls]
},
{ upsert: true }
);
With this, it just replaces the array object with a new object. How to upsert instead of replacing the object?

I think this code will help.
let arr = [
{
"type" : "admin",
"value" : "hello admin",
"id" : "1815f620-b45c-4230-85bb-7ba90ac330ed",
"datetime" : "2019-12-26 15:58:33"
},
{
"type" : "blog",
"value" : "hello blog",
"id" : "1815f620-b45c-4230-85bb-7ba90ac330ed",
"datetime" : "2019-12-26 15:58:33"
}
];
db.users.update(
{ _id: doc._id },
{
$set: {
"url": arr
}
}
);

Related

Update double nested array mongodb

I have the below document which contains double nested array format. I have to update the "level" field to "Senior Engineer" when the "someKey":"somevalue" and "Company":"Company1" and "Name":"Nandhi".
Document
{
"_id" : "777",
"someKey" : "someValue",
"someArray" : [
{
"Company" : "Company1",
"someNestedArray" : [
{
"name" : "Nandhi",
"level" : "Junior Engineer"
},
{
"name" : "Rajan",
"level" : "Senio Engineer"
}
]
}],
{
"Company" : "Company2",
"someNestedArray" : [
{
"name" : "Nandhi",
"level" : "Junior Engineer"
},
{
"name" : "Rajan",
"level" : "Senio Engineer"
}
]
}
]
}
Update Query I tried
db.Test123.updateOne(
{"someKey" : "someValue","someArray.Company":"Company1"},
{$set:{"someArray.$[someNestedArray].level":"Senior Developer"}},
{arrayFilters:[{"someNestedArray.name":"Nandhi"}]}
);
Output Screenshot
As you can seen that, the modifiedCount returns 0. Please advice on this!
You need to define arrayFilter for every level of nesting, try:
db.Test123.update(
{ "someKey" : "someValue" },
{ "$set": { "someArray.$[someArrayDoc].someNestedArray.$[someNestedArrayDoc].level": "Senior Developer" } },
{ arrayFilters: [ {"someArrayDoc.Company": "Company1"}, { "someNestedArrayDoc.name": "Nandhi" } ] }
)

Update Documents From Each Value of an Input Array

I have a JSON with two keys: id and name. I need a way to insert that id on my collection (querys.project.name) when the name on it and on the JSON matches.
Example of my JSON:
var projectsMysql = [
{
"id" : 1,
"name" : "Something"
},
{
"id" : 5,
"name" : "Something else"
},
{
"id" : 50,
"name" : "Some name"
}]
and in my collection about 60 documents like this one:
{
"_id" : ObjectId("58e42bf30a34d641be6c25c2"),
"folio" : "R-666-69",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Something else"
// "projectsMysql_id" : 5 THIS SHOULD BE PLACED HERE
},
}
I wrote a function for Node.js but now I need to run this directly on the mongo shell, I read that I need to use cursor.forEach() method but I couldn't find a way to do it, this is my function:
projectsMysql
.forEach(function(project){
var query = {
name: project.name
}
db.getCollection('requests')
.find(query)
.exec(function(err, response){
if (err) {
return
}
if (response) {
db.getCollection('requests')
.update({id: response.id}, {$set: {
'project.projectsMysql_id': project.id
console.log("element inserted");
}})
.exec(function(err){
});
}
});
});
Can you point me in to the right direction?
What you want here is bulkWrite(), where instead of actually executing an update() for each document within the projectsMysql array you actually just construct a "single" statement made of "multiple" updates which sends to the server and updates the appropriate documents:
db.getCollection('requests').bulkWrite(
projectsMysql.map(({ id, name }) =>
({ "updateOne": {
"filter": { "project.name": name },
"update": { "$set": { "project.id": id } }
}})
)
)
If you expect "multiple documents" to match the condition, then simply switch to updateMany:
db.getCollection('requests').bulkWrite(
projectsMysql.map(({ id, name }) =>
({ "updateMany": {
"filter": { "project.name": name },
"update": { "$set": { "project.id": id } }
}})
)
)
Your array is already in memory so there is not really any point to doing any other kind of "iteration" and you can simply .map() the properties onto the updateOne statements ( or updateMany ) and issue them all in one statement. Updates are only actually processed where there is a "match" and indeed where there is something to actually update, as existing values will be left alone using $set.
To Demonstrate, considering these documents:
{
"folio" : "R-666-69",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Something else"
}
},
{
"folio" : "R-666-67",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Some name"
}
},
{
"folio" : "R-666-68",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Some name"
}
}
{
"folio" : "R-666-64",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Something"
}
},
{
"folio" : "R-666-65",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Different"
}
}
The the following update:
var projectsMysql = [
{
"id" : 1,
"name" : "Something"
},
{
"id" : 5,
"name" : "Something else"
},
{
"id" : 50,
"name" : "Some name"
}]
db.getCollection('requests').bulkWrite(
projectsMysql.map(({ id, name }) =>
({ "updateMany": {
"filter": { "project.name": name },
"update": { "$set": { "project.id": id } }
}})
)
)
Returns the response:
{
"acknowledged" : true,
"deletedCount" : 0,
"insertedCount" : 0,
"matchedCount" : 4,
"upsertedCount" : 0,
"insertedIds" : {
},
"upsertedIds" : {
}
}
And alters the matched documents accordingly:
{
"_id" : ObjectId("5b206a48f7fa0c655d90157a"),
"folio" : "R-666-69",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Something else",
"id" : 5
}
}
{
"_id" : ObjectId("5b206a48f7fa0c655d90157b"),
"folio" : "R-666-67",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Some name",
"id" : 50
}
}
{
"_id" : ObjectId("5b206a48f7fa0c655d90157c"),
"folio" : "R-666-68",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Some name",
"id" : 50
}
}
{
"_id" : ObjectId("5b206a48f7fa0c655d90157d"),
"folio" : "R-666-64",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Something",
"id" : 1
}
}
{
"_id" : ObjectId("5b206a48f7fa0c655d90157e"),
"folio" : "R-666-65",
"alias_purchase" : "Deal",
"project" : {
"description" : "",
"name" : "Different"
}
}

how to update in element array mongodb

I have a exemple data:
"_id" : ObjectId("5694ba11b3957b7ff69c4547"),
"name" : "Okas 1",
"job" : {
"name" : "job try1",
"_id" : ObjectId("5a6ff9f7a336e3bba40a1d5c")
},
"categories" : {
"ss" : [
{
"name" : "10",
"_id" : ObjectId("5a6ff9f7a336e3bba40a1d5c")
},
{
"name" : "50",
"_id" : ObjectId("5a6ff9f7a336e3bba40a1d5f")
}
]
}
if I update with new data .
[{
"name" : "800",
"_id" : ObjectId("5a6ff9f7a336e3bba40a1d5a")
},
{
"name" : "8",
"_id" : ObjectId("5a6ff9f7a336e3bba40a1d5f")
}]
I should get the data
"_id" : ObjectId("5694ba11b3957b7ff69c4547"),
"name" : "Okas 1",
"job" : {
"name" : "job try1",
"_id" : ObjectId("5a6ff9f7a336e3bba40a1d5c")
},
"categories" : {
"ss" : [
{
"name" : "10",
"_id" : ObjectId("5a6ff9f7a336e3bba40a1d5c")
},
{
"name" : "8",
"_id" : ObjectId("5a6ff9f7a336e3bba40a1d5f")
},
{
"name" : "800",
"_id" : ObjectId("5a6ff9f7a336e3bba40a1d5a")
}
]
}
So I want to update if data exeist just update value but if not exist
add new element in to array categories. I try but not working. the
results are not appropriate
You can do something like this:
model.findOne({_id:req.params.id}, (err, data) => {
if (err) throw err;
if(!data) {
var newData = new model({
name: req.body.name
.....
})
newData.save((err, newdata) => {
// Response
})
} else {
data.name = req.body.name
data.save((err, data) => {
// Response
})
}
I hope this is what you want.

Mongodb : get whether a document is the latest with a field value and filter on the result

I am trying to port an existing SQL schema into Mongo.
We have document tables, with sometimes several times the same document, with a different revision but the same reference. I want to get only the latest revisions of the documents.
A sample input data:
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC305",
"code" : "305-D",
"title" : "Document 305",
"creationdate" : ISODate("2011-11-24T15:13:28.887Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-A",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:23:18.807Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-B",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:26:49.447Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-A",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:30:35.757Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-B",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:40:32.957Z"),
"creator" : "X"
}
Given this data, I want this result set (sometimes I want only the last revision, sometimes I want all revisions with an attribute telling me whether it's the latest):
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC305",
"code" : "305-D",
"title" : "Document 305",
"creationdate" : ISODate("2011-11-24T15:13:28.887Z"),
"creator" : "X",
"lastrev" : true
},
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-B",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:26:49.447Z"),
"creator" : "X",
"lastrev" : true
},
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-B",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:40:32.957Z"),
"creator" : "X",
"lastrev" : true
}
I already have a bunch of filters, sorting, and skip/limit (for pagination of data), so the final result set should be mindful of these constraints.
The current "find" query (built with the .Net driver), which filters fine but gives me all revisions of each document:
coll.find(
{ "$and" : [
{ "$or" : [
{ "deletedid" : { "$exists" : false } },
{ "deletedid" : null }
] },
{ "$or" : [
{ "taskid" : { "$exists" : false } },
{ "taskid" : null }
] },
{ "objecttypeuid" : { "$in" : ["xxxxx"] } }
] },
{ "_id" : 0, "Uid" : 1, "lastrev" : 1, "title" : 1, "code" : 1, "creator" : 1, "owner" : 1, "modificator" : 1, "status" : 1, "reference": 1, "creationdate": 1 }
).sort({ "creationdate" : 1 }).skip(0).limit(10);
Using another question, I have been able to build this aggregation, which gives me the latest revision of each document, but with not enough attributes in the result:
coll.aggregate([
{ $sort: { "creationdate": 1 } },
{
$group: {
"_id": "$reference",
result: { $last: "$creationdate" },
creationdate: { $last: "$creationdate" }
}
}
]);
I would like to integrating the aggregate with the find query.
I have found the way to mix aggregation and filtering:
coll.aggregate(
[
{ $match: {
"$and" : [
{ "$or" : [
{ "deletedid" : { "$exists" : false } },
{ "deletedid" : null }
] },
{ "$or" : [
{ "taskid" : { "$exists" : false } },
{ "taskid" : null }
] },
{ "objecttypeuid" : { "$in" : ["xxx"] } }
]
}
},
{ $sort: { "creationdate": 1 } },
{ $group: {
"_id": "$reference",
"doc": { "$last": "$$ROOT" }
}
},
{ $sort: { "doc.creationdate": 1 } },
{ $skip: skip },
{ $limit: limit }
],
{ allowDiskUse: true }
);
For each result node, this gives me a "doc" node with the document data. It has too much data still (it's missing projections), but it's a start.
Translated in .Net:
FilterDefinitionBuilder<BsonDocument> filterBuilder = Builders<BsonDocument>.Filter;
FilterDefinition<BsonDocument> filters = filterBuilder.Empty;
filters = filters & (filterBuilder.Not(filterBuilder.Exists("deletedid")) | filterBuilder.Eq("deletedid", BsonNull.Value));
filters = filters & (filterBuilder.Not(filterBuilder.Exists("taskid")) | filterBuilder.Eq("taskid", BsonNull.Value));
foreach (var f in fieldFilters) {
filters = filters & filterBuilder.In(f.Key, f.Value);
}
var sort = Builders<BsonDocument>.Sort.Ascending(orderby);
var group = new BsonDocument {
{ "_id", "$reference" },
{ "doc", new BsonDocument("$last", "$$ROOT") }
};
var aggregate = coll.Aggregate(new AggregateOptions { AllowDiskUse = true })
.Match(filters)
.Sort(sort)
.Group(group)
.Sort(sort)
.Skip(skip)
.Limit(rows);
return aggregate.ToList();
I'm pretty sure there are better ways to do this, though.
You answer is pretty close. Instead of $last, $max is better.
About $last operator:
Returns the value that results from applying an expression to the last document in a group of documents that share the same group by a field. Only meaningful when documents are in a defined order.
Get the last revision in each group, see code below in mongo shell:
db.collection.aggregate([
{
$group: {
_id: '$reference',
doc: {
$max: {
"creationdate" : "$creationdate",
"code" : "$code",
"Uid" : "$Uid",
"status" : "$status",
"title" : "$title",
"creator" : "$creator"
}
}
}
},
{
$project: {
_id: 0,
Uid: "$doc.Uid",
status: "$doc.status",
reference: "$_id",
code: "$doc.code",
title: "$doc.title",
creationdate: "$doc.creationdate",
creator: "$doc.creator"
}
}
]).pretty()
The output as your expect:
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-B",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:40:32.957Z"),
"creator" : "X"
}
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-B",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:26:49.447Z"),
"creator" : "X"
}
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC305",
"code" : "305-D",
"title" : "Document 305",
"creationdate" : ISODate("2011-11-24T15:13:28.887Z"),
"creator" : "X"
}

How to add new field on array item

I have the following json structure:
{
"_id" : ObjectId("5203af83396d285ea2ecff8f"),
"brand" : "LG",
"comments" : [{
"user_id" : ObjectId("521b2785eda03d0f9cab3566"),
"text" : "Nice TV"
}],
"model" : "47LS5600",
"price" : 499.0,
"thumbnail" : "lg-47LS5600"
}
I need to insert a new field "datetime" in the array "comments" like this:
{
"_id" : ObjectId("5203af83396d285ea2ecff8f"),
"brand" : "LG",
"comments" : [{
"user_id" : ObjectId("521b2785eda03d0f9cab3566"),
"text" : "Nice TV",
"datetime": <value>
}],
"model" : "47LS5600",
"price" : 499.0,
"thumbnail" : "lg-47LS5600"
}
I tried with bellow instruction:
db.tvs.update({ _id: ObjectId("5203af83396d285ea2ecff8f") }, { $addToSet: { "comments.1": { "datetime": Date() } } } )
But it doesn´t inserts the field in the item, it creates another object separate from that item
Check out the link:
http://www.mongodb.org/display/DOCS/Updating#Updating-The%24positionaloperator
Modify Documents: http://docs.mongodb.org/manual/tutorial/modify-documents
Check Out this Code.
db.bios.update(
{ _id: 3 },
{ $set: {
mbranch: 'Navy',
'name.aka': 'Amazing Grace'
}
}
)