Document new features for version 1.5

This commit is contained in:
Simon Binder 2019-06-30 22:16:58 +02:00
parent 9082236be8
commit 464754267e
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
13 changed files with 218 additions and 45 deletions

View File

@ -6,10 +6,15 @@ permalink: /getting-started/
---
# Getting started
_Note:_ If you prefer a tutorial video, Reso Coder has made a detailed video explaining
how to get started. You can watch it [here](https://youtu.be/zpWsedYMczM).
{% include content/getting_started.md %}
Congratulations, you now have a class which you can use to easily write queries.
A detailed guide on how to do that is written [here]({{site.url}}/queries/).
A detailed guide on how to do that in Dart is written [here]({{"/queries" | absolute_url}}).
If you prefer to write SQL and have moor generate the mapping out, check out
[custom queries]({{"queries/custom" | absolute_url}})
PS: You might be asking how you would actually obtain an instance of `MyDatabase` for
your widgets. If so, [here]({{site.url}}/faq/#using-the-database) is some guidance.

View File

@ -11,6 +11,38 @@ features like `GROUP BY` statements or window functions are not yet supported. Y
use these features with custom statements. You don't have to miss out on other benefits
moor brings, though: Parsing the rows and query-streams also work on custom statements.
## Statements with a generated api
Starting from version `1.5`, you can instruct moor to automatically generate a typesafe
API for your select statements. At the moment, this feature is in an experimental state
but it can already handle most statements (`select`, `update` and `delete`). Of course,
you can still write custom sql manually. See the sections below for details.
To use this feature, all you need to is define your queries in your `UseMoor` annotation:
```dart
@UseMoor(
tables: [Todos, Categories],
queries: {
'categoriesWithCount':
'SELECT *, (SELECT COUNT(*) FROM todos WHERE category = c.id) AS "amount" FROM categories c;'
},
)
class MyDatabase extends _$MyDatabase {
// rest of class stays the same
}
```
After running the build step again, moor will have written the `CategoriesWithCountResult` class for you -
it will hold the result of your query. Also, the `_$MyDatabase` class from which you inherit will have the
methods `categoriesWithCount` (which runs the query once) and `watchCategoriesWithCount` (which returns
an auto-updating stream).
Queries can have parameters in them by using the `?` or `:name` syntax. When your queries contain parameters,
moor will figure out an appropriate type for them and include them in the generated methods. For instance,
`'categoryById': 'SELECT * FROM categories WHERE id = :id'` will generate the method `categoryById(int id)`.
You can also use `UPDATE` or `DELETE` statements here. Of course, this feature is also available for [daos]({{"/daos" | absolute_url}}),
and it perfectly integrates with auto-updating streams by analyzing what tables you're reading from or
writing to.
## Custom select statements
You can issue custom queries by calling `customSelect` for a one-time query or
`customSelectStream` for a query stream that automatically emits a new set of items when

View File

@ -51,17 +51,17 @@ Future<List<TodoEntry>> sortEntriesAlphabetically() {
You can also reverse the order by setting the `mode` property of the `OrderingTerm` to
`OrderingMode.desc`.
## Updates and deletes
You can use the generated `row` class to update individual fields of any row:
You can use the generated classes to update individual fields of any row:
```dart
Future moveImportantTasksIntoCategory(Category target) {
// use update(...).write when you have a custom where clause and want to update
// only the columns that you specify (here, only "category" will be updated, the
// title and description of the rows affected will be left unchanged).
// Notice that you can't set fields back to null with this method.
// for updates, we use the "companion" version of a generated class. This wraps the
// fields in a "Value" type which can be set to be absent using "Value.absent()". This
// allows us to separate between "SET category = NULL" (`category: Value(null)`) and not
// updating the category at all: `category: Value.absent()`.
return (update(todos)
..where((t) => t.title.like('%Important%'))
).write(TodoEntry(
category: target.id
).write(TodosCompanion(
category: Value(target.id),
),
);
}
@ -70,13 +70,13 @@ Future update(TodoEntry entry) {
// using replace will update all fields from the entry that are not marked as a primary key.
// it will also make sure that only the entry with the same primary key will be updated.
// Here, this means that the row that has the same id as entry will be updated to reflect
// the entry's title, content and category. Unlike write, this supports setting columns back
// to null. As it set's its where clause automatically, it can not be used together with where.
// the entry's title, content and category. As it set's its where clause automatically, it
// can not be used together with where.
return update(todos).replace(entry);
}
Future feelingLazy() {
// delete the oldest nine entries
// delete the oldest nine tasks
return (delete(todos)..where((t) => t.id.isSmallerThanValue(10))).go();
}
```

View File

@ -21,6 +21,52 @@ and more!
---
# Features
Moor contains a whole set of features that makes working with persistence much easier and safer.
## Declarative tables
With moor, you can declare your tables in pure dart without having to miss out on advanced sqlite
features. Moor will take care of writing the `CREATE TABLE` statements when the database is created.
## Fluent queries
Thanks to the power of Dart build system, moor will let you write typesafe queries:
```dart
Future<User> userById(int id) {
return (select(users)..where((user) => user.id.equals(id))).getSingle();
// runs SELECT * FROM users WHERE id = ?, automatically binds the parameter
// and parses the result row.
}
```
No more hard to debug typos in sql, no more annoying to write mapping code - moor takes
care of all the boring parts.
## Prefer SQL? Moor got you covered
Moor contains a powerful sql parser and analyzer, allowing it to create typesafe APIs for
all your sql queries:
```dart
@UseMoor(
tables: [Categories],
queries: {
'categoryById': 'SELECT * FROM categories WHERE id = :id'
},
)
class MyDatabase extends _$MyDatabase {
// the _$MyDatabase class will have the categoryById(int id) and watchCategoryById(int id)
// methods that execute the sql and parse its result into a generated class.
```
All queries are validated and analyzed during build-time, so that moor can provide hints
about potential errors quickly and generate efficient mapping code once.
## Auto-updating streams
For all your queries, moor can generate a `Stream` that will automatically emit new results
whenever the underlying data changes. This is first-class feature that perfectly integrates
with custom queries, daos and all the other features. Having an auto-updating single source
of truth makes managing perstistent state much easier!
## And much moor...
Moor also supports transactions, DAOs, powerful helpers for migrations, batched inserts and
many more features that makes writing persistence code much easier.
## Getting started
{% include content/getting_started.md %}
@ -28,21 +74,3 @@ You can ignore the `schemaVersion` at the moment, the important part is that you
now run your queries with fluent Dart code
## [Writing queries]({{"queries" | absolute_url }})
## TODO-List
There are some sql features like `group by` statements which aren't natively supported by moor yet.
However, as moor supports [custom sql queries]({{"queries/custom" | absolute_url}}), there are easy
workarounds for most entries on this list. Custom queries work well together with the regular api,
as they integrate with stream queries and automatic result parsing. Starting from version 1.5, moor
also has a custom sql parser that can infer types for variables and result columns. It can generate
typesafe APIs for sql queries.
### Limitations (at the moment)
These aren't sorted by priority. If you have more ideas or want some features happening soon,
let me know by [creating an issue]({{site.github_link}}/issues/new)! Again, note that these only
apply to the Dart api - all of these can be expressed using custom queries which nicely integrates
with the rest of the library.
- No `group by`, count, or window functions
- Support other platforms:
- VM apps
- Web apps via `AlaSQL` or a different engine?
- References (can be expressed via custom constraints, see issue [#14](https://github.com/simolus3/moor/issues/14))

View File

@ -1,13 +1,16 @@
## 1.5.0
This version introduces some new concepts and features, which are explained in more detail below.
Here is a quick overview of the new features.
Here is a quick overview of the new features:
- More consistent and reliable callbacks for migrations. You can now use `MigrationStrategy.beforeOpen`
to run queries after migrations, but before fully opening the database. This is useful to initialize data.
- Greatly expanded documentation, introduced additional checks to provide more helpful error messages
- New `getSingle` and `watchSingle` methods on queries: Queries that you know will only
return one row can now be instructed to return the value directly instead of wrapping it in a list.
- New "update companion" classes to clearly separate between absent values and explicitly setting
values back to null.
- Experimental support for compiled sql queries. Moor can now generate typesafe APIs for
written sql.
values back to null - explained below.
- Experimental support for compiled sql queries: __Moor can now generate typesafe APIs for
written sql__. Read on to get started.
### Update companions
Newly introduced "Update companions" allow you to insert or update data more precisely than before.
Previously, there was no clear separation between "null" and absent values. For instance, let's
@ -19,12 +22,15 @@ default `Value.absent()`.
Don't worry, all your existing code will continue to work, this change is fully backwards
compatible. You might get analyzer warnings about missing required fields. The migration to
update companions will fix that.
update companions will fix that. Replacing normal classes with their update companions is simple
and the only thing needed to fix that. The [documentation](https://moor.simonbinder.eu/queries/#updates-and-deletes)
has been updated to reflect this. If you have additional questions, feel free to
[create an issue](https://github.com/simolus3/moor/issues/new).
### Compiled sql queries
Experimental support for compile time custom statements. Sounds super boring, but it
actually gives you a fluent way to write queries in pure sql. The moor generator will figure
out what your queries return and automatically generate the boring mapping part. Head on to
`TODO: Documentation link` to find out how to use this new feature.
[the documentation](https://moor.simonbinder.eu/queries/custom) to find out how to use this new feature.
Please note that this feature is in an experimental state: Expect minor, but breaking changes
in the API and in the generated code. Also, if you run into any issues with this feature,

View File

@ -58,7 +58,7 @@ class TableWithoutPK extends Table {
queries: {
'allTodosWithCategory': 'SELECT t.*, c.id as catId, c."desc" as catDesc '
'FROM todos t INNER JOIN categories c ON c.id = t.category',
'deleteTodoById': 'DELETE FROM todos WHERE id = ?'
'deleteTodoById': 'DELETE FROM todos WHERE id = ?',
},
)
class TodoDb extends _$TodoDb {

View File

@ -1,3 +1,41 @@
## 1.5.0
This version introduces some new concepts and features, which are explained in more detail below.
Here is a quick overview of the new features:
- More consistent and reliable callbacks for migrations. You can now use `MigrationStrategy.beforeOpen`
to run queries after migrations, but before fully opening the database. This is useful to initialize data.
- Greatly expanded documentation, introduced additional checks to provide more helpful error messages
- New `getSingle` and `watchSingle` methods on queries: Queries that you know will only
return one row can now be instructed to return the value directly instead of wrapping it in a list.
- New "update companion" classes to clearly separate between absent values and explicitly setting
values back to null - explained below.
- Experimental support for compiled sql queries: __Moor can now generate typesafe APIs for
written sql__. Read on to get started.
### Update companions
Newly introduced "Update companions" allow you to insert or update data more precisely than before.
Previously, there was no clear separation between "null" and absent values. For instance, let's
say we had a table "users" that stores an id, a name, and an age. Now, let's say we wanted to set
the age of a user to null without changing its name. Would we use `User(age: null)`? Here,
the `name` column would implicitly be set to null, so we can't cleanly separate that. However,
with `UsersCompanion(age: Value(null))`, we know the difference between `Value(null)` and the
default `Value.absent()`.
Don't worry, all your existing code will continue to work, this change is fully backwards
compatible. You might get analyzer warnings about missing required fields. The migration to
update companions will fix that. Replacing normal classes with their update companions is simple
and the only thing needed to fix that. The [documentation](https://moor.simonbinder.eu/queries/#updates-and-deletes)
has been updated to reflect this. If you have additional questions, feel free to
[create an issue](https://github.com/simolus3/moor/issues/new).
### Compiled sql queries
Experimental support for compile time custom statements. Sounds super boring, but it
actually gives you a fluent way to write queries in pure sql. The moor generator will figure
out what your queries return and automatically generate the boring mapping part. Head on to
[the documentation](https://moor.simonbinder.eu/queries/custom) to find out how to use this new feature.
Please note that this feature is in an experimental state: Expect minor, but breaking changes
in the API and in the generated code. Also, if you run into any issues with this feature,
[reporting them](https://github.com/simolus3/moor/issues/new) would be super appreciated.
## 1.4.0
- Added the `RealColumn`, which stores floating point values
- Better configuration for the serializer with the `JsonKey` annotation and the ability to

View File

@ -3,6 +3,54 @@
Moor is an easy to use, reactive persistence library for Flutter apps. Define your database tables in pure Dart and
enjoy a fluent query API, auto-updating streams and more!
Here are just some of the many features moor provides to make dealing with persistence much
easier:
## Declarative tables
With moor, you can declare your tables in pure dart without having to miss out on advanced sqlite
features. Moor will take care of writing the `CREATE TABLE` statements when the database is created.
## Fluent queries
Thanks to the power of Dart build system, moor will let you write typesafe queries:
```dart
Future<User> userById(int id) {
return (select(users)..where((user) => user.id.equals(id))).getSingle();
// runs SELECT * FROM users WHERE id = ?, automatically binds the parameter
// and parses the result row.
}
```
No more hard to debug typos in sql, no more annoying to write mapping code - moor takes
care of all the boring parts. Moor supports features like order by statements, limits and
even joins with this api.
## Prefer SQL? Moor got you covered
Moor contains a powerful sql parser and analyzer, allowing it to create typesafe APIs for
all your sql queries:
```dart
@UseMoor(
tables: [Categories],
queries: {
'categoryById': 'SELECT * FROM categories WHERE id = :id'
},
)
class MyDatabase extends _$MyDatabase {
// the _$MyDatabase class will have the categoryById(int id) and watchCategoryById(int id)
// methods that execute the sql and parse its result into a generated class.
```
All queries are validated and analyzed during build-time, so that moor can provide hints
about potential errors quickly and generate efficient mapping code once.
## Auto-updating streams
For all your queries, moor can generate a `Stream` that will automatically emit new results
whenever the underlying data changes. This is first-class feature that perfectly integrates
with custom queries, daos and all the other features. Having an auto-updating single source
of truth makes managing perstistent state much easier!
## And much moor...
Moor also supports transactions, DAOs, powerful helpers for migrations, batched inserts and
many more features that makes writing persistence code much easier.
## Getting started
For a more detailed guide on using moor, check out the [documentation](https://moor.simonbinder.eu/).
### Adding the dependency

View File

@ -1,3 +1,10 @@
## 1.5.0
- Parse custom queries and write generated mapping code.
- Refactorings and minor improvements in the generator
For more details on the new features, check out changelog of the
[moor](https://pub.dev/packages/moor#-changelog-tab-) package.
## 1.4.0
- Added the `RealColumn`, which stores floating point values
- Better configuration for the serializer with the `JsonKey` annotation and the ability to

View File

@ -36,8 +36,15 @@ class MoorGenerator extends GeneratorForAnnotation<UseMoor> {
tablesForThisDb.add(state.parseType(table, element));
}
if (queries.isNotEmpty) {
final parser = SqlParser(state, tablesForThisDb, queries)..parse();
resolvedQueries = parser.foundQueries;
}
if (state.errors.errors.isNotEmpty) {
print('Warning: There were some errors while running moor_generator:');
print('Warning: There were some errors while running '
'moor_generator on ${buildStep.inputId.path}:');
for (var error in state.errors.errors) {
print(error.message);
@ -50,12 +57,6 @@ class MoorGenerator extends GeneratorForAnnotation<UseMoor> {
state.errors.errors.clear();
}
if (queries.isNotEmpty) {
final parser = SqlParser(state, tablesForThisDb, queries)..parse();
resolvedQueries = parser.foundQueries;
}
if (tablesForThisDb.isEmpty) return '';
final specifiedDb = SpecifiedDatabase(

View File

@ -38,6 +38,7 @@ class SqlParser {
state.errors.add(MoorError(
critical: true,
message: 'Error while trying to parse $sql: $e, $s'));
return;
}
for (var error in context.errors) {

View File

@ -12,6 +12,7 @@ class ColumnResolver extends RecursiveVisitor<void> {
@override
void visitSelectStatement(SelectStatement e) {
_resolveSelect(e);
visitChildren(e);
}
void _handle(Queryable queryable, List<Column> availableColumns) {

View File

@ -94,7 +94,13 @@ class TypeResolver {
} else if (expr is CaseExpression) {
return resolveExpression(expr.whens.first.then);
} else if (expr is SubQuery) {
// todo
final columns = expr.select.resultSet.resolvedColumns;
if (columns.length != 1) {
// select queries _must_ have exactly one column
return const ResolveResult.unknown();
} else {
return justResolve(columns.single);
}
}
throw StateError('Unknown expression $expr');