2

I have records with this fields:

age: 25
timestamp: 1484089199472

This records inserted by:

@Exclude
public Map<String, Object> toMap() {
    HashMap<String, Object> result = new HashMap<>();

    result.put("age", age);
    result.put("timestamp", ServerValue.TIMESTAMP);
    return result;
}

If i use endAt(24,"age") than its work good, the item is invisible.

But if i want to use endAt in timestamp its not work. For example: endAt(100000,"timestamp") and the row is stay visible. Why?

This is check the type?

How can i filter it?

I dont have any idea! :( I hope someone can help me! Thanks!

EDIT:

Query for get the actually day rows

 @Override
    public Query getQuery(final DatabaseReference databaseReference) {
        Date endTime = new Date();
        endTime.setHours(23);
        endTime.setMinutes(59);
        endTime.setSeconds(59);

        return databaseReference
                .child("toplist")
                .endAt(endTime.getTime(),"timestamp")
                .orderByChild("point")
                .limitToFirst(50);

    }

Data

@IgnoreExtraProperties
public class Rank{

    public int number;
    public String username;
    public int point;


    // [START post_to_map]
    @Exclude
    public Map<String, Object> toMap() {
        HashMap<String, Object> result = new HashMap<>();
        result.put("number", number);
        result.put("username", username);
        result.put("point", point);
        result.put("timestamp", ServerValue.TIMESTAMP);
        return result;
    }

}

Add new random item

private void writeNewRandomTopListItem() {
        Random rnd = new Random();
        Rank item = new Rank();
        item.username = "Test " + rnd.nextInt(20000);
        item.point = rnd.nextInt(2000000);

        mDatabase.child("toplist").push().setValue(item.toMap());
    }

Adapter

Query postsQuery = getQuery(mDatabase);
        mAdapter = new FirebaseRecyclerAdapter<Rank, PostViewHolder>(Rank.class, R.layout.item_post, PostViewHolder.class, postsQuery) {
            @Override
            protected void populateViewHolder(final PostViewHolder viewHolder, final Rank model, final int position) {

                viewHolder.bindToPost(model,position);
            }
        };
        mRecycler.setAdapter(mAdapter);

...

 toplist
   -Ka8m3hN-vRjPZhKH3Rt
     number: 0
     point:  1269424
     timestamp: 148407253222471
     username:  "Test 19727"
   -Ka8m3hWRIQKL7faNqUS
     number:  0
     point:  1398061
     timestamp:  1484075322472
     username: "Test 17679"
AL.
  • 36,815
  • 10
  • 142
  • 281
ketom
  • 2,334
  • 4
  • 17
  • 25
  • 1
    Could you provide your database data from a higher level and some more code on what exact query you are calling? I'm pretty sure I will be able to help you, if you do. – Rodrigo Ehlers Jan 10 '17 at 19:28
  • Thank you for your help. I edited my post. – ketom Jan 10 '17 at 19:39

2 Answers2

4

You should order your nodes by the timestamp child first and secondly order the received items by points on the client side. This was described here.

I would suggest you to use this query instead:

@Override
public Query getQuery(final DatabaseReference databaseReference) {
    Date endTime = new Date();
    endTime.setHours(23);
    endTime.setMinutes(59);
    endTime.setSeconds(59);

    return databaseReference.child("toplist").orderByChild("timestamp")
            .endAt(endTime.getTime()).limitToFirst(50);
}

This way you are telling Firebase to sort your nodes by one of their child nodes which in this case would be timestamp. Hope this helps.

Next you would need to sort your data on the client side. A problem of this would be that realtime changes from firebase would not reorder your items inside a RecyclerView for example.

Once again I recommend reading through this answer.

Community
  • 1
  • 1
Rodrigo Ehlers
  • 1,830
  • 11
  • 25
0

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: enter image description here

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.

Mou
  • 145
  • 1
  • 6