I have a database with a structure, roughly like
// An index to track Ada's memberships
{
"users": {
"alovelace": {
"name": "Ada Lovelace",
// Index Ada's groups in her profile
"groups": {
// the value here doesn't matter, just that the key exists
"techpioneers": true,
"womentechmakers": true
}
},
...
},
"groups": {
"techpioneers": {
"name": "Historical Tech Pioneers",
"members": {
"alovelace": true,
"ghopper": true,
"eclarke": true
}
},
...
}
}
But I can't figure out how to select users who are members of the same set of groups
You'd have to define an additional group_combinations or all_groups index, where you store each user under their combines the groups:
all_groups: {
"techpioneers_womentechmakers": {
alovelace: true
}
}
Related
I have a class which is part of a school and this class has teachers and students, all of them has name and maybe has phone number , I want to get the full data for the classes
but firstly, could you advice me, what is the best for performance and maintaining from the following Dbs :
1st one
"schools":{
"school1":{
"class1":{
"name":"SC1",
"teachers":[{
"name":"T1"
}, {
"name":"T2"
}],
"students":[
{"name":"S1"},
{"name":"S2"}
]
}
}
.
.
.
.
.
.
.
}
and the 2nd
"school":{
"school1":{
"name":"SC1"
},
"school2":{
"name":"SC2"
}
},
"classes": {
"class1": {
"name": "C1"
},
"class2": {
"name": "C2"
}
},
"students": {
"student1": {
"name": "S1",
"phone":"123456789"
},
"student2": {
"name": "S2",
"phone":"123456789"
},
"student3": {
"name": "S3",
"phone":"123456789"
},
"student4": {
"name": "S4",
"phone":"123456789"
}
},
"teachers": {
"student1": {
"name": "T1",
"phone":"123456789"
},
"student2": {
"name": "T2",
"phone":"123456789"
},
"student3": {
"name": "T3",
"phone":"123456789"
},
"student4": {
"name": "T4",
"phone":"123456789"
}
},
"classes_enrollments": {
"class1": {
"teacher1": true,
"teacher3": true,
"student1": true,
"student2": true
},
"class2": {
"teacher2": true,
"teacher4": true,
"student3": true,
"student4": true
},
"class3": {
"teacher1": true,
"teacher2": true,
"student3": true,
"student4": true,
"student1": true,
"student2": true
}
},
"student_friends": {
"student1": {
"student2": true
},
"students2": {
"student1": true,
"student3": true
},
"students3": {
"student2": true
}
},
"teacher_friends": {
"teacher1": {
"teacher2": true
},
"teacher2": {
"teacher1": true,
"teacher3": true
},
"teacher3": {
"teacher2": true
}
}
and for the 2nd way how to get the full data for the class1: in which school and it's name and count of teachers and students and their names and phones
Thank you
I would mix those two.
For code simplicity and reading performance of individual class details, the 2nd scheme would indeed be messy. The 1st scheme would be better, but with some improvements.
Keep the teachers and students paths at root, just like in the 2nd scheme.
Add teacher_enrollments and student_enrollments path at root, to save the ids of the classes that each teacher/student is associated with.
Don't save class teachers and students as arrays inside classes, but use maps instead, similar to what you're saving in the root teachers and students path.
That way, when you edit a teacher from the root path, you can also get a list of all their associated classes (the ids) from the enrollments path, and do a multi-path update for these classes, to update the teacher/student details in each associated class.
If you have lots of data, you might want to maintain a separate path for class summaries, so that you can easily show a list of classes, without having to download the data for all included teachers and students (which would be present multiple times in all these classes).
When you delete a class, you would also want to do a multi-path update to delete all associated enrollments. If the total number of students and teachers is not too big, you can just delete the enrollments for ALL teacheres/students. If you have lots of teachers/students, you could keep your classes_enrollments path (but with intermediate teachers and students before the ids), so that you can make an update with only the required teacher/student ids. (it's actually a lot simpler. You already have the teacher/student IDs in the class info)
// How to delete a class in JavaScript.
// For Java, use updateChildren() instead of update(),
// and supply it with a HashMap instead of a plain object.
const classToDelete = { id: 'class1', teachers: ..., students: ..., school: ... };
const updateObject = {
['classes/'+classToDelete.id]: null },
['schools/'+classToDelete.school.id+'/classes/'+classToDelete.id]: null },
};
Object.keys(classToDelete.teachers).forEach(teacherId => {
updateObject['teachers/'+teacherId +'/classes/'+classToDelete.id] = null;
});
Object.keys(classToDelete.students).forEach(studentId=> {
updateObject['students/'+studentId+'/classes/'+classToDelete.id] = null;
});
dbRef.update(updateObject);
Example database structure (slightly different than instructed, but using the same concepts):
"schools": {
"school1": {
"id": "school1",
"name": "The best school",
"classes": {
"class1": {
"id": "class1",
"name": "The best class"
}
}
}
},
"classes": {
"class1": {
"id": "class1",
"name": "The best class",
"teachers": {
"teacher1": {
"id": "teacher1",
"name": "The best teacher",
"phone": "123"
}
},
"students": {
"student1": {
"id": "student1",
"name": "The best student",
"phone": "456"
}
},
"school": {
"id": "school1",
"name": "The best school"
}
}
},
"teachers": {
"teacher1": {
"id": "teacher1",
"name": "The best teacher",
"phone": "123",
"classes": {
"class1": {
"name": "The best class",
"school": {
"id": "school1",
"name": "The best school"
}
}
}
}
},
"students": {
"student1": {
"id": "student1",
"name": "The best student",
"phone": "456",
"classes": {
"class1": {
"name": "The best class",
"school": {
"id": "school1",
"name": "The best school"
}
}
}
}
}
Good luck!
I'm currently developing basic social network Android app for sharing images. Already have PHP/mySQL back-end but thinking about migrating to Firebase because of some features that I like (e.g. security, fast read/write).
So, I have users, posts, followers, likes, comments (like every other social network nowadays).
Long story short, I want to know if I'm getting this right.
According to this Firebase documentation and its example I should include unique keys from some JSON trees to others like this (example from documentation):
// An index to track Ada's memberships
{
"users": {
"alovelace": {
"name": "Ada Lovelace",
// Index Ada's groups in her profile
"groups": {
// the value here doesn't matter, just that the key exists
"techpioneers": true,
"womentechmakers": true
}
},
...
},
"groups": {
"techpioneers": {
"name": "Historical Tech Pioneers",
"members": {
"alovelace": true,
"ghopper": true,
"eclarke": true
}
},
...
}
}
Does this mean that I will have to include keys from posts, comments, followers etc. to my users tree like this:
{
"users": {
"alovelace": {
"name": "Ada Lovelace",
"profileImage": "http://blabla.bla?xyzooqlL.png",
"about:" : "Exit light, enter night",
"country": "Neverland"
// Index Ada's posts in her profile
"posts": {
// the value here doesn't matter, just that the key exists
"post123": true,
"post234": true
},
// all comments this user wrote
"comments": {
"comment123": true
},
// all posts this user liked
"likes": {
"post123": true
},
"followers": {
"user123": true,
"user234": true
}
},
...
},
"posts": {
"post123": {
"image": "www.bla.bla/sadsadsa.png",
"description": "Hue hue hue",
"likes": {
"alovelace": true,
"ghopper": true,
"eclarke": true
},
"comments": {
"comment123": true,
}
},
"post234": {
"image": "www.bla.bla/233arweeq.png",
"description": "This is nice",
"likes": {
"eclarke": true
},
"comments": {
"comment234": true,
}
},
"comments": {
"comment123": {
"userId": "alovelace"
"text": "cool",
"date": "current"
}
},
...
}
}
Isn't it a little too much if I have for example 5000 followers, 2000 followings, 1500 post likes? When fetching user or any other object I will also fetch all its keys within that object. Imagine fetching 15 very active users with all this data.
If you have any suggestions how should I structure data, please let me know. Any kind of feedback will be useful.
Thanks.
Based on the official Firebase documentation (https://firebase.google.com/docs/database/android/structure-data) it's suggested to structure data in this sort of a format:
// An index to track Ada's memberships
{
"users": {
"alovelace": {
"name": "Ada Lovelace",
// Index Ada's groups in her profile
"groups": {
// the value here doesn't matter, just that the key exists
"techpioneers": true,
"womentechmakers": true
}
},
...
},
"groups": {
"techpioneers": {
"name": "Historical Tech Pioneers",
"members": {
"alovelace": true,
"ghopper": true,
"eclarke": true
}
},
...
}
}
Here's my understanding of the callback flow (By callback I refer to onDataChange of addValueEventListener/addListenerForSingleValueEvent)
Callback for -groups-
....In a loop Callback for each of the members (to get details from -users- node)
........Another Callback if -users- has a field for which data is at a different node
And so on and so forth.
Can this sort of a structure lead to a nested callback scenario, if yes, how to avoid it ?
I'm able to get the data for a particular value in the child using orderByChild and equalTo (cool that it works for nested child as well)
private void getData() {
try {
final DatabaseReference database = FirebaseDatabase.getInstance().getReference();
database.child(Constants.TABLE_TASKS).orderByChild("user/id")
.equalTo("somevalue")
.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Timber.d(dataSnapshot.toString());
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
}
Is there an easy way to get the data where a particular value is not found, basically something like a notEqualTo("somevalue") ?
In the Firebase Query model you can not filter for inequality to a value.
But you can test for the absence of any value (essentially: the absence of a property). For example with this data model:
{
child1: {
"user": {
"id": 1,
"name": "puf"
}
},
child2: {
"user": {
"id": 2,
"name": "abe"
}
},
child3: {
"user": {
"id": 3
}
}
}
I can query for children without a user/name property with:
ref.orderByChild('user/name').equalTo(null)
Which leads to the only child that doesn't have a name:
child3
Feel free to play with my jsbin to see if you get further: http://jsbin.com/liyibo/edit?js,console
Update: I knew I'd answered this before, but couldn't find it earlier. Here's the dupe: is it possible query data that are not equal to the specified condition?. It looks like I have a mistake in there, since clearly I'm testing for the absence of a property in the above code.
I think I've found the solution and this is more of how the database should be designed and actually now I understood the intention behind Firebase guideline
https://firebase.google.com/docs/database/android/structure-data
Original Design:
{
child1: {
"user": {
"id": "id1",
"name": "puf"
}
},
child2: {
"user": {
"id": "id2",
"name": "abe"
}
},
child3: {
"user": {
"id": "id1"
"name": "puf"
}
}
}
Updated Design:
So apart from the storing the id and name of the user, we should also store a node with the id itself as the key and mark it to true
{
child1: {
"user": {
"id": "id1",
"name": "puf"
"id1": true
}
},
child2: {
"user": {
"id": "id2",
"name": "abe"
"id2": true
}
},
child3: {
"user": {
"id": "id1"
"name": "puf"
"id1": true
}
}
}
With the updated design, if i execute ref.orderByChild('user/id1').equalTo(true)
I would get output as Child1 and Child 3
and if i execute ref.orderByChild('user/id1').equalTo(null),
I would get Child2 as the output
I am used loopback's framework to generate my APIs. Now, I am trying to write a custom "RemoteMethod" that will require a long number (timestamp in unix format such as 1466598625506) and return an array of Sync Objects (I am using retrofit to comunicate with the endpoints). In my android app when I call the end point "getRecodsAfterTimestamp" it should return the records with equal or bigger timeStamp value than the provided one in the request. What It returns is all of the records (3 at this time).
This is how my model (sync.json) called Sync looks like:
{
"name": "Sync",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"uuid": {
"type": "string"
},
"table": {
"type": "string"
},
"action": {
"type": "string"
},
"timeChanged": {
"type": "number"
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
And this is my sync.js with the remote method looks like:
module.exports = function(Sync) {
Sync.getRecodsAfterTimestamp = function(timestamp, cb){
// var response;
Sync.find({where:{ or: [
{timeChanged:timestamp},
{timeChanged: {gt:timestamp } }
] }}, function(err, sync) {
cb(null,sync);
// response.push(sync);
});
// cb(null, response);
}
Sync.remoteMethod (
'getRecodsAfterTimestamp',
{
http: {path: '/getRecodsAfterTimestamp', verb: 'get'},
accepts: {arg: 'timeChanged', type: 'number', http: { source: 'query' } },
returns: {
arg: 'data',
type: 'array',
root: true
}
}
);
};
I dont know if it matters but this is my retrofit method declaration:
#GET("Syncs")
Call<List<Sync>> getAllSyncsAfterThisTimeStamp(#Query(("getRecodsAfterTimestamp?timeChanged=")) long timeChanged);
And here I am calling it like that:
Long timeStamp = 1466598625506L;
Log.e(TAG, "Job Service task is running...");
getAllSyncsCall = espcService.getAllSyncsAfterThisTimeStamp(timeStamp);
getAllSyncsCall.enqueue(EspcJobSheculerService.this);
This code returns
This is not the result I want. It should have returned all of the records after 1466598625506 which is two records only.
Your query is correct.
Check in find callback you get right output or not