Elegant way to dynamically query firestore [duplicate] - swift

I have fetch some data from firestore but in my query I want to add a conditional where clause. I am using async-await for api and not sure how to add a consitional where clause.
Here is my function
export async function getMyPosts (type) {
await api
var myPosts = []
const posts = await api.firestore().collection('posts').where('status', '==', 'published')
.get()
.then(snapshot => {
snapshot.forEach(doc => {
console.log(doc.data())
})
})
.catch(catchError)
}
In my main function I am getting a param called 'type'. Based on the value of that param I want to add another qhere clause to the above query. For example, if type = 'nocomments', then I want to add a where clause .where('commentCount', '==', 0), otherwise if type = 'nocategories', then the where clause will be querying another property like .where('tags', '==', 'none')
I am unable to understand how to add this conditional where clause.
NOTE: in firestore you add multiple conditions by just appending your where clauses like - .where("state", "==", "CA").where("population", ">", 1000000) and so on.

Add the where clause to the query only when needed:
export async function getMyPosts (type) {
await api
var myPosts = []
var query = api.firestore().collection('posts')
if (your_condition_is_true) { // you decide
query = query.where('status', '==', 'published')
}
const questions = await query.get()
.then(snapshot => {
snapshot.forEach(doc => {
console.log(doc.data())
})
})
.catch(catchError)
}

For the frontend Web SDK:
Or you can look at this link for a different method:
Firestore conditional where clause using Modular SDK v9
let showPublishStatus: boolean = true
let conditionalConstraint: QueryConstraint = showPublishStatus
? where("status", "==", "published")
: where("status", "!=", "published")
let queryWebSDK = query(collection(db, "Collection"), conditionalConstraint)

Related

Perform simple queries in Cloud Firestore - how can i filter all through select?

I need to to filter data through queries from Firestore, but how can I also get all of them(in my case all states in firestore)? What should be useState value in this case? I'm a newbie :) Thank you for your help, I really appreciate it.
//WHAT useState VALUE SHOULD I USE TO GET ALL STATES IN DATABASE?
const (city, setCity) = useState("CA");
const citiesRef = collection(db, "cities");
const q = query(citiesRef, where("state", "==", city));
Tried to search in firestore docs and google.
you need to use getDocs() method provided by firebase as follows
here, q is your query ( const q = query(collection(db, "cities"), where(....));
add following code -
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data())
});
you can refer to this link
As you wanted to use useState, You can pass an empty string ” “ or Null value and use != instead of = operator in the where clause.
And use getDocs() to retrieve all documents as mentioned by #Prathmesh
Here is the complete code:
const (city, setCity) = useState(" ");
const citiesRef = collection(db, "cities");
const q = query(citiesRef, where("state", "!=", city));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
});

flutter firestore: how to get a query of documents

Im trying to get a list of user objects from firestore through a query. My current attempt looks like this:
List<User> getDiscoveryUsers(
String userId,
) async {
Query<Object?> query =
userCollection.where('finishedOnboarding', isEqualTo: true).limit(10);
var collection = await query.get();
//get the users list from query snapshot
var users = collection.docs.map((doc) => User.fromSnapshot(doc)).toList();
return users;
}
However I am getting the error:
Functions marked 'async' must have a return type assignable to 'Future'.
Try fixing the return type of the function, or removing the modifier 'async' from the function body.
I know there are a few similar questions on stack overflow, but i just cant seem to get this to work. Anyone know whats going on?
Thanks!
Just change the return type of your function from List<User> to Future<List<User>>.
Happy coding:)
your return type should be Future and must wait with await when running query on firestore.
Future<List<User>> getDiscoveryUsers(
String userId,
) async {
Query<Object?> query =
userCollection.where('finishedOnboarding', isEqualTo: true).limit(10);
var collection = await query.get();
//get the users list from query snapshot
var users = collection.docs.map((doc) => User.fromSnapshot(doc)).toList();
return users;
}

Firebase | Problem with collectionGroup query

I'm trying to use a collectionGroup query to fetch the data from a specified farm, using firebase-functions. Here's my code:
app.get('/api/intervals/:farm_id', async (req, res) => {
const farmId = req.params.farm_id;
try {
const querySnapshot = await db
.collectionGroup('farms')
.where("id", "==", farmId)
.get();
const farmData = [];
querySnapshot.forEach((doc) => {
farmData.push(doc.data());
console.log(doc.id, ' => ', doc.data());
});
return res.status(200).send(farmData);
} catch (error) {
console.log(error);
return res.status(500).send(error);
}
});
There is definitely a farm in the database with the supplied code. For example, if I change the code in try to the below, I get the data as expected:
const farmRef = db
.collection('accounts')
.doc('lz8V32bjQGa9x1oecUu9')
.collection('farms')
.doc(farmId);
let farm = await farmRef.get();
let farmData = farm.data();
return res.status(200).send(farmData);
But I want to use a collectionGroup so I don't have to specify the parent account ID also.
What am I doing wrong here? Thanks in advance!
According to my understanding this is related with the fact that in your working code you are assigning directly from DocumentSnapshot, while in example in of the issue you are iterating over QuerySnapshot returned by get from query.
In documentation we can find that documents in QuerySnapshot are in array property docs.
So I think you should change forEach loop accordingly:
querySnapshot.docs.forEach((doc) => {
farmData.push(doc.data());
console.log(doc.id, ' => ', doc.data());
});
I have worked out that the problem was that my code was looking for a field named id. My query works if I manually add a field called ID as highlighted below:

Feather.js + Sequelize + postgres 11 : How to patch a jsonb column?

I would like to update several row of my db with the same object.
let say I have a column customText type jsonb which contains an array of object
here my sequelize model :
customText: {
type: DataTypes.JSONB,
allowNull: true,
field: "custom_text"
}
Now from client I send an object:
const obj = {}
const data = {
textid: "d9fec1d4-0f7a-2c00-9d36-0c5055d64d04",
textLabel: null,
textValue: null
};
obj.customText = data
api.service("activity").patch(null, obj).catch(err => console.log(err));
Like the documentation from feathers.js said if I want to replace multiple record, I send an id equal to null.
So now here come the problem, if I do that my column customText will contain the new object only but I want an array of object, so I want to push the new data in the array. How can I patch the data?
My guess is to use a hook in feathers.js and a raw query with sequelize. But I'm not sure how to do that.
I'm not really sure of my answer but this hook work :
module.exports = function() {
return async context => {
debugger;
const sequelize = context.app.get("sequelizeClient");
const customText = JSON.stringify(context.data.customText[0]);
console.log(customField);
let query =
"UPDATE activity SET custom_text = custom_text || '" +
customText +
"' ::jsonb";
console.log(query);
await sequelize
.query(query)
.then(results => {
console.log(results);
context.results = results;
})
.catch(err => console.log(err));
return context;
I still have a problem because after this hook in feathers, the patch continue so it will update my db again.. so i put a disallow() hook.
Also, with this hook i lost the abilities to listening to event
Also i have a concern with the query, i'm not sure if it's better to use :jsonb_insert over ||

Disable caching in Angular Firestore queries

I am running a firestore query to get data but the query is returning data from cached data queries earlier and then returns additional data (which was not queried earlier) in the second pass from server. Is there a way I can disable caching for firestore queries so that request goes to DB every time I query something.
this.parts$ = this.db.collection<OrderBom>('OrderBom', ref => {
let query : firebase.firestore.Query = ref;
query = query.where('orderPartLC', '==', this.searchValue.toLowerCase());
return query;
}).valueChanges();
Change that .valueChanges() to a .snapshotChanges() then you can apply a filter. See the example below.
I dont like changing default behavior (default configurations). I saw it's a desired behavior and the good practice is to show the data as soon as possible to the user, even if you refresh twice the screen.
I dont think is a bad practice to filter on fromCache === false when we dont have a choise. (In my case I do more requests after i receive this first one so due to promises and other async 'tasks' cache/server order is completly lost )
See this closed issue
getChats(user : User) {
return this.afs.collection<Chat>("chats",
ref => ref.where('participantsId', 'array-contains', user.id)
.snapshotChanges()
.pipe(filter(c=> c.payload.doc.metadata.fromCache === false)).
.pipe(map(//probaly want to parse your object here))
}
if using AngularFire2 you can try:
I read on the Internet that you can disable offline persistence - which caches your results -by not calling enablePersistence() on AngularFireStoreModule.
I have done the first and still had no success, but try it first. What I managed to do to get rid of caching results was to use the get() method from class DocumentReference. This method receives as parameter a GetOptions, which you can force the data to come from server. Usage example:
// fireStore is a instance of AngularFireStore injected by AngularFire2
let collection = fireStore.collection<any>("my-collection-name");
let options:GetOptions = {source:"server"}
collection.ref.get(options).then(results=>{
// results contains an array property called docs with collection's documents.
});
Persistence and caching should be disabled for angular/fire by default but it is not and there is no way to turn it off. As such, #BorisD's answer is correct but he hasn't explained it too well. Here's a full example for converting valueChanges to snapshotChanges.
constructor(private afs: AngularFirestore) {}
private getSequences(collection: string): Observable<IPaddedSequence[]> {
return this.afs.collection<IFirestoreVideo>('videos', ref => {
return ref
.where('flowPlayerProcessed', '==', true)
.orderBy('sequence', 'asc')
}).valueChanges().pipe(
map((results: IFirestoreVideo[]) => results.map((result: IFirestoreVideo) => ({ videoId: result.id, sequence: result.sequence })))
)
}
Converting the above to use snapshotChanges to filter out stuff from cache:
constructor(private afs: AngularFirestore) {}
private getSequences(collection: string): Observable<IPaddedSequence[]> {
return this.afs.collection<IFirestoreVideo>('videos', ref => {
return ref
.where('flowPlayerProcessed', '==', true)
.orderBy('sequence', 'asc')
}).snapshotChanges().pipe(
filter((actions: DocumentChangeAction<any>[], idx: number) => idx > 0 || actions.every(a => a.payload.doc.metadata.fromCache === false)),
map((actions: DocumentChangeAction<any>[]) => actions.map(a => ({ id: a.payload.doc.id, ...a.payload.doc.data() }))),
map((results: IFirestoreVideo[]) => results.map((result: IFirestoreVideo) => ({ videoId: result.id, sequence: result.sequence })))
)
}
The only differences are that valueChanges changes to snapshotChanges and then add the filter DocumentChangeAction and map DocumentChangeAction lines at the top of the snapshotChanges pipe, everything else remains unchanged.
This approach is discussed here