I solved this problem after days with experiments.
The point is,
I'm surprised to found out even query can be ordered by deep nested
children, but it works for children ONE more level down only!
Being such circumstance, I tried to go for node one more level down first and do query for a specific period on it and it works.
This is my data tree on Firebase real time database:

For reading my POJO models stored in Firebase, I set up a helper class serving for me.
So I run FirebaseDatabaseHelper.getInstance().readCatalogFromDatabase(Card.class, listener, start, end);
while time to update my UI with newest database.
About my FirebaseDatabaseHelper, there're two relative methods serving me well for reading models on the database reference() which refers to parent node holding lots of models like "/Card/user_id" node that has bunches of children meaning card details at /Card/user_id/card_id".
/**
* attaches listener to obtain values at child locations of "catalog/" storing lots of models in JSON tree
* reads values created in a specific period and ordered by date
*
* catalog: "Card" for card
* "Comment" for comment
*
* It's surprised to found out even query can be ordered by deep nested children, but children ONE more level down only
* Being such circumstance, go for node one more level down first and do query for a specific period on it
*/
public void readCatalogFromDatabase(final Class<?> clazz, final OnReadDatabaseChildEventListener child_listener, final long start, final long end) {
//Logger.d(">>> end at date before:" + (end/(1*24*60*60*1000) - start/(1*24*60*60*1000)));
Logger.d(">>> start:" + start + ", end:" + end);
DatabaseReference referenceCatalog;
OnReadDatabaseValueEventListener ids_listener;
String catalog;
if (clazz.getName().equals(Card.class.getName())) {
ids_listener = new OnReadDatabaseValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<String> ids = new LinkedList<>();
Logger.d("... user count:" + dataSnapshot.getChildrenCount());
/**
* $ROOT/catalog/user_id/CardId/CardId
* $ROOT/catalog/user_id/CardId/ProfilePhoto
* $ROOT/catalog/user_id/CardId/ProfileTitle
* $ROOT/catalog/user_id/CardId/ProfileName
* $ROOT/catalog/user_id/CardId/ProfileID
* $ROOT/catalog/user_id/CardId/ArticleTitle
* $ROOT/catalog/user_id/CardId/ArticleContent
* $ROOT/catalog/user_id/CardId/ArticleCount
* $ROOT/catalog/user_id/CardId/ArticlePhoto
* $ROOT/catalog/user_id/CardId/ArticlePhotoFileName
* $ROOT/catalog/user_id/CardId/Distance
* $ROOT/catalog/user_id/CardId/Date
* ^[key] :[value] -> initial
* ^[key] :[value] -> 1th for-loop
*/
for (DataSnapshot snapShotOfUserId : dataSnapshot.getChildren()) {
Logger.d("... user(" + snapShotOfUserId.getKey() + ") post count:" + snapShotOfUserId.getChildrenCount());
ids.add(snapShotOfUserId.getKey());
}
for (String id : ids) readListFromDatabase(clazz, id, child_listener, start, end);
FirebaseDatabaseHelper.getInstance().detachValueEventListener(this);
}
@Override
public void onCancelled(DatabaseError databaseError) {
child_listener.onCancelled(databaseError);
FirebaseDatabaseHelper.getInstance().detachValueEventListener(this);
}
};
catalog = Card.class.getSimpleName();
referenceCatalog = getDatabaseChild(catalog, null, null);
referenceCatalog.orderByKey().limitToLast(COUNT_ON_PAGE).addListenerForSingleValueEvent(ids_listener);
mValueEventListeners.put(ids_listener, referenceCatalog);
} else if (clazz.getName().equals(Comment.class.getName())) {
ids_listener = new OnReadDatabaseValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<String> ids = new LinkedList<>();
Logger.d("... card count:" + dataSnapshot.getChildrenCount());
/**
* $ROOT/catalog/card_id/CommentId/CardId
* $ROOT/catalog/card_id/CommentId/CommentId
* $ROOT/catalog/card_id/CommentId/ProfilePhoto
* $ROOT/catalog/card_id/CommentId/ProfileName
* $ROOT/catalog/card_id/CommentId/ProfileID
* $ROOT/catalog/card_id/CommentId/Comment
* $ROOT/catalog/card_id/CommentId/Distance
* $ROOT/catalog/card_id/CommentId/Date
* ^[key] :[value] -> initial
* ^[key] :[value] -> 1th for-loop
*/
for (DataSnapshot snapShotOfCardId : dataSnapshot.getChildren()) {
Logger.d("... card(" + snapShotOfCardId.getKey() + ") comment count:" + snapShotOfCardId.getChildrenCount());
ids.add(snapShotOfCardId.getKey());
}
for (String id : ids) readListFromDatabase(clazz, id, child_listener, start, end);
FirebaseDatabaseHelper.getInstance().detachValueEventListener(this);
}
@Override
public void onCancelled(DatabaseError databaseError) {
child_listener.onCancelled(databaseError);
FirebaseDatabaseHelper.getInstance().detachValueEventListener(this);
}
};
catalog = Comment.class.getSimpleName();
referenceCatalog = getDatabaseChild(catalog, null, null);
referenceCatalog.orderByKey().limitToLast(COUNT_ON_PAGE).addListenerForSingleValueEvent(ids_listener);
mValueEventListeners.put(ids_listener, referenceCatalog);
}
and go to,
/**
* attaches listener to obtain values at child locations of "catalog/id/" storing list of models in JSON tree
* reads values created in a specific period and ordered by date
*
* id: user id for card
* card id for comment
*
* query can be ordered by deep nested ONE more level down children
*/
public void readListFromDatabase(Class<?> clazz, String id, OnReadDatabaseChildEventListener listener, long start, long end) {
//Logger.d(">>> end at date before:" + (end/(1*24*60*60*1000) - start/(1*24*60*60*1000)));
Logger.d(">>> start:" + start + ", end:" + end + ", id:" + id);
DatabaseReference referenceCatalog;
Query query;
String catalog;
if (clazz.getName().equals(Card.class.getName())) {
catalog = Card.class.getSimpleName();
referenceCatalog = getDatabaseChild(catalog, id, null);
query = referenceCatalog.orderByChild("Date").startAt(start).endAt(end).limitToLast(COUNT_ON_PAGE);
query.addChildEventListener(listener);
mChildEventListeners.put(listener, referenceCatalog);
} else if (clazz.getName().equals(Comment.class.getName())) {
catalog = Comment.class.getSimpleName();
referenceCatalog = getDatabaseChild(catalog, id, null);
query = referenceCatalog.orderByChild("Date").startAt(start).endAt(end).limitToLast(COUNT_ON_PAGE);
query.addChildEventListener(listener);
mChildEventListeners.put(listener, referenceCatalog);
}
}
about database reference,
/**
* gets a database reference to location headed with "/catalog/", "/id/" and "/key/" as prefixes(to a child location in JSON tree)
*
* id: user id for card
* card id for comment
* key:
* card id for card
* comment id for comment
*/
private DatabaseReference getDatabaseChild(String catalog, String id, String key) {
Logger.d(">>>");
DatabaseReference reference;
if (catalog == null) {
if (id == null) {
if (key == null) {
Logger.d("... catalog:" + catalog + ", id:" + id + ", key:" + key);
reference = FirebaseDatabase.getInstance().getReference();
} else {
Logger.d("... catalog:" + catalog + ", id:" + id + ", key:" + key);
reference = FirebaseDatabase.getInstance().getReference(key);
}
} else {
if (key == null) {
Logger.d("... catalog:" + catalog + ", id:" + id + ", key:" + key);
reference = FirebaseDatabase.getInstance().getReference(id);
} else {
Logger.d("... catalog:" + catalog + ", id:" + id + ", key:" + key);
reference = FirebaseDatabase.getInstance().getReference(id + "/" + key);
}
}
} else {
if (id == null) {
if (key == null) {
Logger.d("... catalog:" + catalog + ", id:" + id + ", key:" + key);
reference = FirebaseDatabase.getInstance().getReference(catalog);
} else {
Logger.d("... catalog:" + catalog + ", id:" + id + ", key:" + key);
reference = FirebaseDatabase.getInstance().getReference(catalog + "/" + key);
}
} else {
if (key == null) {
Logger.d("... catalog:" + catalog + ", id:" + id + ", key:" + key);
reference = FirebaseDatabase.getInstance().getReference(catalog + "/" + id);
} else {
Logger.d("... catalog:" + catalog + ", id:" + id + ", key:" + key);
reference = FirebaseDatabase.getInstance().getReference(catalog + "/" + id + "/" + key);
}
}
}
return reference;
}
That's the way I go query in a specific period.
Beside, I found some answers mentioned about people should call
addChildEventListener()
following orderByChild()
; However, after
running experiments, I say,
Query query;
query = referenceCatalog.orderByChild("Date").startAt(start).endAt(end).limitToLast(COUNT_ON_PAGE);
query.addChildEventListener(listener);
and
referenceCatalog.orderByChild("Date").startAt(start).endAt(end).limitToLast(COUNT_ON_PAGE).addChildEventListener(listener);
They are same.
I deem their difference to just the way people program in Java with anonymous
class or not.