diff --git a/vlib/orm/README.md b/vlib/orm/README.md index 1a141fedce..56352f4c41 100644 --- a/vlib/orm/README.md +++ b/vlib/orm/README.md @@ -1,32 +1,45 @@ # ORM -## Null +V has a powerful, concise ORM baked in! Create tables, insert records, manage relationships, all +regardless of the DB driver you decide to use. -Use option fields in V structs for fields which can be NULL. Regular, -non-option fields are defied as NOT NULL when creating tables. +## Nullable + +For a nullable column, use an option field. If the field is non-option, the column will be defined +with `NOT NULL` at table creation. + +```v ignore +struct Foo { + notnull string + nullable ?string +} +``` ## Attributes ### Structs -- `[table: 'name']` sets a custom table name +- `[table: 'name']` explicitly sets the name of the table for the struct ### Fields - `[primary]` sets the field as the primary key -- `[unique]` sets the field as unique -- `[unique: 'foo']` adds the field to a unique group +- `[unique]` gives the field a `UNIQUE` constraint +- `[unique: 'foo']` adds the field to a `UNIQUE` group - `[skip]` or `[sql: '-']` field will be skipped - `[sql: type]` where `type` is a V type such as `int` or `f64` -- `[sql: serial]` lets the DB backend choose a column type for a auto-increment field +- `[sql: serial]` lets the DB backend choose a column type for an auto-increment field - `[sql: 'name']` sets a custom column name for the field -- `[sql_type: 'SQL TYPE']` sets the sql type which is used in sql -- `[default: 'raw_sql]` inserts `raw_sql` verbatim in a "DEFAULT" clause when - create a new table, allowing for values like `CURRENT_TIME` +- `[sql_type: 'SQL TYPE']` explicitly sets the type in SQL +- `[default: 'raw_sql']` inserts `raw_sql` verbatim in a "DEFAULT" clause when + creating a new table, allowing for SQL functions like `CURRENT_TIME`. For raw strings, + surround `raw_sql` with backticks (`). - `[fkey: 'parent_id']` sets foreign key for an field which holds an array ## Usage +Here are a couple example structs showing most of the features outlined above. + ```v ignore import time @@ -47,23 +60,55 @@ struct Child { } ``` -### Create +To use the ORM, there is a special interface that lets you use the structs and V itself in queries. +This interface takes the database instance as an argument. ```v ignore +import db.sqlite + +db := sqlite.connect(':memory:')! + sql db { - create table Foo + // query; see below }! ``` -### Drop +When you need to reference the table, simply pass the struct itself. ```v ignore +import models.Foo + +struct Bar { + id int @[primary; sql: serial] +} + sql db { - drop table Foo + create table models.Foo + create table Bar }! ``` -### Insert +### Create & Drop Tables + +You can create and drop tables by passing the struct to `create table` and `drop table`. + +```v ignore +import models.Foo + +struct Bar { + id int @[primary; sql: serial] +} + +sql db { + create table models.Foo + drop table Bar +}! +``` + +### Insert Records + +To insert a record, create a struct and pass the variable to the query. Again, reference the struct +as the table. ```v ignore foo := Foo{ @@ -86,19 +131,48 @@ foo_id := sql db { }! ``` -If the `id` field is marked as `serial` and `primary`, the insert expression +If the `id` field is marked as `sql: serial` and `primary`, the insert expression returns the database ID of the newly added object. Getting an ID of a newly added DB row is often useful. When inserting, `[sql: serial]` fields, and fields with a `[default: 'raw_sql']` -attribute are not sent to the database when the value being sent is the default +attribute, are not sent to the database when the value being sent is the default for the V struct field (e.g., 0 int, or an empty string). This allows the database to insert default values for auto-increment fields and where you have specified a default. +### Select + +You can select rows from the database by passing the struct as the table, and +use V syntax and functions for expressions. Selecting returns an array of the +results. + +```v ignore +result := sql db { + select from Foo where id == 1 +}! + +foo := result.first() +``` + +```v ignore +result := sql db { + select from Foo where id > 1 && name != 'lasanha' limit 5 +}! +``` + +```v ignore +result := sql db { + select from Foo where id > 1 order by id +}! +``` + ### Update +You can update fields in a row using V syntax and functions. Again, pass the struct +as the table. + ```v ignore sql db { update Foo set updated_at = time.now() where name == 'abc' && updated_at is none @@ -108,30 +182,25 @@ sql db { Note that `is none` and `!is none` can be used to select for NULL fields. ### Delete + +You can delete rows using V syntax and functions. Again, pass the struct +as the table. + ```v ignore sql db { delete from Foo where id > 10 }! ``` -### Select -```v ignore -result := sql db { - select from Foo where id == 1 -}! -``` -```v ignore -result := sql db { - select from Foo where id > 1 && name != 'lasanha' limit 5 -}! -``` -```v ignore -result := sql db { - select from Foo where id > 1 order by id -}! -``` +### time.Time Fields + +It's definitely useful to cast a field as `time.Time` so you can use V's built-in time functions; +however, this is handled a bit differently than expected in the ORM. `time.Time` fields are +created as integer columns in the database. Because of this, the usual time functions +(`current_timestamp`, `NOW()`, etc) in SQL do not work as defaults. + +## Example -### Example ```v ignore import db.pg