I am creating an app for recording and searching restaurants with Flutter. I am using Isar for local DB and Riverpod for state management.
There are two collections: restaurants and locations.
Restaurant
model:
@collection
class Restaurant {
Id? id;
@Index(unique: true)
late String name;
final location = IsarLink<Location>();
Restaurant({
required this.name,
});
}
Location
model:
@collection
class Location {
Id? id;
@Index(unique: true)
late String name;
@Backlink(to: 'location')
final restaurants = IsarLinks<Restaurant>();
Location({
required this.name,
});
}
My goal is to implement a search feature that can filter restaurants based on the query string (restaurant name) and locations. I plan to use QueryGroup
because I have to implement other filter values and manage its state later even though it is simplified for this question.
QueryGroup
class:
class QueryGroup {
final String queryString;
final List<Location> filterLocations;
QueryGroup({
required this.queryString,
required this.filterLocations,
});
}
Now I want to implement a restaurant notifier to manage the state of Future<List<Restaurant>>
to show a filtered restaurant list.
final isar = IsarHelper().isar;
@riverpod
class RestaurantsNotifier extends _$RestaurantsNotifier {
@override
Future<List<Restaurant>> build() async {
List<Restaurant> restaurants = await _fetchAllRestaurants();
return restaurants;
}
Future<List<Restaurant>> _fetchAllRestaurants() async {
List<Restaurant> restaurants = await isar.restaurants.where().findAll();
await _loadIsarLinksForRestaurants(restaurants);
state = AsyncValue.data(restaurants);
return restaurants;
}
void searchRestaurantsWithQueryGroup({required QueryGroup queryGroup}) async {
final List<Restaurant> filteredRestaurants = await isar.restaurants
.filter()
.nameContains(queryGroup.queryString, caseSensitive: false)
.and()
.location(
(q) => q.nameContains(
queryGroup.filterLocations[0].name, // I want to make this iteration of locations
caseSensitive: false,
),
)
.findAll();
state = AsyncValue.data(filteredRestaurants);
}
}
If a user's input is like this;
queryString:
French
Locations:[Location(name: 'Shibuya'), Location(name: 'Tokyo')]
Then the search should be French
for the restaurant name AND (Shibuya
OR Tokyo
for the locations).
I referred to the Isar doc (https://isar.dev/queries.html) but I don't get how to achieve this.
How can I modify the searchRestaurantsWithQueryGroup
method?
Looks like isar provides an anyOf
method that might be what you're looking for. This isn't tested/compiled, but your query could look something like this:
final List<Restaurant> filteredRestaurants = await isar.restaurants
.filter()
.nameContains(queryGroup.queryString, caseSensitive: false)
.and
.anyOf(
queryGroup.filterLocations,
(q, location) => q.nameContains(location.name, caseSensitive: false,)
).findAll();