Skip to content

Commit e6a855f

Browse files
Queries: add conditions for Date and DateNano properties #94
Add a new condition class for each one, with methods explicitly named to ensure it is clear how values are passed to the database. This does not break existing user code as both condition classes inherit from the previously used QueryIntegerProperty class.
1 parent f07ecfb commit e6a855f

File tree

6 files changed

+153
-2
lines changed

6 files changed

+153
-2
lines changed

generator/lib/src/code_chunks.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -677,9 +677,13 @@ class CodeChunks {
677677
case OBXPropertyType.Char:
678678
case OBXPropertyType.Int:
679679
case OBXPropertyType.Long:
680+
fieldType = 'Integer';
681+
break;
680682
case OBXPropertyType.Date:
683+
fieldType = 'Date';
684+
break;
681685
case OBXPropertyType.DateNano:
682-
fieldType = 'Integer';
686+
fieldType = 'DateNano';
683687
break;
684688
case OBXPropertyType.Relation:
685689
fieldType = 'Relation';

objectbox/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@
1818
This is different from the existing `maxDBSizeInKB` property in that it is possible to remove data
1919
after reaching the limit and continue to use the database. See the `Store` documentation for more
2020
details.
21+
* For `DateTime` properties new convenience query conditions are generated that accept `DateTime`
22+
and auto-convert to milliseconds (or nanoseconds for `@Property(type: PropertyType.dateNano)`) [#287](https://github.com/objectbox/objectbox-dart/issues/287)
23+
```dart
24+
// For example instead of:
25+
Order_.date.between(DateTime(2024, 1).millisecondsSinceEpoch, DateTime(2024, 2).millisecondsSinceEpoch)
26+
// You can now just write:
27+
Order_.date.betweenMilliseconds(DateTime(2024, 1), DateTime(2024, 2))
28+
```
2129

2230
## 2.4.0 (2023-12-13)
2331

objectbox/lib/objectbox.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ export 'src/query.dart'
2828
QueryStringProperty,
2929
QueryByteVectorProperty,
3030
QueryIntegerProperty,
31+
QueryDateProperty,
32+
QueryDateNanoProperty,
3133
QueryIntegerVectorProperty,
3234
QueryDoubleProperty,
3335
QueryDoubleVectorProperty,

objectbox/lib/src/native/query/query.dart

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,113 @@ class QueryIntegerProperty<EntityT> extends QueryProperty<EntityT, int> {
180180
_opList(list, _ConditionOp.notOneOf, alias);
181181
}
182182

183+
/// This wraps [QueryIntegerProperty] for [DateTime] properties to avoid
184+
/// having to manually convert to [DateTime.millisecondsSinceEpoch] when
185+
/// creating query conditions.
186+
class QueryDateProperty<EntityT> extends QueryIntegerProperty<EntityT> {
187+
QueryDateProperty(super.model);
188+
189+
int _convert(DateTime value) => value.millisecondsSinceEpoch;
190+
191+
/// Like [equals], but converts to [DateTime.millisecondsSinceEpoch] for you.
192+
Condition<EntityT> equalsMilliseconds(DateTime value, {String? alias}) =>
193+
equals(_convert(value), alias: alias);
194+
195+
/// Like [notEquals], but converts to [DateTime.millisecondsSinceEpoch] for you.
196+
Condition<EntityT> notEqualsMilliseconds(DateTime value, {String? alias}) =>
197+
notEquals(_convert(value), alias: alias);
198+
199+
/// Like [greaterThan], but converts to [DateTime.millisecondsSinceEpoch] for you.
200+
Condition<EntityT> greaterThanMilliseconds(DateTime value, {String? alias}) =>
201+
greaterThan(_convert(value), alias: alias);
202+
203+
/// Like [greaterOrEqual], but converts to [DateTime.millisecondsSinceEpoch] for you.
204+
Condition<EntityT> greaterOrEqualMilliseconds(DateTime value,
205+
{String? alias}) =>
206+
greaterOrEqual(_convert(value), alias: alias);
207+
208+
/// Like [lessThan], but converts to [DateTime.millisecondsSinceEpoch] for you.
209+
Condition<EntityT> lessThanMilliseconds(DateTime value, {String? alias}) =>
210+
lessThan(_convert(value), alias: alias);
211+
212+
/// Like [lessOrEqual], but converts to [DateTime.millisecondsSinceEpoch] for you.
213+
Condition<EntityT> lessOrEqualMilliseconds(DateTime value, {String? alias}) =>
214+
lessOrEqual(_convert(value), alias: alias);
215+
216+
/// Like [between], but converts to [DateTime.millisecondsSinceEpoch] for you.
217+
Condition<EntityT> betweenMilliseconds(DateTime value1, DateTime value2,
218+
{String? alias}) =>
219+
between(_convert(value1), _convert(value2), alias: alias);
220+
221+
/// Like [oneOf], but converts to [DateTime.millisecondsSinceEpoch] for you.
222+
Condition<EntityT> oneOfMilliseconds(List<DateTime> values,
223+
{String? alias}) =>
224+
oneOf(values.map(_convert).toList(), alias: alias);
225+
226+
/// Like [notOneOf], but converts to [DateTime.millisecondsSinceEpoch] for you.
227+
Condition<EntityT> notOneOfMilliseconds(List<DateTime> values,
228+
{String? alias}) =>
229+
notOneOf(values.map(_convert).toList(), alias: alias);
230+
}
231+
232+
/// This wraps [QueryIntegerProperty] for [DateTime] properties annotated with
233+
/// `@Property(type: PropertyType.dateNano)` to avoid having to manually convert
234+
/// to nanoseconds ([DateTime.microsecondsSinceEpoch] `* 1000`) when creating
235+
/// query conditions.
236+
class QueryDateNanoProperty<EntityT> extends QueryIntegerProperty<EntityT> {
237+
QueryDateNanoProperty(super.model);
238+
239+
int _convert(DateTime value) => value.microsecondsSinceEpoch * 1000;
240+
241+
/// Like [equals], but converts to nanoseconds
242+
/// ([DateTime.microsecondsSinceEpoch] `* 1000`) for you.
243+
Condition<EntityT> equalsNanoseconds(DateTime value, {String? alias}) =>
244+
equals(_convert(value), alias: alias);
245+
246+
/// Like [notEquals], but converts to nanoseconds
247+
/// ([DateTime.microsecondsSinceEpoch] `* 1000`) for you.
248+
Condition<EntityT> notEqualsNanoseconds(DateTime value, {String? alias}) =>
249+
notEquals(_convert(value), alias: alias);
250+
251+
/// Like [greaterThan], but converts to nanoseconds
252+
/// ([DateTime.microsecondsSinceEpoch] `* 1000`) for you.
253+
Condition<EntityT> greaterThanNanoseconds(DateTime value, {String? alias}) =>
254+
greaterThan(_convert(value), alias: alias);
255+
256+
/// Like [greaterOrEqual], but converts to nanoseconds
257+
/// ([DateTime.microsecondsSinceEpoch] `* 1000`) for you.
258+
Condition<EntityT> greaterOrEqualNanoseconds(DateTime value,
259+
{String? alias}) =>
260+
greaterOrEqual(_convert(value), alias: alias);
261+
262+
/// Like [lessThan], but converts to nanoseconds
263+
/// ([DateTime.microsecondsSinceEpoch] `* 1000`) for you.
264+
Condition<EntityT> lessThanNanoseconds(DateTime value, {String? alias}) =>
265+
lessThan(_convert(value), alias: alias);
266+
267+
/// Like [lessOrEqual], but converts to nanoseconds
268+
/// ([DateTime.microsecondsSinceEpoch] `* 1000`) for you.
269+
Condition<EntityT> lessOrEqualNanoseconds(DateTime value, {String? alias}) =>
270+
lessOrEqual(_convert(value), alias: alias);
271+
272+
/// Like [between], but converts to nanoseconds
273+
/// ([DateTime.microsecondsSinceEpoch] `* 1000`) for you.
274+
Condition<EntityT> betweenNanoseconds(DateTime value1, DateTime value2,
275+
{String? alias}) =>
276+
between(_convert(value1), _convert(value2), alias: alias);
277+
278+
/// Like [oneOf], but converts to nanoseconds
279+
/// ([DateTime.microsecondsSinceEpoch] `* 1000`) for you.
280+
Condition<EntityT> oneOfNanoseconds(List<DateTime> values, {String? alias}) =>
281+
oneOf(values.map(_convert).toList(), alias: alias);
282+
283+
/// Like [notOneOf], but converts to nanoseconds
284+
/// ([DateTime.microsecondsSinceEpoch] `* 1000`) for you.
285+
Condition<EntityT> notOneOfNanoseconds(List<DateTime> values,
286+
{String? alias}) =>
287+
notOneOf(values.map(_convert).toList(), alias: alias);
288+
}
289+
183290
/// For integer vectors (excluding [QueryByteVectorProperty]) greater, less and
184291
/// equal are supported on elements of the vector (e.g. "has element greater").
185292
class QueryIntegerVectorProperty<EntityT> extends QueryProperty<EntityT, int> {

objectbox_test/test/entity.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ class TestEntity {
8888
this.tByteList,
8989
this.tInt8List,
9090
this.tUint8List,
91-
this.ignore});
91+
this.ignore,
92+
this.tDate,
93+
this.tDateNano});
9294

