ent/doc: transaction example and docs

Summary: {F205899335}

Reviewed By: dlvhdr

Differential Revision: D17149531

fbshipit-source-id: cb8595d41ede6f813370564ca688f33d0dfe6905
This commit is contained in:
Ariel Mashraki
2019-09-01 07:21:26 -07:00
committed by Facebook Github Bot
parent 4323141fe2
commit 9b7ea021ef
10 changed files with 322 additions and 54 deletions

View File

@@ -5,10 +5,10 @@ sidebar_label: Quick Introduction
---
`ent` is a simple, yet powerful entity framework for Go built with the following principles:
- Model your data as graph easily.
- Easily modeling your data as a graph structure.
- Defining your schema as code.
- Static typing first based on code generation.
- Make the work with graph-like data in Go easier.
- Static typing based on code generation.
- Simplifying graph traversals.
## Installation
@@ -51,7 +51,7 @@ func (User) Edges() []ent.Edge {
```
Let's add 2 fields to the `User` schema, and then run `entc generate`:
Add 2 fields to the `User` schema, and then run `entc generate`:
```go
package schema
@@ -73,13 +73,13 @@ func (User) Fields() []ent.Field {
}
```
Running `entc generate` from the root directory of the project:
Run `entc generate` from the root directory of the project:
```go
$ entc generate ./ent/schema
```
Will produce the following files:
This produces the following files:
```
ent
├── client.go
@@ -108,8 +108,7 @@ ent
## Create Your First Entity
First thing we need to do, is creating a new `ent.Client`. For the example purpose,
we will use SQLite3.
To get started, create a new `ent.Client`. For this example, we will use SQLite3.
```go
package main
@@ -141,7 +140,7 @@ func main() {
}
```
Now, we're ready to create our user. Let's call this function `Do` for the sake of the example:
Now, we're ready to create our user. Let's call this function `Do` for the sake of example:
```go
func Do(ctx context.Context, client *ent.Client) (*ent.User, error) {
u, err := client.User.
@@ -160,7 +159,7 @@ func Do(ctx context.Context, client *ent.Client) (*ent.User, error) {
## Query Your Entities
`entc` generates a package for each entity schema that contains its predicates, default values, validators
and information about storage elements (like, column names, primary keys, etc).
and additional information about storage elements (column names, primary keys, etc).
```go
package main
@@ -189,15 +188,15 @@ func Query(ctx context.Context, client *ent.Client) (*ent.User, error) {
## Add Your First Edge (Relation)
In this part of the tutorial, we want to declare an edge to another entity in the schema.
In this part of the tutorial, we want to declare an edge (relation) to another entity in the schema.
Let's create 2 additional entities named `Car` and `Group` with a few fields. We use `entc`
to generate the initial schema:
to generate the initial schemas:
```console
$ entc init Car Group
```
And then, we add the rest of the fields manually:
And then we add the rest of the fields manually:
```go
import (
"log"
@@ -226,7 +225,7 @@ func (Group) Fields() []ent.Field {
```
Let's define our first relation. An edge from `User` to `Car` defining that a user
can have 1 or more cars, but a car has only one owner (one-to-many relation).
can **have 1 or more** cars, but a car **has only one** owner (one-to-many relation).
![er-user-cars](https://entgo.io/assets/re_user_cars.png)
@@ -248,7 +247,7 @@ Let's add the `"cars"` edge to the `User` schema, and run `entc generate ./ent/s
}
```
We continue our example, by creating 2 cars, and add them to a user.
We continue our example by creating 2 cars and adding them to a user.
```go
func Do(ctx context.Context, client *ent.Client) error {
// creating new car with model "Tesla".
@@ -285,7 +284,7 @@ func Do(ctx context.Context, client *ent.Client) error {
log.Println("user was created: %v", a8m)
}
```
But, what about querying the "cars" edge? Here's how we do it:
But what about querying the `cars` edge (relation)? Here's how we do it:
```go
import (
"log"
@@ -316,16 +315,16 @@ func Do(ctx context.Context, client *ent.Client) error {
```
## Add Your First Inverse Edge (BackRef)
Assume we have a `Car` object and we want to get its owner; The user that this car belongs to.
Assume we have a `Car` object and we want to get its owner; the user that this car belongs to.
For this, we have another type of edge called "inverse edge" that is defined using the `edge.From`
function.
![er-cars-owner](https://entgo.io/assets/re_cars_owner.png)
The new edge created in the diagram above is transparent, to emphasis that we don't create another
edge in the database, and it is just a back-reference to the real edge.
The new edge created in the diagram above is translucent, to emphasize that we don't create another
edge in the database. It's just a back-reference to the real edge (relation).
Let's add an inverse edge named `"owner"` to the `Car` schema, reference it to the `"cars"` edge
Let's add an inverse edge named `owner` to the `Car` schema, reference it to the `cars` edge
in the `User` schema, and run `entc generate ./ent/schema`.
```go
@@ -381,13 +380,13 @@ func Do(ctx context.Context, client *ent.Client) error {
## Create Your Second Edge
We'll continue our example, by creating a M2M relationship between users and groups.
We'll continue our example by creating a M2M (many-to-many) relationship between users and groups.
![er-group-users](https://entgo.io/assets/re_group_users.png)
As you can see, each group entity can have many users, and a user can be connected to many groups.
A simple "many-to-many" relationship. In the above illustration, the `Group` schema is the owner
of the `users` edge (relationship), and the `User` entity has a back-reference/inverse edge to this
As you can see, each group entity can **have many** users, and a user can **be connected to many** groups;
a simple "many-to-many" relationship. In the above illustration, the `Group` schema is the owner
of the `users` edge (relation), and the `User` entity has a back-reference/inverse edge to this
relationship named `groups`. Let's define this relationship in our schemas:
- `<project>/ent/schema/group.go`:
@@ -435,15 +434,15 @@ relationship named `groups`. Let's define this relationship in our schemas:
}
```
We run `entc` on the schema directory, to re-generate the assets.
We run `entc` on the schema directory to re-generate the assets.
```cosole
$ entc generate ./ent/schema
```
## Run Your First Graph Traversal
In order to run our first graph traversal, we need to generate some data (nodes and edges).
Let's create the following graph using the framework:
In order to run our first graph traversal, we need to generate some data (nodes and edges, or in other words,
entities and relations). Let's create the following graph using the framework:
![re-graph](https://entgo.io/assets/re_graph_getting_started.png)
@@ -517,9 +516,9 @@ func CreateGraph(ctx context.Context, client *ent.Client) error {
}
```
Now, when we have a graph with data, we can run a few queries on it:
Now when we have a graph with data, we can run a few queries on it:
1. Get all user's cars of group named "Github":
1. Get all user's cars within the group named "Github":
```go
import (
@@ -543,7 +542,7 @@ Now, when we have a graph with data, we can run a few queries on it:
}
```
2. Changing the query above, so that the source of the traversal is the user *Ariel*:
2. Change the query above, so that the source of the traversal is the user *Ariel*:
```go
import (
@@ -575,7 +574,7 @@ Now, when we have a graph with data, we can run a few queries on it:
}
```
3. Get all groups that have users (query with look-aside):
3. Get all groups that have users (query with a look-aside predicate):
```go
import (

View File

@@ -63,4 +63,5 @@ $ entc init User Group
If you are used to the definition of relations over edges, that's fine.
The modeling is the same. You can model with `ent` whatever you can model
with other traditional ORMs.
There are many examples in this website that will help you to get started.
There are many examples in this website that will help you to get started,
and can be found in the [edges section](schema-edges.md).

View File

@@ -5,8 +5,129 @@ title: Transactions
## Starting A Transaction
```go
// GenTx generates group of entities in a transaction.
func GenTx(ctx context.Context, client *ent.Client) error {
tx, err := client.Tx(ctx)
if err != nil {
return fmt.Errorf("starting a transaction: %v", err)
}
hub, err := tx.Group.
Create().
SetName("Github").
Save(ctx)
if err != nil {
return rollback(tx, fmt.Errorf("failed creating the group: %v", err))
}
// Create the admin of the group.
dan, err := tx.User.
Create().
SetAge(29).
SetName("Dan").
AddManage(hub).
Save(ctx)
if err != nil {
return rollback(tx, err)
}
// Create user "Ariel".
a8m, err := tx.User.
Create().
SetAge(30).
SetName("Ariel").
AddGroups(hub).
AddFriends(dan).
Save(ctx)
if err != nil {
return rollback(tx, err)
}
fmt.Println(a8m)
// Output:
// User(id=2, age=30, name=Ariel)
// Commit the transaction.
return tx.Commit()
}
// rollback calls to tx.Rollback and wraps the given error
// with the rollback error if occurred.
func rollback(tx *ent.Tx, err error) error {
if rerr := tx.Rollback(); rerr != nil {
err = fmt.Errorf("%v: %v", err, rerr)
}
return err
}
```
The full example exists in [GitHub](https://github.com/facebookincubator/ent/tree/master/examples/traversal).
## Transactional Client
Sometimes, you have an existing code that already work with `*ent.Client`, and you want to change it (or wrap it)
to interact with transactions. For these use cases, you have a transactional client. An `*ent.Client` that you can
get from an existing transaction.
```go
// WrapGen wraps the existing "Gen" function in a transaction.
func WrapGen(ctx context.Context, client *ent.Client) error {
tx, err := client.Tx(ctx)
if err != nil {
return err
}
txClient := tx.Client()
// Use the "Gen" below, but give it the transactional client; no code changes to "Gen".
if err := Gen(ctx, txClient); err != nil {
return rollback(tx, err)
}
return tx.Commit()
}
// Gen generates a group of entities.
func Gen(ctx context.Context, client *ent.Client) error {
// ...
return nil
}
```
The full example exists in [GitHub](https://github.com/facebookincubator/ent/tree/master/examples/traversal).
## Best Practices
Reusable function that runs callbacks in a transaction:
```go
func WithTx(ctx context.Context, client *ent.Client, fn func(tx *ent.Tx) error) error {
tx, err := client.Tx(ctx)
if err != nil {
return err
}
defer func() {
if v := recover(); v != nil {
tx.Rollback()
panic(v)
}
}()
if err := fn(tx); err != nil {
if rerr := tx.Rollback(); rerr != nil {
err = errors.Wrapf(err, "rolling back transaction: %v", rerr)
}
return err
}
if err := tx.Commit(); err != nil {
return errors.Wrapf(err, "committing transaction: %v", err)
}
return nil
}
```
Its usage:
```go
func Do(ctx context.Context, client *ent.Client) {
// WithTx helper.
if err := WithTx(ctx, client, func(tx *ent.Tx) error {
return Gen(ctx, tx.Client())
}); err != nil {
log.Fatal(err)
}
}
```

View File

@@ -40,7 +40,7 @@ class Footer extends React.Component {
</a>
</div>
<div>
<h5>Credits</h5>
<h5>Credit</h5>
<span className="copyright">
The Go gopher was designed by{' '}
<a

View File

@@ -21,7 +21,7 @@ const Block = props => (
<div className="block">
<div className="blockTitle">
<div className="blockTitleText">{props.title}</div>{' '}
<div className="yellowArrow">{arrow}</div>
<a className="yellowArrow" href={props.link}>{arrow}</a>
</div>
<div className="blockContent">{props.content}</div>
</div>
@@ -32,14 +32,17 @@ const Features = () => (
<Block
title="Schema As Code"
content="Simple API for modeling any graph schema as Go objects"
link="docs/schema-def"
/>
<Block
title="Easily Traverse Any Graph"
content="Run queries, aggregations and traverse any graph structure easily"
link="docs/traversals"
/>
<Block
title="Statically Typed And Explicit API"
content="100% statically types and explicit api using code generation"
content="100% statically typed and explicit API using code generation"
link="docs/code-gen"
/>
</div>
);
@@ -75,7 +78,7 @@ class HomeSplash extends React.Component {
</div>
</div>
<p className="projectDesc">
A simple API for modeling any graph using scema as Go objects
A simple API for modeling any graph using schema as Go objects
</p>
</div>
);

View File

@@ -30,10 +30,8 @@ const siteConfig = {
// Used for publishing and more
projectName: 'ent',
organizationName: 'facebook',
// For top-level user or org sites, the organization is still the same.
// e.g., for the https://JoelMarcey.github.io site, it would be set like...
// organizationName: 'JoelMarcey'
organizationName: 'facebookincubator',
customDocsPath: 'md',
// For no header links in the top nav bar -> headerLinks: [],
@@ -41,14 +39,13 @@ const siteConfig = {
{doc: 'getting-started', label: 'Docs'},
{doc: 'getting-started', label: 'GoDoc'},
{href: 'https://github.com/facebookincubator/ent', label: 'Github'},
{href: 'https://github.com/facebookincubator/ent/issues', label: 'Help'},
],
// If you have users set above, you add it here:
users,
/* path to images for header/footer */
headerIcon: 'img/favicon.ico',
headerIcon: 'img/logo.png',
favicon: 'img/favicon.ico',
/* Colors for website */

View File

@@ -90,6 +90,37 @@
@media only screen and (min-width: 1400px) {
}
@media only screen and (max-width: 500px) {
.sideNavVisible .headerWrapper.wrapper header > a {
display: none!important;
}
}
@media only screen and (max-width: 800px) {
.gopherGraph {
margin-top: 25px;
}
.features {
margin: 60px auto 0 !important;
}
}
@media only screen and (max-width: 400px) {
.gopherGraph {
margin-top: 40px;
}
.features {
margin: 30px auto 0!important;
}
}
@media only screen and (max-width: 320px) {
.gettingStartedText {
font-size: 22px!important;
}
}
body {
background: #3d3e3f;
}
@@ -123,13 +154,6 @@ body {
background-color: transparent;
}
.onPageNav .toc-headings > li > a.active,
.toc .toggleNav ul li.navListItemActive a,
.toc .toggleNav ul li a:hover,
.toc .toggleNav ul li a:focus {
font-family: 'Calibre Regular', sans-serif;
}
.imageAlignTop .blockImage {
max-width: 100%;
}
@@ -322,7 +346,7 @@ a {
}
}
@media only screen and (max-width: 1000px) {
@media only screen and (max-width: 1023px) {
.gridBlock .twoByGridBlock img,
.gridBlock .threeByGridBlock img,
.gridBlock .fourByGridBlock img {
@@ -382,6 +406,10 @@ a {
.navigationSlider .slidingNav ul li {
min-width: 100px;
}
.projectTitle {
line-height: 1.3em;
}
}
@media only screen and (max-width: 880px) {
@@ -506,11 +534,13 @@ ol {
width: 1100px;
padding: 0 0;
}
}
.sideNavVisible.separateOnPageNav .fixedHeaderContainer {
margin-top: 35px;
width: 100vw;
}
@media only screen and (min-width: 1000px) {
.sideNavVisible.separateOnPageNav .fixedHeaderContainer {
margin-top: 35px;
width: 100vw;
}
}
.sideNavVisible .navigationSlider .slidingNav ul li a {
@@ -525,8 +555,23 @@ ol {
top: 4px;
left: 5px;
font-size: 30px;
color: #ffe800;
-webkit-transition: all 0.2s ease-in-out;
-moz-transition: all 0.2s ease-in-out;
-o-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
}
.yellowArrow:hover {
color: #ffe800;
-webkit-transform: translateX(5px);
-moz-transform: translateX(5px);
-ms-transform: translateX(5px);
-o-transform: translateX(5px);
transform: translateX(5px);
}
.blockTitleText {
display: inline;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 806 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB