How to update a mongodb object using multiple selection criteria - mongodb

My schema structure looks something like this:
{
"__v":0,
"_id":ObjectId("5708423e897db8255aaaa9dd"),
"podId":169400000005,
"env":[
{
"type":"1",
"id":3852000000035,
"_id":ObjectId("5708423e897db8255aaaa9de")
},
{
"type":"2",
"id":3852000000040,
"_id":ObjectId("5708423e897db8255aaaa9df")
}
],
"name":"Test Build",
"parameters":[
{
"name":"sound.left",
"type":"1",
"paramName":"Left Sound Control",
"paramType":"booleanParameter",
"testValue":null,
"liveValue":null,
"_id":ObjectId("5708423f897db8255aaaaa0d")
},
{
"name":"sound.right",
"type":"1",
"paramName":"Right Sound Control",
"paramType":"booleanParameter",
"testValue":null,
"liveValue":null,
"_id":ObjectId("5708423f897db8255aaaaa0d")
},
...
]
}
I have 3 known variables: podId for podId, codeName for parameters.name, envType for parameters.type. Using them I want to update the object using podId, codeName, and envType, with a new variable paramValue that will contain the testValue value.
What I've tried
db.pods.update({
podId: podId,
parameters: {
$elemMatch: {
name: codeName,
type: envType
}
}
}, {
$set: {
'parameters.$.testValue': paramValue
}
});
I'm trying to update the testValue where podId == podId, parameters.name == codeName, and parameters.type == envType, but the above did not update anything.
I also tried
db.pods.update({
podId: podId,
parameters: {
name: codeName,
type: envType
}
}, {
$push: {
parameters: {
testValue: paramValue
}
}
},
function(err) {
if(err) throw err;
});
basically taking what worked to build the object when I only had to compare the podId, and adding the extra criteria; it didn't work this time.
edit: fixed schema type Number to String

Let these are variables that you have in mongo shell:
> var podId = 169400000005
> var codeName = "sound.right"
> var envType = "1"
> var paramValue = "updatedParamValue"
This query you can try:
db.test.update({"podId":podId, "parameters.name" : codeName, "parameters.type" : envType}, {$set : {"parameters.0.testValue" : paramValue,"parameters.1.testValue" : paramValue}})
Depending upon which testValue you want to update, you can pass index to that.
Update
Following result i get after the update query:
db.test.find().pretty()
{
"_id" : ObjectId("570c850ab9477f18ac5297f9"),
"__v" : 0,
"podId" : 169400000005,
"env" : [
{
"type" : "1",
"id" : 3852000000035,
"_id" : ObjectId("5708423e897db8255aaaa9de")
},
{
"type" : "2",
"id" : 3852000000040,
"_id" : ObjectId("5708423e897db8255aaaa9df")
}
],
"name" : "Test Build",
"parameters" : [
{
"name" : "sound.left",
"type" : "1",
"paramName" : "Left Sound Control",
"paramType" : "booleanParameter",
"testValue" : "updatedParamValue",
"liveValue" : null,
"_id" : ObjectId("5708423f897db8255aaaaa0d")
},
{
"name" : "sound.right",
"type" : "1",
"paramName" : "Right Sound Control",
"paramType" : "booleanParameter",
"testValue" : "updatedParamValue",
"liveValue" : null,
"_id" : ObjectId("5708423f897db8255aaaaa0d")
}
]
}
Thanks.

Related

Using MongoDB $set to update multiple subdocuments

