Firebase query: return if certain value does not exist

398 views
Skip to first unread message

Tan Tolo

unread,
Jan 20, 2018, 11:49:30 AM1/20/18
to Firebase Google Group
Hello,

I am trying to set up a database for an app similar to Tinder. The difference is that I show users items (not persons so they don't swipe back) just once until they swipe. As the search in Firebase seems quite limited, I thought of the following database structure containing a list of "items" and a list of "users".


Proposed Database structure:
items
- 0
   
- name
   
- description
   
- shown (a list of users who this item has been shown to)
     
- 0: 1 (unique user index)
     
- 1: 5
     
- 2 ...
     
...
- 1
- 2
- 3
...


users
- 0 (index)
   
- name
   
- details
- 1 (index)
...
- 2
...



Each user has a specific index. Once the user has swiped on that item I want the index of the user to be added to the "shown" parameter of that specific item. Then I want to be able to search all items and as return get all items which have not been shown to the specific user. This should all be done on Firebase. I cannot afford to download the whole dataset first and process it on the smartphone.
Any ideas whether that is possible, how it can be done or what other alternatives are out there?

Thanks

Andreas B

unread,
Jan 21, 2018, 8:45:03 AM1/21/18
to Firebase Google Group
Is it necessary for your app to show items in a random order, or for the user to be able to "skip" items?

If not, it might be the simplest solution to just circumvent this problem by storing the "ID of last item shown" for each user, and then finding the item with the smallest ID bigger than that.

Tan Tolo

unread,
Jan 22, 2018, 10:50:32 AM1/22/18
to Firebase Google Group
I thought about that, but as I want to add location based search later as well, I guess this won't be possible

Andreas B

unread,
Jan 22, 2018, 11:18:31 AM1/22/18
to Firebase Google Group
One doesn't necessary exclude the other. It's hard to give a good answer without knowing the full story, though, so if this isn't what you're looking for, you could perhaps give some more details. :)

Tan Tolo

unread,
Jan 22, 2018, 9:26:33 PM1/22/18
to Firebase Google Group
Sure :-)

So basically I am about to create an app which shows houses (which are already loaded into the database) to users within a certain radius (around the user). Users can swipe left or right. Whatever way they swipe they should never see a swipe card of that house again. (If they swipe right, of course, there will be a list of "liked" houses. That is easy though).
My big problem is, that I do not want to download the full database to the phone and then search. The dataset might get too big, if the app is successful (hopefully :-) ) If I just download a part of the database, it just might take too long. So I try to let the database do the majority of the work. I had several ideas, but as the queries of the firebase database is so limited nothing has succeeded. I do not want to start using quite expensive options like Algolia (at least not right away for a query I think should be quite straight forward).

PS.: I haven't really looked into the location feature yet. I am thinking of using GeoFire. But also not sure whether that is usable with large datasets. If I have to download the whole database, it is also of no use for me.

PPS.: Currently I am just starting so I will not have a big database. But I want a scalable solution from the beginning and not have to re-implement everything if the database gets bigger.

Andreas B

unread,
Jan 23, 2018, 5:23:56 AM1/23/18
to Firebase Google Group
Ah, understood! :)

You're right, my initial suggestion would be problematic because you're basically dealing not with one ordered list of items/houses, but with many sub-lists based on the users location. To avoid having to download an item only to find that it already has been seen by the user, you could change the structure around, and store for each user the IDs of items he has already seen:

users
  - ID_user1
    - name
    ...
    - seenItems
      - ID_item23: true
      - ID_item42: true
      - ID_item1701: true
  - ID_user2
    ...
items
  - ID_item0 //probably use auto-generated IDs here via push()
    ...
  - ID_item1
  ...

I haven't used GeoFire myself, yet - but as I understand it, it creates and uses a structure separate from the rest of your DB, and only returns a list of IDs that match a certain location. Your app logic could then be something like this:

1. Detect user location
2. Get a list of IDs of items near the user from Firebase
3. For each ID on that list, check if it exists as a key in seenItems of your user object
  a. if true, skip
  b. if false, get individual item from FIrebase and display it to the user

The "seenItems" map could also double as your list of liked items (true for liked, false for seen but not liked?).

Tan Tolo

