Updating Mongo, but not overwrite current data, just append to it - node.js

I need to add to a list of items in Mongo so if I have
items:{item: "apple"}
what would I use to add another item in an object instead of changing that initial object? So I can end up with.
items: {item: "apple"},{item:"orange"},{item:"blueberry"}
Can I use findOneAndUpdate? Or will this over-write the original data. I am having a hard time finding the distinction in the documents.
In closing, what method is used for updating and overwriting and what is used for appending to objects and arrays?

You can use the $addToSet operator.
For example:
db.yourCollection.update(
{ _id: 1 },
{ $addToSet: { items: {item : "orange" } } }
)
The code abode will add the item : "orange" to the items list of the document with id=1

Related

Mongoose - update last element in array (update document with an aggregator)

My question builds off this: Get only the last element of array mongoose. However, I'm using Mongoose and want to also update a field in the last element of the array, not just retrieve it.
How can I modify the last element/object in the rounds array to change from status: started to status: terminated?
Document to modify
{
"_id" : "8844d3f2d25f45df8105db1ab058d7d6",
"rounds" : [
{ "status" : "offline" },
{ "status" : "paused" },
{ "status" : "started" }
],
"updatedAt" : ISODate("2020-07-05T21:21:58.823Z")
}
Seems like using negative indices would make this easy, allowing the n-1 element to be modified. I've seen many questions mention aggregation, but none modify the underlying data. I want to combine aggregation with updating a documenting.
Is it possible to use $set with a negative index?
As of v4.2 MongoDB does not have a straightforward way to update an array element by index.
However starting from v4.2 you can use updates with aggregation pipeline, which allow you to construct a new array based on the current array and replace the current array.
Instead of modifying the last element, you can construct a new array consisting of the first top n - 1 elements combining with a new element { status: "terminated" }.
You can achieve this by using $slice to get the top n - 1 elements and combine with the new element using $concatArrays
This assumes the elements only contains the field status you you want to update a field of an array elements that contains multiple fields, you'll need to merge the updated field with the current element using $mergeObjects
Model.updateOne({ // or updateMany with your condition
_id: "8844d3f2d25f45df8105db1ab058d7d6"
}, [{
$set: {
rounds: {
$concatArrays: [ // combine arrays
{
$slice: [ // get the top n - 1 elements
"$rounds",
{ $subtract: [{ $size: "$rounds" }, 1] }
]
},,
[{ status: "terminated" }] // a new element to replace the last element
]
}
}
}])

How do I use upsert and $addToSet in conjunction?

I would like to insert a document that looks like this:
{
name: "app1",
theArray: [...unique string elements...]
}
I tried to do an upsert using the query below but it somehow creates an array within an array if the document does not exist. If I use $push when the document does not exist, then the array is created fine. However I need to use $addToSet to maintain array element uniqueness.
Current query:
collection1.upsert({
name: "app1",
}, {
$addToSet: {
theArray: data // data is an array of ip addresses eg. ["1.1.1", "2.2.2.2"] which is not unique
}
});
Executing the above query when there is no existing document in the db creates:
{
name: "app-1",
theArray: [
//another array that contains the actual data.
]
}
Is there a way I can get this behavior with just a single query?

DynamoDB update inside an array of objects (nodejs)

I noticed that DynamoDB can add and remove items from an array but how do you search for an specific item inside an object if you want to update that one specifically?
For example:
In MongoDB you can search for someitem.$.subitem and update that specific item.
Is there a way on how to do this with DynamoDB?
Item: {
someitem: [
{
subitem: "id",
somevalue: "something"
}
]
}
I would say this is basic functionality but seems not easy to find (or even unsupported)
AWS does not permit to modify it in a single update request more info was found in the following answers:
updating-a-json-array-in-aws-dynamodb.
The solution that they propose is to change the schema from array to {}, or to implement a custom functions and iterate through each array and find your new id to update, so to speak to programatically update your json and then insert whole object.
TableName : 'tablename',
Key : { id: id},
ReturnValues : 'ALL_NEW',
UpdateExpression : 'set someitem['+`index`+'].somevalue = :reply_content',
ExpressionAttributeValues : { ':reply_content' : updateddata }
array element edit via array index

Node.js and MongoDB if document exact match exists, ignore insert

I am maintaining a collection of unique values that has a companion collection that has instances of those values. The reason I have it that way is the companion collection has >10 million records where the unique values collection only add up to 100K and I use those values all over the place and do partial match lookups.
When I upload a csv file it is usually 10k to 500k records at a time that I insert into the companion collection. What is the best way to insert only values that dont already exist into the unique values collection?
Example:
//Insert large quantities of objects into mongo
var bulkInsert = [
{
name: "Some Name",
other: "zxy",
properties: "abc"
},
{
name: "Some Name",
other: "zxy",
properties: "abc"
},
{
name: "Other Name",
other: "zxy",
properties: "abc"
}]
//Need to insert only values that do not already exist in mongo unique values collection
var uniqueValues = [
{
name:"Some Name"
},
{
name:"Other Name"
}
]
EDIT
I tried creating a unique index on the field, but once it finds a duplicate in the Array of documents that I am inserting, it stops the whole process and doesnt proceed to check any values after the break.
Figured it out. If your doing it from the shell, you need to use Bulk() and create insert jobs like this:
var bulk = db.collection.initializeUnorderedBulkOp();
bulk.insert( { name: "1234567890a"} );
bulk.insert( { name: "1234567890b"} );
bulk.insert( { name: "1234567890"} );
bulk.execute();
and in node, the continueOnError flag works on a straight collection.insert()
collection.insert( [{name:"1234567890a"},{name:"1234567890c"}],{continueOnError:true}, function(err, doc){}
Well, I think the solution here is quite simple if I understand correctly your issue.
Since the process is stopped when it finds a duplicated field you should basically check if the value doesn't already exists before to try to add it.
So, for each element in uniqueValues, make a find/findOne query, if it doesn't return any result then add the element, otherwise don't.

How do I update a subfield of which I know everythig, but don't know the field its a child to?

I have this collection:
{_id, list}
I have the elements in the list, of which each has an _id of its own, but I can't figure out how to update all the elements in the list. I do know that all collections have unique elements in the list, including amongst themselves. And I have the elements with updated properties :|
Hope I explained it clearly.
I hope I've got your problem right.
Suppose, you want to update given sub-document in the list by its _id, but you don't know its parent's _id.
To make an update operation you may use the following command:
db.collection.update({
'list._id': subdoc_id
},{
$set: {
'list.$.field1': value1,
'list.$.field2': value2,
'list.$.field3': value3
}
})
To replace an old sub-document with the new one (equivalent of save):
db.collection.update({
'list._id': subdoc._id
},{
$set: {
'list.$': subdoc
}
})
But you should be aware that this operation will completely erase the old sub-document and replace it with the new one. So, any field not listed in subdoc will be deleted.
{'list._id': subdoc_id} query will find you a parent document containing the given sub-document and 'list.$' selector will point to the queried sub-document.
Update: If you don't know in wich particular list the given sub-document is stored, you'll need to run an update operation on each:
['list1', 'list2', 'nested.list'].forEach(function (list) {
var query = {};
query[list+'._id'] = subdoc._id;
var update = { $set: {} };
update.$set[list+'.$'] = subdoc;
db.collection.update(query, update);
})
An update will be performed on each list where the given sub-document is present.

Resources