From 7fb21ba9f2ad123ce69a24127b86c9074e0d0688 Mon Sep 17 00:00:00 2001 From: Moshe Dicker Date: Sun, 20 Oct 2024 19:03:02 -0400 Subject: [PATCH] ok --- docs/docs/dart_api/manager.md | 98 +++++++++++-- docs/docs/dart_api/tables.md | 2 +- docs/lib/snippets/dart_api/manager.dart | 185 +++++++++++++++++------- docs/lib/snippets/setup/database.dart | 5 +- docs/mkdocs/mkdocs.yml | 3 +- 5 files changed, 218 insertions(+), 75 deletions(-) diff --git a/docs/docs/dart_api/manager.md b/docs/docs/dart_api/manager.md index a241bec8e..165e24b44 100644 --- a/docs/docs/dart_api/manager.md +++ b/docs/docs/dart_api/manager.md @@ -5,29 +5,99 @@ description: Use easier bindings for common queries. --- +Drift offers three main approaches for querying your database: -The examples on this page use the database from the [setup](../setup.md) -instructions. +- **Manages API**: The Manager API provides a simpler, more intuitive interface for common operations +- **Core API**: The Core API provides a more flexible and powerful interface for complex queries +- **Raw SQL**: For those comfortable with SQL, you can write raw SQL queries directly. The SQL is parsed and validated at compile time. -When manager generation is enabled (default), drift will generate a manager for each table in the database. -A collection of these managers are accessed by a getter `managers` on the database class. -Each table will have a manager generated for it unless it uses a custom row class. +This page will cover the Manager API and the Core API. For more information on raw SQL queries, see the [Raw SQL](./raw_sql.md) page. -## Select +??? tip "Disable Manager API" + If you don't want to use the Manager API, you can disable it by setting `generate_manager` to `false` in the `drift` section of your `build.yaml` file. This will save you some build time and reduce the size of your generated code. + + ```yaml title="build.yaml" + targets: + $default: + builders: + drift_dev: + options: + generate_manager: false + ``` + +### Example schema + +The examples on this page use the following database schema: + +{{ load_snippet('before_generation','lib/snippets/setup/database.dart.excerpt.json') }} + + +## Read + +=== "Manager API" + + + To select all rows from a table, just call the `get()`/`watch()` method on the table manager. This will return a list of all rows in the table. + + {{ load_snippet('manager_select','lib/snippets/dart_api/manager.dart.excerpt.json') }} + +=== "Core API" + + Write `SELECT` queries using the `select` method on the database class. This method returns a query object which can be used to retrieve rows from the database. + + Any query can be run once with `get()` or be turned into an auto-updating stream using `watch()`. + + {{ load_snippet('core_select','lib/snippets/dart_api/manager.dart.excerpt.json') }} + + +### Limit and Offset + +You can limit the amount of results returned by calling `limit` on queries. The method accepts +the amount of rows to return and an optional offset. + +=== "Manager API" + + {{ load_snippet('manager_limit','lib/snippets/dart_api/manager.dart.excerpt.json') }} + +=== "Core API" + + {{ load_snippet('core_limit','lib/snippets/dart_api/manager.dart.excerpt.json') }} + + + +### Filtering + +#### Simple filters + +Drift generates prebuilt filters for each column in your table. These filters can be used to filter rows based on the value of a column. + +=== "Manager API" + + You can apply filters to a query by calling `filter()`. The filter method takes a function that should return a filter on the given table. + + {{ load_snippet('manager_filter','lib/snippets/dart_api/manager.dart.excerpt.json') }} + +=== "Core API" + + You can apply filters to a query by calling `where()`. The `where` method takes a function that should map the given table to an `Expression` of boolean. For more details on expressions, see the [expression](./expressions.md) docs. + + {{ load_snippet('core_filter','lib/snippets/dart_api/manager.dart.excerpt.json') }} + +#### Complex filters + +- Use the `&` and `|` operators to combine multiple filters. +- Use `()` to group filters. +- Use `.not` to negate a condition. -The manager simplifies the process of retrieving rows from a table. Use it to read rows from the table or watch -for changes. -{{ load_snippet('manager_select','lib/snippets/dart_api/manager.dart.excerpt.json') }} +=== "Manager API" -The manager provides a really easy to use API for selecting rows from a table. These can be combined with `|` and `&` and parenthesis to construct more complex queries. Use `.not` to negate a condition. + {{ load_snippet('manager_complex_filter','lib/snippets/dart_api/manager.dart.excerpt.json') }} -{{ load_snippet('manager_filter','lib/snippets/dart_api/manager.dart.excerpt.json') }} +=== "Core API" -Every column has filters for equality, inequality and nullability. -Type specific filters for `int`, `double`, `Int64`, `DateTime` and `String` are included out of the box. + {{ load_snippet('core_complex_filter','lib/snippets/dart_api/manager.dart.excerpt.json') }} -{{ load_snippet('manager_type_specific_filter','lib/snippets/dart_api/manager.dart.excerpt.json') }} ### Referencing other tables diff --git a/docs/docs/dart_api/tables.md b/docs/docs/dart_api/tables.md index c72b41d0a..994bcc56e 100644 --- a/docs/docs/dart_api/tables.md +++ b/docs/docs/dart_api/tables.md @@ -1,6 +1,6 @@ --- -title: Tables +title: Schema description: Define the schema of your database. --- diff --git a/docs/lib/snippets/dart_api/manager.dart b/docs/lib/snippets/dart_api/manager.dart index 59fec8a12..68c7a2770 100644 --- a/docs/lib/snippets/dart_api/manager.dart +++ b/docs/lib/snippets/dart_api/manager.dart @@ -1,4 +1,4 @@ -// ignore_for_file: invalid_use_of_internal_member, unused_local_variable, unused_element +// ignore_for_file: invalid_use_of_internal_member, unused_local_variable, unused_element, avoid_single_cascade_in_expression_statements import 'package:drift/drift.dart'; @@ -103,43 +103,6 @@ extension ManagerExamples on AppDatabase { } // #enddocregion manager_delete - // #docregion manager_select - Future selectTodoItems() async { - // Get all items - managers.todoItems.get(); - - // A stream of all the todo items, updated in real-time - managers.todoItems.watch(); - - // To get a single item, apply a filter and call `getSingle` - await managers.todoItems.filter((f) => f.id(1)).getSingle(); - } - // #enddocregion manager_select - - // #docregion manager_filter - Future filterTodoItems() async { - // All items with a title of "Title" - managers.todoItems.filter((f) => f.title("Title")); - - // All items with a title of "Title" and content of "Content" - managers.todoItems.filter((f) => f.title("Title") & f.content("Content")); - - // All items with a title of "Title" or content that is not null - managers.todoItems.filter((f) => f.title("Title") | f.content.not.isNull()); - } - // #enddocregion manager_filter - - // #docregion manager_type_specific_filter - Future filterWithType() async { - // Filter all items created since 7 days ago - managers.todoItems.filter( - (f) => f.createdAt.isAfter(DateTime.now().subtract(Duration(days: 7)))); - - // Filter all items with a title that starts with "Title" - managers.todoItems.filter((f) => f.title.startsWith('Title')); - } -// #enddocregion manager_type_specific_filter - // #docregion manager_ordering Future orderWithType() async { // Order all items by their creation date in ascending order @@ -210,23 +173,6 @@ extension ManagerExamples on AppDatabase { } // #enddocregion manager_filter_custom_back_references -// #docregion manager_references - Future references() async { - /// Get each todo, along with a its categories - final todosWithRefs = await managers.todoItems.withReferences().get(); - for (final (todo, refs) in todosWithRefs) { - final category = await refs.category?.getSingle(); - } - - /// This also works in the reverse - final categoriesWithRefs = - await managers.todoCategory.withReferences().get(); - for (final (category, refs) in categoriesWithRefs) { - final todos = await refs.todoItemsRefs.get(); - } - } - -// #enddocregion manager_references // #docregion manager_prefetch_references Future referencesPrefetch() async { /// Get each todo, along with a its categories @@ -392,3 +338,132 @@ void _managerAggregatedAnnotations(AppDatabase db) async { } // #enddocregion aggregated_annotations } + +Future selectTodoItems(AppDatabase db) async { + // #docregion manager_select + await db.managers.todoItems.get(); + // #enddocregion manager_select + // #docregion core_select + await db.select(db.todoItems).get(); + // #enddocregion core_select + + // #docregion manager_watch + db.managers.todoItems.watch(); + + // #enddocregion manager_watch + // #docregion core_watch + db.select(db.todoItems).watch(); + // #enddocregion core_watch + + // #docregion manager_limit + // Get 1st 10 items + await db.managers.todoItems.limit(10).get(); + // Get the next 10 items + await db.managers.todoItems.limit(10, offset: 10).get(); + // #enddocregion manager_limit + // #docregion core_limit + // Get 1st 10 items + await (db.select(db.todoItems)..limit(10)).get(); + // Get the next 10 items + await (db.select(db.todoItems)..limit(10, offset: 10)).get(); + // #enddocregion core_limit +} + +Future filterTodoItems(AppDatabase db) async { + // #docregion manager_filter + // All items with a title of "Title" + db.managers.todoItems.filter((f) => f.title("Title")); + // #enddocregion manager_filter + + // #docregion core_filter + // All items with a title of "Title" + db.select(db.todoItems)..where((tbl) => tbl.title.equals("Title")); + // #enddocregion core_filter + + // #docregion manager_complex_filter + // All items with a title of "Title" and content of "Content" + db.managers.todoItems.filter((f) => f.title("Title") & f.content("Content")); + + /// Todos that: + /// 1. Have a title that is not "Title" + /// OR + /// 2. Have no content and start with "Hello World" + db.managers.todoItems.filter( + (f) => + f.title("Title").not() | + (f.content.isNull() & f.content.startsWith("Hello World")), + ); + // #enddocregion manager_complex_filter + + // #docregion core_complex_filter + // All items with a title of "Title" and content of "Content" + db.select(db.todoItems) + ..where((tbl) => tbl.title.equals("Title") & tbl.content.equals("Content")); + + /// Todos that: + /// 1. Have a title that is not "Title" + /// OR + /// 2. Have no content and start with "Hello World" + db.select(db.todoItems) + ..where( + (tbl) => + tbl.title.equals("Title").not() | + (tbl.content.isNull() & tbl.content.like("Hello World%")), + ); + // #enddocregion core_complex_filter +} + +Future filterTodoItemsRefs(AppDatabase db) async { + // #docregion manager_filter_references + // All items with a category description of "School" + db.managers.todoItems.filter((f) => f.category.description("School")); + // #enddocregion manager_filter_references + + // #docregion core_filter_references + // All items with a title of "Title" + db.select(db.todoItems).join([ + leftOuterJoin( + db.todoCategory, db.todoCategory.id.equalsExp(db.todoItems.category), + useColumns: false) + ]).where(db.todoCategory.description.equals("School")); + // #enddocregion core_filter_references + + // #docregion manager_references + for (final (todo, refs) + in await db.managers.todoItems.withReferences().get()) { + final category = await refs.category?.getSingle(); + print( + 'Todo ${todo.id} has a category with the description ${category?.description}'); + } + // #enddocregion manager_references + // #docregion manager_references_prefetch + for (final (todo, refs) in await db.managers.todoItems + .withReferences((prefetch) => prefetch(category: true)) + .get()) { + final category = refs.category?.prefetchedData?.firstOrNull; + print( + 'Todo ${todo.id} has a category with the description ${category?.description}'); + } + // #enddocregion manager_references_prefetch + + // #docregion core_references + // Build a select statement with a left outer join to fetch the referenced data + final query = db.select(db.todoItems).join([ + leftOuterJoin( + db.todoCategory, db.todoCategory.id.equalsExp(db.todoItems.category)) + ]) + ..where(db.todoCategory.description.equals("School")); + + // Map the results to a list of tuples containing the todo and category + final results = await query + .asyncMap((p0) => + (p0.readTable(db.todoItems), p0.readTableOrNull(db.todoCategory))) + .get(); + + // Print the results + for (final (todo, category) in results) { + print( + 'Todo ${todo.id} has a category with the description ${category?.description}'); + } + // #enddocregion core_references +} diff --git a/docs/lib/snippets/setup/database.dart b/docs/lib/snippets/setup/database.dart index 92f5a144d..3e6bc2360 100644 --- a/docs/lib/snippets/setup/database.dart +++ b/docs/lib/snippets/setup/database.dart @@ -34,10 +34,10 @@ class TodoCategory extends Table { TextColumn get description => text()(); } +// #enddocregion before_generation // #enddocregion table @DriftDatabase(tables: [TodoItems, TodoCategory]) class AppDatabase extends _$AppDatabase { -// #enddocregion before_generation // After generating code, this class needs to define a `schemaVersion` getter // and a constructor telling drift where the database should be stored. // These are described in the getting started guide: https://drift.simonbinder.eu/getting-started/#open @@ -50,10 +50,7 @@ class AppDatabase extends _$AppDatabase { static QueryExecutor _openConnection() { throw 'should not show as snippet'; } - -// #docregion before_generation } -// #enddocregion before_generation class OpenFlutter { // #docregion flutter diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml index 9d8c2830b..7dc1c9ecb 100644 --- a/docs/mkdocs/mkdocs.yml +++ b/docs/mkdocs/mkdocs.yml @@ -27,6 +27,7 @@ theme: logo: images/icon.png favicon: images/icon.png features: + - content.tabs.link - content.tooltips - navigation.instant - navigation.tracking @@ -94,8 +95,8 @@ nav: - Documentation: - Getting Started: setup.md - dart_api/tables.md - - dart_api/dataclass.md - dart_api/manager.md + - dart_api/dataclass.md - dart_api/transactions.md - Core API: - dart_api/select.md