mirror of
https://github.com/ent/ent.git
synced 2026-05-24 09:31:56 +03:00
doc: explain how to use composite types properly (#4136)
This commit is contained in:
223
doc/md/migration/composite.mdx
Normal file
223
doc/md/migration/composite.mdx
Normal file
@@ -0,0 +1,223 @@
|
||||
---
|
||||
title: Using Composite Types in Ent Schema
|
||||
id: composite
|
||||
slug: composite-types
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import InstallationInstructions from '../components/_installation_instructions.mdx';
|
||||
|
||||
In PostgreSQL, a composite type is structured like a row or record, consisting of field names and their corresponding
|
||||
data types. Setting an Ent field as a composite type enables you to store complex and structured data in a single column.
|
||||
|
||||
This guide explains how to define a schema field type as a composite type in your Ent schema and configure the schema migration
|
||||
to manage both the composite types and the Ent schema as a single migration unit using Atlas.
|
||||
|
||||
:::info [Atlas Pro Feature](https://atlasgo.io/features#pro-plan)
|
||||
Atlas support for [Composite Types](https://atlasgo.io/atlas-schema/hcl#composite-type) is available exclusively to Pro users.
|
||||
To use this feature, run:
|
||||
```
|
||||
atlas login
|
||||
```
|
||||
:::
|
||||
|
||||
## Install Atlas
|
||||
|
||||
<InstallationInstructions />
|
||||
|
||||
## Login to Atlas
|
||||
|
||||
```shell
|
||||
$ atlas login a8m
|
||||
//highlight-next-line-info
|
||||
You are now connected to "a8m" on Atlas Cloud.
|
||||
```
|
||||
|
||||
## Composite Schema
|
||||
|
||||
An `ent/schema` package is mostly used for defining Ent types (objects), their fields, edges and logic. Composite types,
|
||||
or any other database objects do not have representation in Ent models - A composite type can be defined once,
|
||||
and may be used multiple times in different fields and models.
|
||||
|
||||
In order to extend our PostgreSQL schema to include both custom composite types and our Ent types, we configure Atlas to
|
||||
read the state of the schema from a [Composite Schema](https://atlasgo.io/atlas-schema/projects#data-source-composite_schema)
|
||||
data source. Follow the steps below to configure this for your project:
|
||||
|
||||
1\. Create a `schema.sql` that defines the necessary composite type. In the same way, you can configure the composite type in
|
||||
[Atlas Schema HCL language](https://atlasgo.io/atlas-schema/hcl-types#composite-type):
|
||||
|
||||
<Tabs>
|
||||
<TabItem value={"sql"} label={"Using SQL"}>
|
||||
|
||||
```sql title="schema.sql"
|
||||
CREATE TYPE address AS (
|
||||
street text,
|
||||
city text
|
||||
);
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value={"hcl"} label={"Using HCL"}>
|
||||
|
||||
```hcl title="schema.hcl"
|
||||
schema "public" {}
|
||||
|
||||
composite "address" {
|
||||
schema = schema.public
|
||||
field "street" {
|
||||
type = text
|
||||
}
|
||||
field "city" {
|
||||
type = text
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
2\. In your Ent schema, define a field that uses the composite type only in PostgreSQL dialect:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value={"schema"} label={"Schema"}>
|
||||
|
||||
```go title="ent/schema/user.go" {6-8}
|
||||
// Fields of the User.
|
||||
func (User) Fields() []ent.Field {
|
||||
return []ent.Field{
|
||||
field.String("address").
|
||||
GoType(&Address{}).
|
||||
SchemaType(map[string]string{
|
||||
dialect.Postgres: "address",
|
||||
}),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::note
|
||||
In case a schema with custom driver-specific types is used with other databases, Ent falls back to the default type
|
||||
used by the driver (e.g., "varchar").
|
||||
:::
|
||||
</TabItem>
|
||||
<TabItem value={"address"} label={"Address Type"}>
|
||||
|
||||
```go title="ent/schematype/address.go"
|
||||
type Address struct {
|
||||
Street, City string
|
||||
}
|
||||
|
||||
var _ field.ValueScanner = (*Address)(nil)
|
||||
|
||||
// Scan implements the database/sql.Scanner interface.
|
||||
func (a *Address) Scan(v interface{}) (err error) {
|
||||
switch v := v.(type) {
|
||||
case nil:
|
||||
case string:
|
||||
_, err = fmt.Sscanf(v, "(%q,%q)", &a.Street, &a.City)
|
||||
case []byte:
|
||||
_, err = fmt.Sscanf(string(v), "(%q,%q)", &a.Street, &a.City)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements the driver.Valuer interface.
|
||||
func (a *Address) Value() (driver.Value, error) {
|
||||
return fmt.Sprintf("(%q,%q)", a.Street, a.City), nil
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
3\. Create a simple `atlas.hcl` config file with a `composite_schema` that includes both your custom types defined in
|
||||
`schema.sql` and your Ent schema:
|
||||
|
||||
```hcl title="atlas.hcl"
|
||||
data "composite_schema" "app" {
|
||||
# Load first custom types first.
|
||||
schema "public" {
|
||||
url = "file://schema.sql"
|
||||
}
|
||||
# Second, load the Ent schema.
|
||||
schema "public" {
|
||||
url = "ent://ent/schema"
|
||||
}
|
||||
}
|
||||
|
||||
env "local" {
|
||||
src = data.composite_schema.app.url
|
||||
dev = "docker://postgres/15/dev?search_path=public"
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
After setting up our schema, we can get its representation using the `atlas schema inspect` command, generate migrations for
|
||||
it, apply them to a database, and more. Below are a few commands to get you started with Atlas:
|
||||
|
||||
#### Inspect the Schema
|
||||
|
||||
The `atlas schema inspect` command is commonly used to inspect databases. However, we can also use it to inspect our
|
||||
`composite_schema` and print the SQL representation of it:
|
||||
|
||||
```shell
|
||||
atlas schema inspect \
|
||||
--env local \
|
||||
--url env://src \
|
||||
--format '{{ sql . }}'
|
||||
```
|
||||
|
||||
The command above prints the following SQL. Note, the `address` composite type is defined in the schema before
|
||||
its usage in the `address` field:
|
||||
|
||||
```sql
|
||||
-- Create composite type "address"
|
||||
CREATE TYPE "address" AS ("street" text, "city" text);
|
||||
-- Create "users" table
|
||||
CREATE TABLE "users" ("id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, "address" "address" NOT NULL, PRIMARY KEY ("id"));
|
||||
```
|
||||
|
||||
#### Generate Migrations For the Schema
|
||||
|
||||
To generate a migration for the schema, run the following command:
|
||||
|
||||
```shell
|
||||
atlas migrate diff \
|
||||
--env local
|
||||
```
|
||||
|
||||
Note that a new migration file is created with the following content:
|
||||
|
||||
```sql title="migrations/20240712090543.sql"
|
||||
-- Create composite type "address"
|
||||
CREATE TYPE "address" AS ("street" text, "city" text);
|
||||
-- Create "users" table
|
||||
CREATE TABLE "users" ("id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, "address" "address" NOT NULL, PRIMARY KEY ("id"));
|
||||
```
|
||||
|
||||
#### Apply the Migrations
|
||||
|
||||
To apply the migration generated above to a database, run the following command:
|
||||
|
||||
```
|
||||
atlas migrate apply \
|
||||
--env local \
|
||||
--url "postgres://postgres:pass@localhost:5432/database?search_path=public&sslmode=disable"
|
||||
```
|
||||
|
||||
:::info Apply the Schema Directly on the Database
|
||||
|
||||
Sometimes, there is a need to apply the schema directly to the database without generating a migration file. For example,
|
||||
when experimenting with schema changes, spinning up a database for testing, etc. In such cases, you can use the command
|
||||
below to apply the schema directly to the database:
|
||||
|
||||
```shell
|
||||
atlas schema apply \
|
||||
--env local \
|
||||
--url "postgres://postgres:pass@localhost:5432/database?search_path=public&sslmode=disable"
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
The code for this guide can be found in [GitHub](https://github.com/ent/ent/tree/master/examples/compositetypes).
|
||||
@@ -4,14 +4,15 @@ id: domain
|
||||
slug: domain-types
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import InstallationInstructions from '../components/_installation_instructions.mdx';
|
||||
|
||||
|
||||
PostgreSQL domain types are user-defined data types that extend existing ones, allowing you to add constraints that
|
||||
restrict the values they can hold. Setting a field type as a domain type enables you to enforce data integrity and
|
||||
validation rules at the database level.
|
||||
|
||||
This guide explains how to define a schema field type as a domain type in your Ent schema and configure the migration
|
||||
This guide explains how to define a schema field type as a domain type in your Ent schema and configure the schema migration
|
||||
to manage both the domains and the Ent schema as a single migration unit using Atlas.
|
||||
|
||||
:::info [Atlas Pro Feature](https://atlasgo.io/features#pro-plan)
|
||||
@@ -47,6 +48,9 @@ data source. Follow the steps below to configure this for your project:
|
||||
1\. Create a `schema.sql` that defines the necessary domain type. In the same way, you can configure the domain type in
|
||||
[Atlas Schema HCL language](https://atlasgo.io/atlas-schema/hcl-types#domain):
|
||||
|
||||
<Tabs>
|
||||
<TabItem value={"sql"} label={"Using SQL"}>
|
||||
|
||||
```sql title="schema.sql"
|
||||
CREATE DOMAIN us_postal_code AS TEXT
|
||||
CHECK(
|
||||
@@ -55,6 +59,25 @@ CHECK(
|
||||
);
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value={"hcl"} label={"Using HCL"}>
|
||||
|
||||
```hcl title="schema.hcl"
|
||||
schema "public" {}
|
||||
|
||||
domain "us_postal_code" {
|
||||
schema = schema.public
|
||||
type = text
|
||||
null = true
|
||||
check "us_postal_code_check" {
|
||||
expr = "((VALUE ~ '^\\d{5}$'::text) OR (VALUE ~ '^\\d{5}-\\d{4}$'::text))"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
2\. In your Ent schema, define a field that uses the domain type only in PostgreSQL dialect:
|
||||
|
||||
```go title="ent/schema/user.go" {5-7}
|
||||
|
||||
Reference in New Issue
Block a user