I'm using Firebase to retrieve some data of Users.
I need to retrieve only one User with (gameSearching = true
).
My data looks like:
Users
|
|_____ John
| |___ name: John Farmer
| |___ gameSearching: true
|
|_____ James
| |___ name: James Smith
| |___ gameSearching: false
|
|_____ Barbara
|___ name: Barbara Smith
|___ gameSearching: true
I'm running this code:
setData(('Users/' + you + '/'), {gameSearching: true});
var ref = new Firebase("");
var child = ref.orderByChild("gameSearching");
child.on("child_added", function (data) {
var yourself = firebase.getAuth().password.email.split("@", 1)[0];
data.forEach(
function (childSnapshot) {
if (childSnapshot[i].val().gameSearching == true && childSnapshot[i].key() != yourself) {
var opponent = childSnapshot[i].key();
setData(('Users/' + opponent + '/'), {gameSearching: false});
setData(('Users/' + yourself + '/'), {gameSearching: false});
}
}
);
});
Because 'data' has multiple values, I use 'data.forEach' to separate them. After that point I can set a 'return' on 'true' so I only will recieve one User with 'gameSearching: true'.
My firebase rules:
{
"rules": {
".read": true,
".write": true,
".indexOn": "gameSearching"
}
}
When I run this code I get the following error:
'Uncaught Error: No index defined for gameSearching'
I have searched for setting a index on 'gameSearching' using '.indexOn' but not with any succes. .html
Does anyone have a example or a solution?
Edit:
I now got the following code:
var ref = new Firebase("");
ref.orderByChild("gameSearching").equalTo(true).limitToFirst(1).on("child_added", function (data) {
var yourself = firebase.getAuth().password.email.split("@", 1)[0];
var opponent = data.key();
console.log(opponent);
setData(('Users/' + opponent + '/'), {gameSearching: false});
setData(('Users/' + yourself + '/'), {gameSearching: false});
console.log('ja');
});
I edited my rules of my firebase:
{
"rules": {
".read": true,
".write": true,
"Users": {
".indexOn": "gameSearching"
}
}
}
The indexOn error wont show anymore, so that's great. But ref.orderByChild("gameSearching").equalTo(true).limitToFirst(1
) will runs twice. I don't know how that's possible. I'm using limitToFirst(1)`, so I expect 1 child?
I'm using Firebase to retrieve some data of Users.
I need to retrieve only one User with (gameSearching = true
).
My data looks like:
Users
|
|_____ John
| |___ name: John Farmer
| |___ gameSearching: true
|
|_____ James
| |___ name: James Smith
| |___ gameSearching: false
|
|_____ Barbara
|___ name: Barbara Smith
|___ gameSearching: true
I'm running this code:
setData(('Users/' + you + '/'), {gameSearching: true});
var ref = new Firebase("https://grootproject.firebaseio.com/Users");
var child = ref.orderByChild("gameSearching");
child.on("child_added", function (data) {
var yourself = firebase.getAuth().password.email.split("@", 1)[0];
data.forEach(
function (childSnapshot) {
if (childSnapshot[i].val().gameSearching == true && childSnapshot[i].key() != yourself) {
var opponent = childSnapshot[i].key();
setData(('Users/' + opponent + '/'), {gameSearching: false});
setData(('Users/' + yourself + '/'), {gameSearching: false});
}
}
);
});
Because 'data' has multiple values, I use 'data.forEach' to separate them. After that point I can set a 'return' on 'true' so I only will recieve one User with 'gameSearching: true'.
My firebase rules:
{
"rules": {
".read": true,
".write": true,
".indexOn": "gameSearching"
}
}
When I run this code I get the following error:
'Uncaught Error: No index defined for gameSearching'
I have searched for setting a index on 'gameSearching' using '.indexOn' but not with any succes. https://www.firebase.com/docs/security/guide/indexing-data.html
Does anyone have a example or a solution?
Edit:
I now got the following code:
var ref = new Firebase("https://grootproject.firebaseio.com/Users");
ref.orderByChild("gameSearching").equalTo(true).limitToFirst(1).on("child_added", function (data) {
var yourself = firebase.getAuth().password.email.split("@", 1)[0];
var opponent = data.key();
console.log(opponent);
setData(('Users/' + opponent + '/'), {gameSearching: false});
setData(('Users/' + yourself + '/'), {gameSearching: false});
console.log('ja');
});
I edited my rules of my firebase:
{
"rules": {
".read": true,
".write": true,
"Users": {
".indexOn": "gameSearching"
}
}
}
The indexOn error wont show anymore, so that's great. But ref.orderByChild("gameSearching").equalTo(true).limitToFirst(1
) will runs twice. I don't know how that's possible. I'm using limitToFirst(1)`, so I expect 1 child?
2 Answers
Reset to default 15Now that you added your rules, it is clear what is wrong there.
These are your current rules:
{
"rules": {
".read": true,
".write": true,
".indexOn": "gameSearching"
}
}
So you allow everyone read and write access on your entire Firebase and tell it to add an index on gameSearching
. The problem is that you've defined your index at the top-level, instead of at the level where it needs to be in your Firebase.
This is your current data structure:
{
"Score" : {
"sam_lous" : {
"score" : 2
}
},
"Users" : {
"hallo" : {
"gameSearching" : true
},
"sam_lous" : {
"gameSearching" : true
},
"test" : {
"gameSearching" : false
},
"test2" : {
"gameSearching" : true
}
}
}
The gameSearching
property exists only under children of the Users
node. So that is where you should define your .indexOn
:
{
"rules": {
".read": true,
".write": true,
"Users": {
".indexOn": "gameSearching"
}
}
}
With that, your index should be created (in my experience it happens straight away when you save your rules) and the error/warning message should disappear. But to fix the behavior, you should also make the changes that I put in my other answer.
I have to admit that Firebase's documentation on indexing your data, could be clearer on where you define these .indexOn
rules. If someone at Firebase is reading along, can you please add some better examples that highlight that .indexOn
needs to be defined at the level where the data resides?
Your query orders the data, but fails to filter it. If you change it to this, the query will only match users that are searching for a game:
var query = ref.orderByChild("gameSearching").equalTo(true);
This roughly translates to a SQL of WHERE gameSearching = true
, which is what you asked in your previous question. Note that I called the variable query
, because it represents a query on the data. Or if you want to stick to SQL metaphors, it is more similar to a VIEW in a relational database.
Since you're listening to child_added
your function will be invoked once for each user that fits your query. So the forEach
doesn't make any sense to me in your snippet. If you want to use a forEach
you can get all users that match the query at once with the value
event:
var searchers = ref.orderByChild("gameSearching").equalTo(true);
searchers.on('value', function(searchersSnapshot) {
searchersSnapshot.forEach(function(snapshot) {
console.log(snapshot.key());
});
});
Which will currently log this on your Firebase:
hallo
sam_lous
test2
You can also use child_added
to accomplish the same, but in that case you don't need forEach
:
var searchers = ref.orderByChild("gameSearching").equalTo(true);
searchers.on('child_added', function(snapshot) {
console.log(snapshot.key());
});
Fun fact: while on('child_added'
triggers the Firebase WARNING of a missing index, on('value'
does not. But either way: you should really add the index, because without that performance will suffer unacceptably as you add players.
Since you indicate you only need one child, you can also limit the query further to just give you the first child:
var searchers = ref.orderByChild("gameSearching").equalTo(true).limitToFirst(1);
These and many other operations are covered in sections 2 - 5 of the Firebase Web Guide.
.indexOn
, not.indexOf
like you've typed above. If you made the same typo in your rules, it will indeed not work. – Frank van Puffelen Commented Jan 10, 2015 at 15:30