invertase / react-native-firebase

🔥 A well-tested feature-rich modular Firebase implementation for React Native. Supports both iOS & Android platforms for all Firebase services.
https://rnfirebase.io
Other
11.72k stars 2.22k forks source link

🔥 Error "firestore/failed-precondition" when filtering a collection group query in FireStore #3737

Closed appukuttan-shailesh closed 4 years ago

appukuttan-shailesh commented 4 years ago

Issue

I have a database structure as follows (simplified for purposes of this question):

Collection: item_A
    -> Document: params = {someParameter: "value"}
    -> Document: user_01
        -> Sub-collection: orders_item_A
            -> Document: order_AA = {type: "A1", address: {pincode: "000000", city:"Paris"}
            -> Document: order_AB = {type: "A2", address: {pincode: "111111", city:"London"}
            ...
    -> Document: user_02
        -> Sub-collection: orders_item_A
            -> Document: order_AC = {type: "A1", address: {pincode: "222222", city:"Berlin"}
            -> Document: order_AD = {type: "A1", address: {pincode: "333333", city:"Paris"}
            ...

I am using a collection group query to retrieve all the orders under "item_A" (across all users). I am able to get this to work via:

let orders = [];
await firestore()
    .collectionGroup("orders_item_A")
    .get()
    .then(function (querySnapshot) {
        querySnapshot.forEach(function (doc) {
            console.log(doc.id, ' => ', doc.data());
            orders.push(doc.data());
        });
    })

But now I need to refine the above to be able to filter for orders from specific cities (e.g. Paris). So I tried adding a 'where' clause as follows:

let orders = [];
await firestore()
    .collectionGroup("orders_item_A")
    .where("address.city", "==", "Paris")
    .get()
    .then(function (querySnapshot) {
        querySnapshot.forEach(function (doc) {
            console.log(doc.id, ' => ', doc.data());
            orders.push(doc.data());
        });
    })

But this fails, and I get the following message:

[Error: [firestore/failed-precondition] Operation was rejected because the system is not in a state required for the operation's execution. Ensure your query has been indexed via the Firebase console.]

I have already set up a composite index on my FireStore database with the following details:

Collection ID = orders_item_A
Fields indexed = address.city Ascending type Ascending
Status = Enabled

I am not sure what I am doing incorrectly. I wondered if the problem is with using an object property within the 'where' clause (which should not be a problem I believe). So I also tested with a simpler query such as:

.where("type", "==", "A1")

But this too failed. What am I doing wrong?

I tried to find the "link" to create the composite index directly via error message but I just cannot locate this in my logs.

I tried the following (based on discussion here):

But in both these places I simply find the same error message I mentioned earlier. I can find nothing with the tag RNFSCollectionReference or anything else that helps.

On doing adb logcat -s RNFSCollectionReference I simply get (as mentioned by @tiendn here):

image


Project Files

Javascript

Click To Expand

#### `package.json`: "react": "16.11.0", "react-native": "0.62.2", "@react-native-firebase/app": "^7.0.1", "@react-native-firebase/auth": "^6.7.1", "@react-native-firebase/firestore": "^7.0.1", ```json # N/A ``` #### `firebase.json` for react-native-firebase v6: ```json # N/A ```

iOS

Click To Expand

#### `ios/Podfile`: - [x] I'm not using Pods - [ ] I'm using Pods and my Podfile looks like: ```ruby # N/A ``` #### `AppDelegate.m`: ```objc // N/A ```


Android

Click To Expand

#### Have you converted to AndroidX? - [ ] my application is an AndroidX application? - [ ] I am using `android/gradle.settings` `jetifier=true` for Android compatibility? - [x] I am using the NPM package `jetifier` for react-native compatibility? #### `android/build.gradle`: ```groovy // N/A ``` #### `android/app/build.gradle`: ```groovy // N/A ``` #### `android/settings.gradle`: ```groovy // N/A ``` #### `MainApplication.java`: ```java // N/A ``` #### `AndroidManifest.xml`: ```xml ```


Environment

Click To Expand

**`react-native info` output:** ``` OUTPUT GOES HERE ``` - **Platform that you're experiencing the issue on**: - [ ] iOS - [x] Android - [ ] **iOS** but have not tested behavior on Android - [x] **Android** but have not tested behavior on iOS - [ ] Both - **`react-native-firebase` version you're using that has this issue:** "@react-native-firebase/app": "^7.0.1", "@react-native-firebase/auth": "^6.7.1", "@react-native-firebase/firestore": "^7.0.1", - **`Firebase` module(s) you're using that has the issue:** - `e.g. Instance ID` - **Are you using `TypeScript`?** - `N`


appukuttan-shailesh commented 4 years ago

I figured out the problem. My mistake was apparently in having made a "Composite" index (as I mentioned in the question) as it was the opening page on clicking "Indexes". I should have made a "Single field" index:

image

After deleting my existing composite index, I made a new "single field" index with details as:

image

Click 'Next' and on the next screen, "enable" one of the options under 'Collection group scope' (I chose Ascending):

image

It is important to do the above, or else the query will not work. And then everything worked as expected, with my original code:

let orders = []; await firestore() .collectionGroup("orders_item_A") .where("address.city", "==", "Paris") .get() .then(function (querySnapshot) { querySnapshot.forEach(function (doc) { console.log(doc.id, ' => ', doc.data()); orders.push(doc.data()); }); })

Note: If you happen to delete a particular "index", it takes some time (upto 10 minutes on some occasions) to be ready. During this period you will get an error if trying to create an index for the same entry. So wait, and try again after some time.

CrossBread commented 2 years ago

@appukuttan-shailesh thanks for documenting this, I was questioning my sanity. I trusted the "Just run the query and click the URL if one pops up" advice rather than creating my Collection Group Queries up front weeks ago. Now I've burned half a day finally realizing that the reason the query was throwing an error was the missing index, but like you, no URL was in the message, just the error.

chavdardonchev commented 2 years ago

@appukuttan-shailesh thanks for documenting this, I was questioning my sanity. I trusted the "Just run the query and click the URL if one pops up" advice rather than creating my Collection Group Queries up front weeks ago. Now I've burned half a day finally realizing that the reason the query was throwing an error was the missing index, but like you, no URL was in the message, just the error.

If you put your query in a Try/Catch you will not get the link. Just simple "failed-precondition". I removed the try/catch and the exception thrown together with the link.

lipob commented 2 years ago

The onSnapshotfunction accepts three arguments. The third argument is an onErrorcallback.

You can get the link to create the index by using the third argument to print the error in the console.

An example:

const unsusbcribe = onSnapshot(query, (querySnapshot) => {
      const entities: Entity[] = []

      querySnapshot.forEach(doc => {
        entities.push(doc.data() as Entity);
      });

      setEntities(entities);
    }, (error) => console.error(error));
kitfit-dave commented 1 year ago

I also don't get the link, either by catching the exception or just letting it be unhandled. It was only experience with that type of error that led me to realise that I needed to add an index.

Where is the normally helpful error message with he create index link in it for this case?

ohld commented 9 months ago

I just had the same issue.

What I've actually tried to do:

What the issue actually mean:

Note that if you may filter by different fields (sets of fields) you need to make sure you have indexes for all of them.

mikehardy commented 9 months ago

If you run it the app with the query that is missing an index on an android device or emulator and are running adb logcat, do you see a message with an URL to create the index? Just curious - I know if you do the query in the firebase-js-sdk it pops up in console.log at least

ohld commented 9 months ago

@mikehardy Actually I received an URL to create an index... but only a composite one. I got no links regards adding "single field" indexes, just a FAILED_PRECONDITION 9 error. I'm using firebase cloud functions, this error appeared only on deployed functions. If I run them locally I have no issues.

mikehardy commented 9 months ago

Interesting - I've seen the same with cloud functions - that's something you might file upstream with the firebase official site as cloud functions aren't under our control at all and if there is a missing index it would be good to know, composite or single index, yes. The firebase-tools local emulator never appears to care about indexes so I don't find out about them myself until I deploy to my dev or qa backends

50an6xy06r6n commented 7 months ago

We have also started to run into an issue where we get a FAILED_PRECONDITION error with no further details in the Node Admin SDK

TomTasche commented 1 month ago

We have also started to run into an issue where we get a FAILED_PRECONDITION error with no further details in the Node Admin SDK

That's a known issue, follow this thread for updates: https://github.com/googleapis/nodejs-firestore/issues/1866

(Adding this here because it's one of the top Google search results, so more people are likely to come across this thread)