Query firestore based on array contains and orderby - java

I am storing some data in firestore and it looks as follows:
What I have is a collection called Items, inside of it I have many documents that each has its own ID and inside each document there are multiple fields.
One of the fields is UserID which shows which user holds the item.
Now, I had like to query firestore in a way that will return me all of the items where their popularity is greater than 3.5 and when my own ID is not one of the ID's shown in the UserID array.
Is there any option to do so?
Currently im using the following:
db.collection( "Items" ).orderBy( "Popularity", Query.Direction.DESCENDING ).whereGreaterThan( "Popularity", "3.5" ).limit( 50).get()
But it also returns results where my ID appears in the array.

Firestore does not offer any queries that check for the non-existence of data in the document. Firestore indexes only work based on the presence of data - you can't efficiently index things that don't exist. So, the part of your requirements that says "when my own ID is not one of the ID's shown in the UserID array" is not possible. You can instead write code in the client app to filter out the documents where the current user's UID is missing.

Related

Ecommerce search like query using Firebase Firestore

Below is the Category collection DB structure:
and here is the PRODUCT collection DB structure:
I want to create a search query like similar to AMAZOM were you can just type in "shoes" and get a list of saying men shoes, women shoes, sports shoes etc. or just type in the name of the product and fetch the product.
As per my knowledge of Firestore, I have written a search query were it fetches all the data similar to the inputed letter. For example let's say I type in "S it retrieves the list of product starting with "S", below image is the result of it:
But that is not what I wanted and I have little knowledge of how to construct that kind search query that I want. How can I get my result using Firestore? I can twitch the current DB structure if I can achieve what I want but I don't want to destroy the whole structure too.
You can achieve this by using orderby(), startAt() & endAt() query on Product name.
ref.collection("Product/").orderBy("Product").startAt(text).endAt(text +
'\uf8ff').get();
this will return QuerySnapshot of products where name contains that text.

How to sort Firebase data according to Time Created?

Hello Everyone, I have loaded some data from Firebase Realtime Database in the App using Query.
This is the Simple code :
Query query = databaseReference.orderByChild("name").startAt(data).endAt(data + "\uf8ff");
options = new FirebaseRecyclerOptions.Builder<HomeModel>().setQuery(query, HomeModel.class).build();
My database structure is simple ;
M38dJdquf4jE(the random key)
name : "John Doe"
Image : "image link"
Because of orderByChild("name"), whenever a new Data is inserted in the Database is not according to the Time the data is inserted. Instead, it inserts the data in the recyclerView in a Random Order.
How can I fix it, so that the Last data inserted is always at the last in the recyclerView and not inserted in a random manner.
Because of orderByChild("name"), whenever a new Data is inserted in the Database is not according to the Time
When you are using:
.orderByChild("name")
It means that the results that are coming from the database will be ordered according to the values that exist in the "name" property, and not according to a "time".
Instead, it inserts the data in the RecyclerView in a Random Order.
As said above, that's definitely not a random order. If you need to get the results according to the "time" of the addition, then you have two options.
If, for example, the "M38dJdquf4jE" is a pushed key that is provided by the "push()" method, you can use Query's orderBykey() method, which:
Creates a query in which child nodes are ordered by their keys.
You'll get the results ordered because the pushed keys contain a time component.
If those keys are not provided by the "push()" method, to have an order according to a time component, then you should add a property of type "timestamp", as explained in my answer from the following post:
How to save the current date/time when I add new value to Firebase Realtime Database
Once you have that new property in place, you can use:
.orderByChild("timestamp")
To have the results ordered ascending. If you need however a descending order, then you can use my answer from the following post:
How to arrange firebase database data in ascending or descending order?

OR clause in firebase java android

Does anyone know how I can do a common "OR" like in a where clause, in firebase?
I need to do that in the query, because I am sending the query to an adapter. So, i mean, I cannot add a listener and check one value and then another. I need to have the complete query pointing to that result in my query.
What I have is something like:
chat1:
user1Id: "1"
user2Id: "2"
bothUsers: "1_2"
chat2:
user1Id: "2"
user2Id: "4"
bothUsers: "2_4"
I need to get all the chats of the user whose id is "2". I am trying to do a query like:
userLogged = 2;
Query queryRef = firebase.orderByChild("user2Id").equalTo(userLogged);
However it will only get the chats when the user 2 is in the user2Id position. But in the example above, when the user 2 is in the user1Id (chat2) it won't get. And I need to get it. How can I do this?
In a NoSQL database you will often have to model your data for the way your app needs it (see this article for a good explanation on NoSQL Data Modeling). So if you need a list of all chats for a specific user, store that list:
/userChats
user1Id
chat1: true
user2Id
chat1: true
chat2: true
user4Id
chat2: true
This process is called denormalizing and it is covered in the article I linked above, in the Firebase documentation on structuring data and in this blog post.

