I am using liveData in my application. I do a DB Query which returns a LiveData<PagedList<Contact>> of contacts stored in DB.
I want to modify this livedata before giving it to observers. Suppose there are ten contacts in the LiveData list, i want to do some comparison with another list and set which contacts are primary in the LiveData list.
After doing this i want to give it to observers .
e.g -
val allContacts: LiveData<PagedList<Contact>> = getFromDB()
val list: ArrayList<String>() = list of some primary contacts
traverse allContacts and list and set which values in allContacts match the values in list.
which ever values in allContacts match, their isPrimary property will be set to true.
Now after modifying allContacts, i want to submit it to observers like:
allContacts.observe(this, Observer(adapter::submitList))
I tried LiveData.transform, But not able to use it properly.Can anyone suggest me how to achieve it using transform method or some other way.
What you are looking for is a Transformation. Use Transformations.map() to create a new LiveData from a function that will be run everytime the first LiveData changes.
e.g.
val allContacts: LiveData<PagedList<Contact>> = getFromDB()
val contactViewModel = Transformations.map(allContacts, {
// Transform and create new list from old
})
Your problem is rooted in the fact that you wish to "intercept" the updates that will be posted to the LiveData object by the DB. Regardless if this is a good approach you can technically achieve it by wrapping the LiveData that is returned by the DB with your own subclass of LiveData.
In other words. UI --observes--> YourLiveData --observes--> DBLiveData
P.S. I think genrally speaking you could solve your problem by modifying your DB query, but that is just me assuming you already have "primary contacts" in some other table
Related
I have a situation where I need some information about my results, in my code I have this data class/Room's entity.
#Entity(primaryKeys = ["searchId","page","pr_id"])
data class ProductResponse(
var searchId: Int,
val page: Int,
#Embedded(prefix = "pr_")
val products: ProductSearchFormatted
)
Where you can see in the Android Studios App Inspector:
The data is saved as spected in ROOM, when I try to load it from ROOM:
#Query(
"SELECT * FROM PagedSearchResponse WHERE searchId ==:input"
)
fun loadPagedSearchResponse(input: Int): PagingSource<Int, ProductResponse>
I just need the data in the same order that was previously saved, and got the data in different order (ordered by pr_id):
I found out that if I change the primaryKeys order, like
#Entity(primaryKeys = ["pr_id","searchId","page"])
data class ProductResponse(
var searchId: Int,
val page: Int,
#Embedded(prefix = "pr_")
val products: ProductSearchFormatted
)
Now the data's order from ROOM is correct.
Why does this happen? Does the primaryKeys order matter?
Changing primaryKeys order changes the order of the data saved in ROOM
NO it does not, the data is ALWAYS saved in the order in which it is inserted. What is changing in your case, is the ORDER in which the data is extracted. That is because you aren't saying in what ORDER you want the data to be extracted and are leaving that choice to the query planner.
Why is this happened?, does the primaryKeys order matter?.
Yes it can do, especially in the absence of other indexes. Certainly pr_id before search_id will make a difference as the order within the index will be different and that as the WHERE clause is on the search_id then it is likely that the primary key index will be used (as in both cases search_id is an initial column (see the links below))
The query planner is an AI that tries to pick the fastest and most efficient algorithm for each SQL statement.
see :-
https://www.sqlite.org/queryplanner.html
https://www.sqlite.org/optoverview.html
https://www.sqlite.org/queryplanner-ng.html
If you want data to be in ORDER then you should specify an ORDER clause (ORDER BY ....). That is the only way to guarantee an ORDER. Assuming an ORDER without an ORDER clause will very likely result in issues.
Saying that using pr_id prior to search_id makes the composite (multiplte column) index likely to be more beneficial as the pr_id (according to the data shown) is less repeated than the search_id.
The way I fix it, it was simpler than I expected.
In the entity, I added a field called "order", and in my Mediator (I'm using Paging 3) basically did this:
list.mapIndex { s,t ->
ProductResponse(
...
order = s
...
)
}
That way, I'm following the EXACT order from Backend without the need to modify any primaryKeys.
whats up?
I have an app that displays a list of items on Firestore using Kotlin and RecyclerView (from FirebaseUI api).
The DB is structured like this:
Users/userID/document_month_year
I need to query the data from the current user.
Each user has his own document_month_year document.
I read a lot of posts here, but each one tell one thing.. thereĀ“s no consense and nothing seems to work.
This query just sends me all documents from all users, how can I fix this?
private val queryFilteredByPaidStatus = db.collectionGroup(collectionName).whereEqualTo("users", userId)
Like this is an important question, here is the awnser that I
private val queryTest = db.document("users/"+userId).collection(collectionName)
fun getData() : FirestoreRecyclerOptions<Expense> {
return FirestoreRecyclerOptions
.Builder<Expense>()
.setQuery(queryTest, Expense::class.java)
.build()
}
Create a separate collection for documents to be read i.e month_year and for each document, add a field inside it which tells you the uid of the authenticated user, to which the document belongs to. Now you can query the collection like:
firestoreDB.collections("month_year").whereEqualTo("uid",auth.currentUser.uid)
I have a room database setup and I want to query that database N number of times and combine the results of each query into a live data array to display back to the user.
I'm pretty sure I want to be using MediatorLiveData but every example online has a predefined amount of live data sources it is combining.
I have the following setup:
petDao
#Query("SELECT * FROM pet_table WHERE name LIKE :petName")
fun getPetsByPetName(petName: String): LiveData<Pet>
petRepository
fun getPetsByPetName(petNames: List<String>): LiveData<List<Pet>> {
for (petName: String in petNames) {
val pets = petDao.getPetsByPetName(petName)
// Combine into one live list of pets
}
}
Have you tried this in your DAO?
#Query("SELECT * FROM pet_table WHERE name IN (:petNames)")
fun getPetsByPetName(petNames: List<String>): LiveData<List<Pet>>
It should work with a list of up to 999 arguments. (not sure if the parameter has to be an array, or if the list is fine)
As an extension over SQLite bind arguments, Room supports binding a
list of parameters to the query. At runtime, Room will build the
correct query to have matching number of bind arguments depending on
the number of items in the method parameter.
https://developer.android.com/reference/androidx/room/Query
To me it seems more appropriate for the example you've given.
So I have a room database all set up, everything is fine, so I can make queries and inserts, delete etc no problems, however i've just run into a situation where id like to return entries by their Ids and duplicates should be allowed, however room is removing the duplicates, so for instance I send it a list of ids say <1,2,3,2,3> and it returns items by their ids but only sends me <1,2,3> removing the duplicate entries. The query I'm making is below (btw complete noob at sql)
#Query("SELECT * FROM card WHERE cardId IN(:cardId)")
LiveData<List<Card>> getCardsByIds(List<Integer> cardId);
Im using it via a repository I created (just a level of abstraction) and calling this repo from a ViewModel, this ViewModel has a mutable live data integer list containing the ids and using a SwitchMap I get the latest live data. ill include the relevant pieces below
CARD REPO calls my Daos method like this
public LiveData<List<Card>> getCardsByIds(List<Integer> cardIds){
return cardDao.getCardsByIds(cardIds);
}
ViewModel calls for them
private MutableLiveData<List<Integer>> cardIds;
//** constructor etc
cards = Transformations.switchMap(cardIds, id -> cardRepository.getCardsByIds(id));
and through the magic of SwitchMap when the cardIds list updates a new query is made and I observe the ViewModel from my fragment. I've debugged it so I know the list of Ids is correct and has the duplicates Ids, but the returned LiveData list is missing the duplicate Ids. any help?
Edit:
The SQLiteDatabase always presents the results as a Cursor in a table format that resembles that of a SQL database.
Source : Google Developer Training
Before the results are returned by the query, the results are stored in a table, and if there are rows with duplicate primary keys, they would be omitted by default.
To achieve what you intend, you can execute a query to find single element by id in loop and append the result to a list.
Updated DAO method:
#Query("SELECT * FROM card WHERE cardId=:cardId")
LiveData<Card> getCardById(Integer cardId);
Update Repository method:
public LiveData<List<Card>> getCardsByIds(List<Integer> cardIds){
List list = new ArrayList();
for(Integer cardId: cardIds){
list.append(cardDao.getCardById(cardId));
}
return list;
}
Hope this helps.
Original Answer:
If id is the primary key of your model, It doesn't allow duplicate data to be entered. Hence while retrieving you might find duplicates missing.
If you have id with duplicate, create another attribute for primary key. (use autogenerate if you don't have any primary key attribute)
A primary key is by default UNIQUE and NOT NULL.
In my android app, I am using the realmBaseAdapter to dynamically display data from a realm query. I was wondering if there is any way to sort these results within the adapter so that I can group them based on certain properties.
Yes, you can :)
You can you build your RealmResults like this:
RealmResults<User> results = realm.where(User.class).findAllSorted(
"name", RealmResults.SORT_ORDER_ASCENDING);
or sort the it after build it like:
RealmResults<User> result = realm.where(User.class).findAll();
result.sort("age"); // Sort ascending
result.sort("age", RealmResults.SORT_ORDER_DESCENDING);
You can also sort by multi fields like:
RealmResults<AllTypes> results = testRealm.where(AllTypes.class)
.findAllSorted(new String[]{"name", "age"},
new boolean[]{RealmResults.SORT_ORDER_ASCENDING,
RealmResults.SORT_ORDER_ASCENDING});
See Doc of RealmQuery and RealmResults.
You don't have to sort the result every time before using it. Once the results is sorted, it will be sorted even when the database changed and results get updated.