Connection attributes in firebase query React - reactjs

I would like to get books from Firebase where author or title is given with React. Sth like that :
fire.firestore().collection('books').where('name OR author', '==', elFromMyState).
get().
then(snapshot => {
snapshot.forEach(doc => {
let element = doc.data();
array.push(element)
})
setBooks(array);
props.setList(array);
})
but don't know how to do it correctly

Unfortunately, firebase provides limited support for logical OR queries.
You can specify multiple value comparisons (logical OR's) for the same field. For example, looking for either Poland OR Austria in a neighboringCountries field.
You cannot specify multiple fields against which you query a single value. For example, looking for the value Nikola Tesla on fields people OR inventors.
The limitations are explained in more detail in the firebase docs: https://firebase.google.com/docs/firestore/query-data/queries#query_limitations
They explain that in order to work around this, you execute two independent queries, then combine the results on the front end for display.

Related

How can I retrieve a really large data from firestore with a lot of collections and subcollections that need to be displayed separately?

I am pretty new to flutter and firestore, but I got a project I need to make. The app I am making has profiles and each profile displays different data. For example an user has x amount of clients and each client has x amount of different type of information. That being said in my mind I have to have users as the base and for each client to make a lot of sub-collections. However as I’m pretty new to this I don’t exactly know how to implement that in the UI and code behind it. If somebody can point me in some direction, that would be great.
Firestore allows you to paginate your data using cursors.
This is an example From The Docs using the web SDK but the principle is the same:
import { collection, query, orderBy, startAfter, limit, getDocs } from "firebase/firestore";
// Query the first page of docs
const first = query(collection(db, "cities"), orderBy("population"), limit(25));
const documentSnapshots = await getDocs(first);
// Get the last visible document
const lastVisible = documentSnapshots.docs[documentSnapshots.docs.length-1];
console.log("last", lastVisible);
// Construct a new query starting at this document,
// get the next 25 cities.
const next = query(collection(db, "cities"),
orderBy("population"),
startAfter(lastVisible),
limit(25));
In terms of sub collections, each doc you retrieve will have a reference property which can be used to create nested subcollection calls instead of writing a function with the fully built path string.

What's the most efficient and scalable way to handle filtering and sorting of a large collection in cloud firestore?

I have a large collection where each document is of a substantial size and therefore cannot be embedded into a single document. For my feature, I need to sort the collection using orderBy and filter the collection using "array-contains" and "==" where the sort and filter parameters are provided by the user. I was wondering if there was an efficient way to do this by cacheing documents that had already been fetched in previous queries. That being said, does firebase do any cacheing itself and does it already optimize what I'm trying to do in this case, or is there any custom cacheing/optimization I can do?
This is what my implementation looks like right now. This works fine for now however it's not very scalable as it creates a new realtime listener each time any of the filter/sort state changes. Is there any way can I improve this to minimize the total number of documents read?
useEffect(() => {
if (!sort || !status || !search) return;
const unsubscribe = firebase.collection("users")
.where("searchTerms", "array-contains", search)
.where("status", "==", status)
.orderBy(sort)
.onSnapshot((snapshot) => {
// update state hook with snapshot data...
});
return () => unsubscribe();
}, [sort, status, search]);
Thank you for any and all help!
Typically, you will want to use a search indexer to enable functionality like this.
The Firebase documentation recommends using Algolia for full-text search. You want to create a cloud function that indexes the data you want to search on, along with the Firestore document ID. On the frontend, you can use the Algolia API to get search results and then fetch the whole document From Firestore when you need to display it.
An alternative to pagination for "big data" and still supporting complex queries can be done by baking the data for the client into a simplified search collection.
This displays only key information that represents a source document by combining the essential data into a dedicated collection of all the results.
Each document can hold up to 1MB of data each and that can equate to roughly 10k-200k entries based on your data size. It does take some time to set up but it has been effective for handling long-lived data within firebase without additional 3rd party solutions.
The key takeaways are as follows:
This is ideal for data that doesn't update too frequently, multiple changes at once can hit the 1 second limit per document.
All search documents contain two properties, a counter to maintain the current entries and an array of strings that represent your essential data.
Each source document needs to maintain a document ID of its entry document for future updates
On update, you find the search index ID, and use arrayUnion and arrayRemove methods, preferably with a transaction and update the source document.
Optionally, you can use the new Bundle Method to bundle this collection with your app
Resources:
https://firebase.google.com/docs/functions/firestore-events#event_triggers
https://firebase.google.com/docs/firestore/manage-data/add-data#update_elements_in_an_array
https://firebase.googleblog.com/2021/04/firestore-supports-data-bundles.html

Firestore queries where() . - returning unexpected results [duplicate]

I'm working on a React Native application and I'm fetching profiles from a firebase collection.
And I want to add a search functionality where when I enter even the first 1 or 2 (or more) alphabets of a username and press the search button.
I should be able to fetch usernames starting with those 1 or 2 alphabets.
I did check Cloud Firestore queries but couldn't find one for my problem.
UPDATED QUESTION:
In the above code, I'm adding the below code as answered by Renaud Tarnec.
let queries = hashes.map(hash => rangeQueryParams(hash))
.map(range => profiles.where('hash', '>=', range.start).where('hash', '<', range.end)
.orderBy('displayName') // displayName is the name of Field here
.startAt(searchString)
.endAt(searchString + '\uf8ff')
.get());
But this doesn't seems to work. I guess it's because range filter and orderBy are on different fields here.
You should use a combination of orderBy(), startAt() and endAt(), see the documentation here: https://firebase.google.com/docs/firestore/query-data/order-limit-data?authuser=0
var searchString = 'Sh' //Example of value
firebase
.firestore()
.collection('yourCollectioName')
.orderBy('username')
.startAt(searchString)
.endAt(searchString + '\uf8ff')
.get()
.then(...)
The character \uf8ff used in the query is after most regular characters in Unicode, therefore the query matches all values that start with searchString.
An approach for full text search recommended by Firebase is to use Algolia https://firebase.google.com/docs/firestore/solutions/search
Using Firestore’s queries with start at and end at with an ordered collection certainly work. If you have a need for full text search within your app elsewhere that requires spelling tolerance etc for example then moving to Algolia is worth considering instead of using compound firestore queries

Firebase/Firecloud & React - How to store profile images to not need 80 different reads to display all the 80 images?

I'm setting up a React and Firecloud app that displays profiles based on a set of constraints. Based on the users choices, +- 10-100 profile snapshots will be displayed, including the profile image/icon.
All advice online seems to point to using the Firebase storage with the image links within my Collections' Documents.
Now in my mind that means every time a user changes the constraints or refreshes (the info changes pretty often so reluctant to rely on local storage) I will need one read to download all of my profile snapshots (stored in one document) and another 1-100 reads to download each image, is this the only way to do this?
The images/icons are small low resolution pics.
Ideally I'd love to store these with each profile within the document, but cannot figure out how as the only data options are string, number, boolean, map, array, null, timestamp, geopoint or reference.
Or have I got this completely wrong and downloading all of the images counts as only one read? (cost is the main concern with firecloud charging per read and write)
Any ideas would be appreciated.
Take a look into this if you haven't already. There is a lot of information that could be useful.
"You can also retrieve multiple documents with one request by querying documents in a collection. For example, you can use where() to query for all of the documents that meet a certain condition, then use get() to retrieve the results :
db.collection("cities").where("capital", "==", true)
.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
});
})
.catch(function(error) {
console.log("Error getting documents: ", error);
});
By default, Cloud Firestore retrieves all documents that satisfy the query in ascending order by document ID, but you can order and limit the data returned."
Paginating data with query cursors also might be of your interest.

