I have two objects: Author and Book.
#RealmClass
class Author {
#PrimaryKey
val id: String?
val books: RealmList<Book> = RealmList()
}
#RealmClass
class Book {
#PrimaryKey
val id: String?
val countPages: Long
val genre: String
}
And I have data in realm, like this:
{
"id": "author1",
"books": [
{
"id": "book1",
"countPages": 100,
"genre": "fantasy"
},
{
"id": "book2",
"countPages": 150,
"genre": "non-fiction"
}
]
}
I want to find authors with books, which have specific genre and specific pages count. If I write something like this:
realmQuery.where().equalsTo("books.countPages", 100).equalsTo("books.genre", "non-fiction").find()
I'll get one author with id = author1. But it's not true, I should get empty list.
How can I write query to achieve this?
Link queries translate to has at least one of ___ where X is true, so
.equalsTo("books.countPages", 100).equalsTo("books.genre", "non-fiction")
Says "author that has at least one book that has countPages 100, and has at least one book that has genre non-fiction" -- which is true! But it is not what you want.
There are two ways to go about this:
1.) query the existing result set to get a "smaller" result:
realmQuery.where()
.equalTo("books.countPages", 100)
.findAll()
.equalTo("books.genre", "non-fiction")
.findAll()
2.) execute the query on Books, and access the Author via linking objects inverse relationship
#RealmClass
class Book {
#PrimaryKey
val id: String?
val countPages: Long
val genre: String
#LinkingObjects("books")
val authors: RealmResults<Author>? = null
}
And
val books = realm.where<Book>().equalTo("countPages", 100).equalTo("genre", "non-fiction").findAll();
// these books have `authors` field that contains the author
Related
This question already has an answer here:
com.google.firebase.database.DatabaseException: Can't convert object of type java.lang.String to type Data class object KOTLIN
(1 answer)
Closed 1 year ago.
I have read other comments on this same issue, but none of them has touched on a situation like mine
In mine, below describes how the data is structured:
{
"symbols":{
"alphabets":{
"a":{
"available":true,
"text":"A",
"timestamp":1.512686825309134E9
},
"b":{
"available":true,
"text":"B",
"timestamp":1.512687248764272E9
}"NameOfSymbols":"alphabets"
}
}
}
*The reason why mine is showing the error is that it can't convert the string "NameOfSymbols" : "alphabets" to the objects as specified in the data class
So, what can be done about it, I use Kotlin
Is there a way I can exclude that part of the children value while I only get the one that is specified in the data class?
Data Class
data class alphabets(
val name: Names,
var NameOfSymbols: String? = null) {
data class Names(
var available: Boolean? = null,
var text: String? = null,
var timestamp: Long? = null) {
}
}
This structure might work for your case (untested):
data class Message(
#PropertyName("symbols") val symbols: Symbols,
)
data class Symbols(
#PropertyName("alphabets") val alphabets: Alphabets,
)
data class Alphabets(
#PropertyName("a") val a: Alphabet,
#PropertyName("b") val b: Alphabet,
#PropertyName("NameOfSymbols") val nameOfSymbols: String,
)
data class Alphabet(
#PropertyName("available") val available: Boolean,
#PropertyName("text") val text: String,
#PropertyName("timestamp") val timestamp: Long,
)
Usage would be:
// in your ValueEventListener
override fun onDataChange(snapshot: DataSnapshot) {
val value = snapshot.getValue<Message>()
}
If you want to exclude your NameOfSymbols, you should remove it, and add the #IgnoreExtraProperties, like shown below:
#IgnoreExtraProperties
data class Alphabets(
#PropertyName("a") val a: Alphabet,
#PropertyName("b") val b: Alphabet,
)
NOTE, I used these versions of firebase database:
implementation 'com.google.firebase:firebase-database:19.7.0'
implementation 'com.google.firebase:firebase-database-ktx:19.7.0'
ok, After reading the documentation on Structuring Database on the firebase docs website, Structure your database
I realised that i didn't structure my database well, i should have regrouped them after specifying the name
like below
{
// This is a poorly nested data architecture, because iterating the children
// of the "chats" node to get a list of conversation titles requires
// potentially downloading hundreds of megabytes of messages
"chats": {
"one": {
"title": "Historical Tech Pioneers",
"messages": {
"m1": { "sender": "ghopper", "message": "Relay malfunction found. Cause: moth." },
"m2": { ... },
// a very long list of messages
}
},
"two": { ... }
}
}
Although, it said that structuring it that way isn't a nice way, it seems that's what works for me
I'm trying to save objects in the room database.
My profile object class that I want to save in the database:
class Profile(
val exampleString : String = "Example String",
val exampleStringTwo : String = "Example String Two",
val subObjectsList : ArrayList<SubObjecst> = ArrayList()
)
Profile object include Arraylist of Subobjects with this structure:
class SubObjects(
val exampleString : String = "Example 1",
val exampleStringTwo : String = "Example 2",
val exampleStringThree: String = "Example 3"
)
data class ProfileEntity(
#PrimaryKey(autoGenerate = true) val id: Long?,
#ColumnInfo(name = "profile") var profille: Profile
) : Parcelable
How to save Profile object in the room database? I'm stuck on the Type converter for this object.
Using a TypeConverter
Type converters are really for converting simple types, say a date or enum value, however it could be used for this case. You would need to have some way converting your SubObject to a String, probably with some sort of delimiter that you are confident doesn't exist in your object fields already. So something like:
fun SubObject.toString() = "$exampleString##$exampleString2##$exampleString3"
However this doesn't really leverage what SQL is for, which is column-based data.
Using Relationships
This is probably the method you want. By having a separate Entity for your Profile and SubObject models, you can then query the database to return the relationship between your Profile and SubObject, specifically in a one-to-many relationship. In Room, this is implemented using the #Embedded and #Relation annotations. I would recommend taking a look at the Android Devleopers documentation here but for your example you want something like this:
class ResolvedProfile(
#Embedded
val profile: Profile,
#Relation(parentColumn = "profileId", entityColumn = "subObjectProfileId")
val subObjects: List<SubObject>
)
I'm receiving json like this
{
"animal" : {
"type" : "FIRE",
"food" : "potato",
"water": "7up"
}
}
---
{
"animal" : {
"type" : "WATER",
"water": "7up"
}
}
---
{
"animal" : {
"type" : "CYPO",
"counter": 7
}
}
---
{
"animal" : {
"type" : "UNKNOWN",
"food": "Stup",
"water": "Cola",
"counter" : 4
}
}
Am I suposed to create 4 data classes? I mean, I know how they are going to look, they are not going to change, but depends of the type is going to have some parameters or not, so do I have to create 4 data class like for instance the FIRE one
data class AnimalFireResponse{
#SerializedName("type") val myEnum: MyEnum, #SerializedName("food") val food : String, #SerializedName("water") val water : String)
}
But then I do not know what to put on my service
#GET("/dynamic/stuff.php")
fun getAnimal(): Call<List<MyResponseWithDynamicAnimal>>
Is there any other way way generic?
Edit
Maybe it helps but, I know all the type values (FIRE,WAITER,CYPO,UNKNOWN) and I know all of them are going to return the same values, for instance FIRE is going to return always food and water, does it help to you guys?
Yes, you can make a single data class to represent this JSON.
As the names of the fields don't change you can include them all and mark them as nullable.
I suppose you could create 1 data class with all the possible fields and use the fields accordingly. What i mean is:
data class AnimalResponse{
#SerializedName("type") val myEnum: MyEnum,
#SerializedName("food") val food : String= "",
#SerializedName("water") val water : String= "",
#SerializedName("counter") val counter : Integer = -1)
}
If your response doesn't contain a field, it will be initialized with a default value which you can check in your code. However, I feel like the API structure may not be well optimized.(I could be wrong, I am no expert on this)
Use the following DTO if nullable is ok
data class AnimalResponse(
val type: String?,
val food : String?,
val water : String?,
val counter : Integer?
)
I've this json :
{
"products": [{
"id": "150",
"num": "7",
"name": "450 SA"
}, {
"id": "122",
"num": "13",
"name": "Gillette Blue"
}]}
I've created my models from it , i've these classes for it :
#Entity
data class ProductsModel(
#Json(name = "products")
val products: List<Product>
)
#Entity
data class Product(
#PrimaryKey(autoGenerate = false)
val id: String,
#Json(name = "name")
val name: String,
#Json(name = "num")
val num: String,
)
this is my DAO class for inserting data into my room database :
#Dao
interface ProductsDAO {
// 2: Insert
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(product: ProductsModel)
When I want to run the application , I get this error :
error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
How can I save these data into my database ?
Room provides functionality for converting between primitive and boxed types but doesn't allow for object references between entities.
You can simply create a table that has 3 columns - id, name and num. So each row would be a different Product
OR
Your data base should only save the list of Product's and you should provide a TypeConverter, which converts a Product class to and from a known type that Room can persist.
more about type converters - link
I have a question that maybe will be easy for some of you but I really can't solve.
I have a json formatted like this:
{
"id" : "1641S818",
"balance" : "100.20",
"transactions" : [
{
"id" : "item1",
"price" : "1.50",
"description" : "pen",
"date" : "2018-05-14T14:19:00Z"
},
{
"id" : "item1",
"price" : "9.00",
"description" : "book",
"date" : "2018-05-14T08:19:00Z"
}
]
}
I wanted to try and put up an app with Retrofit + Room for the first time, and I'm having trouble building models.
I came up with this data classes for Retrofit and they work fine. This is no surprise as I know how to use it.
data class Account (
val id : String,
val balance : Double,
val transactions : List<Transaction>
)
data class Transaction (
val id : String,
val price : Double,
val description : String,
val date : String
)
The real issues start when I want to save this structure in a database, with this one-to-many relationship existing between the Account and the Transaction. So far I understood that Room cannot handle this type of structure and I should build different classes for the database model. So I did:
#Entity(tableName = "account")
data class AccountData(
#ColumnInfo(name = "id")
#PrimaryKey(autoGenerate = false)
val id: String,
val balance: Double,
)
#Entity(
tableName = "transaction",
foreignKeys = [
ForeignKey(
entity = AccountData::class,
parentColumns = ["id"],
childColumns = ["account_id"],
onUpdate = ForeignKey.CASCADE,
onDelete = ForeignKey.CASCADE
)
]
)
data class TransactionData(
#PrimaryKey(autoGenerate = false)
#ColumnInfo(name = "id")
val transactionId: String,
#ColumnInfo(name = "account_id")
val accountId: String,
val price: Double,
val description: String,
val date: String
)
Now the part where I'm stuck. I cannot find a guide, an example, anything that shows where to convert the models with a mapper (maybe using livedata or rx) with this particular case, which is when we have complex relations between objects and not just plain classes.
You can define separate Mapper file with extension functions like this
fun Transaction.toTransactionData() = TransactionData(...)
And then after API call, probably in UseCase, you can use this function to map your API entity to DB entity and then pass result to Room dao.
UPD 1.
Additionally you can define Transation entity for UI usage. So at UseCase level you operate with RestTransaction and DbTransaction, but passing Transaction to UI, to abstract from its source.