14

I'm looking to get data where two fields equal what I'm passing in.

Here's an example of my code:

this.refApp
  .orderByChild('userUid')
  .startAt(uid).endAt(uid)
  .orderByChild('jobId')
  .startAt(jobId).endAt(jobId)
  .on('value', (snap) => {
     //This currently doesn't get returned.
  });   

In the above example I don't get any compiler errors and code seems fine. However, I hard coded the data so that it would return an object where uid and jobid are equal to.

I can get this to work for one orderByChild but when I do two like above it doesn't seem to do anything.

AngularM
  • 15,982
  • 28
  • 94
  • 169
  • 1
    See http://stackoverflow.com/questions/33336697/nosql-database-design-for-queries-with-multiple-restrictions-firebase, http://stackoverflow.com/questions/27432030/how-to-do-the-following-query-in-firebase-more-than-one-where-condition, http://stackoverflow.com/questions/32538312/filter-products-on-multiple-child-properties-in-firebase – Frank van Puffelen Dec 20 '15 at 00:25
  • Another simple scenario I'm stuck with is for example I want to list my contacts by last_name then by first_name. How do you do that in Firebase? – Rio Bautista Mar 01 '17 at 22:35

3 Answers3

8

You can only use one ordering method.

To query more, you'll need to rethink your data structure. Your current structure probably looks something like this:

{
  "key": {
     "id_1": {
        "userUid": "user_1",
        "jobId": "job_1"
     },
     "id_2": {
        "userUid": "user_1",
        "jobId": "job_2"
     },
     "id_3": {
        "userUid": "user_2",
        "jobId": "job_3"
     }
  }
}

With this structure you're limited to index off of one child key.

Now consider this structure:

{
   "key": {
      "user_1": {
         "id_1": {
            "jobId": "job_1",
            "userUid": "user_1"
         },
         "id_2": {
            "jobId": "job_2",
            "userUid": "user_1"
         }
      }
      "user_2": {
         "id_3": {
            "jobId": "job_3",
            "userUid": "user_2"
         }
      }
   }
}

This structure explicitly creates an index on uid. So now if you want to get all the jobs by user you can write this query:

var ref = new Firebase('<my-firebase-app>');
var uid = 'user_1';
var userRef = ref.child('key').child(uid);
var query = userRef.orderByChild('jobId');
query.on('value', (snap) => console.log(snap.val());
David East
  • 31,526
  • 6
  • 67
  • 82
  • Hi David, I see what you mean about the data structure. Is there really no other way to get an object where two fields are equal to? – AngularM Dec 19 '15 at 22:19
  • I'm just looking to do a simple where statement with an And like SQL. – AngularM Dec 19 '15 at 22:31
  • That does not exist in Firebase land at the moment. You can do something like a SELECT * FROM table WHERE userid = "user_1", but no AND statement. Structuring your data like I specified allows you to do some like the AND. Make sense? – David East Dec 19 '15 at 22:36
  • Yeah it makes sense, but its not going to work for what I need. Do you know when "And" will be available or something similar in Firebase? – AngularM Dec 19 '15 at 22:42
  • Firebase is NoSQL so it's not going to have to the same feature set as a SQL database. What about the solution is not going to work? – David East Dec 19 '15 at 22:46
  • Yeah its a structure that wouldnt work for my current data. The only other way is for me to get by uid then loop in typescript to find a match on jobid. But this could be very slow eventually. – AngularM Dec 19 '15 at 22:47
  • Client side filtering is only slow after thousands of objects. Is that your data set size? – David East Dec 19 '15 at 23:00
  • It's only a few rows currently but in the future it should be around 10,000 items il be looping through – AngularM Dec 19 '15 at 23:04
  • If you're going to have a large data set, you need to learn how to structure it properly. The NoSQL world is quite different than the SQL one. You can't expect them to work the same. Try reading these posts to help: https://www.firebase.com/blog/2015-10-07-how-to-keep-your-data-consistent.html, https://www.firebase.com/docs/web/guide/structuring-data.html – David East Dec 20 '15 at 13:28
  • Proper data structure is the answer. Please let me know if there are any other considerations. – David East Dec 20 '15 at 13:29
  • Hi David, I've made a new question attempting your suggested data structure: http://stackoverflow.com/questions/34382216/firebase-data-structure-issue-for-extracting-an-object-from-nested-structure – AngularM Dec 20 '15 at 14:51
  • I'll take a look. Can you mark this one as resolved if there are no other considerations? – David East Dec 20 '15 at 14:52
  • A NoSQL db can have indexes built from a combination attributes. – Dinoboff Feb 13 '16 at 12:09
7

An other way to do kind of multiple orderByChild in Firebase is to create an orderKey.

Imagine you have this in your base:

{
    userId: {
        firstName: 'Jon',
        lastName: 'Snow',
        birthYear: 283
    }
}

And you want to query: userRef.orderByChild('firstName').equalTo('Jon').orderByChild('lastName').equalTo('Snow')

You need to create a key on your user like this:

{
    userId: {
        firstName: 'Snow',
        lastName: 'Jon',
        birthYear: 283,
        orderName: 'JonSnow'
    }
}

So you can query like this: userRef.orderByChild('orderName').equalTo('JonSnow')

This also works with startAt and endAt if you want to query all the Snow born in a range of years. You first create the key:

{
    userId: {
        firstName: 'Snow',
        lastName: 'Jon',
        birthYear: 283,
        orderNameYear: 'Snow283'
    }
}

So you can query: userRef.orderByChild('orderNameYear').startAt('Snow280').endAt('Snow285')

This will return all Snow born between 280 and 285.

Shiv Kumar Baghel
  • 2,464
  • 6
  • 18
  • 34
GuySake
  • 124
  • 1
  • 6
0

It's not supported.

Depending on the kind of query you need to run on an entity, you will either have to store you jobs by user like @david-east suggested or add a property combining the two properties:

{
 jobId: 'J123',
 userId: 'provider:123',
 jobIdUserIdIndex: 'J123/provider:12345',
 [...]
}

But usually, there are no way to allow all the queries you need. You either have to filter objects on the client side, or duplicate the data to created different view of the data; you will have the job data saved at /jobs/$jobId, /users/$userId/jobs/$jobId and maybe /pendingJobs/$jobId.

I find it's easier to use the orderBy* method only for ordering entities and to duplicate the data to select subsets of them.

I wish Firebase would support indexes combining multiple properties, but for now you need to build those yourself. However having different views allows to apply different security rules to them: e.g. Only one user should have access to /users/$userId/jobs/, only the admin should have access to /jobs/$jobId.

Dinoboff
  • 2,622
  • 2
  • 26
  • 26