#
Read Operations
#
Find Operations
You can search entities in a repository using find() method. There are several ways of using find() method using a filter or find options or both. You can also search an entity using it's id.
#
Filters
Nitrite uses filters to find entities in a repository. A filter is a simple expression which evaluates to true or false. More information about filters can be found here.
#
Find All Entities
You can find all entities in a repository by calling find(). It returns a Cursor object.
Cursor<Product> cursor = productRepository.find();
#
Finding an Entity Using Id
You can find an entity using it's id by calling getById(). It takes an id as input parameter. It returns an entity if the entity is found. Otherwise, it returns null.
var product = await productRepository.getById(ProductId(1));
#
Finding an Entity Using a Filter
You can find an entity using a filter. It takes a Filter object as input parameter. It returns a Cursor object.
If there is an index on the field specified in the filter, this operation will use the index to find the entity. Otherwise, it will scan the entire repository to find the entity.
Cursor<Product> cursor = productRepository.find(filter: where('productName').eq('Apple iPhone 6'));
#
Finding an Entity Using a Filter and Options
You can find an entity using a filter and options. It takes a Filter object as the first input parameter. It takes a FindOptions object as the second input parameter. It returns a Cursor object.
#
FindOptions
More information about FindOptions can be found here.
#
Cursor
Cursor represents a result set of a find operation. It provides methods to iterate over the result of a find operation and retrieve the entities. It also provides methods like projection, join etc. to get the desired result.
#
Iterating over Entities
The Cursor extends Stream of entities. So, you can iterate over the entities using await for loop.
Cursor<Product> cursor = productRepository.find();
await for (var product in cursor) {
// do something with product
}
A Cursor is a stream of entities. It does not load all the entities in memory at once. It loads the entity in memory as needed. So, it is memory efficient.
#
Getting the Entities
You can get the entities from a Cursor using toList() method. It returns a Future<List<T>> object.
Cursor<Product> cursor = productRepository.find();
var products = await cursor.toList();
#
Getting the First Entity
You can get the first entity from a Cursor using first getter. It returns a Future<T?> object.
Cursor<Product> cursor = productRepository.find();
var product = await cursor.first;
#
Getting the Size of the Cursor
You can get the size of the cursor using length getter. It returns a Future<int> object.
Cursor<Product> cursor = productRepository.find();
var size = await cursor.length;
#
Projection
You can project a field or a set of fields from a Cursor using project() method. It takes another entity type as input parameter. It returns a Stream of projected entities.
The projected entity must contain only the fields that needs to be projected.
Let's say you have an entity like this:
@Entity()
@Convertable()
class User {
String? firstName;
String? lastName;
Address? address;
User({this.firstName, this.lastName, this.address});
}
@Convertable()
class Address {
String? city;
String? state;
String? country;
Address({this.city, this.state, this.country});
}
Then you can project the lastName and address.street fields. Then you define a new entity like this:
@Entity()
@Convertable()
class UserProjection {
String? lastName;
StreetAddress? address;
UserProjection({this.lastName, this.address});
}
@Convertable()
class StreetAddress {
String? street;
StreetAddress({this.street});
}
And then you can project the cursor like this:
Cursor<User> cursor = userRepository.find(filter: where('firstName').eq('John'));
var projectedStream = cursor.project<UserProjection>();
#
Join
You can join two cursors using leftJoin() method. It takes another cursor as the first input parameter. It takes a Lookup as the second input parameter. The first type parameter is the type of the foreign entity. The second type parameter is the type of the joined entity. It returns a Stream of joined entities.
The join operation is similar to SQL left outer join operation. It takes two cursors and a lookup object. The lookup object contains the join condition.
Let's say you have two entities like this:
@Entity()
@Convertable()
class User {
String? userId;
String? firstName;
String? lastName;
User({this.userId, this.firstName, this.lastName});
}
@Entity()
@Convertable()
class Order {
String? orderId;
String? userId;
String? productName;
Order({this.orderId, this.userId, this.productName});
}
And you want to join these two entities using userId field. Then you can define a new entity like this:
@Entity()
@Convertable()
class UserOrder {
String? userId;
String? firstName;
String? lastName;
List<Order>? orders;
UserOrder({this.userId, this.firstName, this.lastName, this.orders});
}
And then you can join the cursors like this:
Cursor<User> userCursor = userRepository.find();
Cursor<Order> orderCursor = orderRepository.find();
var lookup = Lookup()
..localField = 'userId'
..foreignField = 'userId'
..alias = 'orders';
var joinedStream = userCursor.leftJoin<Order, UserOrder>(orderCursor, lookup);
#
FindPlan
You can get the FindPlan of a cursor using findPlan getter. It returns a future of FindPlan object.
More information about FindPlan can be found here.
Cursor<Product> cursor = productRepository.find(filter: where('productName').eq('Apple iPhone 6'));
var findPlan = await cursor.findPlan;