Check if a specific field was updated with a single findOneAndUpdate query? - node.js

My items collection has a following schema:
{
"item": "box",
"quantity": 12,
"lastReportedAt": "2021-10-22T00:12:34"
}
Every API call reports the latest quantity for a specific item, so I need to update the doc accordingly.
const newQuantity = 13;
const lastReportedAt = new Date();
db.collection("items").findOneAndUpdate(
{ "item": "box" },
{ $set: { "quantity": newQuantity, "lastReportedAt": newTime } },
{ upsert: true }
);
Here I would like to know if there was a change in "quantity" field, so that I can be notified.
While some of mongodb's update commands return the number of modified documents, but it also accounts for update in lastReportedAt fields.
Is there any way this can be done without using a transaction?

Use returnNewDocument in findOneAndUpdate, and compare with the old document in your application.

Related

Nodejs Elasticsearch query default behaviour

On a daily basis, I'm pushing data (time_series) to Elasticsearch. I created an index pattern, and my index have the name: myindex_* , where * is today date (an index pattern has been setup). Thus after a week, I have: myindex_2022-06-20, myindex_2022-06-21... myindex_2022-06-27.
Let's assume my index is indexing products' prices. Thus inside each myindex_*, I have got:
myindex_2022-06-26 is including many products prices like this:
{
"reference_code": "123456789",
"price": 10.00
},
...
myindex_2022-06-27:
{
"reference_code": "123456789",
"price": 12.00
},
I'm using this query to get the reference code and the corresponding prices. And it works great.
const data = await elasticClient.search({
index: myindex_2022-06-27,
body: {
query: {
match: {
"reference_code": "123456789"
}
}
}
});
But, I would like to have a query that if in the index of the date 2022-06-27, there is no data, then it checks, in the previous index 2022-06-26, and so on (until e.g. 10x).
Not sure, but it seems it's doing this when I replace myindex_2022-06-27 by myindex_* (not sure it's the default behaviour).
The issue is that when I'm using this way, I got prices from other index but it seems to use the oldest one. I would like to get the newest one instead, thus the opposite way.
How should I proceed?
If you query with index wildcard, it should return a list of documents, where every document will include some meta fields as _index and _id.
You can sort by _index, to make elastic search return the latest document at position [0] in your list.
const data = await elasticClient.search({
index: myindex_2022-*,
body: {
query: {
match: {
"reference_code": "123456789"
}
}
sort : { "_index" : "desc" },
}
});

How to update multiple fields (maxLength, maxBreadth or maxArea) of a document based on multiple conditions sent from req.body?

I want to store maximum length, breadth, area ever encountered from a request and store it in the database. In this case, anything can be maximum and based on that I want to update max values in the database for that particular field only.
const body = {
oID: 123, // Primary key
length: 50,
breadth: 50
};
const { length, breadth } = body;
const currentArea = length * breadth;
await totalArea.updateOne(
{ oID: 123 },
{
$set: { $maxLength: maxLength < length ? length : $maxLength }, // I want to update the docs based on mongo query only since this won't work.
$set: { $maxBreadth: maxBreadth < breadth ? breadth : $maxBreadth }, // I want to update the docs based on mongo query only since this won't work.
$set: { $maxArea: maxArea < currentArea ? currentArea : $maxArea } // I want to update the docs based on mongo query only since this won't work.
},
{ upsert: true }
);
In the above example, I have demonstrated the logic using ternary operator and I want to perform the same operation using mongoDB query. i.e update a particular field while comparing it to existing fields from the database and update it if it satisfies the condition.
await totalArea.updateOne(
{ oID: 123
},
{
$max: {
maxLength: length,
maxBreadth: breadth,
maxArea : currentArea
}
},
{ upsert: true }
);
I found this to be working correctly. Thanks to #Mehari Mamo's comment.
If I understand correctly, you want something like this:
db.collection.update({
oID: 123
},
[{$set: {maxLength: {$max: [length, "$maxLength"]},
maxBreadth: {$max: [breadth, "$maxBreadth"]},
maxArea : {$max: [currentArea, "$maxArea "]}
}
}
],
{
upsert: true
})
You can check it here .
The [] on the second step allows you to access the current values of the document fields, as this is an aggregation pipeline.

