Skip to main content

Usage Examples

Description — Usage Examples Demonstrating How to Use Various Features of Couchbase Lite for Dart

Open a Database

Every Database has a name which is used to determine its filename. The full filename is the concatenation of the database name and the extension .cblite2.

When opening a database without specifying a directory it will be put into a default location that is platform dependent:

final db = await Database.openAsync('my-database');

If you want to open a database in a specific directory you can specify the directory like this:

final db = await Database.openAsync(
'my-database',
DatabaseConfiguration(directory: 'my-directory')
);

If a database with the same name already exists in the directory, it will be opened. Otherwise a new database will be created.

When you are done with the database, you should close it by calling Database.close. This will free up any resources used by the database, as well as remove change listeners, close change streams and close associated replicators.

Access a Collection

Documents live in collections.

Existing collections can be accessed through Database.collection:

final collectionInDefaultScope = await database.collection('my-collection');
final collectionInCustomScope = await database.collection('my-collection', 'my-scope');

The default collection is always available and can be accessed through Database.defaultCollection:

final defaultCollection = await database.defaultCollection;

Create a Collection

To create a collection, call Database.createCollection:

final collectionInDefaultScope = await database.createCollection('my-collection');
final collectionInCustomScope = await database.createCollection('my-collection', 'my-scope');

If the collection already exists, it will be returned. Otherwise a new collection will be created.

Create a Document

The default constructor of MutableDocument creates a document with a randomly generated id and optionally initializes it with some properties:

final doc = MutableDocument({
'name': 'Alice',
'age': 29,
});

await collection.saveDocument(doc);

It's also possible to create a document with a specific id:

final doc = MutableDocument.withId('ali', {
'name': 'Alice',
'age': 29,
});

await collection.saveDocument(doc);

Read a Document

To read a Document pass the document's id to Collection.document:

final doc = await collection.document('ali');

// If the document exists, an immutable `Document` is returned.
if (doc != null) {
print('Name: ${doc.string('name')}');
print('Age: ${doc.string('age')}');
}

Update a Document

To update a document, first read it, turn it into a MutableDocument and update its properties. Then save it again with Collection.saveDocument:

final doc = await collection.document('ali');

final mutableDoc = doc!.toMutable();

// You can use one of the typed setters to update the document's properties.
mutableDoc.setArray(MutableArray(['Dart']), key: 'languages');

// Or alternatively, use this subscript syntax to get a [MutableFragment] and
// use it to update the document.
mutableDoc['languages'].array = MutableArray(['Dart']);

// The untyped `setValue` setter does the conversion from a plain Dart collection
// to a document collection (`MutableArray` or `MutableDictionary`) for you.
mutableDoc.setValue(['Dart'], key: 'languages');

// Again, there is an alternative subscript syntax available.
mutableDoc['languages'].value = ['Dart'];


await collection.saveDocument(mutableDoc);

Check out the documentation for Collection.saveDocument to learn about how conflicts are handled.

Delete a Document

To delete a document, you need to read it first and than pass it to Collection.deleteDocument:

final doc = await collection.document('ali');

await collection.deleteDocument(doc);

Check out the documentation for Collection.deleteDocument to learn about how conflicts are handled.

Build a Query With the QueryBuilder API

A Query can be built in a type safe way through the QueryBuilder API.

The query below returns the average age of people with the same name:

final query = const QueryBuilder()
.select(
SelectResult.property('name'),
SelectResult.expression(
Function_.avg(Expression.property('age'))
).as('avgAge'),
)
.from(DataSource.collection(collection))
.groupBy(Expression.property('name'));

final resultSet = await query.execute();
final results = await resultSet
.asStream()
// Converts each result into a `Map`, consisting only of plain Dart values.
.map((result) => result.toPlainMap())
.toList();

print(results);

Given these documents:

[
{'name': 'Alice', 'age': 29},
{'name': 'Bob', 'age': 45},
{'name': 'Alice', 'age': 16},
]

results will be:

[
{'name': 'Alice', 'avgAge': 22.5},
{'name': 'Bob', 'avgAge': 45},
]

Build a Query With SQL++

SQL++ is an extension of SQL for querying JSON style data.

The query below is equivalent to query from the QueryBuilder example above:

final query = await db.createQuery(
'''
SELECT name, avg(age) AS avgAge
FROM users
GROUP BY name
''',
);

Data Sync With Replicator To Sync Gateway

This example synchronizes the database with a remote Sync Gateway instance, without authentication. This only works when Sync Gateway has been configured with the GUEST user.

A ReplicatorConfiguration with only default values creates a Replicator with type ReplicatorType.pushAndPull that is not continuous. You need add the Collections that you want to replicate to the configuration.

After starting this replicator, it will push changes from the local database to the remote database and pull changes from the remote database to the local database and then stop again.

Both Replicator.start and Replicator.stop don't immediately start/stop the replicator. The current status of the replicator is available in ReplicatorStatus.activity.

final config = ReplicatorConfiguration(target: UrlEndpoint('http://localhost:4984/my-database'))
..addCollection(collection);
final replicator = await Replicator.create(config);

await replicator.addChangeListener((change) {
print('Replicator activity: ${change.status.activity}');
});

await replicator.start();

When you are done with the replicator, you should close it by calling close. This will free up any resources used by the replicator, as well as remove change listeners and close change streams.