Perform a simple select query in Firebase Firestore - javascript

How can I perform a simple search in Firebase Firestore to check if a record exists in a collection? I've seen this code in the documentation, but it is not complete.
// Create a reference to the cities collection
var citiesRef = db.collection('cities');
// Create a query against the collection
var queryRef = citiesRef.where('state', '==', 'CA');
In my use case I want to check the rejected contacts collection for a given number. This is my code
const collectionRef = db.collection('rejectedContacts');
const queryRef = collectionRef.where('contact','==',phone);
let contactRejected: boolean = queryRef.get.length>0 ;
if (contactRejected) {
return response.json(
{
status: 0,
message: `Contact , ${phone} is blocked. Please try again with another one.`,
result: null
});
}
I've checked the function with a rejected number, which i added manually in the collection, but it is not responding with the rejected message. How can I get the count or the row itself with a select query. What is wrong with my code?
UPDATE 1
As #Matt R suggested, I updated the code with this
let docRef = db.collection('rejectedContacts').where('contact', '==', phone).get();
docRef.then(doc => {
if (!doc.empty) {
return response.json(
{
status: 0,
message: `Contact , ${phone} is blocked. Please try again with another one.`,
result: null
});
} else {
return response.json(
{
status: 0,
message: `Contact , ${phone} is not blocked`,
result: null
});
}
})
.catch(err => {
console.log('Error getting document', err);
});
But when checked with the existing number, it is returning with not blocked
Edit 2
return collectionRef.select('contact').where('contact', '==', phone).get()
.then(snapShot => {
let x : string = '['
snapShot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
x+= `{${doc.data}},`;
});
return response.send(x);
});
It is only returning the value of x from this line
let x : string = '['

Firestore's get() method returns a promise and response, I would try in this format to handle whether the document exists or not. This format may also lend better error messages when troubleshooting.
EDIT / UPDATED: Using the where clause returns a snapshot that must be iterated through, because it can return multiple documents. See documentation here: https://firebase.google.com/docs/firestore/query-data/get-data#get_multiple_documents_from_a_collection
Updated code with your example.
var collectionRef = db.collection('rejectedContacts');
var query = collectionRef.where('contact','==',phone).get()
.then(snapshot => {
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
});
})
.catch(err => {
console.log('Error getting documents', err);
});

Related

empty array when querying map of array in Firebase

I'm trying to get all messages in a chat. Each doc has a "messages" array, which maps to message body, createdAt, and sender's username. There is a second array for all users in chat.
How do I return all of the last 10 elements of the messages array?
Code:
exports.getChat = (req, res) => {
let chatData = {};
db.doc(`/chats/${req.params.chatId}`)
.get()
.then((doc) => {
if (!doc.exists) {
return res.status(404).json({ error: "Chat not found." });
}
chatData = doc.data();
chatData.chatId = doc.id;
return db
.collection("chats")
.where("chatId", "==", req.params.chatId)
.get();
})
.then((data) => {
chatData.messages = [];
data.forEach((doc) => {
chatData.messages.push(doc.data());
});
return res.json(chatData);
})
.catch((err) => {
console.error(err);
res.status(500).json({ error: err.code });
});
};
The code I have so far returns an empty messages array.
When querying Firestore, it's not possible to instruct the query to filter items out of an array field. You have to read the entire array field, then decide what you want to do with the items in that array. So, this means that a second query is not helpful here. You have everything you need in the first document snapshot.
db.doc(`/chats/${req.params.chatId}`)
.get()
.then((doc) => {
if (!doc.exists) {
return res.status(404).json({ error: "Chat not found." });
}
const chatData = doc.data();
// chatData now contains the entire contents of the document in the screenshot
const messages = chatData.messages
// messages now contains the entire array of messages - use however many want
})

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!

How to query firebase items with specific reference type

I have a parent collection categories and it child collection directories
Directories connected with Categories via Category property
I want to query all directories with category equal to level
this.firestore
.collection<any>('directories', ref => ref.where('categories', '==', 'levels'))
.get()
.pipe(
map(x => {
const out: [] = [];
x.forEach(y => {
out.push(y.data());
});
return out;
})
);
I am getting an empty array in return. How would you fix that?
UPDATE based on the answer provided by #renaud-tarnec:
const categoryDocRef = this.firestore.doc('categories/levels');
this.firestore
.collection<any>('directories', ref => ref.where('categories', '==', categoryDocRef))
.get()
.pipe(
map(x => {
const out: [] = [];
x.forEach(y => {
out.push(y.data());
});
return out;
})
);
Now having an error core.js:15713 ERROR Error: Function Query.where() called with invalid data. Unsupported field value: a custom AngularFirestoreDocument object
If you want to use the DocumentReference data type in a query, you have to build a DocumentReference and use it in your query, as follows (in "standard" JavaScript):
const categoryDocRef = firebase.firestore().doc('categories/levels');
firebase.firestore().collection("directories").where("parent", "==", categoryDocRef)
.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
console.log(doc.id, " => ", doc.data());
});
})
.catch(function(error) {
console.log("Error getting documents: ", error);
});
I've made the assumption that the documents containing the field parent (which , in turn, contains the DocumentReference type data) are in a collection named directories.
UPDATE: It appears that the following won't work with angularFire2, see the comments
So, if I am not mistaking, this would be done as follow in angular, based on the code of your question:
const categoryDocRef = this.firestore.doc('categories/levels');
this.firestore
.collection<any>('directories', ref => ref.where('parent', '==', categoryDocRef))
.get()
...

firebase / firestore docs queries Not working - javascript

As firestore is new, i am having problems using it.
I have to get Collection of all users and traverse it. But it is not working.
db.collection("users").get().then(function(querySnapshot){
console.log(querySnapshot.data());
});
It says:
querySnapshot.data is not a function
And following code:
callFireBase(mobileToCheck){
db.collection("users").where("mobile_no", '==', mobileToCheck).get().then(function(querySnapshot){
if (querySnapshot.exists) {
var userData = querySnapshot.data();
var userId = querySnapshot.id;
console.log(mobileToCheck + "Exist In DB");
}else{
console.log(mobileToCheck + "Do Not Exist In DB");
}
});
}
Is always printing
923052273575 Do Not Exist In DB
Even if it exists, See following image for reference. In docs they have told this (i have used) way.
It looks that tou want to call.data() on collection of documents, not one document. Please see if this code works:
db.collection("users").get().then(function(querySnapshot){
querySnapshot.forEach(doc => {
console.log(doc.data());
});
}).catch(err => {
console.log('Error getting documents', err);
});
You should use docs.map then doc.data(). Here is how to do it with Firestore using async await syntax
import firebase from 'react-native-firebase'
async fetchTop() {
const ref = firebase.firestore().collection('people')
const snapshot = await ref.orderBy('point').limit(30).get()
return snapshot.docs.map((doc) => {
return doc.data()
})
}

Categories