Firestore batch update - check if doc exists - javascript

I try to update documents using batch. It is OK if they exists but if not I have error. I would like to "set" if doc does not exists
var batch = db.batch();
$.each(newProductsArray, function (key, value) {
if (value['sku']) {
console.log(value['sku']);
var sku = value['sku'];
var name = value['name']
docRef = db_user.collection("products").doc(sku);
batch.update(docRef, {
productSKU: sku + "sku",
productName: name
});
}
});
// Commit the batch
batch.commit().then(function () {
...
)
});

It sounds like you want to use set() instead of update(), telling it to merge fields if the document already exists.
batch.set(docRef, { ... }, { merge: true });

Related

How to update a nested object child element, where some condition(s) is/are true

Note: This is not a duplicate question, please read till the end and see the included image.
I have a nested object and an array field inside my collection/document in Firestore.
Main categories
Drinks
Snacks
Items for Drinks are
(Water, Energy, Milk, ...)
Items for Snacks are
(Chips, Biscuits, Corn, ..)
The user may subscribe to both categories for multiple items with an expiration date:
Drinks->Energy
Drinks->Milk
Snack->Chips
I want to update the [expDate] field where [name] is equal to drinks and [type] is equal to [energy]
I have explored Firestore documentation more importantly compound queries in Cloud Firestore and read so many article(s) and questions on stackeoverflow but I couldn't find my answer, below is part of my code which I tr.
db.collection(this.dbName)
.where("name", "==", "drinks")
.where("subscriptions.data.type", "==", "energy")
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
let data = doc.data();
if (data.email == email) {
let subscriptions = data.subscriptions;
subscriptions.forEach((subscription) => {
if (subscription.name == productName) {
let prodTypes = subscription.data;
prodTypes.forEach((prodType) => {
if (prodType.type == itemType) {
let docRef = fb.db.collection(this.dbName).doc(email);
fb.db
.collection(this.dbName)
.doc(email)
.update(docRef, {
subscriptions: [
{
data: [
{
expDate: expiration,
type: itemType,
},
],
name: productName,
},
],
});
}
});
}
});
}
});
})
.catch((error) => {
console.log("Error getting documents: ", error);
});
I don't get any console log result for the above query.
This query won't work:
db.collection(this.dbName)
.where("name", "==", "drinks")
.where("subscriptions.data.type", "==", "energy")
This returns documents that have a:
field name at their root with value "drinks" AND
have a field type nested under data nestedundersubscriptions**under the root** with the value"energy"`
Neither of those fields is present in the document you've shown, so the query won't return that document. If you add the fields under the root of the document, you'll see that the query returns it.
It looks like you're trying to return documents where the subscriptions array contains specific value in its items. For this you'll need to use the array-contains operator. This operation can test if an array field contains a specific complete value, like:
db.collection(this.dbName)
.where("subscriptions", "array-contains", { ... })
But here you have to specify the entire object that must exist in the array. You can't check whether one property if the item exists with a specific value, and you also can't check for a nested array as you seem to have here.
The solution, as is usually the case when dealing with NoSQL databases, is to change/augment your data model to allow the use-case. Since you want to query for documents that have a specific name and type, add top-level fields for all names and types that exist in this document:
{
names: ["drinks", "snacks"],
types: ["energy", "water"]
subscriptions: [
...
]
}
Now you can use (one of) the new top-level fields in your query:
db.collection(this.dbName)
.where("names", "array-contains", "drinks")
You can't add a second array-contains clause though, as a query can only have one such clause in Firestore. So you'll have to filter the types in your application code after using a query to retrieve only documents that contain drinks.
With my understanding firebase compound queries are not working for this kind of cases, I resolved the issue by changing the object with javascript map helper and update the document with return data of the function (changeExpDate), as follow:
changeExpDate(data, name, type, expDate) {
data = [data];
data.map((obj) => {
let subscriptions = obj.subscriptions;
for (var i = 0; i < subscriptions.length; i++) {
let items = obj.subscriptions[i].data;
for (var j = 0; j < items.length; j++) {
if (obj.subscriptions[i].name == name) {
if (items[j].type == type) {
obj.subscriptions[i].data[j].expDate = expDate;
}
}
}
}
});
return data;
}
By considering your conditions, got with following code:
let v = this.changeExpDate(
data, //The document objcet
productName, //Product Name
itemType, //Item type
expiration //Expiration date
);
try {
fb.db
.collection(this.dbName)
.doc(email)
.update(v[0])
.then(function () {
console.log("Updated!");
})
.catch(function (error) {
console.error("Error Updating: ", error);
});
} catch (error) {
console.error(error);
}

How to populate an array inside a map function in js and send it to the server?

This is my ObjectIds array -
obj_ids = [
"5ee71cc94be8d0180c1b63db",
"5ee71c884be8d0180c1b63d9",
"5ee71c494be8d0180c1b63d6",
"5ee71bfd4be8d0180c1b63d4"
]
I am using these objectids to serach whether they exist in the db or not and based on that I want to send the response to server.
This is the code I am trying but I dont know how to populate the array and send it to the server.
var msg = [];
obj_ids.map((ele) => {
Lead.find({ _id: ele._id }, async function (error, docs) {
if (docs.length) {
msg.push(
`Lead already exist for Lead id - ${ele._id} assgined to ${docs[0].salesPerson}`
);
} else {
msg.push(`Lead doesn't exist for Lead id: ${ele._id}`);
const newDuty = new AssignedDuty({
duty: ele._id,
salesPerson: req.body.salesPerson,
});
await newDuty.save();
}
});
});
res.json(msg);
By doing this approach I am getting an empty array. I cannot put res.json(msg) inside the loop. If it is possible by using async-await, please guide me through.
You don't need to make multiple queries to find whether given object ids exist in the database.
Using $in operator, you can make one query that will return all the documents where the _id is equal to one of the object id in the list.
const docs = await Lead.find({
_id: {
$in: [
"5ee71cc94be8d0180c1b63db",
"5ee71c884be8d0180c1b63d9",
"5ee71c494be8d0180c1b63d6",
"5ee71bfd4be8d0180c1b63d4"
]
}
});
After this query, you can check which object id is present in the docs array and which is absent.
For details on $in operator, see $in comparison operator
Your code can be simplified as shown below:
const obj_ids = [
"5ee71cc94be8d0180c1b63db",
"5ee71c884be8d0180c1b63d9",
"5ee71c494be8d0180c1b63d6",
"5ee71bfd4be8d0180c1b63d4"
];
const docs = await Lead.find({
_id: { $in: obj_ids }
});
const msg = [];
obj_ids.forEach(async (id) => {
const doc = docs.find(d => d._id == id);
if (doc) {
msg.push(
`Lead already exist for Lead id - ${doc._id} assgined to ${doc.salesPerson}`
);
}
else {
msg.push(`Lead doesn't exist for Lead id: ${id}`);
const newDuty = new AssignedDuty({
duty: id,
salesPerson: req.body.salesPerson
});
await newDuty.save();
}
});
res.json(msg);

