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.
Files changed (59) hide show
  1. package/README.md +110 -141
  2. package/docs/README.md +34 -230
  3. package/docs/_cover.md +14 -0
  4. package/docs/_sidebar.md +44 -12
  5. package/docs/_topbar.md +5 -0
  6. package/docs/api/doCount.md +109 -0
  7. package/docs/api/doCreate.md +132 -0
  8. package/docs/api/doDelete.md +101 -0
  9. package/docs/api/doRead.md +122 -0
  10. package/docs/api/doReads.md +136 -0
  11. package/docs/api/doUndelete.md +98 -0
  12. package/docs/api/doUpdate.md +129 -0
  13. package/docs/api/getRoleName.md +84 -0
  14. package/docs/api/loadFromPackage.md +153 -0
  15. package/docs/api/marshalRecordFromSourceToObject.md +92 -0
  16. package/docs/api/query.md +133 -0
  17. package/docs/api/rawQueries.md +197 -0
  18. package/docs/api/reference.md +117 -0
  19. package/docs/api/setAuthorizer.md +103 -0
  20. package/docs/api/setDefault.md +90 -0
  21. package/docs/api/setDefaultIdentifier.md +84 -0
  22. package/docs/api/setDomain.md +56 -0
  23. package/docs/api/setIDUser.md +91 -0
  24. package/docs/api/setJsonSchema.md +92 -0
  25. package/docs/api/setProvider.md +87 -0
  26. package/docs/api/setSchema.md +107 -0
  27. package/docs/api/setScope.md +68 -0
  28. package/docs/api/validateObject.md +119 -0
  29. package/docs/architecture.md +316 -0
  30. package/docs/audit-tracking.md +226 -0
  31. package/docs/configuration.md +317 -0
  32. package/docs/providers/meadow-endpoints.md +306 -0
  33. package/docs/providers/mongodb.md +319 -0
  34. package/docs/providers/postgresql.md +312 -0
  35. package/docs/providers/rocksdb.md +297 -0
  36. package/docs/query-dsl.md +269 -0
  37. package/docs/quick-start.md +384 -0
  38. package/docs/raw-queries.md +193 -0
  39. package/docs/retold-catalog.json +61 -1
  40. package/docs/retold-keyword-index.json +15860 -4839
  41. package/docs/soft-deletes.md +224 -0
  42. package/package.json +44 -27
  43. package/scripts/bookstore-seed-postgresql.sql +135 -0
  44. package/scripts/dgraph-test-db.sh +144 -0
  45. package/scripts/meadow-test-cleanup.sh +5 -1
  46. package/scripts/mongodb-test-db.sh +98 -0
  47. package/scripts/postgresql-test-db.sh +124 -0
  48. package/scripts/solr-test-db.sh +135 -0
  49. package/source/Meadow.js +5 -0
  50. package/source/providers/Meadow-Provider-DGraph.js +679 -0
  51. package/source/providers/Meadow-Provider-MongoDB.js +527 -0
  52. package/source/providers/Meadow-Provider-PostgreSQL.js +361 -0
  53. package/source/providers/Meadow-Provider-RocksDB.js +1300 -0
  54. package/source/providers/Meadow-Provider-Solr.js +726 -0
  55. package/test/Meadow-Provider-DGraph_tests.js +741 -0
  56. package/test/Meadow-Provider-MongoDB_tests.js +661 -0
  57. package/test/Meadow-Provider-PostgreSQL_tests.js +787 -0
  58. package/test/Meadow-Provider-RocksDB_tests.js +887 -0
  59. package/test/Meadow-Provider-Solr_tests.js +679 -0
