How make complex query with FirestoreRecyclerAdapter? - java

I'm developing an Android app and I need to make a complex query on my Firestore database and create a FirestoreRecyclerAdapter. To create the adpater I need a FirestoreRecyclerOptions object that take in input the whole query. Reading the documentation, I can't use in my query the methods whereGreaterThan, whereLessThan, oderBy, etc, on different parameters. For example, how can I get users from db who have age greater than/less than AND who have weight greater than/less than?
For example the document's structure in my firestore database is:
Users --->UserID--->userName(String)
--->userAge(number)
--->userHeight(number)
--->userWeight(number)
FirebaseFirestore db;
db = FirebaseFirestore.getInstance();
RecyclerView recyclerView;
recyclerView = (RecyclerView)findViewById(R.id.recyclerViewID);
.
.
.
Query query = db.collection("Users").//the complex query that i need
FirestoreRecyclerOptions<User> options = new FirestoreRecyclerOptions.Builder<User>()
.setQuery(query, User.class)
.build();
adapter = new UsersAdapter(options, this);//get in input options and the context
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
Edit: A possible solution in my last comment to answer 1

There are some query limitations when it comes to Firestore:
Query limitations
Cloud Firestore does not support the following types of queries:
Queries with range filters on different fields, as described in the previous section.
So you cannot query your database on range filters using whereGreaterThan() and whereLessThan() methods on different properties and pass it to an adapter.
Edit:
A possible solution for this issue would be to filter your records client side twice. First query the database using the first property and second using the second property. Unfortunately you cannot achieve this in a single go.
Edit2:
The solution would be to query the database using the first query, get the corresponding elements, query the database again using the second query and get the corresponding elements and then merge the results client side. Now the elements from the database were filtered twice. Pass a list of that elements to an adapter and that's it. Note, when using this solution you cannot use the Firebase-UI library anymore but this how you can get your items filtered twice.

Related

Alternative ways of using dynamic values in creating composite index for queries in Firestore DB

I am having a hard time thinking about what static value to put in a query for my online voting application. What I want to happen is that every voter can see list of candidates on their screens but later removed from the list when they already voted the selected candidate. With this concept in mind, I have constructed a query like this:
FirebaseUser user = mAuth.getCurrentUser();
String voterID = user.getUid();
Query query = notebookRef.orderBy("posOrder").orderBy("candidateFullName").whereEqualTo("Voters." + voterID, false);
FirestoreRecyclerOptions<VoterCandidateItem> options = new FirestoreRecyclerOptions.Builder<VoterCandidateItem>()
.setQuery(query, VoterCandidateItem.class)
.build();
adapter = new VoterCandidateAdapter(options);
recyclerView = findViewById(R.id.candidateList);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
recyclerView.setItemAnimator(null);
Now, using the query, the logcat recommends creating a composite index for this query to make it work, and this is what the Firebase Console showed me:
The problem in creating this composite index is that it will only display the list for the current user who has the UID of TNn7J...J33, and this is not allowed in creating a working composite index.
Here is the screenshot of my Firestore DB to get some of my idea for the application:
If there is a way or a workaround on this problem I am facing, please do drop your answers. Thank you.

I added a map as a field in my Firestore DB document with a sub field inside, and I am facing issues to my RecyclerView

I am facing issues with my RecyclerView in displaying my app's candidates when I include the added map field in my query. I also have created an index for this query but still, nothing comes out of my RecyclerView.
This is the UPDATED query code. If there is something wrong with the coding, please do notify me.
FirebaseUser user = mAuth.getCurrentUser();
String voterID = user.getUid();
Query query = notebookRef.whereEqualTo("registrationStatus", "Accepted").whereEqualTo( voterID, false).orderBy("candidateFullName", Query.Direction.ASCENDING);
FirestoreRecyclerOptions<VoterCandidateItem> options = new FirestoreRecyclerOptions.Builder<VoterCandidateItem>()
.setQuery(query, VoterCandidateItem.class)
.build();
adapter = new VoterCandidateAdapter(options);
recyclerView = findViewById(R.id.candidateList);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
recyclerView.setItemAnimator(null);
This is the UPDATED screenshot of Firestore DB:
And this is the UPDATED screenshot of the index:
The following query:
Query query = notebookRef
.whereEqualTo("registrationStatus", "Accepted")
.whereEqualTo(voterID, false)
.orderBy("candidateFullName", Query.Direction.ASCENDING);
Requires an index, otherwise, it will not work. If you are using Android Studio, you'll find in the logcat an error message that looks like this:
FAILED_PRECONDITION: The query requires an index. You can create it here: https://console.firebase.google.com/v1/r/project/...
The required query can be created either directly in the Firebase Console, or by clicking on that link. If the click doesn't work, simply copy and paste the URL into a web browser and your index will be created automatically for you.
If you choose the console, remember to set:
Collection: Candidates
Fields: registrationStatus ascending and voterID: ascending

