WIP on Json doc updates

This commit is contained in:
Daniel J. Summers 2025-04-24 19:39:53 -04:00
parent 472fde5a9a
commit 07c46fe668
2 changed files with 33 additions and 12 deletions

View File

@ -12,7 +12,8 @@ There are several categories of operations that can be accomplished against docu
- **Save** adds a new document, updating an existing one if the ID is already present ("upsert")
- **Update** updates an existing document, doing nothing if no documents satisfy the criteria
- **Patch** updates a portion of an existing document, doing nothing if no documents satisfy the criteria
- **Find** returns the documents matching some criteria
- **Find** returns the documents matching some criteria as domain objects
- **Json** returns documents as JSON strings or outputs that JSON directly
- **RemoveFields** removes fields from documents matching some criteria
- **Delete** removes documents matching some criteria
@ -23,7 +24,7 @@ There are several categories of operations that can be accomplished against docu
- **byContains** (PostgreSQL only) uses a JSON containment query (the `@>` operator) to find documents where the given sub-document occurs (think of this as an `=` comparison based on one or more properties in the document; looking for hotels with `{ "country": "USA", "rating": 4 }` would find all hotels with a rating of 4 in the United States); applies to all but Update
- **byJsonPath** (PostgreSQL only) uses a JSON patch match query (the `@?` operator) to make specific queries against a document's structure (it also supports more operators than a containment query; to find all hotels rated 4 _or higher_ in the United States, we could query for `"$ ? (@.country == \"USA\" && @.rating > 4)"`); applies to all but Update
Finally, `Find` also has `firstBy*` implementations for all supported criteria types.
Finally, `Find` and `Json` also has `firstBy*` implementations for all supported criteria types.
## Saving Documents
@ -62,10 +63,15 @@ For SQLite, we can utilize a `Field` query with a between operator. (This will a
```php
// SQLite
Patch::byFields('room', [Field::between('roomNumber', 221, 240)], ['inService' => false]);
Patch::byFields('room',
[Field::equal('hotelId', 'abc'), Field::between('roomNumber', 221, 240)],
['inService' => false]);
```
## Finding Documents
> [!NOTE]
> When multiple fields are provided to any `*byFields` function, the default matching behavior is `FieldMatch.All`; any documents identified will match all criteria. Passing `FieldMatch.Any` modifies this behavior to identify documents where any one of the criteria match.
## Finding Documents as Domain Items
Functions to find documents start with `Find::`. There are variants to find all documents in a table, find by ID, find by JSON field comparison, find by JSON containment, or find by JSON Path. The hotel update example above utilizes an ID lookup; the descriptions of JSON containment and JSON Path show examples of the criteria used to retrieve using those techniques.
@ -109,6 +115,13 @@ foreach ($result->items as $item) {
// Do something amazing with $item
}
```
## Finding Documents as JSON
If an application serves endpoints that return JSON, taking the time to retrieve documents, deserialize them into objects, then turning around and serializing those same documents back to JSON - there's a lot of unnecessary processing going on. Static functions on the `Json` object address this by returning, or directly outputting, the JSON text received from the database.
`Json` has the same function names as `Find`, and these functions return the JSON as a string. These always return some string with valid JSON; an empty multiple-document request will be `[]`, and a one-or-none document request will be `{}` in the "none" scenario.
A second set of functions are prefixed with `output` (ex. `Json::outputAll`); these functions `echo` the JSON as it is retrieved from the database. This is the preferred method for JSON APIs, as it incurs no intermediate overhead; take the documents from the database and ship 'em off to the distant end. As these responses end up as a stream of text, we will need to identify this as JSON; `Json::setContentType()` will do this, and should be called before any other output is sent.
## Deleting Documents
@ -126,14 +139,14 @@ Functions to check for existence start with `Exists::`. Documents may be checked
The table below shows which commands are available for each access method. (X = supported for both, P = PostgreSQL only)
| Operation | `all` | `byId` | `byFields` | `byContains` | `byJsonPath` | `firstByFields` | `firstByContains` | `firstByJsonPath` |
|----------------|:-----:|:------:|:----------:|:------------:|:------------:|:---------------:|:-----------------:|:-----------------:|
| `Count` | X | | X | P | P | | | |
| `Exists` | | X | X | P | P | | | |
| `Find` | X | X | X | P | P | X | P | P |
| `Patch` | | X | X | P | P | | | |
| `RemoveFields` | | X | X | P | P | | | |
| `Delete` | | X | X | P | P | | | |
| Operation | `all` | `byId` | `byFields` | `byContains` | `byJsonPath` | `firstByFields` | `firstByContains` | `firstByJsonPath` |
|-----------------|:-----:|:------:|:----------:|:------------:|:------------:|:---------------:|:-----------------:|:-----------------:|
| `Count` | X | | X | P | P | | | |
| `Exists` | | X | X | P | P | | | |
| `Find` / `Json` | X | X | X | P | P | X | P | P |
| `Patch` | | X | X | P | P | | | |
| `RemoveFields` | | X | X | P | P | | | |
| `Delete` | | X | X | P | P | | | |
`Document::insert`, `Document::save`, and `Document::update` operate on single documents.

View File

@ -241,4 +241,12 @@ class Json
{
echo self::firstByJsonPath($tableName, $path, $orderBy);
}
/**
* Set the content type of this page's output to JSON
*/
public static function setContentType(): void
{
header('Content-Type: application/json');
}
}