@@ -0,0 +1,384 @@
1
+ # Quick Start
2
+
3
+ This guide walks you through installing Meadow, connecting to a database, and performing every core CRUD operation.
4
+
5
+ ## Installation
6
+
7
+ Meadow requires [Fable](https://github.com/stevenvelozo/fable) as its runtime container and a connection module for your chosen database. Install all three:
8
+
9
+ ```bash
10
+ npm install meadow fable meadow-connection-mysql
11
+ ```
12
+
13
+ Replace `meadow-connection-mysql` with the connection module that matches your database. Available connection modules include `meadow-connection-mssql`, `meadow-connection-sqlite`, `meadow-connection-rocksdb`, and others.
14
+
15
+ ## Creating a Fable Instance
16
+
17
+ Fable provides configuration, logging, and dependency injection. Create a Fable instance with your database connection settings:
18
+
19
+ ```javascript
20
+ var libFable = require('fable');
21
+
22
+ var _Fable = new libFable(
23
+ {
24
+ MeadowProvider: 'MySQL',
25
+ MySQL:
26
+ {
27
+ Server: 'localhost',
28
+ Port: 3306,
29
+ User: 'root',
30
+ Password: 'my-secret-pw',
31
+ Database: 'bookstore',
32
+ ConnectionPoolLimit: 20
33
+ }
34
+ });
35
+ ```
36
+
37
+ ## Registering a Connection Provider
38
+
39
+ Before Meadow can execute queries, the database connection pool must be registered on the Fable instance. Each connection module attaches its pool to a well-known property on Fable.
40
+
41
+ For MySQL using `meadow-connection-mysql`:
42
+
43
+ ```javascript
44
+ var libMeadowConnectionMySQL = require('meadow-connection-mysql');
45
+
46
+ var tmpConnectionProvider = libMeadowConnectionMySQL.new(_Fable);
47
+ tmpConnectionProvider.connect(
48
+ function (pError)
49
+ {
50
+ if (pError)
51
+ {
52
+ console.log('Connection error:', pError);
53
+ return;
54
+ }
55
+ console.log('Connected to MySQL');
56
+ // Meadow is now ready to execute queries
57
+ });
58
+ ```
59
+
60
+ For the legacy approach, you can also create a pool manually:
61
+
62
+ ```javascript
63
+ var libMySQL = require('mysql2');
64
+
65
+ _Fable.MeadowMySQLConnectionPool = libMySQL.createPool(
66
+ {
67
+ connectionLimit: _Fable.settings.MySQL.ConnectionPoolLimit,
68
+ host: _Fable.settings.MySQL.Server,
69
+ port: _Fable.settings.MySQL.Port,
70
+ user: _Fable.settings.MySQL.User,
71
+ password: _Fable.settings.MySQL.Password,
72
+ database: _Fable.settings.MySQL.Database,
73
+ namedPlaceholders: true
74
+ });
75
+ ```
76
+
77
+ ## Creating a Meadow DAL
78
+
79
+ Create a new Meadow data access layer (DAL) for an entity by calling `meadow.new()` with a Fable instance and an entity scope name:
80
+
81
+ ```javascript
82
+ var libMeadow = require('meadow');
83
+
84
+ var tmpBookDAL = libMeadow.new(_Fable, 'Book');
85
+ ```
86
+
87
+ The scope name (here `'Book'`) determines the database table name for queries and establishes naming conventions for the default identifier column (`IDBook`) and GUID column (`GUIDBook`).
88
+
89
+ ## Setting Provider, Default Identifier, and Schema
90
+
91
+ Chain configuration calls to set up the provider, default identifier, and schema:
92
+
93
+ ```javascript
94
+ var tmpBookDAL = libMeadow.new(_Fable, 'Book')
95
+ .setProvider('MySQL')
96
+ .setDefaultIdentifier('IDBook')
97
+ .setSchema(
98
+ [
99
+ { Column: 'IDBook', Type: 'AutoIdentity' },
100
+ { Column: 'GUIDBook', Type: 'AutoGUID' },
101
+ { Column: 'Title', Type: 'String', Size: '255' },
102
+ { Column: 'Author', Type: 'String', Size: '128' },
103
+ { Column: 'YearPublished', Type: 'Number' },
104
+ { Column: 'CreateDate', Type: 'CreateDate' },
105
+ { Column: 'CreatingIDUser', Type: 'CreateIDUser' },
106
+ { Column: 'UpdateDate', Type: 'UpdateDate' },
107
+ { Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
108
+ { Column: 'Deleted', Type: 'Deleted' },
109
+ { Column: 'DeleteDate', Type: 'DeleteDate' },
110
+ { Column: 'DeletingIDUser', Type: 'DeleteIDUser' }
111
+ ])
112
+ .setDefault(
113
+ {
114
+ IDBook: null,
115
+ GUIDBook: '',
116
+ Title: '',
117
+ Author: '',
118
+ YearPublished: 0,
119
+ CreateDate: false,
120
+ CreatingIDUser: 0,
121
+ UpdateDate: false,
122
+ UpdatingIDUser: 0,
123
+ Deleted: 0,
124
+ DeleteDate: false,
125
+ DeletingIDUser: 0
126
+ });
127
+ ```
128
+
129
+ If you do not call `setProvider()` explicitly, Meadow uses the `MeadowProvider` value from Fable settings (defaulting to `'None'`).
130
+
131
+ ## CRUD Walkthrough
132
+
133
+ Every CRUD operation follows the same pattern: obtain a query from `meadow.query`, configure it, then pass it to a `do*` method with a callback.
134
+
135
+ ### Create a Record
136
+
137
+ ```javascript
138
+ var tmpQuery = tmpBookDAL.query
139
+ .addRecord(
140
+ {
141
+ Title: 'Dune',
142
+ Author: 'Frank Herbert',
143
+ YearPublished: 1965
144
+ });
145
+
146
+ tmpBookDAL.doCreate(tmpQuery,
147
+ function (pError, pCreateQuery, pReadQuery, pRecord)
148
+ {
149
+ if (pError)
150
+ {
151
+ console.log('Create error:', pError);
152
+ return;
153
+ }
154
+ console.log('Created book:', pRecord.IDBook, '-', pRecord.Title);
155
+ });
156
+ ```
157
+
158
+ The create behavior performs three steps internally: checks GUID uniqueness (if a GUID is provided), inserts the record, and reads it back. The callback receives the fully marshalled record with its new auto-generated `IDBook`.
159
+
160
+ ### Read a Single Record
161
+
162
+ ```javascript
163
+ var tmpQuery = tmpBookDAL.query
164
+ .addFilter('IDBook', 1);
165
+
166
+ tmpBookDAL.doRead(tmpQuery,
167
+ function (pError, pQuery, pRecord)
168
+ {
169
+ if (pError)
170
+ {
171
+ console.log('Read error:', pError);
172
+ return;
173
+ }
174
+ if (!pRecord)
175
+ {
176
+ console.log('No record found');
177
+ return;
178
+ }
179
+ console.log('Read:', pRecord.Title, 'by', pRecord.Author);
180
+ });
181
+ ```
182
+
183
+ ### Update a Record
184
+
185
+ ```javascript
186
+ var tmpQuery = tmpBookDAL.query
187
+ .addRecord(
188
+ {
189
+ IDBook: 1,
190
+ Title: 'Dune (Revised Edition)',
191
+ Author: 'Frank Herbert'
192
+ });
193
+
194
+ tmpBookDAL.doUpdate(tmpQuery,
195
+ function (pError, pUpdateQuery, pReadQuery, pRecord)
196
+ {
197
+ if (pError)
198
+ {
199
+ console.log('Update error:', pError);
200
+ return;
201
+ }
202
+ console.log('Updated:', pRecord.Title);
203
+ });
204
+ ```
205
+
206
+ The update behavior automatically adds a filter on the default identifier from the record, executes the update, then reads the record back and returns the marshalled result. The `UpdateDate` and `UpdatingIDUser` columns are automatically stamped.
207
+
208
+ ### List Records (Reads)
209
+
210
+ ```javascript
211
+ var tmpQuery = tmpBookDAL.query
212
+ .setCap(25)
213
+ .setBegin(0)
214
+ .addSort({ Column: 'Title', Direction: 'ASC' });
215
+
216
+ tmpBookDAL.doReads(tmpQuery,
217
+ function (pError, pQuery, pRecords)
218
+ {
219
+ if (pError)
220
+ {
221
+ console.log('Reads error:', pError);
222
+ return;
223
+ }
224
+ console.log('Found', pRecords.length, 'books');
225
+ for (var i = 0; i < pRecords.length; i++)
226
+ {
227
+ console.log(' -', pRecords[i].Title);
228
+ }
229
+ });
230
+ ```
231
+
232
+ ### Count Records
233
+
234
+ ```javascript
235
+ var tmpQuery = tmpBookDAL.query
236
+ .addFilter('Author', 'Frank Herbert');
237
+
238
+ tmpBookDAL.doCount(tmpQuery,
239
+ function (pError, pQuery, pCount)
240
+ {
241
+ if (pError)
242
+ {
243
+ console.log('Count error:', pError);
244
+ return;
245
+ }
246
+ console.log('Frank Herbert has', pCount, 'books');
247
+ });
248
+ ```
249
+
250
+ ### Soft Delete a Record
251
+
252
+ ```javascript
253
+ var tmpQuery = tmpBookDAL.query
254
+ .addFilter('IDBook', 1);
255
+
256
+ tmpBookDAL.doDelete(tmpQuery,
257
+ function (pError, pQuery, pResult)
258
+ {
259
+ if (pError)
260
+ {
261
+ console.log('Delete error:', pError);
262
+ return;
263
+ }
264
+ console.log('Soft-deleted book, affected rows:', pResult);
265
+ });
266
+ ```
267
+
268
+ When the schema contains a `Deleted` column of type `'Deleted'`, the delete operation generates an `UPDATE` statement that sets `Deleted = 1` rather than issuing a `DELETE` statement. Subsequent queries automatically filter out deleted records.
269
+
270
+ ### Undelete a Record
271
+
272
+ ```javascript
273
+ var tmpQuery = tmpBookDAL.query
274
+ .addFilter('IDBook', 1);
275
+
276
+ tmpBookDAL.doUndelete(tmpQuery,
277
+ function (pError, pQuery, pResult)
278
+ {
279
+ if (pError)
280
+ {
281
+ console.log('Undelete error:', pError);
282
+ return;
283
+ }
284
+ console.log('Restored book, affected rows:', pResult);
285
+ });
286
+ ```
287
+
288
+ Undelete sets `Deleted = 0`, making the record visible in normal queries again.
289
+
290
+ ## Setting User Identity
291
+
292
+ Meadow automatically stamps user identity into `CreateIDUser`, `UpdateIDUser`, and `DeleteIDUser` columns. Set the acting user on the DAL instance:
293
+
294
+ ```javascript
295
+ tmpBookDAL.setIDUser(42);
296
+ ```
297
+
298
+ You can also set user identity on a per-query basis:
299
+
300
+ ```javascript
301
+ var tmpQuery = tmpBookDAL.query;
302
+ tmpQuery.query.IDUser = 42;
303
+ ```
304
+
305
+ ## Loading from a Package File
306
+
307
+ Instead of configuring scope, schema, default object, and JSON schema individually, you can define everything in a single JSON package file and load it:
308
+
309
+ ```javascript
310
+ var tmpBookDAL = libMeadow.new(_Fable)
311
+ .loadFromPackage(__dirname + '/Book.json');
312
+ ```
313
+
314
+ The package file structure:
315
+
316
+ ```json
317
+ {
318
+ "Scope": "Book",
319
+ "DefaultIdentifier": "IDBook",
320
+ "Schema": [
321
+ { "Column": "IDBook", "Type": "AutoIdentity" },
322
+ { "Column": "GUIDBook", "Type": "AutoGUID" },
323
+ { "Column": "Title", "Type": "String", "Size": "255" },
324
+ { "Column": "Author", "Type": "String", "Size": "128" },
325
+ { "Column": "CreateDate", "Type": "CreateDate" },
326
+ { "Column": "CreatingIDUser", "Type": "CreateIDUser" },
327
+ { "Column": "UpdateDate", "Type": "UpdateDate" },
328
+ { "Column": "UpdatingIDUser", "Type": "UpdateIDUser" },
329
+ { "Column": "Deleted", "Type": "Deleted" },
330
+ { "Column": "DeleteDate", "Type": "DeleteDate" },
331
+ { "Column": "DeletingIDUser", "Type": "DeleteIDUser" }
332
+ ],
333
+ "DefaultObject": {
334
+ "IDBook": null,
335
+ "GUIDBook": "",
336
+ "Title": "",
337
+ "Author": "",
338
+ "CreateDate": false,
339
+ "CreatingIDUser": 0,
340
+ "UpdateDate": false,
341
+ "UpdatingIDUser": 0,
342
+ "Deleted": 0,
343
+ "DeleteDate": false,
344
+ "DeletingIDUser": 0
345
+ },
346
+ "JsonSchema": {
347
+ "title": "Book",
348
+ "description": "A book in the library.",
349
+ "type": "object",
350
+ "properties": {
351
+ "IDBook": {
352
+ "description": "The unique identifier for a book",
353
+ "type": "integer"
354
+ },
355
+ "Title": {
356
+ "description": "The title of the book",
357
+ "type": "string"
358
+ },
359
+ "Author": {
360
+ "description": "The author of the book",
361
+ "type": "string"
362
+ }
363
+ },
364
+ "required": ["IDBook", "Title"]
365
+ }
366
+ }
367
+ ```
368
+
369
+ You can also load from an in-memory object using `loadFromPackageObject()`:
370
+
371
+ ```javascript
372
+ var tmpBookDAL = libMeadow.new(_Fable)
373
+ .loadFromPackageObject(
374
+ {
375
+ Scope: 'Book',
376
+ DefaultIdentifier: 'IDBook',
377
+ Schema:
378
+ [
379
+ { Column: 'IDBook', Type: 'AutoIdentity' },
380
+ { Column: 'GUIDBook', Type: 'AutoGUID' },
381
+ { Column: 'Title', Type: 'String', Size: '255' }
382
+ ]
383
+ });
384
+ ```
@@ -0,0 +1,193 @@
1
+ # Raw Queries
2
+
3
+ Raw queries let you bypass FoxHound's query generation and provide your own SQL strings directly. This is an escape hatch for complex queries -- JOINs, subqueries, custom reports, and anything the DSL does not natively support.
4
+
5
+ ## What Raw Queries Are
6
+
7
+ When Meadow executes a CRUD operation, FoxHound normally generates the SQL query body from your filters, sort directives, and schema. A raw query override replaces that generated SQL with a string you provide. The override is stored by tag name and is automatically applied when the corresponding behavior runs.
8
+
9
+ Raw queries are managed through the `meadow.rawQueries` object, which is an instance of `Meadow-RawQuery`.
10
+
11
+ ## Setting a Raw Query
12
+
13
+ Use `setQuery` to register a SQL override string by tag name:
14
+
15
+ ```javascript
16
+ var tmpBookDAL = libMeadow.new(_Fable, 'Book')
17
+ .setProvider('MySQL')
18
+ .setDefaultIdentifier('IDBook')
19
+ .setSchema(tmpBookSchema);
20
+
21
+ tmpBookDAL.rawQueries.setQuery('Reads',
22
+ 'SELECT b.IDBook, b.Title, b.Author, a.Name AS AuthorName ' +
23
+ 'FROM Book b ' +
24
+ 'INNER JOIN Author a ON b.IDAuthor = a.IDAuthor ' +
25
+ 'WHERE b.Deleted = 0');
26
+ ```
27
+
28
+ After setting this override, every `doReads` call on this DAL uses your custom SQL instead of the auto-generated query.
29
+
30
+ The `setQuery` method returns the Meadow instance, so you can chain it if desired.
31
+
32
+ ## Loading a Raw Query from a File
33
+
34
+ Use `loadQuery` to read a SQL override from a file on disk. This is useful for keeping complex queries in their own `.sql` files:
35
+
36
+ ```javascript
37
+ tmpBookDAL.rawQueries.loadQuery('Reads', __dirname + '/queries/BookReads.sql',
38
+ function (pSuccess)
39
+ {
40
+ if (pSuccess)
41
+ {
42
+ console.log('Custom Reads query loaded');
43
+ }
44
+ else
45
+ {
46
+ console.log('Failed to load custom Reads query');
47
+ }
48
+ });
49
+ ```
50
+
51
+ The callback receives `true` if the file was loaded successfully, or `false` if there was an error. On failure, the query tag is set to an empty string (which means FoxHound will generate an empty query body rather than falling back to auto-generation).
52
+
53
+ ## Retrieving a Raw Query
54
+
55
+ Use `getQuery` to retrieve a previously stored raw query by tag:
56
+
57
+ ```javascript
58
+ var tmpSQL = tmpBookDAL.rawQueries.getQuery('Reads');
59
+ // Returns the SQL string, or false if no query is set for this tag
60
+ ```
61
+
62
+ ## Checking if a Raw Query Exists
63
+
64
+ Use `checkQuery` to test whether a query has been registered for a given tag:
65
+
66
+ ```javascript
67
+ if (tmpBookDAL.rawQueries.checkQuery('Reads'))
68
+ {
69
+ console.log('Custom Reads query is set');
70
+ }
71
+ else
72
+ {
73
+ console.log('Using auto-generated Reads query');
74
+ }
75
+ ```
76
+
77
+ Returns `true` if a query has been set for the tag, `false` otherwise.
78
+
79
+ ## Override Tags
80
+
81
+ Meadow behaviors check for raw query overrides using specific tag names. The following tags are recognized:
82
+
83
+ | Tag | Behavior | Description |
84
+ |-----|----------|-------------|
85
+ | `Read` | `doRead` | Override the single-record read query |
86
+ | `Reads` | `doReads` | Override the multi-record read query |
87
+ | `Delete` | `doDelete` | Override the delete query |
88
+ | `Undelete` | `doUndelete` | Override the undelete query |
89
+ | `Count` | `doCount` | Override the count query |
90
+
91
+ Additionally, the `Read` override is also used during `doCreate` and `doUpdate` when they read the record back after writing. This means setting a `Read` override affects the read-back step of create and update operations as well.
92
+
93
+ ### Create and Update Do Not Support Raw Query Overrides
94
+
95
+ The `Create` and `Update` tags are **not** supported for raw query overrides. The insert and update SQL generation involves complex parameter binding and column mapping that is tightly coupled to FoxHound's query builder. Attempting to set a `Create` or `Update` override will have no effect.
96
+
97
+ If you need custom insert or update behavior, consider using the provider's database connection directly for those specific operations.
98
+
99
+ ## Examples
100
+
101
+ ### JOIN Query for Reads
102
+
103
+ Create a file `queries/BookWithAuthor.sql`:
104
+
105
+ ```sql
106
+ SELECT
107
+ b.IDBook,
108
+ b.GUIDBook,
109
+ b.Title,
110
+ b.YearPublished,
111
+ b.CreateDate,
112
+ b.CreatingIDUser,
113
+ b.UpdateDate,
114
+ b.UpdatingIDUser,
115
+ b.Deleted,
116
+ b.DeleteDate,
117
+ b.DeletingIDUser,
118
+ a.Name AS AuthorName,
119
+ a.Country AS AuthorCountry
120
+ FROM Book b
121
+ LEFT JOIN Author a ON b.IDAuthor = a.IDAuthor
122
+ WHERE b.Deleted = 0
123
+ ORDER BY b.Title ASC
124
+ ```
125
+
126
+ Load and use it:
127
+
128
+ ```javascript
129
+ tmpBookDAL.rawQueries.loadQuery('Reads', __dirname + '/queries/BookWithAuthor.sql',
130
+ function (pSuccess)
131
+ {
132
+ tmpBookDAL.doReads(tmpBookDAL.query.setCap(50),
133
+ function (pError, pQuery, pRecords)
134
+ {
135
+ for (var i = 0; i < pRecords.length; i++)
136
+ {
137
+ console.log(pRecords[i].Title, 'by', pRecords[i].AuthorName);
138
+ }
139
+ });
140
+ });
141
+ ```
142
+
143
+ ### Custom Count Query
144
+
145
+ ```javascript
146
+ tmpBookDAL.rawQueries.setQuery('Count',
147
+ 'SELECT COUNT(DISTINCT Author) AS RowCount ' +
148
+ 'FROM Book ' +
149
+ 'WHERE Deleted = 0');
150
+
151
+ tmpBookDAL.doCount(tmpBookDAL.query,
152
+ function (pError, pQuery, pCount)
153
+ {
154
+ console.log('Unique authors:', pCount);
155
+ });
156
+ ```
157
+
158
+ Note that the count query must return a result with a `RowCount` column for the MySQL provider (or `Row_Count` for MSSQL) so the provider can extract the numeric value.
159
+
160
+ ### Custom Read Override for Single Records
161
+
162
+ ```javascript
163
+ tmpBookDAL.rawQueries.setQuery('Read',
164
+ 'SELECT b.*, GROUP_CONCAT(t.TagName) AS Tags ' +
165
+ 'FROM Book b ' +
166
+ 'LEFT JOIN BookTag bt ON b.IDBook = bt.IDBook ' +
167
+ 'LEFT JOIN Tag t ON bt.IDTag = t.IDTag ' +
168
+ 'WHERE b.IDBook = :IDBook AND b.Deleted = 0 ' +
169
+ 'GROUP BY b.IDBook');
170
+
171
+ tmpBookDAL.doRead(tmpBookDAL.query.addFilter('IDBook', 42),
172
+ function (pError, pQuery, pRecord)
173
+ {
174
+ console.log(pRecord.Title, '- Tags:', pRecord.Tags);
175
+ });
176
+ ```
177
+
178
+ ### Using Arbitrary Tags
179
+
180
+ You can store any number of custom queries under arbitrary tag names for your own use, even though Meadow only checks the standard tags automatically:
181
+
182
+ ```javascript
183
+ tmpBookDAL.rawQueries.setQuery('TopAuthors',
184
+ 'SELECT Author, COUNT(*) AS BookCount ' +
185
+ 'FROM Book ' +
186
+ 'WHERE Deleted = 0 ' +
187
+ 'GROUP BY Author ' +
188
+ 'ORDER BY BookCount DESC ' +
189
+ 'LIMIT 10');
190
+
191
+ // Retrieve later for manual use
192
+ var tmpSQL = tmpBookDAL.rawQueries.getQuery('TopAuthors');
193
+ ```
@@ -1,5 +1,5 @@
1
1
  {
2
- "Generated": "2026-02-18T01:05:03.079Z",
2
+ "Generated": "2026-03-02T02:11:44.637Z",
3
3
  "GitHubOrg": "stevenvelozo",
4
4
  "DefaultBranch": "master",
5
5
  "Groups": [
@@ -55,6 +55,16 @@
55
55
  "Key": "dist",
56
56
  "Description": "",
57
57
  "Modules": [
58
+ {
59
+ "Name": "FableTest-RocksDB",
60
+ "Repo": "FableTest-RocksDB",
61
+ "Group": "dist",
62
+ "Branch": "master",
63
+ "HasDocs": false,
64
+ "HasCover": false,
65
+ "Sidebar": [],
66
+ "DocFiles": []
67
+ },
58
68
  {
59
69
  "Name": "indoctrinate_content_staging",
60
70
  "Repo": "indoctrinate_content_staging",
@@ -72,6 +82,52 @@
72
82
  "Key": "docs",
73
83
  "Description": "",
74
84
  "Modules": [
85
+ {
86
+ "Name": "api",
87
+ "Repo": "api",
88
+ "Group": "docs",
89
+ "Branch": "master",
90
+ "HasDocs": true,
91
+ "HasCover": false,
92
+ "Sidebar": [],
93
+ "DocFiles": [
94
+ "api/doCount.md",
95
+ "api/doCreate.md",
96
+ "api/doDelete.md",
97
+ "api/doRead.md",
98
+ "api/doReads.md",
99
+ "api/doUndelete.md",
100
+ "api/doUpdate.md",
101
+ "api/getRoleName.md",
102
+ "api/loadFromPackage.md",
103
+ "api/marshalRecordFromSourceToObject.md",
104
+ "api/query.md",
105
+ "api/rawQueries.md",
106
+ "api/reference.md",
107
+ "api/setAuthorizer.md",
108
+ "api/setDefault.md",
109
+ "api/setDefaultIdentifier.md",
110
+ "api/setDomain.md",
111
+ "api/setIDUser.md",
112
+ "api/setJsonSchema.md",
113
+ "api/setProvider.md",
114
+ "api/setSchema.md",
115
+ "api/setScope.md",
116
+ "api/validateObject.md"
117
+ ]
118
+ },
119
+ {
120
+ "Name": "css",
121
+ "Repo": "css",
122
+ "Group": "docs",
123
+ "Branch": "master",
124
+ "HasDocs": true,
125
+ "HasCover": false,
126
+ "Sidebar": [],
127
+ "DocFiles": [
128
+ "css/docuserve.css"
129
+ ]
130
+ },
75
131
  {
76
132
  "Name": "providers",
77
133
  "Repo": "providers",
@@ -83,8 +139,12 @@
83
139
  "DocFiles": [
84
140
  "providers/README.md",
85
141
  "providers/alasql.md",
142
+ "providers/meadow-endpoints.md",
143
+ "providers/mongodb.md",
86
144
  "providers/mssql.md",
87
145
  "providers/mysql.md",
146
+ "providers/postgresql.md",
147
+ "providers/rocksdb.md",
88
148
  "providers/sqlite.md"
89
149
  ]
90
150
  },