From ad94a226e4906de93d5b52fce9bbeb20b7af86ea Mon Sep 17 00:00:00 2001 From: Marcelo Soares Date: Wed, 24 Jun 2026 12:35:07 -0300 Subject: [PATCH 1/4] docs: Add docs for the `unique(per...)` index shorthand --- docs/06-concepts/02-models/01-models.md | 2 +- docs/06-concepts/06-database/04-indexing.md | 40 +++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/docs/06-concepts/02-models/01-models.md b/docs/06-concepts/02-models/01-models.md index ef0e9846..08826105 100644 --- a/docs/06-concepts/02-models/01-models.md +++ b/docs/06-concepts/02-models/01-models.md @@ -547,7 +547,7 @@ fields: | [**type (index)**](database/indexing) | The type of index to create. | ✅ | | | | [**parameters (index)**](database/indexing#vector-indexes) | Parameters for specialized index types like HNSW and IVFFLAT vector indexes. | ✅ | | | | [**distanceFunction (index)**](database/indexing#vector-indexes) | Distance function for vector indexes. Allowed values depend on the field's vector type and index type — see [vector indexes](database/indexing#vector-indexes). | ✅ | | | -| [**unique**](database/indexing) | Boolean flag to make the entries unique in the database. | ✅ | | | +| [**unique**](database/indexing) | Creates a unique index. | ✅ | | | | [**default**](#default-values) | Sets the default value for both the model and the database. This keyword cannot be used with **relation**. | ✅ | | | | [**defaultModel**](#default-values) | Sets the default value for the model side. This keyword cannot be used with **relation**. | ✅ | | | | [**defaultPersist**](#default-values) | Sets the default value for the database side. This keyword cannot be used with **relation** and **!persist**. | ✅ | | | diff --git a/docs/06-concepts/06-database/04-indexing.md b/docs/06-concepts/06-database/04-indexing.md index 1556c3f5..a2376739 100644 --- a/docs/06-concepts/06-database/04-indexing.md +++ b/docs/06-concepts/06-database/04-indexing.md @@ -59,6 +59,46 @@ fields: name: String, unique ``` +### Composite unique constraints + +When a value should be unique only within a scope — for example, a setting key that is unique per user — use `unique(per=...)` on the field that must be unique within that scope. Serverpod auto-generates a composite unique index with the `per` columns first, followed by the annotated field. + +```yaml +class: UserSetting +table: user_setting +fields: + userId: int + key: String, unique(per=userId) +``` + +In this example, two rows can share the same `key` if they belong to different users, but the same user cannot have two rows with the same `key`. + +For a scope that spans multiple columns, pass a list of field names: + +```yaml +class: Product +table: product +fields: + tenantId: int + category: String + sku: String, unique(per=[tenantId, category]) +``` + +You can also use the equivalent expanded form: + +```yaml +class: UserSetting +table: user_setting +fields: + tenantId: int + category: String + sku: String +indexes: + product_unique_idx: + fields: tenantId, category, sku + unique: true +``` + ## Specifying index type It is possible to add a type key to specify the index type. From 31e05b0a99678471f168304165c78fb42293d37f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Mendon=C3=A7a=20Soares?= Date: Thu, 25 Jun 2026 11:30:53 -0300 Subject: [PATCH 2/4] fix: Replace em-dash by a parenthesis Co-authored-by: Jamiu Okanlawon <50176100+developerjamiu@users.noreply.github.com> --- docs/06-concepts/06-database/04-indexing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/06-concepts/06-database/04-indexing.md b/docs/06-concepts/06-database/04-indexing.md index a2376739..536a78f6 100644 --- a/docs/06-concepts/06-database/04-indexing.md +++ b/docs/06-concepts/06-database/04-indexing.md @@ -61,7 +61,7 @@ fields: ### Composite unique constraints -When a value should be unique only within a scope — for example, a setting key that is unique per user — use `unique(per=...)` on the field that must be unique within that scope. Serverpod auto-generates a composite unique index with the `per` columns first, followed by the annotated field. +When a value should be unique only within a scope (for example, a setting key that is unique per user), use `unique(per=...)` on the field that must be unique within that scope. Serverpod auto-generates a composite unique index with the `per` columns first, followed by the annotated field. ```yaml class: UserSetting From ea3f3f8d5da80c824c7b916c0c19762bf61675a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Mendon=C3=A7a=20Soares?= Date: Thu, 25 Jun 2026 11:33:42 -0300 Subject: [PATCH 3/4] fix: Fix class name due to copy-paste error Co-authored-by: Jamiu Okanlawon <50176100+developerjamiu@users.noreply.github.com> --- docs/06-concepts/06-database/04-indexing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/06-concepts/06-database/04-indexing.md b/docs/06-concepts/06-database/04-indexing.md index 536a78f6..c1710df6 100644 --- a/docs/06-concepts/06-database/04-indexing.md +++ b/docs/06-concepts/06-database/04-indexing.md @@ -87,8 +87,8 @@ fields: You can also use the equivalent expanded form: ```yaml -class: UserSetting -table: user_setting +class: Product +table: product fields: tenantId: int category: String From d5796b524cdf5448d5bcbdc2baa9ce96b161e014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Mendon=C3=A7a=20Soares?= Date: Thu, 25 Jun 2026 11:36:50 -0300 Subject: [PATCH 4/4] fix: Clarify that the expanded form requires explicit naming Co-authored-by: Jamiu Okanlawon <50176100+developerjamiu@users.noreply.github.com> --- docs/06-concepts/06-database/04-indexing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/06-concepts/06-database/04-indexing.md b/docs/06-concepts/06-database/04-indexing.md index c1710df6..ab1c3cac 100644 --- a/docs/06-concepts/06-database/04-indexing.md +++ b/docs/06-concepts/06-database/04-indexing.md @@ -84,7 +84,7 @@ fields: sku: String, unique(per=[tenantId, category]) ``` -You can also use the equivalent expanded form: +You can also use the equivalent expanded form, where you name the index yourself: ```yaml class: Product