jsonbadger 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -17
- package/docs/{architecture-flow.md → advanced/architecture-flow.md} +1 -1
- package/docs/{jsonb-ops.md → advanced/jsonb-ops.md} +4 -2
- package/docs/{query-translation.md → advanced/query-translation.md} +13 -10
- package/docs/api/connection.md +1 -2
- package/docs/api/document.md +1 -1
- package/docs/api/field-types.md +1 -1
- package/docs/api/index.md +1 -1
- package/docs/api/query-builder.md +8 -8
- package/docs/examples.md +52 -81
- package/package.json +1 -1
- /package/docs/{lifecycle → advanced}/model-compilation.md +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# JsonBadger
|
|
2
2
|
|
|
3
|
-
JsonBadger is a Node.js library
|
|
3
|
+
JsonBadger is a Node.js library for working with complex JSON data in PostgreSQL through a schema-driven model API. Instead of building nested JSONB queries by hand, you define schemas and use document-style models, query operators, and persistence helpers.
|
|
4
4
|
|
|
5
5
|
## Why did I create this utility?
|
|
6
6
|
|
|
@@ -36,8 +36,7 @@ const db_uri = 'postgresql://user:pass@localhost:5432/dbname';
|
|
|
36
36
|
const options = {
|
|
37
37
|
debug: false,
|
|
38
38
|
max: 10,
|
|
39
|
-
ssl: false
|
|
40
|
-
auto_index: true
|
|
39
|
+
ssl: false
|
|
41
40
|
};
|
|
42
41
|
const connection = await JsonBadger.connect(db_uri, options);
|
|
43
42
|
|
|
@@ -48,7 +47,8 @@ const user_schema = new JsonBadger.Schema({
|
|
|
48
47
|
type: {type: String},
|
|
49
48
|
email: {type: String, index: {unique: true, name: 'idx_users_email_unique'}}
|
|
50
49
|
}, {
|
|
51
|
-
id_strategy: JsonBadger.
|
|
50
|
+
id_strategy: JsonBadger.ID_STRATEGY.bigserial,
|
|
51
|
+
auto_index: true
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
// 3. Declare indexes on schema (path-level + schema-level compound index)
|
|
@@ -59,16 +59,15 @@ user_schema.create_index({using: 'btree', paths: {name: 1, type: -1}});
|
|
|
59
59
|
const User = connection.model({
|
|
60
60
|
name: 'User',
|
|
61
61
|
schema: user_schema,
|
|
62
|
-
table_name: 'users'
|
|
63
|
-
auto_index: false // optional model-level override
|
|
62
|
+
table_name: 'users'
|
|
64
63
|
});
|
|
65
64
|
|
|
66
|
-
// 5. Run migrations manually
|
|
65
|
+
// 5. Run migrations manually
|
|
67
66
|
await User.ensure_table();
|
|
68
|
-
await User.
|
|
67
|
+
await User.ensure_indexes();
|
|
69
68
|
|
|
70
69
|
// 6. Save a document
|
|
71
|
-
const saved_user = await
|
|
70
|
+
const saved_user = await User.from({
|
|
72
71
|
name: 'john',
|
|
73
72
|
age: 30,
|
|
74
73
|
type: 'admin'
|
|
@@ -84,7 +83,7 @@ const found_user = await User.find_one({
|
|
|
84
83
|
// found_user?.to_json() -> { id, name, age, type, created_at, updated_at }
|
|
85
84
|
```
|
|
86
85
|
|
|
87
|
-
When using `
|
|
86
|
+
When using `ID_STRATEGY.uuidv7`, declare it on the schema. JsonBadger checks support automatically during `Model.ensure_table()` using capability data captured at `connect(...)`.
|
|
88
87
|
|
|
89
88
|
## Examples Cheat Sheet
|
|
90
89
|
|
|
@@ -96,15 +95,15 @@ For the document state flow from `new Model(...)` through `save()`, hydration, d
|
|
|
96
95
|
|
|
97
96
|
* **JSONB-first**: Model API designed specifically for PostgreSQL JSONB.
|
|
98
97
|
* **Validation**: FieldType-based schema validation built in.
|
|
99
|
-
* **Querying**: Mongo-style query operators
|
|
98
|
+
* **Querying**: Mongo-style query operators such as `$gt`, `$regex`, `$contains`, and `$elem_match`.
|
|
100
99
|
* **Runtime document APIs**: `get`, `set`, alias virtuals, serialization helpers, `mark_modified`/dirty tracking, and immutable enforcement after save.
|
|
101
|
-
* **JSON
|
|
102
|
-
* **Migrations**: Helpers for `ensure_table` and `
|
|
103
|
-
* **Configurable row IDs**: use `
|
|
100
|
+
* **JSON updates**: `update_one(...)` supports implicit keys, `$set`, `$unset`, and `$replace_roots`.
|
|
101
|
+
* **Migrations**: Helpers for `ensure_table`, `ensure_indexes`, and `ensure_model`.
|
|
102
|
+
* **Configurable row IDs**: use `ID_STRATEGY.bigserial` and `ID_STRATEGY.uuidv7`.
|
|
104
103
|
* **ID create semantics**: for `uuidv7`, caller-provided `id` is used on create when present; otherwise PostgreSQL generates it. For `bigserial`, create ignores caller-provided `id` and PostgreSQL generates it.
|
|
105
104
|
* **Timestamp helper semantics**: `created_at` and `updated_at` are helper fields. Create keeps caller values when provided, otherwise auto-fills both. Updates keep caller `updated_at` when provided, otherwise auto-refresh `updated_at`.
|
|
106
|
-
* **Configurable index lifecycle**: `auto_index`
|
|
107
|
-
* **UUIDv7 compatibility
|
|
105
|
+
* **Configurable index lifecycle**: `auto_index` is a schema option that controls whether `ensure_model()` also ensures indexes.
|
|
106
|
+
* **UUIDv7 compatibility checks**: JsonBadger checks support automatically and fails early when unavailable.
|
|
108
107
|
* **Document instance returns**: data-returning methods return document instances with top-level base fields (`id`, `created_at`, `updated_at`), and `.to_json()` / `.$serialize()` for plain-object snapshots.
|
|
109
108
|
* **Modern**: Native ESM package.
|
|
110
109
|
|
|
@@ -118,7 +117,7 @@ For the document state flow from `new Model(...)` through `save()`, hydration, d
|
|
|
118
117
|
* [`docs/api/field-types.md`](docs/api/field-types.md)
|
|
119
118
|
* [`docs/examples.md`](docs/examples.md) (complete example cheat sheet for queries, updates, and runtime document methods)
|
|
120
119
|
* [`docs/lifecycle.md`](docs/lifecycle.md) (document phases, hydration/save flow, dirty tracking, and serialization)
|
|
121
|
-
* [`docs/query-translation.md`](docs/query-translation.md) (
|
|
120
|
+
* [`docs/advanced/query-translation.md`](docs/advanced/query-translation.md) (advanced PostgreSQL reference for operators and query behavior)
|
|
122
121
|
* [`docs/local-integration-testing.md`](docs/local-integration-testing.md)
|
|
123
122
|
* [`CHANGELOG.md`](CHANGELOG.md) for release notes and version history
|
|
124
123
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# JSONB Update Pipeline
|
|
2
2
|
|
|
3
|
-
This
|
|
3
|
+
This page is a maintainer reference for the JSONB update flow.
|
|
4
|
+
|
|
5
|
+
If you only want to use `update_one(...)`, use [`../examples.md`](../examples.md) instead.
|
|
4
6
|
|
|
5
7
|
## Example
|
|
6
8
|
|
|
7
|
-
This page describes the
|
|
9
|
+
This page describes the lower-level JSONB operator layer after `Model.update_one(...)` input has already been normalized.
|
|
8
10
|
|
|
9
11
|
```js
|
|
10
12
|
const payload = {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# query translation
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Advanced reference for people who want to understand how JsonBadger queries relate to PostgreSQL JSONB features.
|
|
4
|
+
|
|
5
|
+
If you only want to use the library, start with [`../examples.md`](../examples.md) and [`../api/query-builder.md`](../api/query-builder.md).
|
|
4
6
|
|
|
5
7
|
## base behavior
|
|
6
8
|
|
|
@@ -44,17 +46,18 @@ Top-level semantics note:
|
|
|
44
46
|
- `$json_path_exists` -> `@?` with a `::jsonpath` parameter
|
|
45
47
|
- `$json_path_match` -> `@@` with a `::jsonpath` parameter
|
|
46
48
|
- invalid/empty JSONPath values fail before SQL execution
|
|
49
|
+
- these operators require native PostgreSQL JSONPath support
|
|
47
50
|
|
|
48
51
|
## JSON update operators
|
|
49
52
|
|
|
50
53
|
- `$set` -> `jsonb_set(target, path, value, true)`
|
|
51
|
-
- `$
|
|
52
|
-
- `$
|
|
54
|
+
- `$unset` -> `target #- path`
|
|
55
|
+
- `$replace_roots` -> replace the full JSONB target
|
|
56
|
+
- implicit top-level keys are normalized into `$set`
|
|
53
57
|
|
|
54
58
|
Update path behavior:
|
|
55
59
|
- Dot paths are validated before SQL execution.
|
|
56
|
-
-
|
|
57
|
-
- Conflicting update paths in a single `update_one(...)` call (same path or parent/child overlap) fail before SQL execution.
|
|
60
|
+
- Tracker-delta shapes (`replace_roots`, `set`, `unset`) are normalized into operator-style buckets before SQL generation.
|
|
58
61
|
|
|
59
62
|
## regex
|
|
60
63
|
|
|
@@ -70,7 +73,7 @@ Update path behavior:
|
|
|
70
73
|
|
|
71
74
|
## PostgreSQL capability map
|
|
72
75
|
|
|
73
|
-
This table is
|
|
76
|
+
This table is an advanced PostgreSQL reference for the currently supported query and update operators.
|
|
74
77
|
|
|
75
78
|
| JsonBadger feature | PostgreSQL operator/function | Notes | Indexability expectation |
|
|
76
79
|
| --- | --- | --- | --- |
|
|
@@ -85,11 +88,11 @@ This table is the implementation-facing capability map for currently supported q
|
|
|
85
88
|
| `$has_key` | `?` | Top-level key existence on the left JSONB value | GIN on JSONB value is the expected index family |
|
|
86
89
|
| `$has_any_keys` | `?|` | Any-key existence | GIN on JSONB value is the expected index family |
|
|
87
90
|
| `$has_all_keys` | `?&` | All-keys existence | GIN on JSONB value is the expected index family |
|
|
88
|
-
| `$json_path_exists` | `@?` | JSONPath existence predicate | Can benefit from JSONB GIN indexing depending on operator class/query shape |
|
|
89
|
-
| `$json_path_match` | `@@` | JSONPath predicate match | Can benefit from JSONB GIN indexing depending on operator class/query shape |
|
|
91
|
+
| `$json_path_exists` | `@?` | JSONPath existence predicate; requires native PostgreSQL JSONPath support | Can benefit from JSONB GIN indexing depending on operator class/query shape |
|
|
92
|
+
| `$json_path_match` | `@@` | JSONPath predicate match; requires native PostgreSQL JSONPath support | Can benefit from JSONB GIN indexing depending on operator class/query shape |
|
|
90
93
|
| `update_one.$set` | `jsonb_set(...)` | Creates missing path segments when configured (`true`) | N/A (write-path function) |
|
|
91
|
-
| `update_one.$
|
|
92
|
-
| `update_one.$
|
|
94
|
+
| `update_one.$unset` | `#-` | Removes one JSON path from the target | N/A (write-path function) |
|
|
95
|
+
| `update_one.$replace_roots` | direct JSONB replacement | Replaces the full JSONB root before later operations | N/A (write-path function) |
|
|
93
96
|
|
|
94
97
|
Index helper behavior:
|
|
95
98
|
- `schema.create_index({using: 'gin', path: 'profile.city'})` creates a GIN index on the extracted JSONB path expression.
|
package/docs/api/connection.md
CHANGED
|
@@ -22,7 +22,6 @@ const connection = await JsonBadger.connect(uri, options);
|
|
|
22
22
|
|
|
23
23
|
- `max`: pool size
|
|
24
24
|
- `debug`: logs SQL/debug events when `true`
|
|
25
|
-
- `auto_index`: server-wide default for automatic index creation when `Model.ensure_table()` is called
|
|
26
25
|
- any supported `pg` pool options (`ssl`, `host`, `port`, `user`, `password`, `database`)
|
|
27
26
|
|
|
28
27
|
## Connection Lifecycle
|
|
@@ -39,7 +38,7 @@ await connection.disconnect();
|
|
|
39
38
|
- `connect(...)` performs a PostgreSQL capability scan and caches the result
|
|
40
39
|
- later schema-level `uuidv7` selections are checked against the cached capability info during `Model.ensure_table()`
|
|
41
40
|
|
|
42
|
-
> **Note:**
|
|
41
|
+
> **Note:** You do not need to run manual version checks during normal usage. JsonBadger performs the compatibility checks it needs automatically.
|
|
43
42
|
|
|
44
43
|
## Connection Reuse Pattern
|
|
45
44
|
|
package/docs/api/document.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Most app code should go through `Model.from(...)`, `Model.hydrate(...)`, and model instance methods such as `doc.get(...)`, `doc.set(...)`, and `doc.save()`.
|
|
6
6
|
|
|
7
|
-
Use `Document` directly
|
|
7
|
+
Use `Document` directly only for advanced or framework-level code, such as custom persistence flows or adopting trusted state through `doc.rebase(...)`.
|
|
8
8
|
|
|
9
9
|
## TOC
|
|
10
10
|
|
package/docs/api/field-types.md
CHANGED
package/docs/api/index.md
CHANGED
|
@@ -32,4 +32,4 @@ Default export (`JsonBadger`) includes the same plus:
|
|
|
32
32
|
- [`model.md`](model.md)
|
|
33
33
|
- [`query-builder.md`](query-builder.md)
|
|
34
34
|
- [`field-types.md`](field-types.md)
|
|
35
|
-
- [`../query-translation.md`](../query-translation.md)
|
|
35
|
+
- [`../advanced/query-translation.md`](../advanced/query-translation.md)
|
|
@@ -47,7 +47,9 @@ Read/query execution:
|
|
|
47
47
|
- JSONB key existence: `$has_key`, `$has_any_keys`, `$has_all_keys`
|
|
48
48
|
- JSONPath: `$json_path_exists`, `$json_path_match`
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
> **Note:** Use `$json_path_exists` and `$json_path_match` only when your PostgreSQL version supports native JSONPath queries.
|
|
51
|
+
|
|
52
|
+
See [`../advanced/query-translation.md`](../advanced/query-translation.md) when you want the advanced PostgreSQL reference behind these operators.
|
|
51
53
|
|
|
52
54
|
## Base-field Query Rules
|
|
53
55
|
|
|
@@ -67,15 +69,13 @@ Operator matrix:
|
|
|
67
69
|
|
|
68
70
|
## JSON Key Existence
|
|
69
71
|
|
|
70
|
-
- `$has_key`
|
|
71
|
-
- `$has_any_keys`
|
|
72
|
-
- `$has_all_keys`
|
|
72
|
+
- `$has_key` checks whether one key is present
|
|
73
|
+
- `$has_any_keys` checks whether any key from a list is present
|
|
74
|
+
- `$has_all_keys` checks whether all keys from a list are present
|
|
73
75
|
|
|
74
76
|
When used with a nested field path, JsonBadger extracts that nested JSONB value first, then applies the existence operator to the extracted value.
|
|
75
77
|
|
|
76
78
|
## JSONPath
|
|
77
79
|
|
|
78
|
-
- `$json_path_exists`
|
|
79
|
-
- `$json_path_match`
|
|
80
|
-
|
|
81
|
-
JsonBadger binds the JSONPath string as a `::jsonpath` parameter.
|
|
80
|
+
- `$json_path_exists` checks whether a JSONPath query finds a match
|
|
81
|
+
- `$json_path_match` checks whether a JSONPath predicate matches
|
package/docs/examples.md
CHANGED
|
@@ -4,7 +4,7 @@ JsonBadger is a PostgreSQL-backed document mapper for working with JSONB data th
|
|
|
4
4
|
|
|
5
5
|
This page is an example-first cheat sheet for users and AI agents. It shows copy-pasteable JsonBadger usage in increasing complexity and covers the currently implemented query and update operator surface.
|
|
6
6
|
|
|
7
|
-
Use this page for syntax and working shapes. Use [`docs/api/index.md`](api/index.md) for the module API map and [`docs/query-translation.md`](query-translation.md)
|
|
7
|
+
Use this page for syntax and working shapes. Use [`docs/api/index.md`](api/index.md) for the module API map and [`docs/advanced/query-translation.md`](advanced/query-translation.md) only when you want the advanced PostgreSQL reference behind the query surface.
|
|
8
8
|
Use [`docs/lifecycle.md`](lifecycle.md) when you need the document phase map instead of operator syntax.
|
|
9
9
|
|
|
10
10
|
## How to Read This Page
|
|
@@ -69,10 +69,10 @@ When a snippet uses a different value (for example `name: 'jane'`), either seed
|
|
|
69
69
|
- Query/sort support for base fields is top-level only (no dotted base-field paths like `created_at.value`).
|
|
70
70
|
|
|
71
71
|
`id` behavior:
|
|
72
|
-
- Create with `
|
|
72
|
+
- Create with `ID_STRATEGY.uuidv7`:
|
|
73
73
|
- caller-provided `id` is used.
|
|
74
74
|
- if `id` is omitted, PostgreSQL default `uuidv7()` generates it.
|
|
75
|
-
- Create with `
|
|
75
|
+
- Create with `ID_STRATEGY.bigserial`:
|
|
76
76
|
- caller-provided `id` is ignored silently.
|
|
77
77
|
- PostgreSQL sequence generates it.
|
|
78
78
|
- Update paths cannot mutate `id`.
|
|
@@ -88,7 +88,7 @@ Timestamp helper behavior:
|
|
|
88
88
|
|
|
89
89
|
## Setup and Connect
|
|
90
90
|
|
|
91
|
-
> Important: `
|
|
91
|
+
> Important: `ID_STRATEGY.uuidv7` requires native PostgreSQL `uuidv7()` support (PostgreSQL 18+).
|
|
92
92
|
|
|
93
93
|
```js
|
|
94
94
|
import JsonBadger from 'jsonbadger';
|
|
@@ -97,27 +97,27 @@ const db_uri = 'postgresql://user:pass@localhost:5432/dbname';
|
|
|
97
97
|
const options = {
|
|
98
98
|
debug: false,
|
|
99
99
|
max: 10,
|
|
100
|
-
ssl: false
|
|
101
|
-
auto_index: true,
|
|
102
|
-
id_strategy: JsonBadger.IdStrategies.bigserial
|
|
100
|
+
ssl: false
|
|
103
101
|
};
|
|
104
102
|
|
|
105
103
|
const connection = await JsonBadger.connect(db_uri, options);
|
|
106
104
|
```
|
|
107
105
|
|
|
108
|
-
UUIDv7
|
|
106
|
+
Schema-level UUIDv7 selection (PostgreSQL 18+ native `uuidv7()` required):
|
|
109
107
|
|
|
110
108
|
```js
|
|
111
109
|
const db_uri = 'postgresql://user:pass@localhost:5432/dbname';
|
|
112
|
-
const
|
|
113
|
-
id_strategy: JsonBadger.IdStrategies.uuidv7
|
|
114
|
-
};
|
|
110
|
+
const connection = await JsonBadger.connect(db_uri);
|
|
115
111
|
|
|
116
|
-
const
|
|
112
|
+
const event_schema = new JsonBadger.Schema({
|
|
113
|
+
name: String
|
|
114
|
+
}, {
|
|
115
|
+
id_strategy: JsonBadger.ID_STRATEGY.uuidv7
|
|
116
|
+
});
|
|
117
117
|
```
|
|
118
118
|
|
|
119
119
|
Notes:
|
|
120
|
-
- JsonBadger checks native `uuidv7()` support automatically during `
|
|
120
|
+
- JsonBadger checks native `uuidv7()` support automatically during `Model.ensure_table()` using the capability snapshot captured at `connect(...)`.
|
|
121
121
|
- Run `ensure_table()` / `ensure_indexes()` during startup/bootstrap before normal runtime operations, or use `ensure_model()` when you want the combined path.
|
|
122
122
|
|
|
123
123
|
Teardown example:
|
|
@@ -150,7 +150,7 @@ const AuditLog = connection.model({
|
|
|
150
150
|
schema: new JsonBadger.Schema({
|
|
151
151
|
event_name: String
|
|
152
152
|
}, {
|
|
153
|
-
id_strategy: JsonBadger.
|
|
153
|
+
id_strategy: JsonBadger.ID_STRATEGY.uuidv7 // PostgreSQL 18+ native uuidv7() required
|
|
154
154
|
}),
|
|
155
155
|
table_name: 'audit_logs'
|
|
156
156
|
});
|
|
@@ -160,15 +160,15 @@ const Counter = connection.model({
|
|
|
160
160
|
schema: new JsonBadger.Schema({
|
|
161
161
|
label: String
|
|
162
162
|
}, {
|
|
163
|
-
id_strategy: JsonBadger.
|
|
163
|
+
id_strategy: JsonBadger.ID_STRATEGY.bigserial // schema override
|
|
164
164
|
}),
|
|
165
165
|
table_name: 'counters'
|
|
166
166
|
});
|
|
167
167
|
```
|
|
168
168
|
|
|
169
169
|
Notes:
|
|
170
|
-
- `id_strategy` lives on the schema. If omitted, the library default is `
|
|
171
|
-
- `
|
|
170
|
+
- `id_strategy` lives on the schema. If omitted, the library default is `uuidv7`.
|
|
171
|
+
- `ID_STRATEGY.uuidv7` uses database-generated IDs and JsonBadger validates support automatically.
|
|
172
172
|
|
|
173
173
|
Create-time `id` behavior example:
|
|
174
174
|
|
|
@@ -205,6 +205,8 @@ const user_schema = new JsonBadger.Schema({
|
|
|
205
205
|
},
|
|
206
206
|
orders: [{sku: String, qty: Number, price: Number}],
|
|
207
207
|
payload: {}
|
|
208
|
+
}, {
|
|
209
|
+
id_strategy: JsonBadger.ID_STRATEGY.bigserial
|
|
208
210
|
});
|
|
209
211
|
|
|
210
212
|
// Schema-level indexes (single path and compound)
|
|
@@ -215,9 +217,7 @@ const connection = await JsonBadger.connect(db_uri, options);
|
|
|
215
217
|
const User = connection.model({
|
|
216
218
|
name: 'User',
|
|
217
219
|
schema: user_schema,
|
|
218
|
-
table_name: 'users'
|
|
219
|
-
auto_index: true,
|
|
220
|
-
id_strategy: JsonBadger.IdStrategies.bigserial
|
|
220
|
+
table_name: 'users'
|
|
221
221
|
});
|
|
222
222
|
```
|
|
223
223
|
|
|
@@ -274,7 +274,7 @@ Quick FieldType reference:
|
|
|
274
274
|
|
|
275
275
|
Edge cases and practical notes:
|
|
276
276
|
- In-place changes to nested values under `doc.document[...]` bypass `doc.set(...)`; prefer `doc.set(...)` when you want assignment-time casting and setter logic.
|
|
277
|
-
- Arrays default to `
|
|
277
|
+
- Arrays default to `null` unless you explicitly declare a default such as `default: []`.
|
|
278
278
|
- For `Map`-like paths, prefer `document.set('handles.github', 'name')` so casting and dirty tracking run.
|
|
279
279
|
|
|
280
280
|
## Create and Save Documents
|
|
@@ -419,10 +419,10 @@ const UniqueUser = connection.model({
|
|
|
419
419
|
await UniqueUser.ensure_table();
|
|
420
420
|
await UniqueUser.ensure_indexes();
|
|
421
421
|
|
|
422
|
-
await
|
|
422
|
+
await UniqueUser.from({email: 'john@example.com'}).save();
|
|
423
423
|
|
|
424
424
|
try {
|
|
425
|
-
await
|
|
425
|
+
await UniqueUser.from({email: 'john@example.com'}).save();
|
|
426
426
|
} catch(error) {
|
|
427
427
|
if(error.name === 'query_error') {
|
|
428
428
|
const error_payload = error.to_json();
|
|
@@ -596,6 +596,8 @@ await User.find({payload: {$has_key: 'profile.city'}}).exec();
|
|
|
596
596
|
|
|
597
597
|
`$json_path_exists` (`@?`) and `$json_path_match` (`@@`):
|
|
598
598
|
|
|
599
|
+
> **Note:** Use these operators only when your PostgreSQL version supports native JSONPath queries.
|
|
600
|
+
|
|
599
601
|
Assumes:
|
|
600
602
|
- Your seeded `payload` includes shapes like `items[*].qty` and `score` (as shown in `## Create and Save Documents`).
|
|
601
603
|
|
|
@@ -618,7 +620,7 @@ Expected behavior:
|
|
|
618
620
|
|
|
619
621
|
## Update Operators (`update_one`)
|
|
620
622
|
|
|
621
|
-
`update_one(...)` supports `$set`, `$
|
|
623
|
+
`update_one(...)` supports `$set`, `$unset`, `$replace_roots`, plus implicit top-level keys that are routed into `$set`.
|
|
622
624
|
|
|
623
625
|
Assumes (for update and delete sections below):
|
|
624
626
|
- A matching row exists (examples use `name: 'john'` and `name: 'missing'`).
|
|
@@ -627,7 +629,7 @@ Assumes (for update and delete sections below):
|
|
|
627
629
|
Target shape reminder:
|
|
628
630
|
- `tags` is an array, `profile` is an object, and `payload.profile` / `payload.score` are nested JSON values in the seeded row.
|
|
629
631
|
|
|
630
|
-
|
|
632
|
+
Use `$set` to assign or replace one or more JSON values:
|
|
631
633
|
|
|
632
634
|
```js
|
|
633
635
|
const updated_user = await User.update_one(
|
|
@@ -645,86 +647,55 @@ const updated_user = await User.update_one(
|
|
|
645
647
|
Returns: `updated_user` is `User | null`.
|
|
646
648
|
Snapshot shape: `updated_user?.document` -> `{ id, data, created_at, updated_at }` when the default slug is still `data`.
|
|
647
649
|
|
|
648
|
-
`$
|
|
650
|
+
Use `$unset` to remove one or more JSON paths:
|
|
649
651
|
|
|
650
652
|
```js
|
|
651
653
|
await User.update_one({name: 'john'}, {
|
|
652
|
-
$
|
|
653
|
-
'
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
await User.update_one({name: 'john'}, {
|
|
658
|
-
$insert: {
|
|
659
|
-
'tags.0': {
|
|
660
|
-
value: 'after_first',
|
|
661
|
-
insert_after: true
|
|
662
|
-
}
|
|
663
|
-
}
|
|
654
|
+
$unset: [
|
|
655
|
+
'payload.cleanup_flag',
|
|
656
|
+
'profile.country'
|
|
657
|
+
]
|
|
664
658
|
});
|
|
665
659
|
```
|
|
666
660
|
|
|
667
|
-
`$
|
|
668
|
-
|
|
669
|
-
Target shape reminder for `$set_lax`:
|
|
670
|
-
- `payload` is a JSON object; these examples update or delete nested keys inside `payload`.
|
|
661
|
+
`$replace_roots` (replaces the full JSONB document):
|
|
671
662
|
|
|
672
663
|
```js
|
|
673
664
|
await User.update_one({name: 'john'}, {
|
|
674
|
-
$
|
|
675
|
-
'
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
}
|
|
665
|
+
$replace_roots: {
|
|
666
|
+
name: 'john',
|
|
667
|
+
age: 31,
|
|
668
|
+
status: 'active',
|
|
669
|
+
profile: {city: 'Orlando'}
|
|
679
670
|
}
|
|
680
671
|
});
|
|
681
672
|
```
|
|
682
673
|
|
|
683
|
-
|
|
674
|
+
Implicit top-level keys are routed into `$set`:
|
|
684
675
|
|
|
685
676
|
```js
|
|
686
677
|
await User.update_one({name: 'john'}, {
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
value: null,
|
|
690
|
-
create_if_missing: false,
|
|
691
|
-
null_value_treatment: 'return_target'
|
|
692
|
-
}
|
|
693
|
-
}
|
|
678
|
+
age: 32,
|
|
679
|
+
'profile.city': 'Tampa'
|
|
694
680
|
});
|
|
695
681
|
```
|
|
696
682
|
|
|
697
|
-
Multiple update operators in one call (application order is `$
|
|
683
|
+
Multiple update operators in one call (application order is `$replace_roots` -> `$unset` -> `$set`):
|
|
698
684
|
|
|
699
685
|
```js
|
|
700
686
|
await User.update_one({name: 'john'}, {
|
|
701
|
-
$
|
|
702
|
-
'
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
'tags.0': {value: 'vip', insert_after: true}
|
|
687
|
+
$replace_roots: {
|
|
688
|
+
name: 'john',
|
|
689
|
+
status: 'active',
|
|
690
|
+
profile: {city: 'Miami'}
|
|
706
691
|
},
|
|
707
|
-
$
|
|
708
|
-
'
|
|
709
|
-
|
|
710
|
-
null_value_treatment: 'delete_key'
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
});
|
|
714
|
-
```
|
|
715
|
-
|
|
716
|
-
Conflict rule (rejected before SQL):
|
|
717
|
-
|
|
718
|
-
```js
|
|
719
|
-
await User.update_one({name: 'john'}, {
|
|
692
|
+
$unset: [
|
|
693
|
+
'profile.country'
|
|
694
|
+
],
|
|
720
695
|
$set: {
|
|
721
|
-
payload:
|
|
722
|
-
},
|
|
723
|
-
$set_lax: {
|
|
724
|
-
'payload.value': 'ok'
|
|
696
|
+
'payload.score': 50
|
|
725
697
|
}
|
|
726
698
|
});
|
|
727
|
-
// throws: conflicting update paths (same path or parent/child overlap)
|
|
728
699
|
```
|
|
729
700
|
|
|
730
701
|
Timestamp helper examples on update:
|
|
@@ -875,7 +846,7 @@ For the full lifecycle contract, see [`docs/lifecycle.md`](lifecycle.md).
|
|
|
875
846
|
| JSON containment | `$contains` |
|
|
876
847
|
| JSONB key existence | `$has_key`, `$has_any_keys`, `$has_all_keys` |
|
|
877
848
|
| JSONPath | `$json_path_exists`, `$json_path_match` |
|
|
878
|
-
| Updates (`update_one`) | `$set`, `$
|
|
849
|
+
| Updates (`update_one`) | implicit keys, `$set`, `$unset`, `$replace_roots` |
|
|
879
850
|
|
|
880
851
|
## Related Docs
|
|
881
852
|
|
|
@@ -885,5 +856,5 @@ For the full lifecycle contract, see [`docs/lifecycle.md`](lifecycle.md).
|
|
|
885
856
|
- [`docs/api/schema.md`](api/schema.md) (schema API and index declaration rules)
|
|
886
857
|
- [`docs/api/query-builder.md`](api/query-builder.md) (query builder chain and filter families)
|
|
887
858
|
- [`docs/lifecycle.md`](lifecycle.md) (document phases, hydration/save flow, dirty tracking, and serialization)
|
|
888
|
-
- [`docs/query-translation.md`](query-translation.md) (PostgreSQL
|
|
859
|
+
- [`docs/advanced/query-translation.md`](advanced/query-translation.md) (advanced PostgreSQL reference)
|
|
889
860
|
- [`docs/local-integration-testing.md`](local-integration-testing.md) (local integration test setup)
|
package/package.json
CHANGED
|
File without changes
|