Firestore whereNotIn Query - firebase

Working with flutter and firestore. I have a collection of widgets. I query these widgets and displaying them to the user one at a time so they can tell me whether they have ever seen this widget in real life before. The query:
firestore.collection('widgets').where('docID', whereNotIn: seenBeforeList).get()
Where 'seenBeforeList' is a array in the user document to keep track of the widgets they have marked as seen.
Let's say I have 20 widgets. The user has marked the first 11 widgets as seen and closes the app. Next launch the query says, give me all the widgets that the user hasn't already seen (the last 9). Essentially, I don't want to show the widgets the user has already marked as seen. However firestore has the, 'value.length <= 10' - filters support a maximum of 10 elements in the value [List], rule so after the user marks the first 10 widgets (seenBeforeList) this query fails.
I can't seem to think of a different solution. Maybe a different data structure...I'm not sure. Am I missing something? Any thoughts would be much appreciated! Thanks in advance!

It sounds lame but I think the usual suggested solution is to just loop for 10/n times and aggregate the gets into one list you then await. This is also the solution I would personally go with
// Note, haven't tested this code at all
Future<List<QuerySnapshot<T>>> getNotInList(
List<String> seenBeforeList,
) async {
final snapshotsFutures = <Future<QuerySnapshot<T>>>[];
for (var i = 0, limit = 10; i < seenBeforeList.length; i++) {
final ids = seenBeforeList
.getRange(i * limit, max((i + 1) * limit, seenBeforeList.length))
.toList();
// final seenBeforeList = ids.slice(i, i * limit); ?
snapshotsFutures.add(
firestore
.collection('widgets')
.where('docID', whereNotIn: ids)
.get(),
);
}
return await Future.wait(snapshotsFutures);
}
For a similar question asked in context of react native, there's this SO answer as well. noSQL definitely has some weird limitations but what can you do :\
Edit
This will not work as some futures will have results that you don't need for the reason their exclude ids are different from others

Related

basic firebase + dialogflow - repeated agent add dialogflow

really appreciate the helps
I have been following this video with this code.
My code looks like this
function angerEmotionCapture(agent) {
const angryTo = agent.parameters.angryDirectedTo;
return admin.database().ref('directedTo').transaction((directedTo)=>{
let target = directedTo;
agent.add(`previous entry was ${target}`);
target = angryTo;
agent.add(`new entry is ${target}`);
return directedTo;
});
}
The purpose of this is to capture a conversation topic and store it in the database.
I'm planning to use it for multiple purposes that's why I don't use context.
This code is only the first step to see if I can capture it properly.
When doing this, the agent response always look like this
previous entry was null
new entry is boss
previous entry was friends
new entry is boss
Here "friends" and "boss" are expected. However, the first repetition is not expected and it always gives null. Despite of that, this correctly update the database
I want to understand why is there a repetition here
Thanks, really appreciate the time

Deleting user account triggers deletion of another nodes

