1

I have two questions,

  1. How do you save a LocalTime rage with ObjectBox, for example:
class LocalTimeRage {
   LocalTime opens;
   LocalTime closes;
}

And 2. How can you query with ObjectBox a local time range, for example, query object that matches within opens and closes LocalTime values, for example, query for opens between 07:00 AM and closes 8:00 PM (in LocalTime) as such ObjectBox will return objects that have the opening and closing times matching the range.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Fireburn
  • 981
  • 6
  • 20

1 Answers1

4

This is based on ObjectBox v2.8.1.

ObjectBox supports a relatively limited list of built-in types. The list does not include any of the modern java.time classes, but we can use the ObjectBox @Convert annotation.

I think it is best to completely avoid the old and problematic Date class, so this example converts LocalDateTime values to longs (however, Date is one of their built-in supported types):

import java.time.LocalTime;
import io.objectbox.annotation.Entity;
import io.objectbox.annotation.Id;
import io.objectbox.annotation.Convert;
import io.objectbox.converter.PropertyConverter;

@Entity
public class LocalTimeRange {

    @Id
    private long id;

    @Convert(converter = LocalTimeConverter.class, dbType = Long.class)
    private LocalTime opens;

    @Convert(converter = LocalTimeConverter.class, dbType = Long.class)
    private LocalTime closes;

    public static class LocalTimeConverter implements PropertyConverter<LocalTime, Long> {

        @Override
        public LocalTime convertToEntityProperty(Long databaseValue) {
            if (databaseValue == null) {
                return null;
            }
            return LocalTime.ofSecondOfDay(databaseValue);
        }

        @Override
        public Long convertToDatabaseValue(LocalTime entityProperty) {
            if (entityProperty == null) {
                return null;
            }
            long seconds = (entityProperty.getHour() * 60 * 60) + 
                    (entityProperty.getMinute() * 60) + 
                    entityProperty.getSecond();
            return seconds;
        }

    }

    public LocalTimeRange(Long id) {
        this.id = id;
    }

    public LocalTimeRange(long id, LocalTime opens, LocalTime closes) {
        this.id = id;
        this.opens = opens;
        this.closes = closes;
    }

    public LocalTimeRange() {
    }
    
    // getters and setters not shown

}

Now we can create and store a couple of test objects:

BoxStore store = MyObjectBox.builder().name("objectbox-demo-db").build();

Box<LocalTimeRange> box = store.boxFor(LocalTimeRange.class);

// start with no objects:
box.query().build().remove();

// add two objects:
LocalTimeRange ltrOne = new LocalTimeRange(1, 
        LocalTime.of(9, 30, 0), //9:30:00 (9:30am)
        LocalTime.of(10, 15, 0));
box.put(ltrOne);

LocalTimeRange ltrTwo = new LocalTimeRange(2, 
        LocalTime.of(10, 05, 0),
        LocalTime.of(11, 45, 0));
box.put(ltrTwo);

And then we can query the data store:

// this will find both objects:
LocalTime testTime = LocalTime.of(10, 10, 0);

// this will find the 2nd object
//LocalTime testTime = LocalTime.of(10, 20, 0);

// convert the localtime to seconds:
Long seconds = localTimeToSeconds(testTime);

List<LocalTimeRange> localTimeRanges = box.query()
        .less(LocalTimeRange_.opens, seconds)
        .greater(LocalTimeRange_.closes, seconds)
        .build().find();

for (LocalTimeRange ltr : localTimeRanges) {
    System.out.println(ltr.toString());
}

store.close();

---

private static long localTimeToSeconds(LocalTime lt) {
    return (lt.getHour() * 60 * 60) + 
                (lt.getMinute() * 60) + 
                lt.getSecond();
}

The query operators are also limited in the types they can handle:

less(LocalTimeRange_.opens, seconds)

Here, seconds is a long - because there is no query operator which takes the Java type we want to use (LocalTime).

You may want to refine my example, to ensure that test times which fall exactly on one of the boundary values are handled correctly (there is no "less than or equal to" query method, for example). You can build more sophisticated queries with equal(), and(), or(), and so on.

(This approach handles LocalTime to the nearest second. If you want sub-second precision, you would need to update the logic to include LocalTime's support for nanoseconds.)

andrewJames
  • 19,570
  • 8
  • 19
  • 51
  • How about when `LocalTimeRange` is a property/field of an Object? E.g. `class Yoga { List schedules; }` `class Schedule { String day; LocalTimeRange opens; LocalTimeRange closes; }` – quarks Dec 23 '20 at 23:06
  • I don't know how to use a converter for that case (the `Schedule` class), given the need to map each property to one (and only one) of the supported built-in types. Also, for your `List` example, the documentation suggests _you could convert a List of Strings to a JSON array resulting in a single string for the database_. That seems like we've moved away from storing objects, at this point, and instead are storing documents. I assume there _must be_ a way to do all this with objects... Maybe this needs a new question? – andrewJames Dec 24 '20 at 00:55
  • From the docs: https://docs.objectbox.io/queries "contains" only works with String so querying whether property "schedules" contains a given `Schedule` may not work entirely since it is not a String literal and a custom class/object. – quarks Dec 24 '20 at 05:35
  • How about this? https://stackoverflow.com/questions/65439440/localtime-range-query-with-objectbox – quarks Dec 24 '20 at 14:12