As I am trying to short my data. So I decided to save userLikeCollection as
questionID = true/false -> where true means like and false means dislike. And if this document not exist it means it is neither like nor disliked...
and there ID comprises of (userID + questionID)
Now I want to query whether this question is like by a user. Is there a way to achieve it.
db.collection("userLikeCollection").where(questionID ).exist()
or
read that document which has property string name questionID
db.collection("userLikeCollection").whereStringProperty(questionID).exist()
where userLikeDocument look like below
(userID + questionID) -> Document Unique ID
- questionID = true/false //where questionID = any unique id for each document.
Edit Question
UserLikeDocument - 1
blahblahQuestionIdOne = true;
UserLikeDocument - 2
blahblahQuestionIdTwo = true;
UserLikeDocument - 3
blahblahQuestionIdThree = true;
UserLikeDocument - 4
blahblahQuestionIdFour = true;
Now I want to query whether this question is like by a user. Is there a way to achieve it.
db.collection("userLikeCollection").where(questionID ).exist()
Yes there is. To solve this, you should use a query and a get() call. In code it looks like this:
Query query = db.collection("userLikeCollection").whereEqualTo("questionID", true);
query.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
if (document.exists()) {
Boolean questionID = document.getBoolean("questionID");
}
}
}
}
});
In this code, the query will help you find all documents in which the questionID property will hold the value of true and the use of document.exists() will help find if that document actually exists.
or read that document which has property string name questionID
db.collection("userLikeCollection").whereStringProperty(questionID).exist()
In this case you should also use a query but instead of passing a boolean value to the whereEqualTo() method, you should pass a literal String:
Query query = db.collection("userLikeCollection").whereEqualTo("questionID", id);
In which the id holds a value of type String which is the actual id of the question that you are looking for.
(userID + questionID) -> Document Unique ID - questionID = true/false //where questionID = any unique id for each document.
This is actually possible in Firestore but only if the name of the properties are different, let's say questionID that holds a boolean value and and a id property that holds a String value. This can be done by chaining two whereEqualTo() call like this:
Query query = db.collection("userLikeCollection")
.whereEqualTo("questionID", true)
.whereEqualTo("id", id);
Edit:
According to your comment:
whereEqualTo("questionID", true) comes only if it is liked but when it is dislike .It will not retrieved
That's correct. To solve your problem, according to the official documentation regarding Query limitations:
Cloud Firestore does not support the following types of queries:
Queries with a != clause. In this case, you should split the query into a greater-than query and a less-than query. For example,
although the query clause where("age", "!=", "30") is not supported,
you can get the same result set by combining two queries, one with
the clause where("age", "<", "30") and one with the clause
where("age", ">", 30).
So with other words, there is no != (not equal to) operator in Firestore. As they say, the option that you have is to split your query into a greater-than and a less-than query and then it will work perfectly fine.
You can also create another query that looks like this:
Query query = db.collection("userLikeCollection").whereEqualTo("questionID", false);
In this case, you get the question that have the questionID property false.
Edit2:
According to your edited question, there is no way in Firestore to filter documents based on a dynamic id. There is also no way to use wildcars. The properties should have the same name.
Related
In my firestore database,there are 12+ documents.I am getting the first 3 documents correctly by calling the below function on button click. But on the secondclick, though the documentReference is passed correctly, its not retrieving any data.The querySnapshot size is coming 0. What could be the problem.
Given below is the declaration
private val db: FirebaseFirestore = FirebaseFirestore.getInstance()
private val colRef: CollectionReference = db.collection("Notebook")
private var lastResult: DocumentReference? = null
private lateinit var query: Query
and below is the onButtonClick code :
private fun loadNoteNew() {
#Suppress("SENSELESS_COMPARISON", "LiftReturnOrAssignment")
if (lastResult == null) {
query = colRef.orderBy("priority")
.limit(3)
} else {
Log.i(TAG, "Start ${lastResult!!.id}")
query = colRef.orderBy("priority")
.startAfter(lastResult)
.limit(3)
}
Log.i(TAG, "before get")
query.get()
.addOnSuccessListener { querySnapshot ->
var data = ""
Log.i(TAG, "querySnapshot Size : ${querySnapshot.size()}")
if (lastResult != null) {
Log.i(TAG, "querySnapshot ID : ${lastResult!!.id}")
}
for (snapshot in querySnapshot) {
val note = snapshot.toObject(Note::class.java)
note.id = snapshot.id
val title = note.title
val desc = note.description
val priority = note.priority
data += "${note.id} \nTitle =$title \nDescription = $desc\nPriority : $priority\n\n"
}
if (querySnapshot.size() > 0) {
data += "---------------\n\n"
textView_loadData.append(data)
lastResult = querySnapshot.documents[querySnapshot.size() - 1].reference
Log.i(TAG, lastResult!!.id)
}
}
}
Given below is the logcat for first click
I/FireStoreExample: before get
I/FireStoreExample: querySnapshot Size : 3
I/FireStoreExample: P9hIw4Ai7w4IHP6H3ew3
and given below is the logcat of second click
I/FireStoreExample: Start P9hIw4Ai7w4IHP6H3ew3
I/FireStoreExample: before get
I/FireStoreExample: querySnapshot Size : 0
I/FireStoreExample: querySnapshot ID : P9hIw4Ai7w4IHP6H3ew3
Please help me find out,where i am getting it wrong.
Thanks
The second query result is empty because of a misunderstanding on the semantics of query pagination using startAt and startAfter methods.
Let's say the Notebook collection contains N documents. When you make the first query you're asking for the first 3 documents ordered by the priority field so the query is returning documents 1..3. Then upon the second click you're expecting the query to return the next 3 results so indeed you're expecting documents 4..6. The keypoint here is that both startAt and startAfter paginate based on the value of the ordered field rather than with the last document retrieved. Overall the semantics of startAt and startAfter are roughly as follows.
orderby(X).startAt(Y) => Return documents whose X field is greater than or equal Y
orderby(X).startAfter(Y) => Return documents whose X field is strictly greater than Y
With that in mind, let's examine what the code is actually doing when you make the second query:
// At the end of the first query...
lastResult = querySnapshot.documents[querySnapshot.size() - 1].reference
// Second query
query = colRef.orderBy("priority")
.startAfter(lastResult)
.limit(3)
In the code above you're asking for the documents whose "priority" field is greater than document reference "P9hIw4Ai7w4IHP6H3ew3" and indeed there are no documents greater than that, therefore the result set is empty. Here is api reference for both.
There is yet another thing to note. Because these methods filter upon the fields value the position of the cursor could be ambiguous. For instance, if you have 4 documents with priority 3 and already retrieved the leading three if you set startAfter(3) you'll be missing a document. Similarly, if startAt(3) were to be made you'll get back the same three documents. This is also pointed out in the documentation. All in all you have a couple of options to make this work as intended:
Add another orderby in another field so that documents are uniquely identified by the combination so to prevent any cursor ambiguity and be able to use startAfter with guarantees. Next snippet build upon the doc samples and your code.
// first query
query = colRef.orderBy("priority")
.orderBy("AnotherField")
.limit(3)
// Save last document
lastResult = querySnapshot.documents[querySnapshot.size() - 1]
// Second and next queries
query = colRef.orderBy("priority")
.orderBy("AnotherField")
.startAfter(lastResult)
.limit(3)
Lastly remember that it might be simpler to just query all the documents if they're not many and delay optimizations until they become a performance issue.
I am trying to create a query which only selects documents whose reference is equal to a given reference, using Java for Android development. A document which it would match contains the reference for the path "/users/someUser". I am creating the reference like so:
DocumentReference ref = mDatabase.document("users/someUser");
I have also tried:
DocumentReference ref = mDatabase.document("/users/someUser");
Then the query:
Query query = mDatabase.collection("myCollection").whereEqualTo("refField", ref).limit(10);
However, when I run the query and check the task.isSuccessful() in the onComplete method, it's not passing, i.e. it didn't work, whereas when I remove the .whereEqualTo(), it passes and the task's result isn't empty. How can I properly use .whereEqualTo() to check for all documents containing a specific reference?
An example of a document that should match my query would be:
/myCollection/GDpojS5koac2C7YlIqxS which contains the field:
refField: /users/someUser (value of type reference)
And an example of a document that should not match my query would be:
/myCollection/J5ZcVAMYU1nI5XZmh6Bv which contains the field:
refField: /users/wrongUser (value of type reference)
I think you need to add a get() method to run the query and add an onCompletionListener.
Something like this should work:
mDatabase.collection("myCollection")
.whereEqualTo("refField", ref)
.limit(10)
.get()
.addOnCompleteListener({task ->
if(task.isSuccessful){
val result = task.result
})
The above example is in kotlin, but i guess in java it is something similar
You need not to worry about the documents, if you create a query based on your fields then all the documents will be returned in the "QuerySnapshot" object,
for eg,
CollectionReference collectionReference = db.collection(FIRESTORE_USERS);
DocumentReference documentReference = collectionReference.document(userID);
CollectionReference notificationCollection = documentReference.collection(FIRESTORE_NOTIFICATIONS);
notificationCollection.whereEqualTo(USER_TYPE, userType)
.whereGreaterThanOrEqualTo(SEND_AT, calendar.getTime())
.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot documentSnapshots) {
List<DocumentSnapshot> snapshotsList = documentSnapshots.getDocuments();
ArrayList<NotificationCollections> notificationCollectionsArrayList = new ArrayList<>();
for (DocumentSnapshot snapshot : snapshotsList) {
// each document having that particular field based on query
}
}});
in the above example I am fetching all those documents which match a particular user id and also having time greater than or equal to supplied time (time will not be used in your case)
I hope this helps...
Happy coding :)
I'm trying to list documents that matches field String value from ArrayList.
Simply:
I have ArrayList with tags stored at runtime
and documents with field tag
and I want to query documents that matches tag with one of tags stored in ArrayList. Is this possible with official query or does I have to download all documents and filter it client-side? Thanks for any answers.
Also, this is my method generating query:
public static Query getQueryForFollowed(DocumentSnapshot snapshots) {
if (snapshots == null || !snapshots.exists()) {
return FirebaseFirestore.getInstance().collection("posts").whereEqualTo("null", "null"); // return query that will get nothing
}
ArrayList<String> f = processFollowedTags(snapshots);
Query query = FirebaseFirestore.getInstance()
.collection("posts")
.whereEqualTo("tag", f.get(0));
for (int i = 1; i < f.size(); i++) {
query = query.whereEqualTo("tag", f.get(i));
}
return query;
}
I have debugged code and query has contained requested conditions, but query didn't found any document matching it.
Try This
Query query = FirebaseFirestore.getInstance()
.collection("posts")
.whereEqualTo("tag", f.get(0)).orderBy("tag", Query.Direction.ASCENDING);;
After some more search on Google I have found that querying field to multiple values is not available.
According to:
https://stackoverflow.com/a/46633294/8428193
https://github.com/firebase/firebase-js-sdk/issues/321
Below code snippet may help you.
fun arrayContainsQueries() {
// [START array_contains_filter]
val citiesRef = db.collection("cities")
citiesRef.whereArrayContains("regions", "west_coast")
// [END array_contains_filter]
}
ref : git
As of Nov 2019 this is now possible to do with the in query.
With the in query, you can query a specific field for multiple values
(up to 10) in a single query. You do this by passing a list containing
all the values you want to search for, and Cloud Firestore will match
any document whose field equals one of those values.
it would look like this:
Query query = FirebaseFirestore.getInstance()
.collection("posts")
.whereIn("tag", f);
Firestore database image
Hello, I just tried to use Firestore. I had some problem when getting document id.
The question is, I want to get a document id (red box) which has value (blue box) in it.
I use the following query:
collection("mychannel").whereEqualTo("74wRU4xHrcV9oWAXEkKeRNp41c53")
But did not give results.
Thanks!
As in the official documentation:
Although Cloud Firestore can store arrays, it does not support querying array members or updating single array elements.
So there is no way in which you can use the following query:
collection("mychannel").whereEqualTo("74wRU4xHrcV9oWAXEkKeRNp41c53")
If you only want to get the entire userId array you need to iterate over a Map like this:
collection("mychannel").document("1fReXb8pgQvJzFdzpkSy").get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
Map<String, Object> map = document.getData();
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (entry.getKey().equals("userId")) {
Log.d("TAG", entry.getValue().toString());
}
}
}
}
}
});
But note, even if userId object is stored in the database as an array, entry.getValue() returns an ArrayList, not an array.
So the output will be:
[74wRU4xHrcV9oWAXEkKeRNp41c53]
A better approach will be if you consider this alternative database structure, where each user id is the key in a map and all values are true:
userId: {
"74wRU4xHrcV9oWAXEkKeRNp41c53": true,
"AdwF...": true,
"YsHs...": true
}
This question is answered here: Firestore: Query by item in array of document
In summary, don't use arrays to store data in Firestore as the query you are trying to do is not available yet (remember it is still in beta). You should use a Map instead.
I would like to know what is the best practice to save users preferences in my firestore database.
I would try to explain with an example...
Case 1
I have this kind of Document in my "users" Collection (the name is random generated by Firebase) with 3 fields :
user_uid : String
nickname : String
android_lover : boolean
In my Android project, when I want to search the Document of the user "DFDDE45554SDC", I search where user_uid = "DFDDE45554SDC".
Case 2
I have this kind of Document in my "users" Collection (the name is created with the UID of the user) with 2 fields :
nickname : String
android_lover : boolean
In my Android project, when I want to search the Document of the user "DFDDE45554SDC", I just search the Document "DFDDE45554SDC".
I specify : I don't want duplicate users.
So, what is the best practice (security, optimisation,...) ? Why ?
I would suggest that Case 2 is more effective, for a few reasons:
We already know the user's ID, so don't need to use a different ID here.
Using usersCollection.document(userId) is simple to construct and is a direct DocumentReference, rather than a Query, therefore:
A DocumentReference can be stored in the Firestore database, whereas a Query cannot.
A DocumentReference would likely scale better than instructing the Firestore database to perform a filter query using whereEqualTo("user_uid", userId) (although with indexing, the performance difference is likely negligible at this point).
A Query will always return a collection of results (even if there is only 1), rather than the exact document.
There isn't currently a need for a different randomly-generated ID for each document within the users collection because the user ID is already unique.
You only need 1 document for each user, so this is a sure-fire way to ensure there won't be any duplicates.
The only real incentive I can think of to use Case 1 would be to standardise your document naming scheme with other collections in your database, but this doesn't really matter so much with Firestore.
For a quick example of the two in Android:
Case 1
db.collection("users")
.whereEqualTo("user_uid", "DFDDE45554SDC")
.limit(1)
.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (DocumentSnapshot document : task.getResult()) {
// Even with limit(1), we still receive a collection
// so iterate this to obtain the desired document
}
}
}
});
Case 2
db.collection("users")
.document("DFDDE45554SDC")
.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful() && task.getResult() != null) {
// We have access to the single desired document directly
DocumentSnapshot document = task.getResult();
}
}
});