I've been trying to update a particular object in a Mongodb document without any luck using the findOneAndUpdate() method. This is what my collectiom looks like
{
_id: new ObjectId("61da0ab855483312e8f4483b"),
products: [
{
createdAt: 2022-01-08T22:05:44.635Z,
_id: new ObjectId("61da0ab855483312e8f4483c"),
productCode: 'otf',
productName: 'facebookmeta',
claims: [Array],
permissions: []
},
{
createdAt: 2022-01-08T22:05:44.635Z,
_id: new ObjectId("61da0ab855483312e8f4483f"),
productCode: '4pf',
productName: 'twitteroauth',
claims: [Array],
permissions: [Array]
}
],
__v: 0
}
When i try something like this. i am trying to find a singular object based on its product code and update that object
ProductModel.findOneAndUpdate({productCode: userData.productCode}, dataToBeUpdated, {new: true})
it returns a
Performing an update on the path '_id' would modify the immutable field '_id
i am suspecting that MongoDB is applying query conditions on collection and returns the result with the matching documents so its probably returning both objects which is probably the cause of the error i am encountering but i could be wrong.
How can i efficiently point it to the right object and perform an update
MongoDB supports the single-level deep update of subdocument using positional operator ($).
db.collection_name.update({
_id: ObjectId("61da0ab855483312e8f4483b"),
"products.productCode": "4pf"
},
{
"$set": {
"products.$.productName": "twitteroauth name updated"
}
})
In the above update clause, $ will be replaced by the matching index of a subdocument at a runtime.
For example $ will be replaced by value 1 at a runtime because of "products.productCode": "4pf" condition
Please note the below points:
You must include the array field as part of the query document.
The positional operator cannot be used for updates inside of a nested array.
Additional Reference: https://docs.mongodb.com/manual/reference/operator/update/positional/
Related
I have an array of objects, where each object have it's own "_id".
I am trying to use updateMany(), instead calling updateOne for each object, so I don't make too many requisitions to mongoDB.
for example, this is the object:
[
{
_id: 123,
name: "apple",
qty: 2
},
{
_id: 555,
name: "melon",
qty: 1
},
{
_id: 799,
name: "avocado",
qty: 0
}
]
I want to updateMany() with this array of objects, and each object must be a Document inside a collection, I tried many solutions, and none worked, only worked with insert() where I can send the entire array, and it will make each object be a Document, the problem for using insert is, this object comes every day, and some values may change, like name and qty, but _id keeps the same.
I tried the following
sync function updateMany(client) {
result = await client.db("sample_db").collection("CollectionA")
..updateMany({},
{asd},
{upsert: true});
}
but didn't work, I tried other solutions to, like, making each object use $setOnInsert for _id and $set the rest..
Is it possible to do it without the need of insert() or making one call for each object?
The language I am using is Javascript, running on Node.JS.
I 'm facing an issue while trying to get some results from a mongoDB aggregation pipeline.
Here's what my DB look like:
var dbSchema = mongoose.Schema({
identity: Number,
parametres: {
style: {
fuel: [styleSchema],
gasoline: [styleSchema],
},
},
And here's what the styleSchema looks like:
var styleSchema = new mongoose.Schema({
date: Date,
value: Number,
type: String,
});
I'm trying to extract ALL the objects in 'fuel' and 'gasoline' which are of some kind of 'type'.
I've tried to group both in a unique array with concatArray and then match the 'type' I want by:
db.aggregate([
{$match:
{'identity':3,
}},
{$project: {all: {$concatArrays: ['$parametres.style.fuel','$parametres.style.gasoline']} }},
{$match: {'$all.type': 'example'}},
Before trying to match the second time, I've got a unique array ('all') and I try to match some things on it, but nothing works (I've tried 'all.type' also)...
I've probably misunderstood the way I have to use the 'match' query as I am a beginner, so thanks for your time and your answers,
Arthur
db.aggregate([
{
$match:{identity:3}
},
{
$project: {all: {$concatArrays: ['$parametres.style.fuel','$parametres.style.gasoline']} }
},
{$unwind: "$all"},
{$match: {"all.type": 'example'}},
{$group : {_id: null, all:{$push:"$all"}}}
])
Probably you are trying to do something like this.
In aggregate operation, the $ is used in right side of : to mention field name and in left side to mention operator.
So when you are using "$all.type" in left-hand side MongoDB is treating it as an operator which is not available in the Mongodb operator list.
Another thing is that when you do any query over an array. Mongodb sends back the full array if atleast one of the element matches the condition. So we need to use $unwind operator to deconstruct the array before doing any query.
I have a program where I'm requesting weather data from a server, processing the data, and then saving it to an mlab account using mongoose. I'm gathering 10 years of data, but the API that I'm requesting the data from only allows about a year at a time to be requested.
I'm using findOndAndUpdate to create/update the document for each weather station, but am having trouble updating the arrays within the data object. (Probably not the best way to describe it...)
For example, here's the model:
const stnDataSchema = new Schema(
{
station: { type: String, default: null },
elevation: { type: String, default: null },
timeZone: { type: String, default: null },
dates: {},
data: {}
},
{ collection: 'stndata' },
{ runSettersOnQuery: true }
)
where the dates object looks like this:
dates: ["2007-01-01",
"2007-01-02",
"2007-01-03",
"2007-01-04",
"2007-01-05",
"2007-01-06",
"2007-01-07",
"2007-01-08",
"2007-01-09"]
and the data object like this:
"data": [
{
"maxT": [
0,
null,
4.4,
0,
-2.7,
etc.....
what I want to have happen is when I run findOneAndUpdate I want to find the document based on the station, and then append new maxT values and dates to the respective arrays. I have it working for the date array, but am running into trouble with the data array as the elements I'm updated are nested.
I tried this:
const update = {
$set: {'station': station, 'elevation': elevation, 'timeZone': timeZone},
$push: {'dates': datesTest, 'data.0.maxT': testMaxT}};
StnData.findOneAndUpdate( query, update, {upsert: true} ,
function(err, doc) {
if (err) {
console.log("error in updateStation", err)
throw new Error('error in updateStation')
}
else {
console.log('saved')
but got an output into mlab like this:
"data": {
"0": {
"maxT": [
"a",
"b",
the issue is that I get a "0" instead of an array of one element. I tried 'data[0].maxT' but nothing happens when I do that.
The issue is that the first time I run the data for a station, I want to create a new document with data object of the format in my third code block, and then on subsequent runs, once that document already exists, update the maxT array with new values. Any ideas?
You are getting this output:
"data": {
"0": {
"maxT": [
"a",
"b",
because you are upserting the document. Upserting gets a bit complicated when dealing with arrays of documents.
When updating an array, MongoDB knows that data.0 refers to the first element in the array. However, when inserting, MongoDB can't tell if it's meant to be an array or an object. So it assumes it's an object. So rather than inserting ["val"], it inserts {"0": "val"}.
Simplest Solution
Don't use an upsert. Insert a document for each new weather station then use findOndAndUpdate to push values into the arrays in the documents. As long as you insert the arrays correctly the first time, you will be able to push to them without them turning into objects.
Alternative Simple Solution if data just Contains one Object
From your question, it looks like you only have one object in data. If that is the case, you could just make the maxT array top-level, instead of being a property of a single document in an array. Then it would act just like dates.
More Complicated MongoDB 3.6 Solution
If you truly cannot do without upserts, MongoDB 3.6 introduced the filtered positional operator $[<identifier>]. You can use this operator to update specific elements in an array which match a query. Unlike the simple positional operator $, the new $[<identifier>] operator can be used to upsert as long as an exact match is used.
You can read more about this operator here: https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/
So your data objects will need to have a field which can be matched exactly on (say name). An example query would look something like this:
let query = {
_id: 'idOfDocument',
data: [{name: 'subobjectName'}] // Need this for an exact match
}
let update = {$push: {'data.$[el].maxT': testMaxT}}
let options = {upsert: true, arrayFilters: [{'el.name': 'subobjectName'}]}
StnData.findOneAndUpdate(query, update, options, callbackFn)
As you can see this adds much more complexity. It would be much easier to forget about trying to do upserts. Just do one insert then update.
Moreover mLab currently does not support MongoDB 3.6. So this method won't be viable when using mLab until 3.6 is supported.
I have a Mongoose schema with, among other things, an array of objects like so:
multipleThings: [{
field1: String,
field2: String,
field3: String,
thingId : { type: ObjectId, default: ObjectId }
}]
In my code I do a .findOne, which returns my object. myObject.multipleThings is an Array[0] at this point. I simply want to push something to this array, so I do
myObject.multipleThings.push(anObjectICreated)
And I get
undefined is not a function
at DocumentArray.SchemaType.applySetters (.../node_modules/mongoose/lib/schematype.js:570:26)
at Array.MongooseArray.mixin.push (.../node_modules/mongoose/lib/types/array.js:292:27)
at {The location of .push above in my code}
I don't understand what is stopping me from being able to push to the array?
I am still new to this as well put may I suggest you try myObject.multipleThings[0].push(anObjectICreated)
I am attempting to insert items at a specific index in an array that may or may not be empty. For example, say I have the following document in my mongoDb collection:
{
title: "abe",
questions: []
}
I would like to do something like set the 5th element in the array to a specific value. I was playing with the mongo db command line and I was able to do the following:
> mytest = []
[ ]
> mytest[4] = 'test'
test
> mytest
[ undefined, undefined, undefined, undefined, "test" ]
Which is basically what I want, but when I attempt to do this from my node.js code I am getting weird results (weird as in, items are not in the correct index). Code below:
Mongoose Schema Definition:
mongoose = require("mongoose")
Schema = mongoose.Schema
surveySchema = new Schema
title: String
questions: [
type: Schema.Types.ObjectId
ref: 'Question'
]
Code doing the update:
surveys.update {id: doc.survey_id},
{
$push: {
questions: {
$each: [doc._id],
$position: doc.sort_order
}
}
},
{upsert:false, multi:true},
callback
The code above is being executed in an asynchronous loop, so it's possible the last item will be inserted before the first item and so on.
Can anyone see any reason why items would not be getting inserted at the correct index? Why does my basic example work but the code does not? Could it have to do with the fact that my schema defines questions as an array of ObjectIds?
Documentation for MongoDB $position:
If the number is greater or equal to the length of the array, the $position modifier has no effect and the operator adds elements to the end of the array.