ListViews and large lists recovered from a remote service

I need to implement a web service for a feed of videos and consume it with an Android client.
By the way my implementation was a method getVideos(offset,quantity) with a MySQL table that returns the result of the query SELECT * FROM videos ORDER BY id DESC LIMIT offset,quantity where the id is an auto-incremental value.
But, since it is a very active feed I've detected the following erroneous case:
The database have the videos 1,2,3...10.
The Android client request the videos offset=0 , quantity=5 so the items 10,9,8,7,6 are returned. The user start to play some videos and in the meanwhile 5 new videos are published, so now the table contains the items 1,2,3...15 now. Then the user continues scrolling and, when the user reach the end of the list, the client attempts to request the next bundle: offset=5, quantity=5, but the same items are returned, appearing duplicates (or adding nothing) into the ListView.
What if the best approach for this problem?
If you don't want data to repeat then don't use OFFSET, use a where clause on id instead.
Check what's the last id you were given and then run a query like:
SELECT * FROM videos WHERE id<:last_id ORDER BY id DESC LIMIT 0,:quantity
Not only this guarantees the results will not repeat but also it should actually be faster since the db won't have to calculate all the offset rows.
UPDATE
How about getting a maximum value of id column when you make the first query and then adding to WHERE that all the results have to be lower or equal to that original value? That way you won't ever get duplicates unless you update some position. Better yet if you add a modification time column to your rows and use time of the first query. That way you won't show edited rows but at least they won't break the order.
SELECT *
FROM videos
WHERE mtime < :original_query_time
ORDER BY id DESC
LIMIT :offset, :quantitiy;

Google Search API: Loading all datastore data into document builder for full text search on all records

I have been following the tutorial regarding the Google Search API at https://developers.google.com/appengine/docs/java/search/overview. The information I have found is very clear on how to build the document and load it into an index. What I am not sure of is how to load the datastore data into the document.
What am trying to achieve is a simple %LIKE% query on a few fields. For example, I am working on a music library. If the user types in "glory", then I would like to use the Search API to return all entities with "glory" somewhere in the title. I have implemented the "starts with" work around by adding the search text to "\uFFFD", however, I find this insufficient. My users will be very novice, and it would also be helpful if they didn't have to pick a field as in a traditional search. So full text search seems the solution.
Here are my questions:
Should each record in my datastore be a document? Or all the records into one document? I have a pretty well fixed datastore size of only 1000 records. Could anyone provide an example of the correct method?
I would like to return the entire datastore entity (it's only 8 fields) as an Iterable of the type of my entity. Do we specify each field we need to return? The example just says:
for (ScoredDocument scoredDocument : results) {
// process scoredDocument
}
Does anybody have an example of what comes out of the stored document? Is it exactly what we put in or must you identify each field again? Or an example of processes a ScoredDocument returning a datastore entities?
If anybody could help fill in these blanks for me, I would appreciate it.
Thank you for looking at this with me.
What am trying to achieve is a simple %LIKE% query on a few fields
In order to achieve this you need to "tokenize" your records name, GAE provides FULL TEXT SEARCH so in order for you to get partial matches you need to add partial matches for every record so:
If your record's name is "Glory" you should add the tokens for "G","Gl","Glo","Glor","y","ry","ory","lory"
Here's a very basic implementation I use to provide partial search results (only for "starts with" not implementing "end with")
public void addField(String name, String value, boolean tokenize) {
addField(Field.newBuilder().setName(name).setText(value));
if (tokenize) {
for (int i = startTokenIndex ; i < value.length() ;i++) {
addField(Field.newBuilder().setName("token" + (lastTokenIndex++))
.setText(value.substring(0, i)));
}
}
}
Should each record in my datastore be a document?
Yes. you could even match the document ID with the entity's datastore ID for quick matching. (or you can just add it as a separate field)
I would like to return the entire datastore entity (it's only 8
fields) as an Iterable of the type of my entity. Do we specify each
field we need to return?
You need to store your entity's ID in your document, that way when your search returns a set of documents you just retrieve all entities with those IDs.
Does anybody have an example of what comes out of the stored document?
Is it exactly what we put in or must you identify each field again? Or
an example of processes a ScoredDocument returning a datastore
entities?
Documents return all fields you stored in them, plus a lot of data like scoring, id, etc. The "processing" in your case would consist of getting the entity id form the Document.
If you are certain your records wont grow above 1000 you could virtually store everything within your search index. Just bear in mind the index is not designed for that and will face some serious limitations when scaling, which the datastore obviously doesn't.

Categories