Deleting json value with javascript

I have tried to many ways , but i am stuck with a simple function in javascript, and i don't know where i need to looking for ... the problem is this:
I have a Json file like this one:
{
"blacklist": [
{
"email": "strangemail#gmail.com"
},
{
"email": "strangemail1#gmail.com"
},
{
"email": "strangemail2#gmail.com"
},
{
"email": "fianlt#gmail.com"
},
{
"email": "finalstatustest#gmail.com"
}
]
}
I would like simple remove an email with a simple function like this one:
function cancel(email) // parameter that contain the value to delete
{
let rawdata = fs.readFileSync('pvt.json'); //get local json file
let mydata = JSON.parse(rawdata); //parsing rawdata
var key = email; //setting up key
delete mydata.blacklist[key]; //using delete function for delete an element
let data = JSON.stringify(mydata, null, 2); //stringify the result
fs.writeFileSync('pvt.json', data); // overwrite local file with new one with all changes
}
the problem is ... it doesn't works ... i don't know why ... i tried to read the documentation, but i didn't found any solution 😢
The delete operator is for removing a property from an object, using the property's name. You're trying to remove an entry from an array, using the value of a property of an object in the array.
Assuming email is a variable containing the email address in the entry you want to remove, filter is one easy way to do that:
mydata.blacklist = mydata.blacklist.filter(entry => entry.email !== email);
filter builds a new array from the entries in the original array that meet the criteria in the callback — in this case, that their email property doesn't match the email address you want to remove.
If you wanted to modify the array in place rather than creating a new one, you'd use findIndex and splice:
const index = mydata.blacklist.findIndex(entry => entry.email === email);
if (index !== -1) {
mydata.blacklist.splice(index, 1); // Remove the entry at the index
}
Delete works to delete a key-value from an object. Here you have an array of items[objects]. You should use filter to remove unwanted element.
Update:
function cancel(selectedEmail) {
let rawdata = fs.readFileSync("pvt.json"); //get local json file
let mydata = JSON.parse(rawdata); //parsing rawdata
mydata.blacklist = mydata.blacklist.filter(
(item) => item.email !== selectedEmail.email
);
fs.writeFileSync("pvt.json", JSON.stringify(mydata, null, 2)); // overwrite local file with new one with all changes
}
Sample:
const info = {
blacklist: [
{
email: "strangemail#gmail.com",
},
{
email: "strangemail1#gmail.com",
},
{
email: "strangemail2#gmail.com",
},
{
email: "fianlt#gmail.com",
},
{
email: "finalstatustest#gmail.com",
},
],
};
const selectedEmail = {email: "finalstatustest#gmail.com"}
info.blacklist = info.blacklist.filter(item => item.email !== selectedEmail.email)
console.log(info)