Find CouchDB docs missing an arbitrary field

I need a CouchDB view where I can get back all the documents that don't have an arbitrary field. This is easy to do if you know in advance what fields a document might not have. For example, this lets you send view/my_view/?key="foo" to easily retrieve docs without the "foo" field:
function (doc) {
var fields = [ "foo", "bar", "etc" ];
for (var idx in fields) {
if (!doc.hasOwnProperty(fields[idx])) {
emit(fields[idx], 1);
}
}
}
However, you're limited to asking about the three fields set in the view; something like view/my_view/?key="baz" won't get you anything, even if you have many docs missing that field. I need a view where it will--where I don't need to specify possible missing fields in advance. Any thoughts?
This technique is called the Thai massage. Use it to efficiently find documents not in a view if (and only if) the view is keyed on the document id.
function(doc) {
// _view/fields map, showing all fields of all docs
// In principle you could emit e.g. "foo.bar.baz"
// for nested objects. Obviously I do not.
for (var field in doc)
emit(field, doc._id);
}
function(keys, vals, is_rerun) {
// _view/fields reduce; could also be the string "_count"
return re ? sum(vals) : vals.length;
}
To find documents not having that field,
GET /db/_all_docs and remember all the ids
GET /db/_design/ex/_view/fields?reduce=false&key="some_field"
Compare the ids from _all_docs vs the ids from the query.
The ids in _all_docs but not in the view are those missing that field.
It sounds bad to keep the ids in memory, but you don't have to! You can use a merge sort strategy, iterating through both queries simultaneously. You start with the first id of the has list (from the view) and the first id of the full list (from _all_docs).
If full < has, it is missing the field, redo with the next full element
If full = has, it has the field, redo with the next full element
If full > has, redo with the next has element
Depending on your language, that might be difficult. But it is pretty easy in Javascript, for example, or other event-driven programming frameworks.
Without knowing the possible fields in advance, the answer is easy. You must create a new view to find the missing fields. The view will scan every document, one-by-one.
To avoid disturbing your existing views and design documents, you can use a brand new design document. That way, searching for the missing fields will not impact existing views you may be already using.

Resources