Mongoose: create multiple documents if filter finds none

I'm trying to create multiple documents based on a filter: if document not found => create it.
After searching a bit I found that the correct way to do so is by using updateMany and setting upsert: true (docs).
This made somewhat sense from the documentation's example but as I understand it the filter modifier would be used for the newly-created document. As in the example:
try {
db.inspectors.updateMany(
{ "Sector" : { $gt : 4 }, "inspector" : "R. Coltrane" },
{ $set: { "Patrolling" : false } },
{ upsert: true }
);
} catch (e) {
print(e);
}
"Inspector" : "R. Coltrane" would be inserted to the newly-created document.
But what if my setOnInsert modifier contains the same field as the one in the filter?
What my code looks like:
//first find the already-created tags
await tagModel.find({"tagName": tags}).select('tagName -_id').exec()
.then(async (result: Tag[])=>{
//create the new tags
const newTags = tags.map((tag: any)=>new tagModel({tagName: tag}));
//now insert only the new tags, filtering out the already-created tags ("result")
await tagModel.updateMany(
{"tagName": result},
{$setOnInsert: newTags} ,
{upsert: true},
(err:any, res:any)=>{
...
At first, result is an empty Array ([]). What is created in my MongoDB database is a new Tag document, but its tagName is the result object. Meaning, it looks like:
{
"_id": {
"$oid": "61659c92c6267fe11963b236"
},
"tagName": {
"$in": []
}
}
So essentially my question is, what am I suppose to do in this case where my update modifier should replace my filter query? Perhaps it's just something bad in my code that makes the updateMany function to malfunction? Or should I replace it with a different function?

how to check if the document updated was really modified?

I have an "items" collection and this is an example document in the collection:
{ "id": 0, "price": 23.55 }
I'm updating writing a query that updates an item's price,
but want to check if the price field has really modified.
For example, following query is legal but
does not actually modify the document.
db.collection('items').updateOne(
{ id: 0 },
{ $set: { price: 23.55 } }
);
Is there any methods I can achieve this?
updateOne will return nModified you can get to know whether the document was modified or not.
{ "n": 1, "nModified": 1 }
https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#updateOne
https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#~updateWriteOpCallback
https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#findOneAndUpdate
Use findOneAndUpdate with returnOriginal set to false, you'll get the updated document in the callback.
const options = { returnOriginal: false; } // to return the updated document
findOneAndUpdate(filter, update, options, callback)
https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#~findAndModifyCallback
Document returned from the findAndModify command. If no documents were found, value will be null by default (returnOriginal: true), even if a document was upserted; if returnOriginal was false, the upserted document will be returned in that case.

Mongodb query not working in nodejs. Why is this code not working?

My variable id has the object id in it, but when I run this updateOne query it doesn't update the document in the database. When I replace the updateOne argument with updateOne({}) it does properly update the first record in the database, so I know my set syntax is right... so the problem must be in this line of code:
{"_id": "ObjectId(\""+id+"\")"}},
but when I print that line of code out with a console.log, it looks identical to the mongoDB query which works. Can anyone help me figure out why that line doesn't work? Is there a type casting problem going on or something like that?
db.collection('profile').updateOne(
{"_id": "ObjectId(\""+id+"\")"}},
{ $set:
{
"star_rating": updatedProfile.test ,
"address.street": updatedProfile.street,
"address.city": updatedProfile.city,
"address.postalcode": updatedProfile.postal,
"address.country": updatedProfile.country,
"phonenumber": updatedProfile.phone,
"birthday": updatedProfile.datepicker
}
}, //set
{
upsert: false //do not create a doc if the id doesn't exist
}
); //updateOne
Can you please try this and see if it works:
var ObjectID = require('mongodb').ObjectID,
db.collection('profile').updateOne(
{"_id": new ObjectID(id)}},
{ $set:
{
"star_rating": updatedProfile.test ,
"address.street": updatedProfile.street,
"address.city": updatedProfile.city,
"address.postalcode": updatedProfile.postal,
"address.country": updatedProfile.country,
"phonenumber": updatedProfile.phone,
"birthday": updatedProfile.datepicker
}
}, //set
{
upsert: false //do not create a doc if the id doesn't exist
}); //updateOne

Resources