How to use Supabase streams to query "IS NOT NULL" where clause - postgresql

How do I make the following query in supabase on a stream listening for changes:
select * from public.messages where "to" IS NOT NULL;
From the documentation the closest, I could get was doing the filtering with an "equal to" expression. As captured below:
_messagesStream = supabase
.from('messages:to=eq.123')
.stream(['id'])
.order('created_at')
.execute()
.map((maps) => maps
.map((map) => Message.fromMap(map: map, myUserId: myUserId))
.toList());
But what I need is a query with "IS NOT NULL". A work around I found was to handle complex queries in a view, but the issue here is, I cannot listen for events on view.
Kindly assist.

I think it is not possible.
I checked supabase.dart and I can't find any solution on how to implement it.
But you can filter it on your side:
_messagesStream = supabase
.from('messages:to=eq.123')
.stream(['id'])
.order('created_at')
.execute()
.map((maps) => maps
.where((element) => element['to'] != null)
.map((map) => Message.fromMap(map: map, myUserId: myUserId))
.toList());

From Supabase docs / Filters / is()
const { data, error } = await supabase
.from('countries')
.select()
.is('name', null)

Related

EF Core AsSplitQuery not respecting OrderBy

I am using EF Core to query my DB. As I have some includes i get this warning
Compiling a query which loads related collections for more than one collection navigation either via 'Include' or through projection but no 'QuerySplittingBehavior' has been configured. By default Entity Framework will use 'QuerySplittingBehavior.SingleQuery' which can potentially result in slow query performance. See https://go.microsoft.com/fwlink/?linkid=2134277 for more information. To identify the query that's triggering this warning call 'ConfigureWarnings(w => w.Throw(RelationalEventId.MultipleCollectionIncludeWarning))'
so when i add AsSplitQuery()
public async Task<Board> GetBoardAsync (Guid id) {
return await _context.Boards.Include (x => x.Lists.OrderBy(x => x.Order))
.ThenInclude (x => x.Items.OrderBy(x => x.Order))
.AsSplitQuery()
.FirstOrDefaultAsync (x => x.Id == id);
}
OrderBy is not respected when returning data.
How to overcome this warning and respect OrderBy
thanks
Try this:-
public async Task<Board> GetBoardAsync (Guid id) {
return await _context.Boards.Include (x => x.Lists.OrderBy(x => x.Order))
.ThenInclude (x => x.Items.OrderBy(x => x.Order))
.Where(x => x.Id == id)
.AsSplitQuery().FirstOrDefaultAsync();
}
Also, you can use AsNoTracking() for queries to better performance.hope it will resolve your issue.
UPDATE
Use ThenBy instead of OrderBy because ThenBy works for several sorting criteria.

How to update value within firestore query using dart

Here is a query that returns some data from the firestore database.
After getting the docs, I want to update a specific key(distance_away) value before returning the final List
return db
.collection(Global.marketplaceRef)
.where("point.geohash", isGreaterThanOrEqualTo: range.lower)
.where("point.geohash", isLessThanOrEqualTo: range.upper)
.limit(20)
.snapshots()
.map((list) => (list.docs
.map((e) {
// Update value here before returning doc.
// This doesn't seem to do that
return (e.data().update('distance_away', (value) => 100.toString())).toList();
})
).toList()
);
I want to alter the distance_away value of each doc.
You need to access the reference object not .data() try something like this
.map((list) => (list.docs
.map((e) {
return (e.reference.update({'distance_away': ['100']}
).toList()

Not getting link for creating firestore index

In my flutter app, I am running a firestore query like this :
final userDoc = await _firestore.userDocument();
final name = nameStr.toUpperCase();
yield* userDoc.firestore
.collectionGroup('persons')
.where(
'name',
isGreaterThanOrEqualTo: name,
isLessThan: name.substring(0, name.length - 1) +
String.fromCharCode(
name.codeUnitAt(name.length - 1) + 1),
)
.snapshots()
.map(
(snapshot) => right<Failure, List<Person>>(
snapshot.docs
.map((doc) => Dto.fromFirestore(doc).toDomain())
.toList(),
),
)
.onErrorReturnWith((e) {
if (e is PlatformException && e.message.contains('PERMISSION_DENIED')) {
return ...;
} else {
print(e.toString());
return ...;
}
});
It shows the following error :
I/flutter (27416): [cloud_firestore/failed-precondition] Operation was rejected because the system is not in a state required for the operation's execution. If performing a query, ensure it has been indexed via the Firebase console.
Probably because of the where, I need to add index in console, so in the error message I am expecting an url to add index, but not getting it. I tried adb logcat, not even there.
In the documentation for collection group queries you will find at the bottom:
"Before using a collection group query, you must create an index that supports your collection group query. You can create an index through an error message, the console, or the Firebase CLI.
For the web and mobile SDKs, you must also create rules that allow your collection group queries."
So you will need to create security rules for your collection group and then, if you can't get the error message with adb logcat, you will need to manually create the index either through the Firebase console or the Firebase CLI.
You need to separate your where statements.
.collectionGroup('persons')
.where(
'name',
isGreaterThanOrEqualTo: name)
.where('name', isLessThan: name.substring(0, name.length - 1) +
String.fromCharCode(
name.codeUnitAt(name.length - 1) + 1),
)

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:

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