Multiple where clauses in Google CloudFirestore - google-cloud-datastore

I am in Angular 4, I need to compose a query with the multiple where clauses. Here is my code below.
How can I add the multiple where clauses into the queryfn?
this.db.collection('cities', x => x.where('city', '==', 'CA'))
.snapshotChanges()
Thank you

As you can see by this related question, it is possible to either chain or append your clauses to the query reference:
// option 1
const ref = admin.firestore().collection('collectionName')
const query = ref.where('foo', '==', 'bar').where('baz', ==, 'buz')
query.get()
.then(snapshot => {
snapshot.forEach(res => console.log(res.data())
})
// option 2
const ref = admin.firestore().collection('collectionName')
const query = ref.where('foo', '==', 'bar')
query.where('biz', <=, 5)
query.where('zaz', ==, 9)
query.get()
.then(snapshot => {
snapshot.forEach(res => console.log(res.data())
})

Related

Firestore query cursor always returning same data

I'm working on a virtualized table that lazily subscribes to a new batch of items in firestore. The issue is that query cursor returns the same batch of data, regardless of the startAt index being different.
const subscribeForItems = (
startIndex: number,
handleItems: (items: FirestoreItem[]) => void,
): (() => void) => {
const firestoreQuery = query(
collection(firestore, RootCollections.Items),
orderBy('price'),
startAt(startIndex),
limit(LIMIT),
);
return onSnapshot(firestoreQuery, (querySnapshot) => {
const items: FirestoreItem[] = [];
querySnapshot.forEach((doc) => {
items.unshift(
FirestoreUtils.extractDocumentData<FirestoreItem>(doc),
);
});
console.log(startIndex)// START INDEX IS DIFFERENT HERE EACH INVOCATION
console.log(items)// ITEMS ARE SAME HERE EACH INVOCATION (AS IF I'M ALWAYS PASSING THE SAME START INDEX)
handleItems(items);
});
};
So the issue was setting the limit. Instead of
const firestoreQuery = query(
collection(firestore, RootCollections.Items),
orderBy('price'),
startAt(startIndex),
limit(LIMIT),
);
it should be
const firestoreQuery = query(
collection(firestore, RootCollections.Items),
orderBy('price'),
startAt(startIndex),
limit(LIMIT + startIndex),
);
I find limit() method to be named counterintuitively, perhaps endAt() would have been a better name, if I need to do the addition myself

NGRX selectors: factory selector within another selector without prop in createSelector method

Using the factory selector pattern const selectA = (id: number) => createSelector(...) I have an instance where I want to reuse this selector within another selector (that iterates through an array of IDs) but I don't know the value to pass into the factor selector when calling createSelector.
So I have a selector that I use whenever I want to get a slice of the state for component A.
const selectA = (id: number) =>
createSelector(
selector1.selectEntityMap,
selector2.selectEntityMap,
selector3ById(id),
(
thing1,
thing2,
thing3
) => {
return ...
});
Now I want to get a list of component A for each item in an array.
const selectListOfA = (ids: number[]) =>
createSelector(
selectA,
(selectorA) => {
return ids.map((id) => selectorA(id));
});
The problem is selectA, which is now a factory selector, expects a parameter, but I don't know it when calling createSelector.
I can get the code to compile by creating another factory onto of the factory
const selectAFactory = () => selectA;
And then reference the new factory in the createSelector
const selectListOfA = (ids: number[]) =>
createSelector(
selectAFactory, <<< here
(selectorA) => {
return ids.map((id) => selectorA(id));
});
But of course, what's now happening is the selector is returning a list of MemoizedSelector[].
This pattern doesn't seem like it should be this complicated, are people not reusing their selectors in this way, what am I missing?
The function returned by selectA is a standard function, ie nothing magical about it, as explained well in this blog post: https://dev.to/zackderose/ngrx-fun-with-createselectorfactory-hng
This means selectListOfA can simply call the function returned from selectA for each id and an array of the state slices for component A will be returned:
export const selectListOfA = (ids: number[]) =>
createSelector(
(state) => state,
(state) => ids.map((id) => selectA(id)(state))
);
This works as expected but since the projector function will be executed every time anything in the store changes (recreating the selector for every id) this solution will have major performance issues.
We could just as well simplify the code to this with equally poor performance:
const selectListOfA = (ids: number[]) =>
(state) => ids.map(
(id: number) => selectA(id)(state)
);
If we instead supply an array of selectors as input to the createSelector call then Ngrx will be able to correctly determine when it has to reevaluate the selectA selectors:
const selectListOfA = (ids: number[]) =>
createSelector(
ids.map((id) => selectA(id)), // This results in an array of selectors
(...resultArr) => resultArr
);
However, Typescript will complain since the createSelector method does not have a matching overload declared for an array of variable length so we need to loosen up the input type of the array (to any) as well as specify the return type of selectListOfA.
The answer to the question is thus:
const selectListOfA = (ids: number[]) =>
(createSelector(
ids.map((id) => selectA(id)) as any,
(...resultArr) => resultArr
) as (state) => string[]);

Firebase, query a list of docs by a list of doc ID

If we want to query one doc by its ID, we can do this:
firebase
.firestore()
.collection("Your collection")
.doc("documentId")
.get()
.then((docRef) => { console.log(docRef.data()) })
.catch((error) => { })
How to query a set of docs given a list of IDs, for example:
var list_of_ids = ["documentId1", "documentId2", "documentId3"]
firebase
.firestore()
.collection("Your collection")
.doc(list_of_ids)
.get()
.then((docRef) => { console.log(docRef.data()) })
.catch((error) => { })
Answer
Thanks Dharmaraj.
var list_of_ids = ["documentId1", "documentId2", "documentId3"]
firebase
.firestore()
.collection("Your collection")
.where('__name__', 'in', list_of_ids)
.get()
.then((docRef) => { console.log(docRef.data()) })
.catch((error) => { })
If the array has less than or equal to10 IDs then you can use in operator:
.where('__name__', 'in', list_of_ids)
If the array contains more than 10 items then you would have to make separate request for each document (or make batches of 10).

How to delete multiple documents with specific value in firestore

I'm trying to delete many docs with specific categoryId value from my collection, but I'm doing this in a wrong way I think.
async deleteCol(id: string) {
const cars: firebase.firestore.QuerySnapshot
= await this.db.collection('cars', ref => ref.where('categoryId', '==', id)).ref.get();
const batch = this.db.firestore.batch();
cars.forEach(car => {
batch.delete(car);
});
batch.commit();
}
There are two problems:
typescript shows an error for car in batch.delete(car);
Argument of type 'QueryDocumentSnapshot' is not assignable to parameter of type 'DocumentReference'.   Property 'firestore' is missing in type 'QueryDocumentSnapshot'.
If there are for example two cars and each has different categoryId, then forEach is triggered twice (for each document, not for document with specific categoryId), but should only one time or maybe there is a better and easier way to delete all docs by specific condition?
UPDATE:
Ok, so this version is working :)
public async deleteCol(id: string): Promise<void> {
const carsList: Observable<firestore.QuerySnapshot> = await this.db.collection('cars', ref => ref.where('categoryId', '==', id)).get();
const batch = this.db.firestore.batch();
carsList.pipe(
mergeMap(cars => cars.docs),
map((car: QueryDocumentSnapshot) => batch.delete(car.ref))
).toPromise().then(() => batch.commit());
}

How can I get specific document data from firestore querysnapshot?

I got a querysnapshot in a function.
And want to bring the whole querysnapshot to another function (functionTwo).
In functionTwo, I want to get a specific document in the querysnapshot WITHOUT forEach. The specific doc can be changed by different cases.
ref_serial_setting.get()
.then(querysnapshot => {
return functionTwo(querysnapshot)
})
.catch(err => {
console.log('Error getting documents', err)
})
let functionTwo = (querysnapshot) => {
// getting value
const dataKey_1 = "dataKey_1"
// Tried 1
const value = querysnapshot.doc(dataKey_1).data()
// Tried 2
const value = querysnapshot.document(dataKey_1).data()
// Tried 3 (Put 'data_name': dataKey_1 in that doc)
const value = querysnapshot.where('data_name', '==', dataKey_1).data()
}
The result are all these trying are not a function.
How can I get specific document data from querysnapshot??
or
Is there any easy method to change the querysnapshot to JSON?
You can get an array of the document snapshots by using the docs property of a QuerySnapshot. After that you'll have to loop through getting the data of the doc snapshots looking for your doc.
const docSnapshots = querysnapshot.docs;
for (var i in docSnapshots) {
const doc = docSnapshots[i].data();
// Check for your document data here and break when you find it
}
Or if you don't actually need the full QuerySnapshot, you can apply the filter using the where function before calling get on the query object:
const dataKey_1 = "dataKey_1";
const initialQuery = ref_serial_setting;
const filteredQuery = initialQuery.where('data_name', '==', dataKey_1);
filteredQuery.get()
.then(querySnapshot => {
// If your data is unique in that document collection, you should
// get a query snapshot containing only 1 document snapshot here
})
.catch(error => {
// Catch errors
});
Theres an easy way to do this, each QuerySnapshot has a property docs which returns an array of QueryDocumentSnapshots. See QuerySnapshot documentation.
let citiesRef = db.collection('cities');
let query = citiesRef.where('capital', '==', true).get().then(snapshot => {
snapshot.docs[0]; // => returns first document
});
let citiesRef = db.collection('cities');
let query = citiesRef.where('capital', '==', true).get()
.then(snapshot => {
if (snapshot.empty) {
console.log('No matching documents.');
return;
}
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
});
})
.catch(err => {
console.log('Error getting documents', err);
});
from https://firebase.google.com/docs/firestore/query-data/get-data
you can use this code :
const querySnapshot = await getDocs(collection(db, "collectionNaame"));
const docSnapshots = querySnapshot.docs;
for (var i in docSnapshots) {
console.log(i)
const doc = docSnapshots[i].data();
console.log(doc)
Just do
db.doc(<<ref>>).get()
this returns a promise
[here ]: https://firebase.google.com/docs/firestore/query-data/get-data#get_a_document is the link to the docs

Resources