objectbox-java icon indicating copy to clipboard operation
objectbox-java copied to clipboard

Relation list order

Open stleusc opened this issue 2 years ago • 5 comments
trafficstars

I have a problem that is pretty much similar to #243.

But I can add some details to it. Let's say I have a class called Event that defines an event and the attendees (teams).

There is an overall list of all teams and each team can be in multiple events. That is where the challenge comes in.

I need to be able to manually 'sort'/rearrange the teams in each event. The order matters. I am struggling to store the 'sortorder' anywhere. I can not add it to the team class since the identical team can be in several events and with that will have a unique position in each event. That would leave the event class as the logical place. But I am not seeing a good way to do that either.

Does ObjectBox support a solution for that? Basically maintaining the sortorder of the ToMany in ANY way?

@Entity
public class Event {
    @Id public long id;
    public String name;
    public ToMany<Team> teams;
}

@Entity
public class Team {
    @Id public long id;
    public String name;
    ...
}

stleusc avatar Aug 14 '23 19:08 stleusc

There are proposed changes and a request for input in https://github.com/objectbox/objectbox-java/issues/243#issuecomment-341355922

We welcome any feedback or other suggestions.

greenrobot-team avatar Aug 21 '23 08:08 greenrobot-team

You can add a list of long-IDs to the Event-entity and persist those with a converter:

    @Convert(converter = ListLongConverter.class, dbType = String.class)
    public List<Long> teamsOrder;

The converter is responsible for converting the List-type to something you can persist in ObjectBox. See https://docs.objectbox.io/advanced/custom-types#convert-annotation-and-property-converter

For ease of use, I post some kotlin-code here of how I use it with Gson for such a case, since I already have Gson in my project, but maybe just for this, you should simply convert the list to a string of comma-separated values and vice versa:

class ListLongConverter : PropertyConverter<List<Long>?, String?> {
    private val gson = Gson()
    private val listLongType: Type = object : TypeToken<List<Long>?>() {}.type

    override fun convertToEntityProperty(databaseValue: String?): List<Long>? {
        return gson.fromJson(databaseValue, listLongType)
    }

    override fun convertToDatabaseValue(entityProperty: List<Long>?): String? {
        return gson.toJson(entityProperty)
    }
}

And last but not least, you can then set a comparator on the ToMany-field to tell it to sort its content according to the order of the teamsOrder-list (again kotlin-code, but you may get the idea):

    event.teams.setComparator { team1, team2 ->
        // something like that, did not test
        val team1OrderIndex = event.teamsOrder.indexOf(team1.id)
        val team2OrderIndex = event.teamsOrder.indexOf(team2.id)
        sign(team1OrderIndex - team2OrderIndex).toInt()
    }

See https://objectbox.io/docfiles/java/current/io/objectbox/relation/ToMany.html#setComparator(java.util.Comparator) for details

ajans avatar Oct 12 '23 11:10 ajans

@Entity
data class AutoControl(
    @Id
    var id: Long = 0,
    @Convert(converter = ControlTimerConverter::class, dbType = Long::class)
    var control_timers: ControlTimer? = null,
    var effect_raise: Boolean = false,
    var enabled: Boolean = false,
    var name: String? = null,
    var negative_controller: List<Int>? = null,
    var positive_controller: List<Int>? = null,
    var run_max: Int = 0,
    var run_min: Int = 0,
    var time: Int = 0,
    var sensors: List<Sensor>? = null,
    var timer_enabled: Boolean = false,
)

@Entity
data class ControlTimer(
    @Id
    var id: Long = 0,
    var start_time: String? = null,
    var stop_time: String? = null
)

class ControlTimerConverter : PropertyConverter<ControlTimer?, Long?> {
    override fun convertToEntityProperty(databaseValue: Long?): ControlTimer? {
        return if (databaseValue == null || databaseValue == 0L) {
            null
        } else {
            ControlTimer(id = databaseValue)
        }
    }

    override fun convertToDatabaseValue(entityProperty: ControlTimer?): Long? {
        return entityProperty?.id
    }
}

error: [ObjectBox] Relation target class 'Integer' defined in class 'AutoControl' could not be found (is it an @Entity?)

jy-98 avatar Jan 22 '24 09:01 jy-98

@greenrobot-team The examples given for custom types are too simple. Are there any more complex examples. https://docs.objectbox.io/advanced/custom-types

jy-98 avatar Jan 23 '24 00:01 jy-98

@jy-98 Instead of commenting on unrelated issues, please create new issues!

error: [ObjectBox] Relation target class 'Integer' defined in class 'AutoControl' could not be found

This is because List<Int> is assumed to be a relation:

var negative_controller: List<Int>? = null,
var positive_controller: List<Int>? = null,

If you want to avoid using a converter for these, try the supported IntArray.

As for custom type examples: please explain what you are looking for. Only then can I help!

Note: I will hide these comments as they are off-topic in this issue.

greenrobot-team avatar Jan 23 '24 07:01 greenrobot-team

Closing this as duplicate of #154 (not sure why I did not find it back then). There is also an experimental API for ordering mentioned there.

greenrobot-team avatar Jun 10 '24 05:06 greenrobot-team