Javascript:Cloud Firestore merge is replacing the data - javascript

my initial code for writing data is this
var Cloud = firebase.firestore();
Cloud.collection("IPA").doc("Allipas").set({
IPlist: "A;B;",
})
.then(function() {
console.log("Document successfully written!");
})
.catch(function(error) {
console.error("Error writing document: ", error);
});
Then i want to merge new information into my Field
var Cloud = firebase.firestore();
Cloud.collection("IPA").doc("Allipas").set({
IPlist: "C;",
} , {merge : true})
.then(function() {
console.log("Document successfully written!");
})
.catch(function(error) {
console.error("Error writing document: ", error);
});
but it replaces the field with 'C' only and i cant see A & B

The merge: true option merges the fields you provide in the API call with the existing fields in the document. It does not merge a single value with the existing value of a field.
If you want to update an existing value of a field, you will have to first read the value of that field, then update it in your code, and finally write it back to the database.
This typically is done in a transaction to ensure nobody else can write conflicting updates at the same time. An example:
var docRef = Cloud.collection("IPA").doc("Allipas");
Cloud.runTransaction(function(transaction) {
// This code may get re-run multiple times if there are conflicts.
return transaction.get(docRef).then(function(doc) {
if (!doc.exists) {
throw "Document does not exist!";
}
var newIPlist = doc.data().IPlist + "C;";
transaction.update(docRef, { IPList: newIPList });
});
}).then(function() {
console.log("Transaction successfully committed!");
}).catch(function(error) {
console.log("Transaction failed: ", error);
});
Note that Firebase recommends not using such composite value, or arrays, precisely for this reason: having to read the existing value before updating it reduces the scalability of your solution. Have a look at the documentation on working with arrays, list, and sets for alternatives.

Your merge can be change for update?
updteSomething() {
this.db.collection('IPA').doc(Allipas).update({
IPlist: ""A;B;C;",
})
.then(function () {
console.log("Document successfully deleted!");
}).catch(function (error) {
console.error("Error removing document: ", error);
});
}

Related

MongoNetworkError: connection establishment was cancelled

I'm trying to update a database using Mongoose, but I'm getting this Network error while running my node app.
const mongoose = require('mongoose')
mongoose.connect("mongodb://localhost:27017/fruitsDB")
const fruitsSchema = new mongoose.Schema({
name: {
type: String,
required: [true, "Why no Name?"]
},
rating: {
type: Number,
min: 1,
max: 10
},
review: String
});
const Fruit = mongoose.model("Fruit", fruitsSchema)
Fruit.find(function(err, fruits){
if(err){
console.log(err)
}
else{
mongoose.connection.close();
fruits.forEach(function(fruit){
console.log(fruit.name)
})
}
})
Fruit.updateOne({_id:"62b6a681eb136efde7ed17bc"}, {name: "Banana"}, function(err){
if(err){
console.log(err)
}
else{
console.log("Successfully updated the document")
}
})
Error: Commnad line error while running the node app
MongoNetworkError: connection establishment was cancelled
at connectionFailureError
at CancellationToken.<anonymous>
at Object.onceWrapper (node:events:641:28)
at CancellationToken.emit (node:events:527:28)
at ConnectionPool.close
at Server.destroy
at destroyServer
at eachAsync
It's a simple Node app created using Mongoose.
Calling the find function last worked for me. I mean, like this -
Fruit.updateOne({_id:"62b6a681eb136efde7ed17bc"}, {name: "Banana"}, function(err){
if(err){
console.log(err)
}
else{
console.log("Successfully updated the document")
}
})
Fruit.find(function(err, fruits){
if(err){
console.log(err)
}
else{
mongoose.connection.close();
fruits.forEach(function(fruit){
console.log(fruit.name)
})
}
})
Closing the connection should be at end which is the reason why the code is not getting properly executed.
I tried Calling the find function last but still getting the same error like this:
MongoNetworkError: connection establishment was cancelled
I don't know how to tackle this issue when it occurs in a running application but for now if you want to just insert the docs in collection then just comment the .find method completely and then run the application it will be inserted successfully and then comment the .updateOne method and uncomment the .find method by doing you will be successfully added the docs and could get the find result.
I did the same!
OR
I found out that for some reason .find method gets executed before .updateOne so the connection were being closed before the collection gets updated.
So, if we do this it works.
Fruit.updateOne({_id:"62b6a681eb136efde7ed17bc"}, {name: "Banana"}, function(err) {
if (err) {
console.log(err)
} else {
Fruit.find(function(err, fruits) {
if (err) {
console.log(err)
} else {
mongoose.connection.close();
fruits.forEach(function(fruit) {
console.log(fruit.name)
})
}
});
console.log("Successfully updated the document")
}
})
You can't close the connection in the find method. You are not able to interact with the db after you closed the connection. Just put mongoose.connection.close() at the end.
Just Do something like this:
`People.insertMany([man1, man2, man3],
function (err) {
if (err) {
console.log(err);
} else {
console.log("Successfully saved all
models in the database");
mongoose.connection.close();
}
});`
Closing the connection inside insertmany worked for me
After reading some solutions the TL:DR is: the mongoose.disconnect();, it's the one causing problems, the connection is being terminated before you can update anything
You should be careful while using crud methods on database. Because those methods are asynchronous.
In your case the find method executed first and closed the database connection prior to updateOne method.
Solution: You can solve that by simply changing the logic in your code like embedding find method inside updateOne method or viseversa according to your need.(By embedding we are making a way to execute them in order)
Fruit.find(function(err, fruits) {
if (err) {
console.log(err)
} else {
fruits.forEach(function(fruit) {
console.log(fruit.name)
})
Fruit.updateOne({_id:"62b6a681eb136efde7ed17bc"}, {name: "Banana"}, function(err) {
if (err) {
console.log(err)
} else {
mongoose.connection.close();
console.log("Successfully updated the document")
}
})
}
})