Db structure:
--followers
-followedUser1
-user1
-followedUser2
-user1
-user2
--users
-user1
-followed
-followedUser1
-followedUser2
-user2(followedUser1)
-followed
-followedUser2
-user3(followedUser2)
Everytime user follows(onCreate) & unfollows(onDelete) under followers/{followedUser}/{followerUser} path, it triggers function which increment or derement count and assigning or detaching posts from follower. It's working via fanout method, and there're no problems. Now, worse part comes when some user deletes account completely together with detaching his followers from himself(because his account will be a ghost), i've set trigger onDelete to indicate whenever it'll happen, then iterating through this user's (i.e.user3) followers removes himself from corresponding followers plus his account, it looks like this then:
--followers
-followedUser1
-user1
-followedUser2
-user1
-user2
--users
-user1
-followed
-followedUser1
-user2(followedUser1)
Now, problematic part - when promise returns i'd like to also remove whole follower/followedUser2(because it's a ghost now) path but... there is a trigger which unfortunately executes for every follower under(onDelete). So, is there any chance to remove path above(levelup) deletetion trigger without triggering children itself? Or any other approach would be great, thanks
edit: Dont get me wrong, it'll work but if number of followers under followedUser would be "a lot" server will die after 100... trigger
At this time, there is no way to filter what deletion events will trigger the function, so you are correct, the function will be triggered once for each user the deleted user was following. I recognize this is one of many use cases where such functionality would be useful, so if you get a chance, please fill out a feature request here.
It looks like your multiple-update problem can be fixed with a multi-location updates
In very quickly hacked together and not tested typescript:
export const cleanupFollowers = functions.auth.user().onDelete(event => {
const user = event.data.userId;
const followersNode = admin.database().ref(`followers/${user}`);
const followers = _.keys(await followersNode.once('value'));
// Every follower also has a reverse node for this user. Get the list of keys:
const reverseNodesToDelete = followers.map(follower => `followers/${follower}/${user}`);
// Model this update as a map of deep key -> null to delete all at once
let cleanup = _.keyBy(reverseNodesToDelete, null);
// add one more update: deleting full node for the deleted user.
cleanup[`followers/${user}`] = null;
// do all deletions as one database request:
return admin.database().ref().update(cleanup);
}
Note that this will still fire your counting function, but that should be fine to run in parallel. It probably makes your app simpler to have each invariant captured separately.

Firebase and AngularFire - $add in a array - unexpected behaviour

dayPath = ref.path.toString() + '/' + configId + '/screens/' + screenIndex + '/days/',
// the ref for the days object
daysRef = fbutil.ref(dayPath),
// the sync for the days object
daysSync = fbutil.syncObjectReference(daysRef);
// the collection as a $firebase array
var list = daysSync.$asArray(),
items = [],
number;
console.log(list);
list.$add({dummy: 'Test'});
According with the documentation, when I use $add with $asArray, the $add supposed to do a "push". But instead it's creating a hash key instead a numeric index.
So, the dummy: test has a parent containing a hash key. The expected is a numeric index, I mean : array item.
Can someone give me some help? I just have 1 week of experience in this database.
The result is this one...
screens
...0
.......days
..........0
..........1
..........2
.........-JrT5ZATDELIR3gXAvah
................dummy: test
AngulareFire is built on the Firebase JavaScript SDK. So when AngularFire's documentation says it uses push internally, it is not referring to JavaScript's Array.push, but to Firebase's push operation. And Firebase's push generates its own keys, it does not generate regular array indexes.
The reason for that is best explained in Firebase's documentation on arrays, but essentially boils down to: arrays don't work well in distributed environments, because all clients have to agree on the array.length in order to be able to add a new item.
So $firebaseArray.$add will generated a so-called push ID. They are ordered, like array indexes, but can be generated across clients without risk of conflicts.
I noticed that you're on a somewhat older version of AngularFire. I highly recommend that you follow the "official" quickstart and guide for AngularFire.
I would like to comment but i don't have enough reputation yet so i'm doing it here.
The solution in my eyes is very simple:
Instead of:
list.$add({dummy: 'Test'});
Do:
list[index] = {dummy: 'Test'};

Firebase "Where" like search

Tryng to get a simple result using "Where" style in firebase but get null althe time, anyone can help with that?
http://jsfiddle.net/vQEmt/68/
new Firebase("https://examples-sql-queries.firebaseio.com/messages")
.startAt('Inigo Montoya')
.endAt('Inigo Montoya')
.once('value', show);
function show(snap) {
$('pre').text(JSON.stringify(snap.val(), null, 2));
}
Looking at the applicable records, I see that the .priority is set to the timestamp, not the username.
Thus, you can't startAt/endAt the user's name as you've attempted here. Those are only applicable to the .priority field. These capabilities will be expanding significantly over the next year, as enhancements to the Firebase API continue to roll out.
For now, your best option for arbitrary search of fields is use a search engine. It's wicked-easy to spin one up and have the full power of a search engine at your fingertips, rather than mucking with glacial SQL-esque queries. It looks like you've already stumbled on the appropriate blog posts for that topic.
You can, of course, use an index which lists users by name and stores the keys of all their post ids. And, considering this is a very small data set--less than 100k--could even just grab the whole thing and search it on the client (larger data sets could use endAt/startAt/limit to grab a recent subset of messages):
new Firebase("https://examples-sql-queries.firebaseio.com/messages").once('value', function(snapshot) {
var messages = [];
snapshot.forEach(function(ss) {
if( ss.val().name === "Inigo Montoya" ) {
messages.push(ss.val());
}
});
console.log(messages);
});
Also see: Database-style queries with Firebase

Limit reading children from a location through client side

var commentsRef = new Firebase('https://test.firebaseio.com/comments');
var last10Comments = commentsRef.limit(10);
//Rendering last 10 comments
last10Comments.on('child_added', function (snapshot) {
});
From the client side a user can change the limit number and can render all comments from comments reference.
Is there any way to restrict reading limit to some number at any point of time for a location?
No, there isn't currently a way to put Firebase security rules around that type of limiting of data. Another approach that would work would be to have another section of the tree that contains a denormalized portion of the data that just contains the last 10 comments and nothing more.
Thanks for bringing this up. I've added this to our internal tracker to keep it in mind when we design V2 of our security API.

Resources