Skip to content

Commit

Permalink
Support efficient skip on RealmResults
Browse files Browse the repository at this point in the history
  • Loading branch information
nielsenko committed Sep 1, 2023
1 parent 29ff9f1 commit 4f8eca8
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 7 deletions.
20 changes: 14 additions & 6 deletions lib/src/results.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ import 'realm_object.dart';
class RealmResults<T extends Object?> extends Iterable<T> with RealmEntity implements Finalizable {
final RealmObjectMetadata? _metadata;
final RealmResultsHandle _handle;
final int _skipOffset; // to support skip efficiently

final _supportsSnapshot = <T>[] is List<RealmObjectBase?>;

RealmResults._(this._handle, Realm realm, this._metadata) {
RealmResults._(this._handle, Realm realm, this._metadata, [this._skipOffset = 0]) {
setRealm(realm);
assert(length >= 0);
}

/// Gets a value indicating whether this collection is still valid to use.
Expand All @@ -47,11 +49,11 @@ class RealmResults<T extends Object?> extends Iterable<T> with RealmEntity imple
@override
T elementAt(int index) {
if (this is RealmResults<RealmObjectBase>) {
final handle = realmCore.resultsGetObjectAt(this, index);
final handle = realmCore.resultsGetObjectAt(this, _skipOffset + index);
final accessor = RealmCoreAccessor(metadata, realm.isInMigration);
return RealmObjectInternal.create(T, realm, handle, accessor) as T;
} else {
return realmCore.resultsGetElementAt(this, index) as T;
return realmCore.resultsGetElementAt(this, _skipOffset + index) as T;
}
}

Expand All @@ -72,7 +74,7 @@ class RealmResults<T extends Object?> extends Iterable<T> with RealmEntity imple

/// The number of values in this `Results` collection.
@override
int get length => realmCore.getResultsCount(this);
int get length => realmCore.getResultsCount(this) - _skipOffset;

@override
T get first {
Expand Down Expand Up @@ -102,6 +104,12 @@ class RealmResults<T extends Object?> extends Iterable<T> with RealmEntity imple
return this[0];
}

@override
RealmResults<T> skip(int count) {
RangeError.checkValueInInterval(count, 0, length, "count");
return RealmResults<T>._(_handle, realm, _metadata, _skipOffset + count);
}

/// Creates a frozen snapshot of this query.
RealmResults<T> freeze() {
if (isFrozen) {
Expand Down Expand Up @@ -211,9 +219,9 @@ class _RealmResultsIterator<T extends Object?> implements Iterator<T> {
int _index;
T? _current;

_RealmResultsIterator(RealmResults<T> results)
_RealmResultsIterator(RealmResults<T> results, {int index = 0})
: _results = results,
_index = -1;
_index = index - 1;

@override
T get current => _current ??= _results[_index];
Expand Down
30 changes: 29 additions & 1 deletion test/results_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
// ignore_for_file: unused_local_variable

import 'dart:math';
import 'dart:typed_data';

import 'package:ffi/ffi.dart';
import 'package:test/test.dart' hide test, throws;
import '../lib/realm.dart';
import 'test.dart';
Expand Down Expand Up @@ -832,7 +834,7 @@ Future<void> main([List<String>? args]) async {

queryWithListArg("uuidProp", [uid_1, uid_2, null], expected: 2);
queryWithListArg("nullableUuidProp", [null], expected: 3);

queryWithListArg("intProp", [1, 2, null], expected: 2);
queryWithListArg("nullableIntProp", [null], expected: 3);

Expand Down Expand Up @@ -911,4 +913,30 @@ Future<void> main([List<String>? args]) async {
expect(result.length, 1);
expect(result.first.name, 'secondary school 1');
});

test('RealmResults.skip', () {
final config = Configuration.local([Task.schema]);
final realm = getRealm(config);
const max = 10;
realm.write(() {
realm.addAll(List.generate(max, (_) => Task(ObjectId())));
});

final results = realm.all<Task>();
for (var i = 0; i < max; i++) {
expect(results.skip(i).length, max - i);
for (var j = 0; j < max - i; j++) {
expect(results.skip(i)[j], results[i + j]);
expect(results.skip(i).skip(j)[0], results[i + j]); // chained skip
}
expect(
() => results.skip(max + i + 1),
throwsA(isA<RangeError>().having((e) => e.invalidValue, 'count', max + i + 1)),
);
expect(
() => results.skip(-i - 1),
throwsA(isA<RangeError>().having((e) => e.invalidValue, 'count', -i - 1)),
);
}
});
}

0 comments on commit 4f8eca8

Please sign in to comment.