I have such Article-documents:
{
"_id" : "rNiwdR8tFwbTdr2oX",
"createdAt" : ISODate("2018-08-25T12:23:25.797Z"),
"title" : "Happy",
"lines" : [
{
"id" : "5efa6ad451048a0a1807916c",
"text" : "Test 1",
"align" : "left",
"indent" : 0
},
{
"id" : "ae644f39553d46f85c6e1be9",
"text" : "Test 2"
},
{
"id" : "829f874878dfd0b47e9441c2",
"text" : "Test 3"
},
{
"id" : "d0a46ef175351ae1dec70b9a",
"text" : "Test 4"
},
{
"id" : "9bbc8c8d01bc7029220bed3f",
"text" : "Test 5"
},
{
"id" : "6b5c02996a830f807e4d8e35",
"text" : "Test 6",
"indent" : 0
}
]
}
I need to update some Lines.
For example I have array with ids of the line which must be updated.
let lineIds = [
"5efa6ad451048a0a1807916c",
"829f874878dfd0b47e9441c2",
"6b5c02996a830f807e4d8e35"
];
So I try to update attributes "attr" for the "lines" and I do following:
'articles.updateLines': function (articleId, lineIds, attr, value) {
return Articles.update({
'_id': articleId,
'lines.id': { $in: lineIds }
},
{
$set: {
['lines.$.' + attr]: value
}
},
{ multi: true }
);
}
The problem is that just the first line (with id="5efa6ad451048a0a1807916c") is updated.
Any ideas? Thanks! :)
You can use $[]. This will works only MongoDB version 3.6 and above.
Refer link :
https://docs.mongodb.com/manual/reference/operator/update/positional-all/
You can also see this stackoverflow question reference:
How to add new key value or change the key's value inside a nested array in MongoDB?
You can convert below query in your function
db.col.update(
{ '_id':"rNiwdR8tFwbTdr2oX", },
{ $set: { "lines.$[elem].text" : "hello" } },
{ arrayFilters: [ { "elem.id": { $in: lineIds } } ],
multi: true
})
'lines.id': { $in: lineIds }
This won't work, because lines is an array.
What I understand from your question, you can prepare a new array with proper processing and replace the lines array with the new one. Here is an idea how to do this:
'articles.updateLines': function (articleId, lineIds, attr, value) {
let lines = Articles.findOne(articleId).lines;
// prepare a new array with right elements
let newArray = [];
for(let i=0; i<lines.length; i++){
if(lineIds.includes(lines[i].id)){
newArray.push(value)
}
else newArray.push(lines[i])
}
return Articles.update({
'_id': articleId,
},
{
$set: {
lines: newArray
}
}
);
}

MongoDB update all subelements from subarray [duplicate]

This question already has answers here:
How to Update Multiple Array Elements in mongodb
(16 answers)
Closed 6 years ago.
I have a collection with a following schema:
{
"_id" : ObjectId("52dfba46daf02aa4630cf529"),
"hotelVenue" : {
"rooms" : [
{
"clientId" : "ROOM_1",
"roomName" : "Executive"
},
{
"clientId" : "ROOM_2",
"roomName" : "Premium"
}
]
}
},
{
"_id" : ObjectId("52dfc2f9daf02aa2632bc8af"),
"hotelVenue" : {
"rooms" : [
{
"clientId" : "ROOM_1",
"roomName" : "Studio Room"
},
{
"clientId" : "ROOM_2",
"roomName" : "Soho Suite"
},
{
"clientId" : "ROOM_3",
"roomName" : "Luxury Suite"
}
]
}
}
I need to genearate unique id for all the records -> subarray.
i.e., in the example there is rooms so for each and every room type, I need give a unique id basically using ObjectId().
Output should look something like the below, where roomId being generated.
{
"_id" : ObjectId("52dfba46daf02aa4630cf529"),
"hotelVenue" : {
"rooms" : [
{
"clientId" : "ROOM_1",
"roomName" : "Executive",
"roomId" : "56f8cb3f0c658b4bc26172342"
},
{
"clientId" : "ROOM_2",
"roomName" : "Premium",
"roomId" : "56f8cb3f0c658b4bc26176d4"
}
]
}
}
I have written this script where it gives the following error: Error: Line 9: Unexpected token +
db.venues.find().forEach(function(data)
{
data.hotelVenue.rooms.forEach(function(roomItem)
{
db.venues.update({_id:data._id,'data.hotelVenue.rooms.clientId' : roomItem.clientId},
{
$set:
{
'hotelVenue.rooms.'+roomItem.clientId+'.roomId' : ObjectId()
}
});
});
})
EDIT: This should do it in a more efficient manner by only saving each document once;
db.venues.
find({"hotelVenue.rooms": { $elemMatch: {roomId: {$exists: 0}}}}).
forEach(function(doc) {
doc.hotelVenue.rooms.forEach(function(room) {
if(!room.roomId) {
room.roomId = ObjectId();
}
});
db.venues.save(doc);
});
The find filters out only documents that are in need of updating. After that, it's just a matter of updating the document as needed and calling save.
Of course, backups are in order before running potentially destructive queries from random people on the Internet against your production data set.

Mongodb: Finding and updating object property from array

