fenecdb 0.1.10
fenecdb: ^0.1.10 copied to clipboard
fenecdb embedded in a Dart or Flutter app -- a vector-native database in a file on the device, queried with FenecQL or a builder, with live queries as Streams.
fenecdb for Dart #
fenecdb embedded in a Dart or Flutter app: the database is a file on the
device, the engine the native library (crates/fenec-ffi) the server and the
browser module share their code and file format with -- on its own, or a
replica a server keeps in step.
In a Flutter app, depend on fenecdb_flutter, which bundles the library for
iOS, Android and macOS; elsewhere, point Fenec.library (or
FENEC_LIBRARY) at libfenec_ffi.
import 'package:fenecdb/fenecdb.dart';
final db = await Fenec.open('${dir.path}/app.fenec');
await db.execute('create collection todos (title text, done bool @hash)');
await db.from('todos').insert({'title': 'milk', 'done': false});
final open = await db.from('todos').where('done', false).rows();
final near = await db.query(r'get notes near embed $1 limit 5', [embedding]); // a Float32List
db.live(db.from('todos').where('done', false)).listen((rows) => print(rows));
Every call runs on an isolate the database keeps for itself, since a call
may wait for the lock or an fsync and an FFI call blocks its isolate. A live
query is a Stream, run again after a write to a collection it reads, once a
burst. Every write is fsynced before it returns unless the file is opened
with flags: Fenec.noSync, which leaves the writes for sync(); flush()
hands them to the system, which outlives the app being killed.
Sync with a server #
final db = await Fenec.openSynced(
url: 'https://api.example.com', token: jwt,
shapes: [Shape('todos', where: {'owner': me}, key: 'key')],
path: '${dir.path}/todos.fenec',
tokenProvider: refreshToken,
);
await db.replica!.ready();
The same Fenec: reads and live queries are the file's, a write to a
shape's collection shows at once and is sent -- queued in the file while
the server cannot be reached, under an idempotency key so it lands once --
and one the server refuses is put back and comes on replica.refusals.
replica.status says online, offline or catchingUp, the writes not
yet answered and the last error, and statuses streams it; call
replica.resume() on AppLifecycleState.resumed. openSynced, not
sync: Dart has no static and instance member of one name, and sync() is
the fsync. The requests and the stream are dart:io's HttpClient's, so
a server is reached through TLS. Fenec.connect(url, token:) is a server
with no file: every query a request, the same builder.
The query builder makes the text the JavaScript builder makes of the same
chain, to the byte: integrations/builder-golden.json holds the chains,
and make dart-test runs every one. To see the text and parameters a chain
builds, for logging or a test, toFenecQL() returns them and runs nothing.