Is there a way to do this without nested using nested promise?

In the below cloud function, I am populating a collection-1 with an autogenerated ID and 5 field values. While adding each document, I am populating another collection with the document name as one of the properties containing the earlier auto-generated document name as the field,
Collection-1
-auto-id
-property1
-property2
-property3
Collection-2
property2
-auto-id from collection-1
Collection-2 is maintained for faster lookup of the data.
exports.addSafe = functions.https.onCall((data, context) => {
// The HTTP endpoint is going to receive an object with an attribute "data", which is going to contain an array of objects with every single safe data point to add
for (let i=0; i<data.length; i++) {
db.collection('Safes').add(data[i])
.then((docRef) => {
db.collection('Safes-Hardware').doc(data[i]['Mac address Check']).set({
"ID" : docRef.id
})
.then((value) =>{
console.log("Reference added with ID: ", value.id);
return { message: "Successful" }
})
.catch(err => {
console.log('Oops!, error while adding lookup details',err);
return { message: "Error while adding lookup details",err }
})
console.log('Mac written with ID: ', docRef.id);
return { message: "Success is within the palm of our hands." }
})
.catch(err => {
console.log('Error logged', err);
})
}
}
})
Updated Code - Using nested async-await
exports.addSafe = functions.https.onCall((data, context) => {
// The HTTP endpoint is going to receive an object with an attribute "data", which is going to contain an array of objects with every single safe data point to add
const attributesToDelete = ["CARTON#", "NO#"] // This first function call is implemented initially because of the first CSV file that I was given, which includes unnecessary columns, like "Carton" or "No". The factory producing the safes should send a CSV file with no unecessary extra data. If they do, this function should theoretically take care of removing those data points, to ensure that the database only holds the necessary data points ;)
deleteAttributes(data, attributesToDelete);
let validated = true;
//validateForm(data);
if (validated === false) {
console.log('Data cannot be validated. Misses the correct attributes')
} else {
for (let i=0; i<data.length; i++) {
try
{
// eslint-disable-next-line no-await-in-loop
var ifPresent = db.collection("Safes-Hardware").doc(data[i]['Mac address Check']);
ifPresent.get()
.then(async (doc)=>{
if (!doc.exists)
{
console.log("Document does not exit. Proceeding to add");
try{
// eslint-disable-next-line no-await-in-loop
const docRef = await db.collection('Safes').add(data[i])
console.log('Mac written with ID: ', docRef.id);
try{
// eslint-disable-next-line no-await-in-loop
await db.collection('Safes-Hardware').doc(data[i]['Mac address Check'])
.set({
"ID" : docRef.id
})
console.log("Reference added");
}
catch(err){
console.log("Error while adding reference",err)
}
}
catch(err){
console.log("Error while adding data to 'Safe' collection")
}
}
else
{
console.log("Document exists in database. Skipping safe with MAC Address: ",data[i]['Mac address Check']);
}
return { message: "Success is within the palm of our hands." }
})
.catch((error)=>{
console.log("Error while checking for duplicates", error);
});
}
catch(error){
console.log("Error logged",error)
}
}
}
})
What would be a better way to do this instead of using nested promises?
When I am not populating the second collection- the code works flawlessly. But when the second collection is also being populated - I get the following error once in a while (3/10 times)
Error:
Error logged { Error: The referenced transaction has expired or is no longer valid.
at Http2CallStream.call.on (/srv/node_modules/#grpc/grpc-js/build/src/client.js:96:45)
at emitOne (events.js:121:20)
at Http2CallStream.emit (events.js:211:7)
at process.nextTick (/srv/node_modules/#grpc/grpc-js/build/src/call-stream.js:71:22)
at _combinedTickCallback (internal/process/next_tick.js:132:7)
at process._tickDomainCallback (internal/process/next_tick.js:219:9)
code: 3,
details: 'The referenced transaction has expired or is no longer valid.',
metadata: Metadata { options: undefined, internalRepr: Map {} } }
Collections - Safe
Safes-Hardware
Please try to just first create a collection with the Custom Document Name and then set the data into the document as following:
const doc = db.collection('Safes').doc(data[i]['Mac address Check'])
doc.set({"ID" : docRef.id })

Cloud Firestore can not find custom document id

This function is returning false if i try to get the custom document id.
It is only returning true when I enter document id on the firebase console.
checkDot() {
this.db.firestore.collection(this.DOT).doc(this.DOT).get()
.then( doc => {
console.log('Data is ', doc.exists);
if (doc.exists) {
// this.isDotExist = true;
console.log(doc, 'Colection exists');
}
else {
// new Account Create
console.log('Colection doos not exist');
this.presentConfirm();
}
});
This function stores user input in the database
async createNewAccount() {
// Binding data from user input
const { Company, Fname, Email, Password } = this;
try {
// creating user account
const res = await this.afAuth.auth.createUserWithEmailAndPassword(Email, Password).then(cred => {
// DOT value passed by another page, others from user input
this.db.collection(this.DOT).doc(this.DOT).collection(Company).doc(Fname).set({ Name: Fname });
});
this.showAlert('Succes', 'You have successfully registered!');
this.route.navigate(['']);
console.log(res);
} catch (err) {
this.showAlert('Error', err.message);
// console.dir(err);
}
}
As you can check in this question from the Community Query Firebase Firestore documents by the ID, there is a special method that you can use query via documentId. The method is this: 'FieldPath.documentId()'
Another reference is the official documentation Execute Query, where you can find the following example of code that you can use as a start point, to return documents via ID.
db.collection("collection").where("document", "==", true)
.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
});
})
.catch(function(error) {
console.log("Error getting documents: ", error);
});
Besides that, there is the below question from the Community with more information and examples, related to a similar to yours, that might help you.
How to perform collection group query using document ID in Cloud Firestore
Let me know if the information helped you!

