3

I'm implementing an orbit.js adapter for firebase, orbit-firebase.

I'm looking for an efficient way to query for multiple records so that I can resolve relationships between objects e.g. course.participants

{
  course: {
    'c1': {
      participants: ['p1', 'p2']
    }
  },
  participant: {
    'p1': {
      name: "Jim"
    },
    'p2': {
      name: "Mark"
    }
  }
}

Given I have the ids 'p1' and 'p2' what's an efficient way to query for both of them?

I can't use a query because I'm using security rules with the participants i.e. the user that's trying to resolve course.participants doesn't have access to all of the participants (bear in mind this is a contrived example).

Ben Aubin
  • 5,542
  • 2
  • 34
  • 54
opsb
  • 29,325
  • 19
  • 89
  • 99
  • So, what are the security rules? Each player can read from `course`, but can only access information about themselves under `participant`? – Seamus Apr 19 '15 at 01:59

1 Answers1

5

I'd recommend that you move away from arrays in your JSON structures. These are nothing but pain in real-time, distributed data and don't work particularly well with security rules and situations like this.

Given this structure:

course: {
  'c1': {
    participants: {
       'p1': true, 'p2': true
    }
  }
}

I could join these fairly easily. You can get a normalized ref that behaves just like a Firebase ref by using Firebase.util's NormalizedCollection:

var ref = new Firebase(...);
var coll = new Firebase.util.NormalizedCollection(
   ref.child('course/c1/participants'),
   ref.child('participant')
).select('participant.name').ref();

coll.on('child_added', function(snap) {
   console.log('participant ' + snap.key(), snap.val());
});

Note that this data structure (sans the array) will also make it simpler to enforce read rules on participant data and the like by allowing you to directly reference the user ids under $courseid/participants/, since they are now keys that can match a $ variable.

Kato
  • 40,352
  • 6
  • 119
  • 149
  • My apologies, the associations are in fact store in the format that you recommend (key: true). I wasn't aware of the Firebase.util.NormalizedCollection, that looks very useful. Is it using the same mechanism as queries under the hood? I'm wondering if it'll work in the case that there are security rules that block the user's access to some participants (which prevents me from using queries). – opsb Apr 21 '15 at 19:45
  • It's not the same as queries. Only the master index--the first ref--is looked up in full (and you can use queries on it to reduce the value set). The others are accessed individually by key, so NormalizedCollection should work with restrictive security rules where queries do not. – Kato Apr 21 '15 at 19:52
  • Ok, I'm not sure that I'd see any speed benefit from doing that then as I'm already accessing the records individually by id. Extending the query api to work with security rules would really help here. Is that feature on the roadmap at all? – opsb Apr 21 '15 at 19:54
  • I suppose a pertinent question is, does loading them individually actually incur much of a time cost when using websockets(vs loading a batch)? – opsb Apr 21 '15 at 19:56
  • 1
    Not really much of a time cost for individual lookups. Sockets help a great deal. There can be performance implications when we get into the millions of requests (there is some setup/teardown required for each once() request, although not much). There's no real speed benefit, just a much simpler code structure, since the joined data is treated as a single Firebase ref. – Kato Apr 21 '15 at 20:07