meadow 2.0.22 → 2.0.26
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 +110 -141
- package/docs/README.md +34 -230
- package/docs/_cover.md +14 -0
- package/docs/_sidebar.md +44 -12
- package/docs/_topbar.md +5 -0
- package/docs/api/doCount.md +109 -0
- package/docs/api/doCreate.md +132 -0
- package/docs/api/doDelete.md +101 -0
- package/docs/api/doRead.md +122 -0
- package/docs/api/doReads.md +136 -0
- package/docs/api/doUndelete.md +98 -0
- package/docs/api/doUpdate.md +129 -0
- package/docs/api/getRoleName.md +84 -0
- package/docs/api/loadFromPackage.md +153 -0
- package/docs/api/marshalRecordFromSourceToObject.md +92 -0
- package/docs/api/query.md +133 -0
- package/docs/api/rawQueries.md +197 -0
- package/docs/api/reference.md +117 -0
- package/docs/api/setAuthorizer.md +103 -0
- package/docs/api/setDefault.md +90 -0
- package/docs/api/setDefaultIdentifier.md +84 -0
- package/docs/api/setDomain.md +56 -0
- package/docs/api/setIDUser.md +91 -0
- package/docs/api/setJsonSchema.md +92 -0
- package/docs/api/setProvider.md +87 -0
- package/docs/api/setSchema.md +107 -0
- package/docs/api/setScope.md +68 -0
- package/docs/api/validateObject.md +119 -0
- package/docs/architecture.md +316 -0
- package/docs/audit-tracking.md +226 -0
- package/docs/configuration.md +317 -0
- package/docs/providers/meadow-endpoints.md +306 -0
- package/docs/providers/mongodb.md +319 -0
- package/docs/providers/postgresql.md +312 -0
- package/docs/providers/rocksdb.md +297 -0
- package/docs/query-dsl.md +269 -0
- package/docs/quick-start.md +384 -0
- package/docs/raw-queries.md +193 -0
- package/docs/retold-catalog.json +61 -1
- package/docs/retold-keyword-index.json +15860 -4839
- package/docs/soft-deletes.md +224 -0
- package/package.json +44 -27
- package/scripts/bookstore-seed-postgresql.sql +135 -0
- package/scripts/dgraph-test-db.sh +144 -0
- package/scripts/meadow-test-cleanup.sh +5 -1
- package/scripts/mongodb-test-db.sh +98 -0
- package/scripts/postgresql-test-db.sh +124 -0
- package/scripts/solr-test-db.sh +135 -0
- package/source/Meadow.js +5 -0
- package/source/providers/Meadow-Provider-DGraph.js +679 -0
- package/source/providers/Meadow-Provider-MongoDB.js +527 -0
- package/source/providers/Meadow-Provider-PostgreSQL.js +361 -0
- package/source/providers/Meadow-Provider-RocksDB.js +1300 -0
- package/source/providers/Meadow-Provider-Solr.js +726 -0
- package/test/Meadow-Provider-DGraph_tests.js +741 -0
- package/test/Meadow-Provider-MongoDB_tests.js +661 -0
- package/test/Meadow-Provider-PostgreSQL_tests.js +787 -0
- package/test/Meadow-Provider-RocksDB_tests.js +887 -0
- package/test/Meadow-Provider-Solr_tests.js +679 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
# RocksDB Provider
|
|
2
|
+
|
|
3
|
+
> Embedded key-value store for high-throughput scenarios with in-memory filtering
|
|
4
|
+
|
|
5
|
+
The RocksDB provider connects Meadow to [RocksDB](https://rocksdb.org/), a high-performance embedded key-value store. Unlike SQL-based providers, RocksDB does not use a FoxHound dialect for query generation. Instead, records are stored as key-value pairs and all filtering is performed in-memory after a full scan. Direct key lookups are O(1), making this provider ideal for high-throughput scenarios where reads are primarily by key.
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
### Install Dependencies
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install meadow meadow-connection-rocksdb
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Register the Connection
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
const libFable = require('fable').new(
|
|
19
|
+
{
|
|
20
|
+
RocksDB:
|
|
21
|
+
{
|
|
22
|
+
RocksDBFolder: './data/rocksdb',
|
|
23
|
+
KeyMode: 'GUID'
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const libMeadowConnectionRocksDB = require('meadow-connection-rocksdb');
|
|
28
|
+
|
|
29
|
+
// Register the connection service
|
|
30
|
+
libFable.serviceManager.addServiceType('MeadowRocksDBProvider', libMeadowConnectionRocksDB);
|
|
31
|
+
libFable.serviceManager.instantiateServiceProvider('MeadowRocksDBProvider');
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Create a Meadow DAL
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
const libMeadow = require('meadow');
|
|
38
|
+
|
|
39
|
+
const meadow = libMeadow.new(libFable, 'Book')
|
|
40
|
+
.setProvider('RocksDB')
|
|
41
|
+
.setDefaultIdentifier('IDBook')
|
|
42
|
+
.setSchema([
|
|
43
|
+
{ Column: 'IDBook', Type: 'AutoIdentity' },
|
|
44
|
+
{ Column: 'GUIDBook', Type: 'AutoGUID' },
|
|
45
|
+
{ Column: 'Title', Type: 'String', Size: '255' },
|
|
46
|
+
{ Column: 'Author', Type: 'String', Size: '128' },
|
|
47
|
+
{ Column: 'CreateDate', Type: 'CreateDate' },
|
|
48
|
+
{ Column: 'CreatingIDUser', Type: 'CreateIDUser' },
|
|
49
|
+
{ Column: 'UpdateDate', Type: 'UpdateDate' },
|
|
50
|
+
{ Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
|
|
51
|
+
{ Column: 'DeleteDate', Type: 'DeleteDate' },
|
|
52
|
+
{ Column: 'DeletingIDUser', Type: 'DeleteIDUser' },
|
|
53
|
+
{ Column: 'Deleted', Type: 'Deleted' }
|
|
54
|
+
]);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Connection Management
|
|
58
|
+
|
|
59
|
+
### Connection Configuration
|
|
60
|
+
|
|
61
|
+
| Setting | Type | Default | Description |
|
|
62
|
+
|---------|------|---------|-------------|
|
|
63
|
+
| `RocksDB.RocksDBFolder` | string | -- | Filesystem path to the RocksDB data directory |
|
|
64
|
+
| `RocksDB.KeyMode` | string | `'GUID'` | Key generation strategy: `'GUID'` or `'ID'` |
|
|
65
|
+
|
|
66
|
+
### Key Modes
|
|
67
|
+
|
|
68
|
+
The `KeyMode` setting determines how records are keyed in the store:
|
|
69
|
+
|
|
70
|
+
| Mode | Key Format | Description |
|
|
71
|
+
|------|------------|-------------|
|
|
72
|
+
| `GUID` | `M-E-{Scope}-{GUID}` | Keys use the record's GUID (default) |
|
|
73
|
+
| `ID` | `M-EBI-{Scope}-{ID}` | Keys use the record's integer identity |
|
|
74
|
+
|
|
75
|
+
For example, with a `Book` scope:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
GUID mode: M-E-Book-a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
|
79
|
+
ID mode: M-EBI-Book-42
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Storage Model
|
|
83
|
+
|
|
84
|
+
### Key-Value Architecture
|
|
85
|
+
|
|
86
|
+
RocksDB is a key-value store, not a relational database. Each Meadow record is serialized as a JSON value stored under a structured key. This has important implications:
|
|
87
|
+
|
|
88
|
+
- **No SQL dialect** -- The provider does not use FoxHound for query generation
|
|
89
|
+
- **No JOINs** -- Cross-entity queries are not supported
|
|
90
|
+
- **No aggregations** -- Beyond COUNT, no aggregate functions are available
|
|
91
|
+
- **In-memory filtering** -- All filter operations require scanning records and evaluating filters in memory
|
|
92
|
+
|
|
93
|
+
### Direct Key Lookup
|
|
94
|
+
|
|
95
|
+
When reading a record by its primary key (GUID or ID depending on KeyMode), the provider performs a direct key lookup against RocksDB. This is an O(1) operation and is extremely fast regardless of the total number of records in the store.
|
|
96
|
+
|
|
97
|
+
### In-Memory Filter Engine
|
|
98
|
+
|
|
99
|
+
For queries with filters, sorts, or pagination, the provider:
|
|
100
|
+
|
|
101
|
+
1. Scans all records in the scope prefix range
|
|
102
|
+
2. Deserializes each record from JSON
|
|
103
|
+
3. Evaluates filter conditions against each record in memory
|
|
104
|
+
4. Applies sorting in memory
|
|
105
|
+
5. Applies pagination (begin/cap) in memory
|
|
106
|
+
6. Returns the matching result set
|
|
107
|
+
|
|
108
|
+
This means that filtered reads scale linearly with the total number of records in the scope. For large datasets with complex filter requirements, consider a SQL-based provider instead.
|
|
109
|
+
|
|
110
|
+
## CRUD Operations
|
|
111
|
+
|
|
112
|
+
### Create
|
|
113
|
+
|
|
114
|
+
Serializes the record as JSON and stores it under the generated key.
|
|
115
|
+
|
|
116
|
+
```javascript
|
|
117
|
+
meadow.doCreate(
|
|
118
|
+
meadow.query.addRecord({ Title: 'Dune', Author: 'Frank Herbert' }),
|
|
119
|
+
(pError, pCreateQuery, pReadQuery, pRecord) =>
|
|
120
|
+
{
|
|
121
|
+
console.log('New ID:', pRecord.IDBook);
|
|
122
|
+
console.log('Stored with key: M-E-Book-' + pRecord.GUIDBook);
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Read
|
|
127
|
+
|
|
128
|
+
Performs a direct key lookup when filtering by the primary key, or an in-memory scan for other filters.
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
131
|
+
// Direct key lookup (O(1))
|
|
132
|
+
meadow.doRead(
|
|
133
|
+
meadow.query.addFilter('IDBook', 42),
|
|
134
|
+
(pError, pQuery, pRecord) =>
|
|
135
|
+
{
|
|
136
|
+
console.log('Title:', pRecord.Title);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// In-memory filtered scan
|
|
140
|
+
meadow.doReads(
|
|
141
|
+
meadow.query.setCap(25).setBegin(0).addFilter('Author', 'Frank Herbert', '='),
|
|
142
|
+
(pError, pQuery, pRecords) =>
|
|
143
|
+
{
|
|
144
|
+
pRecords.forEach((pBook) => console.log(pBook.Title));
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Update
|
|
149
|
+
|
|
150
|
+
Reads the existing record by key, merges the updated fields, and writes the updated record back.
|
|
151
|
+
|
|
152
|
+
```javascript
|
|
153
|
+
meadow.doUpdate(
|
|
154
|
+
meadow.query
|
|
155
|
+
.addFilter('IDBook', 42)
|
|
156
|
+
.addRecord({ Title: 'Updated Title' }),
|
|
157
|
+
(pError, pUpdateQuery, pReadQuery, pRecord) =>
|
|
158
|
+
{
|
|
159
|
+
console.log('Updated:', pRecord.Title);
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Delete
|
|
164
|
+
|
|
165
|
+
Executes a soft delete by setting the Deleted flag on the stored record.
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
meadow.doDelete(
|
|
169
|
+
meadow.query.addFilter('IDBook', 42),
|
|
170
|
+
(pError, pQuery, pResult) =>
|
|
171
|
+
{
|
|
172
|
+
console.log('Deleted records:', pResult);
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Undelete
|
|
177
|
+
|
|
178
|
+
Reverses a soft delete by clearing the Deleted flag.
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
meadow.doUndelete(
|
|
182
|
+
meadow.query.addFilter('IDBook', 42),
|
|
183
|
+
(pError, pQuery, pResult) =>
|
|
184
|
+
{
|
|
185
|
+
console.log('Restored records:', pResult);
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Count
|
|
190
|
+
|
|
191
|
+
Scans the scope prefix and counts matching records in memory.
|
|
192
|
+
|
|
193
|
+
```javascript
|
|
194
|
+
meadow.doCount(
|
|
195
|
+
meadow.query,
|
|
196
|
+
(pError, pQuery, pCount) =>
|
|
197
|
+
{
|
|
198
|
+
console.log('Total books:', pCount);
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Limitations
|
|
203
|
+
|
|
204
|
+
| Feature | Supported | Notes |
|
|
205
|
+
|---------|-----------|-------|
|
|
206
|
+
| Direct key lookup | Yes | O(1) performance |
|
|
207
|
+
| Filtered reads | Yes | In-memory post-scan filtering |
|
|
208
|
+
| Sorting | Yes | In-memory after scan |
|
|
209
|
+
| Pagination (begin/cap) | Yes | In-memory after scan |
|
|
210
|
+
| COUNT | Yes | In-memory count after scan |
|
|
211
|
+
| JOINs | No | Not supported in key-value model |
|
|
212
|
+
| Aggregations (SUM, AVG) | No | Only COUNT is available |
|
|
213
|
+
| FoxHound dialect | No | Provider uses its own filter engine |
|
|
214
|
+
|
|
215
|
+
## When to Use RocksDB
|
|
216
|
+
|
|
217
|
+
| Use Case | Recommendation |
|
|
218
|
+
|----------|---------------|
|
|
219
|
+
| High-throughput key lookups | Excellent -- O(1) reads by key |
|
|
220
|
+
| Embedded applications | Excellent -- no server process required |
|
|
221
|
+
| Write-heavy workloads | Good -- RocksDB is optimized for writes |
|
|
222
|
+
| Complex filtered queries | Consider SQL providers instead |
|
|
223
|
+
| JOIN-heavy data models | Use MySQL, MSSQL, or PostgreSQL |
|
|
224
|
+
| Browser applications | Use ALASQL provider instead |
|
|
225
|
+
|
|
226
|
+
## Full Setup Example
|
|
227
|
+
|
|
228
|
+
A complete working example using RocksDB for local key-value storage:
|
|
229
|
+
|
|
230
|
+
```javascript
|
|
231
|
+
const libFable = require('fable').new(
|
|
232
|
+
{
|
|
233
|
+
RocksDB:
|
|
234
|
+
{
|
|
235
|
+
RocksDBFolder: './data/bookstore',
|
|
236
|
+
KeyMode: 'GUID'
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const libMeadow = require('meadow');
|
|
241
|
+
const libMeadowConnectionRocksDB = require('meadow-connection-rocksdb');
|
|
242
|
+
|
|
243
|
+
// Register the connection service
|
|
244
|
+
libFable.serviceManager.addServiceType('MeadowRocksDBProvider', libMeadowConnectionRocksDB);
|
|
245
|
+
libFable.serviceManager.instantiateServiceProvider('MeadowRocksDBProvider');
|
|
246
|
+
|
|
247
|
+
// Create the DAL
|
|
248
|
+
const meadow = libMeadow.new(libFable, 'Book')
|
|
249
|
+
.setProvider('RocksDB')
|
|
250
|
+
.setDefaultIdentifier('IDBook')
|
|
251
|
+
.setSchema([
|
|
252
|
+
{ Column: 'IDBook', Type: 'AutoIdentity' },
|
|
253
|
+
{ Column: 'GUIDBook', Type: 'AutoGUID' },
|
|
254
|
+
{ Column: 'Title', Type: 'String', Size: '255' },
|
|
255
|
+
{ Column: 'Author', Type: 'String', Size: '128' },
|
|
256
|
+
{ Column: 'CreateDate', Type: 'CreateDate' },
|
|
257
|
+
{ Column: 'CreatingIDUser', Type: 'CreateIDUser' },
|
|
258
|
+
{ Column: 'UpdateDate', Type: 'UpdateDate' },
|
|
259
|
+
{ Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
|
|
260
|
+
{ Column: 'DeleteDate', Type: 'DeleteDate' },
|
|
261
|
+
{ Column: 'DeletingIDUser', Type: 'DeleteIDUser' },
|
|
262
|
+
{ Column: 'Deleted', Type: 'Deleted' }
|
|
263
|
+
]);
|
|
264
|
+
|
|
265
|
+
// Insert a record
|
|
266
|
+
meadow.doCreate(
|
|
267
|
+
meadow.query.addRecord({ Title: 'Dune', Author: 'Frank Herbert' }),
|
|
268
|
+
(pError, pCreateQuery, pReadQuery, pRecord) =>
|
|
269
|
+
{
|
|
270
|
+
console.log('Created book with ID:', pRecord.IDBook);
|
|
271
|
+
console.log('GUID:', pRecord.GUIDBook);
|
|
272
|
+
console.log('Key: M-E-Book-' + pRecord.GUIDBook);
|
|
273
|
+
|
|
274
|
+
// Direct key lookup
|
|
275
|
+
meadow.doRead(
|
|
276
|
+
meadow.query.addFilter('IDBook', pRecord.IDBook),
|
|
277
|
+
(pError, pQuery, pRecord) =>
|
|
278
|
+
{
|
|
279
|
+
console.log('Read back:', pRecord.Title, 'by', pRecord.Author);
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Error Handling
|
|
285
|
+
|
|
286
|
+
All operations follow the same error handling pattern:
|
|
287
|
+
|
|
288
|
+
- Storage errors are stored in `pQuery.parameters.result.error`
|
|
289
|
+
- Key generation or serialization failures are logged as warnings
|
|
290
|
+
- File system errors (e.g., missing RocksDBFolder) bubble through the callback
|
|
291
|
+
|
|
292
|
+
## Related Documentation
|
|
293
|
+
|
|
294
|
+
- [Providers Overview](providers/README.md) -- Comparison of all providers
|
|
295
|
+
- [SQLite Provider](providers/sqlite.md) -- Embedded SQL alternative
|
|
296
|
+
- [ALASQL Provider](providers/alasql.md) -- In-memory SQL alternative
|
|
297
|
+
- [meadow-connection-rocksdb](https://github.com/stevenvelozo/meadow-connection-rocksdb) -- Connection module source
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# Query DSL
|
|
2
|
+
|
|
3
|
+
Meadow uses [FoxHound](https://github.com/stevenvelozo/foxhound) as its query DSL. FoxHound provides a fluent, chainable API for building database queries without writing raw SQL. Meadow wraps FoxHound and manages query lifecycle, cloning, and schema injection.
|
|
4
|
+
|
|
5
|
+
## Getting a Query
|
|
6
|
+
|
|
7
|
+
Access a new query through the `meadow.query` property:
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
var tmpQuery = tmpBookDAL.query;
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Every access to `.query` returns a fresh, independent clone of the internal FoxHound instance. The clone comes pre-configured with the Meadow entity scope and schema.
|
|
14
|
+
|
|
15
|
+
## Query Cloning and Isolation
|
|
16
|
+
|
|
17
|
+
Each call to `meadow.query` produces an isolated query object. Configuring one query never affects another:
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
var tmpQueryA = tmpBookDAL.query
|
|
21
|
+
.addFilter('Author', 'Asimov')
|
|
22
|
+
.setCap(10);
|
|
23
|
+
|
|
24
|
+
var tmpQueryB = tmpBookDAL.query
|
|
25
|
+
.addFilter('Author', 'Herbert')
|
|
26
|
+
.setCap(50);
|
|
27
|
+
|
|
28
|
+
// tmpQueryA and tmpQueryB are completely independent.
|
|
29
|
+
// tmpQueryA filters for Asimov with cap 10.
|
|
30
|
+
// tmpQueryB filters for Herbert with cap 50.
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This isolation is critical for concurrent operations. You can safely build multiple queries in parallel without state leakage.
|
|
34
|
+
|
|
35
|
+
## Filtering
|
|
36
|
+
|
|
37
|
+
Add filters to constrain which records are returned. The `addFilter` method accepts a column name, a value, and an optional operator.
|
|
38
|
+
|
|
39
|
+
### Basic Syntax
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
// Equality (default operator)
|
|
43
|
+
tmpQuery.addFilter('Author', 'Frank Herbert');
|
|
44
|
+
|
|
45
|
+
// With explicit operator
|
|
46
|
+
tmpQuery.addFilter('YearPublished', 1960, '>=');
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Supported Operators
|
|
50
|
+
|
|
51
|
+
| Operator | Description | Example |
|
|
52
|
+
|----------|-------------|---------|
|
|
53
|
+
| `=` | Equal to (default) | `addFilter('Status', 'Active')` |
|
|
54
|
+
| `!=` | Not equal to | `addFilter('Status', 'Deleted', '!=')` |
|
|
55
|
+
| `>` | Greater than | `addFilter('Price', 10, '>')` |
|
|
56
|
+
| `<` | Less than | `addFilter('Price', 100, '<')` |
|
|
57
|
+
| `>=` | Greater than or equal | `addFilter('YearPublished', 2000, '>=')` |
|
|
58
|
+
| `<=` | Less than or equal | `addFilter('YearPublished', 2020, '<=')` |
|
|
59
|
+
| `LIKE` | Pattern match | `addFilter('Title', '%Dune%', 'LIKE')` |
|
|
60
|
+
| `IN` | Value in set | `addFilter('IDBook', '1,2,3', 'IN')` |
|
|
61
|
+
|
|
62
|
+
### Combining Filters
|
|
63
|
+
|
|
64
|
+
Multiple filters are combined with `AND` logic:
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
var tmpQuery = tmpBookDAL.query
|
|
68
|
+
.addFilter('Author', 'Frank Herbert')
|
|
69
|
+
.addFilter('YearPublished', 1965, '>=')
|
|
70
|
+
.addFilter('YearPublished', 1985, '<=');
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
This generates a query equivalent to:
|
|
74
|
+
|
|
75
|
+
```sql
|
|
76
|
+
WHERE Author = 'Frank Herbert'
|
|
77
|
+
AND YearPublished >= 1965
|
|
78
|
+
AND YearPublished <= 1985
|
|
79
|
+
AND Deleted = 0
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Note that `Deleted = 0` is added automatically when the schema contains a `Deleted` column. See [Soft Deletes](soft-deletes.md) for details.
|
|
83
|
+
|
|
84
|
+
## Pagination
|
|
85
|
+
|
|
86
|
+
Control result set size and offset with `setCap` and `setBegin`.
|
|
87
|
+
|
|
88
|
+
### setCap(n)
|
|
89
|
+
|
|
90
|
+
Limits the number of records returned:
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
// Return at most 25 records
|
|
94
|
+
var tmpQuery = tmpBookDAL.query
|
|
95
|
+
.setCap(25);
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### setBegin(n)
|
|
99
|
+
|
|
100
|
+
Sets the starting offset for pagination:
|
|
101
|
+
|
|
102
|
+
```javascript
|
|
103
|
+
// Skip the first 50 records, return the next 25
|
|
104
|
+
var tmpQuery = tmpBookDAL.query
|
|
105
|
+
.setCap(25)
|
|
106
|
+
.setBegin(50);
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Pagination Example
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
var tmpPageSize = 20;
|
|
113
|
+
var tmpPageNumber = 3;
|
|
114
|
+
|
|
115
|
+
var tmpQuery = tmpBookDAL.query
|
|
116
|
+
.setCap(tmpPageSize)
|
|
117
|
+
.setBegin(tmpPageSize * tmpPageNumber);
|
|
118
|
+
|
|
119
|
+
tmpBookDAL.doReads(tmpQuery,
|
|
120
|
+
function (pError, pQuery, pRecords)
|
|
121
|
+
{
|
|
122
|
+
console.log('Page', tmpPageNumber, ':', pRecords.length, 'records');
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Sorting
|
|
127
|
+
|
|
128
|
+
Add sort directives with `addSort`. Each sort directive is an object with `Column` and `Direction` properties.
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
131
|
+
var tmpQuery = tmpBookDAL.query
|
|
132
|
+
.addSort({ Column: 'Author', Direction: 'ASC' })
|
|
133
|
+
.addSort({ Column: 'Title', Direction: 'DESC' });
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Sort directives are applied in the order they are added.
|
|
137
|
+
|
|
138
|
+
## Column Selection
|
|
139
|
+
|
|
140
|
+
Restrict which columns are returned with `setDataElements`. This is useful for performance when you only need specific fields.
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
var tmpQuery = tmpBookDAL.query
|
|
144
|
+
.setDataElements(['IDBook', 'Title', 'Author']);
|
|
145
|
+
|
|
146
|
+
tmpBookDAL.doReads(tmpQuery,
|
|
147
|
+
function (pError, pQuery, pRecords)
|
|
148
|
+
{
|
|
149
|
+
// Each record only contains IDBook, Title, and Author
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Distinct
|
|
154
|
+
|
|
155
|
+
Return only unique rows with `setDistinct`:
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
var tmpQuery = tmpBookDAL.query
|
|
159
|
+
.setDataElements(['Author'])
|
|
160
|
+
.setDistinct(true);
|
|
161
|
+
|
|
162
|
+
tmpBookDAL.doReads(tmpQuery,
|
|
163
|
+
function (pError, pQuery, pRecords)
|
|
164
|
+
{
|
|
165
|
+
// Returns unique authors only
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Record Attachment
|
|
170
|
+
|
|
171
|
+
Attach a record to the query for create and update operations using `addRecord`:
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
// For doCreate
|
|
175
|
+
var tmpQuery = tmpBookDAL.query
|
|
176
|
+
.addRecord(
|
|
177
|
+
{
|
|
178
|
+
Title: 'Neuromancer',
|
|
179
|
+
Author: 'William Gibson',
|
|
180
|
+
YearPublished: 1984
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
tmpBookDAL.doCreate(tmpQuery,
|
|
184
|
+
function (pError, pCreateQuery, pReadQuery, pRecord)
|
|
185
|
+
{
|
|
186
|
+
console.log('Created:', pRecord.IDBook);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// For doUpdate (must include the default identifier)
|
|
190
|
+
var tmpQuery = tmpBookDAL.query
|
|
191
|
+
.addRecord(
|
|
192
|
+
{
|
|
193
|
+
IDBook: 5,
|
|
194
|
+
Title: 'Neuromancer (Anniversary Edition)'
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
tmpBookDAL.doUpdate(tmpQuery,
|
|
198
|
+
function (pError, pUpdateQuery, pReadQuery, pRecord)
|
|
199
|
+
{
|
|
200
|
+
console.log('Updated:', pRecord.Title);
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
For updates, the record must include the default identifier column (e.g. `IDBook`). Meadow automatically adds a filter on this column to ensure only the intended record is updated.
|
|
205
|
+
|
|
206
|
+
## Delete Tracking Control
|
|
207
|
+
|
|
208
|
+
By default, queries exclude records where `Deleted = 1`. To include deleted records in your results, disable delete tracking on the query:
|
|
209
|
+
|
|
210
|
+
```javascript
|
|
211
|
+
var tmpQuery = tmpBookDAL.query
|
|
212
|
+
.setDisableDeleteTracking(true);
|
|
213
|
+
|
|
214
|
+
tmpBookDAL.doReads(tmpQuery,
|
|
215
|
+
function (pError, pQuery, pRecords)
|
|
216
|
+
{
|
|
217
|
+
// pRecords includes both active and soft-deleted records
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## User Identity
|
|
222
|
+
|
|
223
|
+
The user identity attached to a query is used for auto-stamping `CreateIDUser`, `UpdateIDUser`, and `DeleteIDUser` columns.
|
|
224
|
+
|
|
225
|
+
### Setting User Identity on the DAL
|
|
226
|
+
|
|
227
|
+
The preferred approach is to set the user ID on the Meadow instance, which applies to all queries:
|
|
228
|
+
|
|
229
|
+
```javascript
|
|
230
|
+
tmpBookDAL.setIDUser(42);
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Setting User Identity Per Query
|
|
234
|
+
|
|
235
|
+
Override the user identity on an individual query:
|
|
236
|
+
|
|
237
|
+
```javascript
|
|
238
|
+
var tmpQuery = tmpBookDAL.query;
|
|
239
|
+
tmpQuery.query.IDUser = 99;
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
The per-query value takes precedence over the DAL-level value.
|
|
243
|
+
|
|
244
|
+
### Disabling Auto-Stamps
|
|
245
|
+
|
|
246
|
+
To prevent automatic date and user stamps on a query, set the disable flags:
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
var tmpQuery = tmpBookDAL.query
|
|
250
|
+
.addRecord(
|
|
251
|
+
{
|
|
252
|
+
IDBook: 5,
|
|
253
|
+
Title: 'Manual Update'
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// Prevent automatic UpdateDate stamping
|
|
257
|
+
tmpQuery.query.disableAutoDateStamp = true;
|
|
258
|
+
|
|
259
|
+
// Prevent automatic UpdatingIDUser stamping
|
|
260
|
+
tmpQuery.query.disableAutoUserStamp = true;
|
|
261
|
+
|
|
262
|
+
tmpBookDAL.doUpdate(tmpQuery,
|
|
263
|
+
function (pError, pUpdateQuery, pReadQuery, pRecord)
|
|
264
|
+
{
|
|
265
|
+
// UpdateDate and UpdatingIDUser were not changed
|
|
266
|
+
});
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
See [Audit Tracking](audit-tracking.md) for more details on how auto-stamping works.
|