I have a collection with multiple documents which follow this structure:
{
"_id" : {
"oid" : XXX
},
"name" : "Name",
"videos" [
{
"id" : 1,
"thumbnail" : "thumbnail.jpg",
"name" : "Name here"
},
{
"id" : 2,
"thumbnail" : "thumbnail.jpg",
"name" : "Name here"
},
{
"id" : 3,
"thumbnail" : "thumbnail.jpg",
"name" : "Name here"
}
]
}
I want to find and update the a thumbnail of a video, of which I only know the id, but not which document it is in.
This is what I've tried so far, but it's not working properly. All the examples I found relied on knowing the document id, and the array position of the object to update. I also found that doing a query like this found the document okay, but set the whole document as the new thumbnail!
db.collection(COLLECTION-NAME, function(err, collection){
collection.update(
{ 'videos.id' : 2 },
{ $set: { thumbnail: "newThumbnail.jpg" } },
function(err, result){
if (!err){
console.log('saved', result)
} else {
console.log('error', err);
}
}
);
});
Use the $ positional operator to update the value of the thumbnail field within the embedded document having the id of 2:
db.collection.update(
{ "videos.id": 2 },
{ "$set": { "videos.$.thumbnail" : "newThumbnail.jpg" } }
)

Using 'findAndModify' on mongodb within multiple nested array (complex)

