Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store Map values as Long instead of Int #1181

Open
1 task done
alghe-global opened this issue Jul 2, 2024 · 8 comments
Open
1 task done

Store Map values as Long instead of Int #1181

alghe-global opened this issue Jul 2, 2024 · 8 comments
Labels
documentation Improvements or additions to documentation

Comments

@alghe-global
Copy link

Is there an existing issue?

Use case

I am storing a mapping:

val mapping = mutableMapOf(
    "key1" to mutableMapOf(
        "subkey1" to 1L,
        "subkey2" to 1L,
        "subkey3" to 1L
    ),
   "key2" to 1L

that is represented in the data class as follows:

@Entity
data class MyDataClass(
    @Id var id: Long = 0,
    var mapping: MutableMap<String, Any>? = null

ObjectBox will store the Long values as Ints due to not being wide enough. These breaks use cases where we do want to store the Long values as Long and not as Ints (for example test cases that use the same generated mock data to store in the database and to test against).

Proposed solution

Have an annotation that tells ObjectBox to store Long values as Long.

Alternatives

Tried using a PropertyConverter however this breaks for the mapping I've provided.

Additional context

Kotlin
Android

@alghe-global alghe-global added the enhancement New feature or request label Jul 2, 2024
@alghe-global
Copy link
Author

Tried to workaround it by implementing:

val mapping = mutableMapOf(
    "key1" to mutableMapOf(
        "subkey1" to 1L,
        "subkey2" to 1L,
        "subkey3" to 1L
    ),
    "key2" to mutableMapOf(
        "subkey4" to 1L
    )
)

and

@Entity
data class MyDataClass(
    @Id var id: Long = 0,
    var mapping: MutableMap<String, MutableMap<String, Long>? = null
)

however, ObjectBox still stores Long as Integer.

@greenrobot-team
Copy link
Member

greenrobot-team commented Jul 3, 2024

Thanks for this great issue! This behavior is due to ObjectBox using a FlexObjectConverter subclass by default for these "flex" properties, see their discussion in the docs, notably the converter class documentation.

As you hinted at, try to override this with a specific converter. The built-in StringLongMapConverter should do what you want:

@Convert(converter = StringLongMapConverter::class, dbType = ByteArray::class)
var mapping: MutableMap<String, Any>? = null

Note: labeled this issue with "more info required" so it will auto-close in a few days if there are no follow-up comments.

Edit: also added this as an example in the docs linked above.

@greenrobot-team greenrobot-team added the more info required Further information is requested label Jul 3, 2024
@alghe-global
Copy link
Author

I've tried

@Convert(converter = StringLongMapConverter::class, dbType = ByteArray::class)
var mapping: MutableMap<String, Any>? = null

however, the retrieved type from the database is still Integer instead of Long.


Steps to reproduce

  1. Have the entity:
import io.objectbox.annotation.Convert
import io.objectbox.annotation.Id
import io.objectbox.converter.StringLongMapConverter

@Entity
data class MyDataClass(
    @Id var id: Long = 0,
    @Convert(converter = StringLongMapConverter::class, dbType = ByteArray::class)
    var mapping: MutableMap<String, Any>? = null
)
  1. Have the mock data generation source:
class MockDataSource {

    fun generateData(): List<MyDataClass> {
        val mappingOne = mutableMapOf(
            "key1" to mutableMapOf(
                "subkey1" to 1L,
                "subkey2" to 1L,
                "subkey3" to 1L
            ),
           "key2" to 1L
        )

        val mappingTwo = mutableMapOf(
            "key1" to mutableMapOf(
                "subkey1" to 2L,
                "subkey2" to 2L,
                "subkey3" to 2L
            ),
           "key2" to 2L
        )

        val mappingThree = mutableMapOf(
            "key1" to mutableMapOf(
                "subkey1" to 3L,
                "subkey2" to 3L,
                "subkey3" to 3L
            ),
           "key2" to 3L
        )

        val mappingsList = listOf(
            MyDataClass(
                mapping = mappingOne
            ),
            MyDataClass(
                mapping = mappingTwo
            ),
            MyDataClass(
                mapping = mappingThree
            )
        )

        return mappingsList
    }
}
  1. Test:
fun `test MyDataClass`() {
    val mappingsList = MockDataSource().generateData()

    /** Insert test mappings into the database */
    boxStore.boxFor<MyDataClass>().put(mappingsList)

    <get query from database>

    assertEquals(mappingsList, queryResult)
}

I've left out <get query from database> since in my case it's convoluted (since it's Android with Jetpack Compose, I implement a repository and order based on field timestamp and return a flow - it'll be too long to write it here).

Please note this is with ObjectBox v3.8.0 in Android using Kotlin

@github-actions github-actions bot removed the more info required Further information is requested label Jul 4, 2024
@alghe-global
Copy link
Author

I've tried the converter using the second layout and it worked.

@alghe-global
Copy link
Author

Additionally, what's strange, is that with:

var mapping: MutableMap<String, Long>? = null

ObjectBox will correctly return a Long and not an Integer

@greenrobot-team
Copy link
Member

greenrobot-team commented Jul 8, 2024

Thanks! Did you by chance debug this and verified that the converter actually restores as Long? E.g. it hits this line:

I'll try to reproduce this once I have time. Maybe this is a Kotlin thing.

@greenrobot-team greenrobot-team added the more info required Further information is requested label Jul 8, 2024
@github-actions github-actions bot removed the more info required Further information is requested label Jul 9, 2024
@alghe-global
Copy link
Author

I've only ran the test with debugger by setting a breakpoint on the assert line and verifying the type of the values returned by the mock data source against the ones returned by the database query call.

@greenrobot-team
Copy link
Member

greenrobot-team commented Jul 24, 2024

We tried to reproduce this and using the converter I suggested works.

Edit: I also added flex properties to the supported types list with a strong hint to read the documentation about limitations.

Note: labeled this issue with "more info required" so it will auto-close in a few days if there are no follow-up comments.

@greenrobot-team greenrobot-team added more info required Further information is requested documentation Improvements or additions to documentation and removed enhancement New feature or request labels Jul 24, 2024
@github-actions github-actions bot removed the more info required Further information is requested label Jul 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

2 participants