unread,
Jan 23, 2018, 5:29:56 PM1/23/18
to Firebase Google Group
Thanks, that is a pretty simple, but great idea. I will try to implement that.

But why do you use a structure like that? With a string concatenated to a number and morover using a boolean value which is anyway always true (guess not seenItems are not even in the list)


    - seenItems
      - ID_item23: true
      - ID_item42: true
      - ID_item1701: true

Would'nt this make more sense?



    - seenItems
      - 23
      - 42
      - 1701



Or is that structure not possible with firebase?

Andreas B

unread,
Jan 24, 2018, 4:59:59 AM1/24/18
to Firebase Google Group
The formatting of the ID string was just meant as a placeholder, not as the exact style I'd use. Sorry, I should have made that more clear. In most cases, especially when dealing with lists where more than one user can add elements to the list, I would use the auto-generated IDs that are created via push(): https://firebase.google.com/docs/database/admin/save-data#section-push

Regarding why the IDs are used as keys instead of values, this is detailed here (especially the "data that scales" section, but the rest of the page might be interesting to you as well): https://firebase.google.com/docs/database/web/structure-data#fanout

The value of the key doesn't matter for the original purpose (finding out whether some element exists), so it is often set to true. That doesn't mean it has to be that way, though. In your application, it might be appropriate to use both true and false, or a numerical value, to store some piece of data on top of "user has seen this item". For example, you could use false to mean "user has seen and ignored some item", and true to mean "user has seen and liked some item".

Tan Tolo

unread,
Jan 27, 2018, 10:59:39 PM1/27/18
to Firebase Google Group
I managed to create the structures in firebase database and storage. I use the push() function to create names for the images. As there might be more than one photo for a house I created one folder per house with several images (up to now only one each). As there seems to be no function to read out the directory of the storage, the structure which is also on database should be useful. It looks like that:


keysHouses


-L3tMd3a-R895v6Eg6mZ     <- name of folder for house #1
:
{-L3tMd3c9dkUQXY28gsl: "i"}    <- name of image from house #1 (maybe more later)

-L3tMkFbfy-fTfJvv-s7          <- name of folder for house #2
:
{-L3tMkFdyS0lC09Z-GkA: "i"}

-L3tMtsjdcwhECI-m0eH
:
{-L3tMtskWhm2W96qXiSX: "i"}

-L3tNmjvg71e1_osdfa9
:
{-L3tNmjww6NaV2m5tXXX: "i"}

I can read and print out the data as follows:

        this.fdb.database.ref().child('keysHouses').once('value').then(function(datasnapshot){
        console.log( datasnapshot.val());
    });

But my problem is that I do not know how to access the single elements and loop through them. I tried to index them like an array, but that didn't work. Once I can access each item, I can directly access the image on the firebase storage.

Any idea how I can access each single item?

Thanks

Andreas B

unread,
Jan 28, 2018, 6:38:57 AM1/28/18
to Firebase Google Group
You can access single items by plugging its key into the database reference. In your example:

this.fdb.database.ref().child('keysHouses').child('-L3tMd3a-R895v6Eg6mZ')...

Where do you get those keys in the first place? In your original example, you were trying to display items based on a nearby search using GeoFire. Basically, GeoFire would return a list of these keys, which you could then use to load and display individual items.

Alternatively, you can access the whole list (warning, might become very large) or a part of it, and then loop over all of the lists elements and handle each one separately. Both looping and ways to access only parts of a list (number of elements, sorting, ...) is described here: https://firebase.google.com/docs/database/android/lists-of-data

Kato Richardson

unread,
Jan 30, 2018, 11:54:00 AM1/30/18
to Firebase Google Group
Andreas is on the right track here. You should look them up directly if you already have the keys.

For posterity, you can also access the specific elements of a downloaded snapshot using datasnapshot.forEach() as documented here.

I hope that helps!

☼, Kato

--
You received this message because you are subscribed to the Google Groups "Firebase Google Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to firebase-talk+unsubscribe@googlegroups.com.
To post to this group, send email to fireba...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/firebase-talk/e0349bc5-5ef8-45a1-a868-c14bba53ec7b%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--

Kato Richardson | Developer Programs Eng | kato...@google.com | 775-235-8398

Reply all
Reply to author
Forward
0 new messages