0

I would like to show date wise item list in my app. The list will have a header with date and sum of a value of a column.

24.12.2021 25
Book1 10
Book2 05
Book3 10

This is the entity:

@Entity(tableName = "books")
public class Books {
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    private int id;

    @ColumnInfo(name = "date")
    @TypeConverters({TimestampConverter.class})
    private long mDate;

    @ColumnInfo(name = "quantity")
    private double mQuantity;

    @ColumnInfo(name = "name")
    private String mName;

I am trying to use nested recyclerview and have created a Parent class :

public class ParentEntry {
    private double totalBooks;
 
    private long dateToday;

    public  List<Books> books;
}

This is the dao:

 @Query("SELECT SUM(quantity) as totalBooks, date as dateToday, *  FROM books GROUP BY date ORDER 
  BY date Asc ")
    LiveData<List<ParentEntry>> getAllParentEntries();

But I am getting the following errors: error: Cannot figure out how to read this field from a cursor. warning: The query returns some columns [id, date, quantity, name] which are not used. You can use @ColumnInfo annotation on the fields to specify the mapping. You can annotate the method with @RewriteQueriesToDropUnusedColumns to direct Room to rewrite your query to avoid fetching unused columns. ParentEntry has some fields [books] which are not returned by the query. If they are not supposed to be read from the result, you can mark them with @Ignore annotation. You can suppress this warning by annotating the method with @SuppressWarnings(RoomWarnings.CURSOR_MISMATCH). Columns returned by the query:totalBooks, dateToday, id, date, quantity, name. Fields in ParentEntry: totalBooks, dateToday, books. LiveData<List> getAllParentEntries();

How can I query both the list and sum of quantity column and date in a single query ? Any help is highly appreciated.

ANKIT
  • 87
  • 1
  • 8

1 Answers1

0

For room to build a List within a POJO you need an @Relation.

So the ParentEntity needs to be something like :-

class ParentEntry {
    long dateToday;
    double totalBooks;
    @Relation(entity = Books.class,
    parentColumn = "dateToday",
    entityColumn = "date")
    List<Books> booksList;

}

So you need a query that then retrieves the non-list values (the dateToday and the totalBooks) with column names that match the member variable names, which could be :-

@Transaction
@Query("SELECT date AS dateToday, total(quantity) AS totalBooks FROM books GROUP BY date ORDER BY date ASC;")
abstract List<ParentEntryV2> getParentEntries();
  • note total rather than sum has been used as total is guaranteed to return a floating point value, sum can return null and can also throw an integer overflow exception. see https://sqlite.org/lang_aggfunc.html#sumunc

e.g. from a books table :-

enter image description here

Using:-

    for(ParentEntryV2 pev2: dao.getParentEntries()) {
        Log.d("PEINFO_V2","Date is " + pev2.dateToday + " TotalBooks is " + pev2.totalBooks + " Number of Books in list is " + pev2.booksList.size());
        for (Books b: pev2.booksList) {
            Log.d("PEINFO_V2","\tBook is " + b.getmName() + " quantity is " + b.getmQuantity());
        }
    }

results in :-

D/PEINFO_V2: Date is 1640124943 TotalBooks is 30.0 Number of Books in list is 3
D/PEINFO_V2:    Book is Book1 quantity is 10.0
D/PEINFO_V2:    Book is Book2 quantity is 10.0
D/PEINFO_V2:    Book is Book3 quantity is 10.0
D/PEINFO_V2: Date is 1640211343 TotalBooks is 30.0 Number of Books in list is 3
D/PEINFO_V2:    Book is Book4 quantity is 10.0
D/PEINFO_V2:    Book is Book5 quantity is 10.0
D/PEINFO_V2:    Book is Book6 quantity is 10.0
D/PEINFO_V2: Date is 1640297743 TotalBooks is 30.0 Number of Books in list is 3
D/PEINFO_V2:    Book is Book7 quantity is 10.0
D/PEINFO_V2:    Book is Book8 quantity is 10.0
D/PEINFO_V2:    Book is Book9 quantity is 10.0

Additional

regarding the date issues (i.e. you only want the date part from a date/time) then you could use the following BUT it requires 2 queries and an abstract class rather then an interface and additional function to invoke both queries in a single transaction the Dao's. The Dao's being :-

@Transaction
@Query("SELECT date AS dateToday, total(quantity) AS totalBooks FROM books GROUP BY date(date/:timefactor,'unixepoch') ORDER BY date ASC;")
abstract List<ParentEntryV2> getParentEntries(long timefactor);

@Query("SELECT * FROM books WHERE date(date/:timefactor,'unixepoch')=date(:date/:timefactor,'unixepoch')")
abstract List<Books> getBooksPerDate(long date,long timefactor);

@Transaction
@Query("")
List<ParentEntryV2> getParentEntriesV2() {
    List<ParentEntryV2> parentEntryV2List = getParentEntries(1 /* 1000 if precision to millisecs */);
    for(ParentEntryV2 pe: parentEntryV2List) {
        pe.booksList = getBooksPerDate(pe.dateToday,1 /* 1000 if precision to millisecs */);

    }
    return parentEntryV2List;
}
  • note that timefactor allows 1 or 1000 for switching between precision to 1 second (1) and precision to millis (1000).
  • Although the first query is basically identical bar the GROUP BY, the second overwrites the booklist (books with exact date/time match) with the required booklist (books that match the date).
MikeT
  • 51,415
  • 16
  • 49
  • 68
  • Thanks a lot. I will try it out and let you know – ANKIT Dec 24 '21 at 13:03
  • I tried what you suggested but I do not think that this is what I wanted. I need to use the nested recycler view with a parent and a child adapter so that I can set on click listener on child adapter items and also set different views for child and parent items. I think I need room to return the list and the heading details as separate variables , for this I need ParentEntry class and have to add Book as a variable. I can get List and heading details in two separate queries but how to get them from a single query? – ANKIT Dec 24 '21 at 14:01
  • But there is a problem. The query is returning only one row per date, means instead of a list of books , List, only one Book is returned per date. – ANKIT Dec 25 '21 at 06:16
  • @ANKIT probably because of the precision at which you are storing the dates. This will affect the grouping and also the rows that `@Relation` extracts. e.g. 1640157415 is 2021-12-22 as just the date but 2021-12-22 07:16:55 if you consider the time. Therefore it is not going to be the same (grouped or linked/mapped via the `@Relation`) to 1640157416 (1 second later). You have not indicated how you have stored the date other than it's a long. If you want the lazy way of letting room do the underlying query, then you have to tailor your schema and how you store the data accordingly. – MikeT Dec 25 '21 at 07:43
  • I have stored the date with time in milliseconds, 2021-12-22 07:16:55 is converted to long and then stored in database. I even tried using GROUP BY strftime('%d-%m-%Y', date/1000, 'unixepoch' ,'localtime') but this also did not help. – ANKIT Dec 25 '21 at 07:48
  • @ANKIT it won't help either as i said *** (grouped or linked/mapped via the @Relation)*** so the grouping may be correct BUT the relationship between date and dateToday will not be resolved but for the row(s) that EXACTLY match (so down to the millisec). try using `entityColumn = "strftime('%d-%m-%Y',date/1000,'unixepoch')")` and you get *error: Cannot find the child entity column `strftime('%d-%m-%Y',date/1000,'unixepoch')` in a.a.so70471336javaroom.Books. Options: id, date, quantity, name List booksList;*. i.e. Follow rules or break single query mandate. – MikeT Dec 25 '21 at 07:59
  • Thanks a lot for explaining. I will try few more things and let you know. – ANKIT Dec 25 '21 at 08:37
  • @ANKIT added break the single query mandate example, which if it's really do in one transaction for LiveData then it does it in a single transaction. – MikeT Dec 25 '21 at 08:58
  • Thanks for providing the queries. I will try the options and see if it helps. How will it affect the app if I change Dao class from interface to abstract? – ANKIT Dec 25 '21 at 09:14
  • @ANKIT for dao's themselves you just have to have abstract .... but you can also have functions as per the example. Otherwise no affect. Personally I prefer abstract class as it is more flexible. – MikeT Dec 25 '21 at 09:20