How to add a new key:value pair to existing sub object of a mongoDB document

I am trying to add a new key:value pair to an existing object of mongoDB document, but no steps are helping me
I tried $each, $push $addtoset but i understood those are for arrays then i tried $det but it is updating the existing key:value pair with new key:value pair
Here is my document
{
test:"abc",
test2:"cdf",
test3:{ 1:"one"}
}
if you observer test3 key in the above document i have already 1:"one" now i want to add new key value in the same object
Like
{
test:"abc",
test2:"cdf",
test3:{ 1:"one", 2:"two", 3:"three"}
}
is it possible in mongoDB?
Here is the mongo Query
let val = parseInt(DYNAMICVALUE)
var changfeemaildot = (req.session.email).replace(/\./g, '#')
var seld = {
_id: ObjectId(rx[0]._id)
};
var seldu = {
$set:{
emails: {
[changfeemaildot]: val
}
}
};
var collection =
connection.get().collection('problems');
collection.update(seld, seldu, function (err, rail) {
});
You can use $set. So your code can be something like this
db.collection.update({<your_condition>}, {$set: {"test3.2": "two", "test3.3": "three"}});
In your case, it will be something like this
var seldu = {$set: {["emails." + changfeemaildot]: val}}
You can use $set with findOneAndUpdate. So your code can be something like this
const { Types, connection } = require("mongoose");
const productList = await connection.collection('products').find({}).toArray()
productList.forEach(async function(myDoc) {
await connection.collection('products').
updateOne({ productId: Types.ObjectId(myDoc.productId) }, {
$set: {
productDisplayId: 'anything you want'
}
});
});

Insert and merge a particular field in firestore firebase

I have a doc in my collection in this format
name: xyz,
email: xyz#email.com,
age: 30,
address: {
street_no: {
complete_address: somedata,
pincode: somepin
},
city:somecity,
state:somestate,
landmark:nearby
}
And inside this doc I am trying to insert and merge the complete_address with the previous record. To achieve that I am trying this
const database = firebase.firestore();
var dataRef = database.collection('collection');
var query = dataRef.doc(key+"").get().then(doc=>{
if(!doc.exists){
res.send("doesn't exist");
}else{
//few checks
if(doc.data().accessid != accessid){
res.send("accessid doesn't match")
}
//here I am trying to insert and merge with the previous data
var form_path = 'address.street_no.complete_address';
dataRef.doc(key+"."+form_path).set({
another_address
}, {merge: true});
}
}).catch(err=>{
console.log(err)
})
But when I execute this it just add another document in a collection followed by this path key.address.street_no.complete_address.
What can I do to only insert and merge with the previous complete_address ?
There is . instead of / in form_path because got few ideas from this link
I believe your issue lies within the next couple of lines starting at
var form_path = 'address.street_no.complete_address';
Next, You're using dataRef.doc(key+"."+form_path)
which means the only document being set is
/addressCollection/key.{addressCollectionId}
and addressCollectionId being address.street_no.complete_address
Instead what you want to do is access the property within the document using dot notation like so.
address: {
street_no: {
complete_address
Example.
someDocument.update({
"address.street_no.complete_address": "some_data"
});
Note that "some_data" will replace what ever data is currently stored. You'll want to do one read and merge the data. For example.
const anotherAddress = { address: "123 Fake Street" };
const document = firebase
.firestore()
.collection("addressCollection")
.doc("someAddressId");
document
.get()
.then(snap => {
const data = snap.data();
const completeAddress = data.address.street_no.complete_address };
// We're using the spread operator here to combine the current completeAddress with anotherAddress
return { completeAddress, ...anotherAddress };
})
.then(newCompleteAddress =>
document.update({
"address.street_no.complete_address": newCompleteAddress
})
);
I got this working.
So I figured out what I was trying to do earlier will create another document in a collection with data respect to it. So I start treated everything as an object and passed an object data to set() method.
const database = firebase.firestore();
var dataRef = database.collection('collection');
var query = dataRef.doc(key+"").get().then(doc=>{
if(!doc.exists){
res.send("doesn't exist");
}else{
//few checks
if(doc.data().accessid != accessid){
res.send("accessid doesn't match")
}
//here I am trying to insert and merge with the previous data
var mergeData = {
address : {
}
}
var new_address = {
key: "address_data"
}
mergeData.address[street_no] = {complete_address : address}
if(dataRef.doc(key+"").set(mergeData, {merge: true})){
res.send("done")
}else{
res.send("failed")
}
}
}).catch(err=>{
console.log(err)
})

Categories