Retrieveing data from document of a collection in firestore database with document ID

I want to make a chat application with chat room by implementing firebase friendly chat app. I want to get all the information from "rooma" documentid of message collection. But i am not able to get the information from the document with ID "rooma" but i can access all the information from "message" collection.
my code is:
function loadMessages() {
// Create the query to load the last 12 messages and listen for new ones.
var query = firebase.firestore()
.collection('messages').where(firebase.firestore.FieldPath.documentId(), '==', 'rooma').get()
.orderBy('timestamp', 'desc')
.limit(12);
// Start listening to the query.
query.onSnapshot(function(snapshot) {
snapshot.docChanges().forEach(function(change) {
if (change.type === 'removed') {
deleteMessage(change.doc.id);
} else {
var message = change.doc.data();
displayMessage(change.doc.id, message.timestamp, message.name,
message.text, message.profilePicUrl, message.imageUrl);
}
});
});
}
my database structure is:
Imgur
If you want to get the data of "the document with ID 'rooma'", just do as follows, according to the documentation:
var docRef = db.collection("messages").doc("rooma");
docRef.get().then(function(doc) {
if (doc.exists) {
console.log("Document data:", doc.data());
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
If you want to use onSnapshot(), in order to "listen to a document", you just have to do as follows, according to the documentation:
var docRef = db.collection("messages").doc("rooma");
docRef.onSnapshot(function(doc) {
console.log("Current data: ", doc.data());
});

Can I query a nested document value in firestore?

I'm looking to do a search on the following data in firestore:
Collection->Document->{date{month:10,year:2017}}
var ref = db.collection(collection).doc(document)
ref.where('date.month', '==', 10).get().then(doc=>{
if (!doc.exists) {
console.log('No such document!');
} else {
console.log('Document data:', doc.data());
}
}).catch(err => {
console.log('Error getting document', err);
});
The above pseudo code does not work. Any suggestions?
It looks like you are querying a document:
var ref = db.collection(collection).doc(document)
In stead you should be querying your collection:
var ref = db.collection(collection)
Your query will pick up all documents, which meet "date.month==10" criteria among array of documents in your collection.
Also I think you have to change how you parse the data coming from .get() because it's going to be an array:
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
console.log(doc.id, " => ", doc.data());
});
})
This link should be also helpful to get the idea.

Categories