How to arrange data from Firestore in ListView by biggest int values

I had a Firestore collection named "Users" and Documents of user names , and an integer field of every username , now i wanna compare all those fields and put the biggest one on the top of the list and so on till 10 items , thanks .
Assuming that you have the users of your app added as documents within the Users collection and the name of the property is score, to achieve this, please use the following code:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
Query query = rootRef.collection("Users")
.orderBy("score", Query.Direction.DESCENDING)
.limit(10);

Using Firebase to retrieve entire object based on 1 attribute?

I have the following Firebase database structure, and I would like to retrieve the entire object that has a host of "Mike 22".
This is not in a ValueEventListener event, so I don't have access to the DataSnapshot. How can I retrieve the object based on that query? I want to do it in an SQL style where you'd type SELECT object FROM objects WHERE host = "Mike 22". Is there any way to do this?
Currently I have the reference to the database as seen below:
mDatabase = FirebaseDatabase.getInstance().getReference().child("objects");
This is covered in Sorting and filtering data in the Android Guide. Use orderByChild and equalTo:
mDatabase = FirebaseDatabase.getInstance().getReference().child("objects").orderByChild("host").equalTo("Mike 22");

Order by another table with Liferay's DynamicQuery

I have a problem while I'm making a Dynamic Query in Liferay 6. I'm trying to make a query to order JournalArticles based on their view count. The view count is specified in another table (AssetEntry).
I'm stuck with this:
DynamicQuery query = DynamicQueryFactoryUtil.forClass(
JournalArticle.class, "articleParent", PortalClassLoaderUtil.getClassLoader());
//adding criterions
query.add(...);
DynamicQuery dq0 = DynamicQueryFactoryUtil.forClass(AssetEntry.class, "asset",
PortalClassLoaderUtil.getClassLoader())
.setProjection(ProjectionFactoryUtil.property("asset.classPK"))
.add(PropertyFactoryUtil.forName("asset.companyId")
.eqProperty("articleParent.companyId"))
.add(PropertyFactoryUtil.forName("asset.groupId")
.eqProperty("articleParent.groupId"));
query.add(PropertyFactoryUtil.forName("articleParent.resourcePrimKey").in(dq0))
.addOrder(OrderFactoryUtil.desc("asset.viewCount"));
With this I get an error message saying: could not resolve property: asset of: com.liferay.portlet.journal.model.impl.JournalArticleImpl.
If I remove the addOrder-call, this error disappears. How should I add the order statement so the main query is aware of asset.viewCount?
AssetEntryQuery assetEntryQuery = new AssetEntryQuery();
assetEntryQuery.setClassName(JournalArticle.class.getName());
assetEntryQuery.setXXX //adding criterions
assetEntryQuery.setOrderByCol1("viewCount");
List<AssetEntry> assetEntries = AssetEntryServiceUtil.getEntries(assetEntryQuery);
I am afraid that there is no direct way to do this with the DynamicQuery API.
I think you would need to use Service builder Finders i.e. Custom Query with Service builder.
You can't use dynamic query because there is no direct reference from JournalArticle entity to AssetEntry entity.
One possibility is to retrieve ordered ids of articles from the AssetEntry table (basically you dq0), then do another query and do the sorting programatically (you have the ids ordered).
Finally I think that this line
query.add(PropertyFactoryUtil.forName("articleParent.resourcePrimKey").in(dq0))
doesn't do what you think it does. resoucePrimKey is reference to resource table for permissions. You need to use id column.

Categories

Resources