You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit e65fca9
Browse filesBrowse the repository at this point in the historyBrowse files
docs(go): show the element match in the references and the JSON example
The stashgen README's "Encrypted JSON documents" table gets the Match
row and its SQL, with when Elements and Paths are generated, how
encrypt.AtPath makes a predicate from a path, what a path's type
checks, the run-time Member form, what is refused before the engine,
which arrays get no accessor, the folded equality and the 22023
bound. Its list of what stashgen writes, and -name, name Paths. The
SDK README and package doc show the call, and the CHANGELOG records it
under Unreleased.
The typed-document example orders each visit's at and place, and finds
the customer with one visit at home after 15, with the paths from
Paths: Ada has a visit at home and a visit after 15, but no one visit
that is both. Its generated file is regenerated from the new tag.
Refs #1442
Claude-Session: https://claude.ai/code/session_01MrxG73fnX1p7oiizh3GRG5
`At` has one accessor per ordered path, named for its keys (`$.visits[*].at` is `At.VisitsAt`, `$.a.1` is `At.A1`); two paths with one name, or one path given twice, stop `stashgen`.
183
184
An ordering compares the value `->` extracts at its path, and EQL's `<` compares ordering terms alone, so take the selector from the same accessor (`At.Age.Selector`) rather than spelling the path again.
@@ -193,6 +194,50 @@ SELECT id FROM customers ORDER BY (
193
194
```
194
195
195
196
with `$1` from `At.VisitsAt.Selector(ctx, c)` and `$2` from `At.VisitsAt.Gt(ctx, c, v)`; for the latest visit, order by `ORDER BY 1 DESC NULLS LAST` inside and `DESC` outside.
197
+
198
+
Two queries at a wildcard path, an `EqAt` and an ordering say, can each be answered by a different element.
199
+
To test several members of one element together, use `Match` under `Elements`.
200
+
`Elements` has one accessor per array whose elements have an ordered member, named for the array's keys as `At` names a path: with `ordered=$.visits[*].at;$.visits[*].place`, `$.visits[*]` is `Elements.Visits`, with the members `at` and `place`.
201
+
A root array (`ordered=$[*].name`) is `Elements.Root`.
202
+
An ordered path that ends at its wildcard (`$.tags[*]`), or has none, gives no accessor, and a field without one has no `Elements`.
203
+
Two arrays whose keys give one name (`$.a_b[*]` and `$.aB[*]` are both `AB`), and an array whose keys give no name, get no accessor: `stashgen` still writes the field and its `At`, and prints a notice naming the ordered paths, which the generated file repeats in a comment.
204
+
205
+
The generated `Paths` names each accessor's members, nested as the JSON nests them: `$.visits[*].place` is `Paths.Profile.Visits.Place`.
206
+
`encrypt.AtPath` makes a predicate from one, with `Eq`, `Lt`, `Lte`, `Gt`, `Gte` or `Between` (inclusive).
207
+
208
+
```go
209
+
// rows with one visit that is at home and after 15
210
+
q, err:= customers.Fields.Profile.Elements.Visits.Match(ctx, cipher, encrypt.JSONReadOptions{},
SELECT id FROM customers WHEREeql_v4.json_tree_element_match(profile, $1::eql_v4.query_json_elements)
218
+
```
219
+
220
+
A path is an `encrypt.Path[E, T]`.
221
+
`E` is a type `stashgen` writes for the array (`ProfileFieldElementsVisits`), and `Match` takes only `encrypt.ElementPredicate[E]` of its own array, so a predicate on another array's member does not compile.
222
+
`T` is the member's Go type in the document's struct, read as `encoding/json` reads the document (through pointers, promoted fields, maps and slices), so a value of another type does not compile either.
223
+
It is `any` where the type names none: a `json.RawMessage` document, a path the struct does not hold, or a member inside a type with its own `MarshalJSON`.
224
+
`Paths` holds only fields, and its paths' methods are the SDK's, so any JSON key can name a member.
225
+
With `-name Customer`, it is `CustomerPaths`, as `Fields` is `CustomerFields`.
226
+
227
+
For a filter built at run time, from text such as `visits:(place = "home" AND at > 15)`, name the member with the array's `Member`, written as it is below the array in the tag (`place`, or `address.city`):
228
+
229
+
```go
230
+
visits:= customers.Fields.Profile.Elements.Visits
231
+
q, err:= visits.Match(ctx, cipher, encrypt.JSONReadOptions{}, visits.Member("place").Eq("home"), visits.Member("at").Gt(15))
232
+
```
233
+
234
+
Its values are `any`, as the orderings take them.
235
+
A member the field does not order is refused before the engine is asked, with `encrypt.ErrTerm`, naming the member, the array and the members it orders; no predicates, a zero `ElementPredicate`, and a member with a wildcard, are refused with `encrypt.ErrEncoding`.
236
+
`Paths` names each key on its own, so an array whose members it cannot name gets no accessor and the same notice: a key that gives no Go name there (`$.a.1[*].x`, whose `1``At` joins to `A1X`), or one name for two paths (`$.a[*].b` beside `$.a[*].b.c`, or `$.x.a_b[*].m` beside `$.x.aB.n[*].o`).
237
+
Each comparison is of the member's ordering term, so `Eq` is an equality of folded values: text compares case- and accent-folded (`"home"` equals `"Home"`), and integers past 2^53 can tie.
238
+
`EqAt` at the member's full path is exact, of any element.
239
+
`Match` reads each array to `opts.WildcardBound` elements, 64 when it is 0, and a row whose array is longer fails the query with SQLSTATE 22023 rather than answer over part of it: set the bound past the longest array.
240
+
The function runs on each row a scan gives it, so narrow the rows first with an indexed predicate, such as `@>` on the same values.
196
241
`OpenOne` reads one value and refuses a read whose path has a wildcard before anything is opened; open a wildcard read with `Open`.
197
242
A needle and a value are Go values: a `json.RawMessage` of JSON text crosses as it is, and anything else is marshalled with `encoding/json`.
198
243
`EqAt` and the orderings take a scalar: a string, a number, a bool, or `nil` for JSON `null`.
@@ -253,6 +298,7 @@ For `-type User`, the file `user_stash.go` holds:
253
298
`encrypt_into=TextMatch` answers `Matches`, which returns an `encrypt.EQLMatchQuery`: the `eql.TextMatchQuery` operand, with `Verify` and `TargetLimit`.
254
299
The query of `Matches` carries `Verify` and `TargetLimit`, because its answer can hold rows that do not match; an exact operator's query carries neither.
255
300
See the encrypt package's README for each method's result and the query with `database/sql`.
301
+
-`Paths`, when a `JsonSearch` field orders members of an array's elements: those members, nested as the JSON nests them, for `encrypt.AtPath` and the array's `Match` (see [Encrypted JSON documents](#encrypted-json-documents)).
256
302
-`String` and `LogValue` on `EncryptedUser`, which print the passthrough fields and hide the sealed ones.
257
303
- A copy of the fields of `User`, so a change to them stops the build.
258
304
@@ -263,7 +309,7 @@ Generated code uses no reflection, and no function in it panics.
263
309
| Flag | Meaning |
264
310
|---|---|
265
311
|`-type T`| The struct that carries the `stash` tags. Required. |
266
-
|`-name N`| Write `EncryptN`, `DecryptN` and `NFields`. A package holds one `Encrypt`; the second struct in a package needs a name. |
312
+
|`-name N`| Write `EncryptN`, `DecryptN` and `NFields` (and `NPaths`). A package holds one `Encrypt`; the second struct in a package needs a name. |
267
313
|`-for P.F`|`T` declares the tags for `F`, a type in another package. Each field of `T` names a field of `F` with the same name and type, and every exported field of `F` is named. |
268
314
|`-model Name=R`| A model `R` for separate columns: one field for each column, each tagged with the output it holds, `stash:"email"` or `stash:"email,equality"`. Writes `EncryptName` and `DecryptName`. Any number. |
269
315
|`-model Name=R:D`| The same, for an `R` that cannot carry tags. The struct `D` in your package declares them. |
|`JsonSearch` (`eql.JSONSearch`) | struct, map, slice, or `json.RawMessage`| containment, equality at a path, ordering at declared paths, selection, subtree reads; see [Encrypted JSON documents](#encrypted-json-documents)|
523
+
|`JsonSearch` (`eql.JSONSearch`) | struct, map, slice, or `json.RawMessage`| containment, equality at a path, ordering at declared paths, several members of one array element, selection, subtree reads; see [Encrypted JSON documents](#encrypted-json-documents)|
524
524
525
525
Any built-in Go integer field takes an integer EQL type (ADR-0002): the field
526
526
is sealed at the type's width, and opens back into the field's type. A type
@@ -658,6 +658,35 @@ element of each row, so an ordering over every element is written over
658
658
`eql_v4.json_tree_path_query(col, $sel)`, its matches (the stashgen README
659
659
shows the SQL).
660
660
661
+
Two such queries can each be answered by a different element. To test
662
+
several members of one array element together, use `Match` on the array's
663
+
accessor under `Elements`. A field has one for each array whose elements
664
+
have an ordered member (`ordered=$.visits[*].at;$.visits[*].place`). The
665
+
generated `Paths` names those members, nested as the JSON nests them, and
666
+
`encrypt.AtPath` makes a predicate from one:
667
+
668
+
```go
669
+
q, err:= users.Fields.Profile.Elements.Visits.Match(ctx, cipher, encrypt.JSONReadOptions{},
0 commit comments