Query Result Sets
The execution of a Couchbase Lite database query returns an array of results, a result set.
The result set format and its handling varies slightly depending on the type of
SelectResult expressions used. The result set formats you may encounter
include those generated by:
-
SelectResult.allββsee: All Properties -
Expression.propertyββsee: Specific Properties -
Meta.idβ Metadata (such as the_id) ββsee: Document ID Only -
Function_.countββ see: Select Count Only
To process the results of a query, you first need to execute it using
Query.execute.
-
The result set of an aggregate query contains one result per aggregation group ββsee Select Count Only.
-
The result set of a query returning document properties contains zero or more results. Each result represents the data from a document that matched your search criteria (the
WHEREclause). The composition of each result is determined by the combination ofSelectResultexpressions provided in theSELECTclause.
Select All Propertiesβ
Queryβ
The SELECT clause for this type of query, which returns all document
properties for each document matching the query criteria, is fairly
straightforward ββsee Example 1.
final database = await Database.openAsync('travel-sample');
final collection = await database.defaultCollection;
final query = const QueryBuilder()
.select(SelectResult.all())
.from(DataSource.collection(collection));
Result Set Formatβ
The result set returned by queries using SelectResult.all contains
dictionaries ββone for each document matching the query criteria.
Each result contains a key-value pair, where the key is the collection name (or its alias, if one was provided) and the value is a dictionary representing each document's properties ββsee: Example 2.
[
{
"_default": {
"callsign": "MILE-AIR",
"country": "United States",
"iata": "Q5",
"icao": "MLA",
"id": 10,
"name": "40-Mile Air",
"type": "airline"
}
},
{
"_default": {
"callsign": "ALASKAN-AIR",
"country": "United States",
"iata": "AA",
"icao": "AAA",
"id": 10,
"name": "Alaskan Airways",
"type": "airline"
}
}
]
Result Set Accessβ
In this case, access the retrieved document properties by looking up a dictionary in each result ββas shown in Example 3.
final resultSet = await query.execute();
await for (final result in resultSet.asStream()) {
final docProps = result.dictionary(0)!;
final id = docProps.string('id');
final name = docProps.string('name');
final type = docProps.string('type');
final city = docProps.string('city');
print("$id $name $type $city");
}
Select Specific Propertiesβ
Queryβ
Here we use Expression.property to specify the document properties we want
our query to return ββsee: Example 4.
final database = await Database.openAsync('hotels');
final collection = await database.defaultCollection;
final query = const QueryBuilder()
.select(
SelectResult.expression(Meta.id).as('docId'),
SelectResult.expression(Expression.property('id')),
SelectResult.expression(Expression.property('type')),
SelectResult.expression(Expression.property('name')),
)
.from(DataSource.collection(collection));
Result Set Formatβ
The result set returned when selecting only specific document properties contains dictionaries ββone for each document matching the query criteria.
Each result comprises a key-value pair for each selected document property ββsee Example 5.
[
{
"docId": "XjO9Ohk96F",
"id": "hotel123",
"type": "hotel",
"name": "Hotel Ghia"
},
{
"docId": "w2DCGPJ-0m",
"id": "hotel456",
"type": "hotel",
"name": "Hotel Deluxe"
}
]
Result Set Accessβ
Access the retrieved properties by looking them up in each result β as shown in Example 6.
final resultSet = await query.execute();
await for (final result in await resultSet.asStream()) {
final docId = result.string('docId')!;
print('processing doc: $docId');
final id = result.string('id')!;
final hotel = Hotel(id);
hotel.type = result.string('type')!;
hotel.name = result.string('name')!;
Do something with the hotel...
}
Select Document ID Onlyβ
Queryβ
You would typically use this type of query if retrieval of document properties directly would consume excessive amounts of memory and-or processing time ββsee: Example 7.
final database = await Database.openAsync('hotels');
final collection = await database.defaultCollection;
final query = QueryBuilder.createAsync()
.select(SelectResult.expression(Meta.id))
.from(DataSource.collection(collection));
Result Set Formatβ
The result set returned by queries using a SelectResult expression of the
form SelectResult.expression(Meta.id) contains dictionaries ββone for each
document matching the query criteria. Each result contains the ID under the id
key ββsee Example 8.
[
{
"id": "hotel123"
},
{
"id": "hotel456"
}
]
Result Set Accessβ
In this case, access the properties of a document by unpacking the id and using it to get the document from the collection ββsee: Example 9.
final resultSet = await query.execute();
await for (final result in resultSet.asStream()) {
final docId = result.string('docId');
print('processing doc: $docId');
final doc = (await collection.document(docId))!;
final hotelId = doc.string('id');
final name = doc.string('name');
final city = doc.string('city');
final type = doc.string('type');
Do something with the variables...
}
Select Count Onlyβ
Queryβ
final database = await Database.openAsync('hotels');
final collection = await database.defaultCollection;
final query = const QueryBuilder()
.select(SelectResult.expression(Function_.count(Expression.all())).as('count'))
.from(DataSource.collection(collection))
.groupBy(Expression.property('type'));
Result Set Formatβ
The result set returned by a count such as
SelectResult.expression(Function_.count(Expression.all())).as('count')
contains dictionaries with a key-value pair. The key is the count name, as
defined using SelectResultAs.as ββsee: Example 11 for the format and
Example 10 for the query.
{
"count": 6
}
Result Set Accessβ
Access the count using its alias name ("count" in this example) ββsee Example 12.
final resultSet = await query.execute();
final results = await resultSet.allResults();
final result = results.first;
final count = result.integer('count');
print("There are $count documents.");
Handling Paginationβ
One way to handle pagination in high-volume queries is to retrieve the results
in batches. Use the LIMIT and OFFSET clauses to return a defined number of
results starting from a given offset ββsee: Example 13.
const offset = 0;
const limit = 20;
final database = await Database.openAsync('hotels');
final collection = await database.defaultCollection;
final query = const QueryBuilder()
.select(SelectResult.all())
.from(DataSource.collection(collection))
.limit(
Expression.integer(limit),
offset: Expression.integer(offset),
);
JSON Result Setsβ
Couchbase Lite for Dart provides a convenience API to convert query results to:
- Plain Dart objects through
toPlainMapandtoPlainListonResult. - JSON strings through
Result.toJson.
Convert Result to Modelβ
In Dart/Flutter you can use code generators to auto generate the code for
handing serialization. See
Creating model classes the json serializable way
for more information. The example below shows how this can be done using the
class Hotel with methods generated from the code generators. Also note when
updating your model classes you will be required to run this command from the
terminal to update your code generated classes:
flutter pub run build_runner build --delete-conflicting-outputs
// hotel.dart
part 'hotel.g.dart';
(explicitToJson: true)
class Hotel {
const Hotel({
required this.id,
required this.name,
required this.city,
required this.country,
this.description,
});
String id;
String type;
String name;
String city;
String country;
String? description;
factory Hotel.fromJson(Map<String, Object?> json) => _$HotelFromJson(json);
Map<String, Object?> toJson() => _$HotelToJson(this);
}
(explicitToJson: true)
class HotelDao {
const HotelDao(this.hotel);
final Hotel hotel;
factory HotelDao.fromJson(Map<String, Object?> json) =>
_$HotelDaoFromJson(json);
Map<String, Object?> toJson() => _$HotelDaoToJson(this);
}
final database = await Database.openAsync('hotels');
final collection = await database.defaultCollection;
final query = const QueryBuilder()
.select(SelectResult.all().as('hotel'))
.from(DataSource.collection(collection));
final resultSet = await query.execute();
await for (final result in resultSet.asStream()) {
final map = result.toPlainMap();
final hotelDao = HotelDao.fromJson(map);
final hotel = hotelDao.hotel;
// Do something with the hotel...
}
JSON String Formatβ
If your query selects all properties then the JSON format returned by
Result.toJson will be:
{
"collection-name": {
"key1": "value1",
"keyx": "valuex"
}
}
If your query selects a sub-set of available properties then the JSON format
returned by Result.toJson will be:
{
"key1": "value1",
"keyx": "valuex"
}