Mongoose get updated document after instance.update() - node.js

I am executing a mongoose Model.findById() function in order to return a single instance by utilizing an express route.
Model.findById(modelid)
.then(instance => {
if(instance.isOwnedBy(user)) {
return instance.update({$push: {days: req.params.dayid}}, {new: true})
.then(foo => res.send(foo))
} else {
res.status(401).send("Unauthorized")
}
})
the above code returns an object containing the opTime, electionId...etc instead of returning the newly updated document instance. How am I able return the newly updated document after the instance.update() method?

If instance.isOwnedBy(user) and _id: modelid can be merged into one mongo query, it's better to use findOneAndUpdate() but in that way, if it doesn't find any document match, you can not know which part of the query causes the not found.
And because I don't know much about your models, conditions, I can not answer how to do it with findOneAndUpdate() but there is another way that modify the document and call save() method.
Example base on your code:
Model.findById(modelid)
.then(instance => {
if(instance && instance.isOwnedBy(user)) {
if(instance.days) instance.days.push(req.params.dayid);
else instance.days = [req.params.dayid];
instance.save().then(function(instance){
res.send(instance);
})
} else {
res.status(401).send("Unauthorized")
}
})
Note: You should check if the instance exist or not before modify it.

Related

How to Update All Documents in a Collection With Thousands of Documents in FireStore with Firebase Admin?

Updated to reflect Firebase-Admin, rather than v9
New Update: solution at the bottom
How to update a single field (in a map) for all documents in a collection with thousands of documents -- in firebase-admin/firestore (10.0.2)? I attempted to use this:
import { getFirestore } from 'firebase-admin/firestore'
const db = getFirestore()
db.collection("users").get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
doc.ref.update({
"words.subscription": 0
})
})
})
I use it within a Node.js (v12) cloud function. The function runs, I see no errors, but none of the documents are updated. Previously, I attempted to use set() because some documents may not have the field, but Frank let me know update() can update fields that don't exist too.
However, this updated code also does not update all the documents in the 'users' collection.
Thank you in advance.
Update/Solution
#Dharmara - thank you for the gist. It did not work as-is, so I decided to change to async/await and wrap it in try/catch (to hopefully find out why it wasn't working), like so:
try {
let querySnapshot = await db.collection('users').get()
if (querySnapshot.size === 0) {
console.log('No documents to update')
return 'No documents to update'
}
const batches: any[] = [] // hold batches to update at once
querySnapshot.docs.forEach((doc, i) => {
if (i % 500 === 0) {
batches.push(db.batch())
}
const batch = batches[batches.length - 1]
batch.update(doc.ref, { "words.subscription": 0 })
})
await Promise.all(batches.map(batch => batch.commit()))
console.log(`${querySnapshot.size} documents updated`)
return `${querySnapshot.size} documents updated`
}
catch (error) {
console.log(`***ERROR: ${error}`)
return error
}
When I tested this however, it worked. From what I can tell it's the same as the then() way you had in the gist (and from what I had found elsewhere in SO).
If you wanted to put this into an answer I'd gladly mark it as the answer. Your code helped tremendously. Thank you.
The dot notation you use to write a nested field only works when you call update(), not for set(). Luckily update() works for non-existing fields, it only requires that the document already exists.
So for the namespaced API of v8 and before that'd be:
doc.ref.update({
"words.subscription": 0
})
For the modular API for v9 and beyond, it's:
update(doc.ref, {
"words.subscription": 0
})

I want to insert documents using upsert in mongoose

I am fetching data from an external api and I want to insert multiple records if they are not already there . I am creating an array of objects like this :
data=[
{
match_id: 212167781,
player1Id: 129753806,
player2Id: 129753811,
player1Details: '5f070c8f79f62c00042b0cd8',
player2Details: '5f3267fe6a0f891b3604b999'
},
{
match_id: 212167782,
player1Id: 129753799,
player2Id: null,
player1Details: '5f070ade79f62c00042b0cd6'
}
]
Now for instance if the second record is not already present then I want it to be inserted and I have tried upsert but nothing happens . Please help .
You should be able to do this using a simple loop and Model.findOneAndUpdate in combination with the upsert option:
async function upsertMany(data) {
for(const entry of data) {
await YourModel.findOneAndUpdate(entry, entry, { upsert: true });
}
}
// call this with your data-array
await upsertMany(data);