I have the next document in a collection:
{
"_id" : ObjectId("546a7a0f44aee82db8469f6d"),
...
"valoresVariablesIterativas" : [
{
"asignaturaVO" : {
"_id" : ObjectId("546a389c44aee54fc83112e9")
},
"valoresEstaticos" : {
"IT_VAR3" : "",
"IT_VAR1" : "",
"IT_VAR2" : "asdasd"
},
"valoresPreestablecidos" : {
"IT_ASIGNATURA" : "Matemáticas",
"IT_NOTA_DEFINITIVA_ASIGNATURA" : ""
}
},
{
"asignaturaVO" : {
"_id" : ObjectId("546a3d8d44aee54fc83112fa")
},
"valoresEstaticos" : {
"IT_VAR3" : "",
"IT_VAR1" : "",
"IT_VAR2" : ""
},
"valoresPreestablecidos" : {
"IT_ASIGNATURA" : "Español",
"IT_NOTA_DEFINITIVA_ASIGNATURA" : ""
}
}
]
...
}
I want modify an element of the valoresEstaticos, I know the fields "_id", "asignaturaVO", and the key of the item valoresEstaticos that I want modify.
Which is the correct query for this?, I have this:
db.myCollection.findAndModify({
query:{"_id" : ObjectId("546a7a0f44aee82db8469f6d")},
update: {
{valoresVariablesIterativas.asignaturaVO._id: ObjectId("546a389c44aee54fc83112e9")},
{ $set: {}}
}
})
but I dont know how to build a query :(
Help me please, Thank you very much!
You can just use update.
db.myCollection.update(
{ "_id" : ObjectId("546a7a0f44aee82db8469f6d"), "valoresVariablesIterativas.asignaturaVO._id" : ObjectId("546a389c44aee54fc83112e9") },
{ "$set" : { "valoresVariablesIterativas.$.valoresEstaticos.IT_VAR3" : 99 } }
)
assuming you want to update key IT_VAR3. The key is the positional update operator $. The condition on the array in the query portion of the update is redundant for finding the document, but necessary to use the $ to update the correct array element.

Is it possible to retrieve a 'time span' from a MongoDB query, using the timestamp within an ObjectId?

We have a basic enquiry management tool that we're using to track some website enquiries in our administration suite, and we're using the ObjectId of each document in our enquiries collection to sort the enquiries by the date they were added.
{
"_id" : ObjectId("53a007db144ff47be1000003"),
"comments" : "This is a test enquiry. Please ignore. We'll delete it shortly.",
"customer" : {
"name" : "Test Enquiry",
"email" : "test#test.com",
"telephone" : "07890123456",
"mobile" : "07890123456",
"quote" : false,
"valuation" : false
},
"site" : [],
"test" : true,
"updates" : [
{
"_id" : ObjectId("53a007db144ff47be1000001"),
"status" : "New",
"status_id" : ObjectId("537de7c3a5e6e668ffc2335c"),
"status_index" : 100,
"substatus" : "New Web Enquiry",
"substatus_id" : ObjectId("5396bb9fa5e6e668ffc23388"),
"notes" : "New enquiry received from website.",
},
{
"_id" : ObjectId("53a80c977d299cfe91bacf81"),
"status" : "New",
"status_id" : ObjectId("537de7c3a5e6e668ffc2335c"),
"status_index" : 100,
"substatus" : "Attempted Contact",
"substatus_id" : ObjectId("53a80e06a5e6e668ffc2339e"),
"notes" : "In this test, we pretend that we've not managed to get hold of the customer on the first attempt.",
},
{
"_id" : ObjectId("53a80e539b966b8da5c40c36"),
"status" : "Approved",
"status_id" : ObjectId("52e77a49d85e95f00ebf6c72"),
"status_index" : 200,
"substatus" : "Enquiry Confirmed",
"substatus_id" : ObjectId("53901f1ba5e6e668ffc23372"),
"notes" : "In this test, we pretend that we've got hold of the customer after failing to contact them on the first attempt.",
}
]
}
Within each enquiry is an updates array of objects which also have an ObjectId as their main identity field. We're using an $unwind and $group aggregation to pull the first and latest updates, as well as the count of updates, making sure we only take enquiries where there have been more than one update (as one is automatically inserted when the enquiry is made):
db.enquiries.aggregate([
{
$match: {
"test": true
}
},
{
$unwind: "$updates"
},
{
$group: {
"_id": "$_id",
"latest_update_id": {
$last: "$updates._id"
},
"first_update_id": {
$first: "$updates._id"
},
"update_count": {
$sum: 1
}
}
},
{
$match: {
"update_count": {
$gt: 1
}
}
}
])
This results in the following output:
{
"result" : [
{
"_id" : ObjectId("53a295ad122ea80200000005"),
"latest_update_id" : ObjectId("53a80bdc7d299cfe91bacf7e"),
"first_update_id" : ObjectId("53a295ad122ea80200000003"),
"update_count" : 2
},
{
"_id" : ObjectId("53a007db144ff47be1000003"),
"latest_update_id" : ObjectId("53a80e539b966b8da5c40c36"),
"first_update_id" : ObjectId("53a007db144ff47be1000001"),
"update_count" : 3
}
],
"ok" : 1
}
This is then passed through to our code (node.js, in this case) where we perform a few operations on it and then present some information on our dashboard.
Ideally, I'd like to add another $group pipeline aggregation to the query which would subtract the timestamp of first_update_id from the timestamp of latest_update_id to give us a timespan, which we could then use $avg on.
Can anyone tell me if this is possible? (Thank you!)
As Neil already pointed out, you can't get to the timestamp from the ObjectId in the aggregation framework.
You said that speed is not important, so using MapReduce you can get what you want:
var map = function() {
if (this.updates.length > 1) {
var first = this.updates[0];
var last = this.updates[this.updates.length - 1];
var diff = last._id.getTimestamp() - first._id.getTimestamp();
var val = {
latest_update_id : last._id,
first_update_id : first._id,
update_count : this.updates.length,
diff: diff
}
emit(this._id, val);
}
};
var reduce = function() { };
db.runCommand(
{
mapReduce: "enquiries",
map: map,
reduce: reduce,
out: "mrresults",
query: { test : true}
}
);
This are the results:
{
"_id" : ObjectId("53a007db144ff47be1000003"),
"value" : {
"latest_update_id" : ObjectId("53a80e539b966b8da5c40c36"),
"first_update_id" : ObjectId("53a007db144ff47be1000001"),
"update_count" : 3,
"diff" : 525944000
}
}
Edit:
If you want to get the average diff for all documents you can do it like this:
var map = function() {
if (this.updates.length > 1) {
var first = this.updates[0];
var last = this.updates[this.updates.length - 1];
var diff = last._id.getTimestamp() - first._id.getTimestamp();
emit("1", {diff : diff});
}
};
var reduce = function(key, values) {
var reducedVal = { count: 0, sum: 0 };
for (var idx = 0; idx < values.length; idx++) {
reducedVal.count += 1;
reducedVal.sum += values[idx].diff;
}
return reducedVal;
};
var finalize = function (key, reducedVal) {
reducedVal.avg = reducedVal.sum/reducedVal.count;
return reducedVal;
};
db.runCommand(
{
mapReduce: "y",
map: map,
reduce: reduce,
finalize : finalize,
out: "mrtest",
query: { test : true}
}
);
And the example output:
> db.mrtest.find().pretty()
{
"_id" : "1",
"value" : {
"count" : 2,
"sum" : 1051888000,
"avg" : 525944000
}
}