9395
TestEntity.filled({
9496
this.id = 1,

objectbox_test/test/query_test.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,34 @@ void main() {
195195
expect(box.query(TestEntity_.tInt.between(3, 7)).build().count(), 3);
196196
});
197197

198+
test('date and date nano convenience conditions', () {
199+
const count = 6;
200+
final dates = [for (var i = 1; i <= count; i++) DateTime.utc(2000, 1, i)];
201+
box.putMany([for (var d in dates) TestEntity(tDate: d, tDateNano: d)]);
202+
203+
queryAndAssert(QueryBuilder<TestEntity> builder) {
204+
final items = builder.build().find();
205+
expect(items.length, 3);
206+
expect(items[0].tDate!.day, 3);
207+
expect(items[1].tDate!.day, 4);
208+
expect(items[2].tDate!.day, 5);
209+
}
210+
211+
final from = dates[2];
212+
final to = dates[4];
213+
214+
// With the existing QueryIntegerProperty (now a super type)
215+
queryAndAssert(box.query(TestEntity_.tDate
216+
.between(from.millisecondsSinceEpoch, to.millisecondsSinceEpoch)));
217+
queryAndAssert(box.query(TestEntity_.tDateNano.between(
218+
from.microsecondsSinceEpoch * 1000, to.microsecondsSinceEpoch * 1000)));
219+
// With the new QueryDateProperty
220+
queryAndAssert(box.query(TestEntity_.tDate.betweenMilliseconds(from, to)));
221+
// With the new QueryDateNanoProperty
222+
queryAndAssert(
223+
box.query(TestEntity_.tDateNano.betweenNanoseconds(from, to)));
224+
});
225+
198226
test('.count matches of `greater` and `less`', () {
199227
box.putMany(<TestEntity>[
200228
TestEntity(tLong: 1336, tString: 'mord'),

0 commit comments

Comments
 (0)