Unable to perform operations over Mongoose result

Product
.findAll()
.then((products) => {
/* Perform operations */
}
The above query returns an array of products. For eg,
[
{
name: Mobile,
price: 10000
},
{
name: Laptop,
price: 20000
},
]
I need to make some changes to the products array (add new fields based on the values of existing fields, and delete those existing fields). I tried few methods, but none are working:
products.forEach((product) => {
product.[updatedPrice] = updatedPrice;
delete product[price];
}
Array.map() is also not working.
The following works, but I don't know the working behind, and why it is happening. Also, how to delete a field using the same.
products.forEach((product) => {
product.set('updatedPrice', updatedPrice, {strict: false})
}
The thing here to note is that in .then((products) products object here is not a JSON object but a Mongoose Document object and the set method you are using is defined by the mongoose. You can refer it from here https://mongoosejs.com/docs/guide.html#strict
We can do 2 things:
use lean() (returns a plain js object)
Product.findAll().lean().then((products) => {
/* Perform operations*/
});
use toJSON() method on products object to convert mongoose object to js object
Product.findAll().then((products) => {
products = products.toJSON();
/* Perform operations*/
});
Thanks

Proper way to check a property of Mongoose Document is empty

Suppose a schema
const sch = new mongoose.Schema({
obj: {
subObj: String;
}
});
Then I observe that an non-existing or empty property of a document gives me isEmpty == false.
import { isEmpty } from 'lodash';
// Insert an empty document (i.e. no `obj` property)
Sch.create([{}]);
Sch.findOne({}. (err, doc) => {
// Below gives `{}`
console.log(doc.obj);
// Below gives `false`
console.log(`isEmpty == ${isEmpty(doc.obj)`);
});
I suspect that it is because the document contains obj as its key, i.e. Object.keys(doc).includes('obj') == true or Object.getOwnPropertyNames(doc).includes('obj') == true. But I have no idea to deal with it.
What is a proper way to check emptiness of a mongoose document property ?
Update:
The reason that you are getting that is:
console.log(Object.keys(doc.obj), Object.keys({}));
when running the command above I get: [ '$init', 'subObj' ] [] which means that your Object is not really empty, lodash is probably checking those attributes
You could use something like this:
Sch.findOne({}, (err, doc) => {
if (JSON.stringify(doc.obj) === JSON.stringify({}) ) {
// logic goes here
}
});
I found the solution in mongoose itself. The below does what I tried with lodash
doc.$isEmpty('obj')
Reference
Document.prototype.$isEmpty()

Cloud firestore trigger query by documentID for array of ids

I am trying to write a transaction that first query documents by documentId from a list of ids, then makes some updates.
I am getting the error:
The corresponding value for FieldPath.documentId() must be a string or a DocumentReference.
For example:
const indexArray = [..list of doc ids...]
const personQueryRef = db.collection("person").where(admin.firestore.FieldPath.documentId(), "in", indexArray)
return db.runTransaction(transaction => {
return transaction.get(personQueryRef).then(personQuery => {
return personQuery.forEach(personRef => {
transaction.update(personRef, { ...update values here })
//more updates etc
})
})
})
I am wanting to do this in an onCreate and onUpdate trigger. Is there another approach I should be taking?
Update
The error still persists when not using a transaction, so this is unrelated to the problem.
The problem does not occur when the query is .where(admin.firestore.FieldPath.documentId(), "==", "just_one_doc_id"). So, the problem is with using FieldPath.documentId() and in.
It sounds like the type of query you're trying to do just isn't supported by the SDK. Whether or not that's intentional, I don't know. But if you want to transact with multiple documents, and you already know all of their IDs, you can use getAll(...) instead:
// build an array of DocumentReference objects
cost refs = indexArray.map(id => db.collection("person").doc(id))
return db.runTransaction(transaction => {
// pass the array to getAll()
return transaction.getAll(refs).then(docs => {
docs.forEach(doc => {
transaction.update(doc.ref, { ...update values here })
})
})
})

Resources