meadow 2.0.28 → 2.0.29
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/.quackage.json +9 -0
- package/README.md +3 -0
- package/docs/_sidebar.md +1 -0
- package/docs/schema/README.md +36 -0
- package/docs/schema/json-columns.md +188 -0
- package/package.json +9 -8
- package/source/providers/Meadow-Provider-ALASQL.js +61 -6
- package/source/providers/Meadow-Provider-MSSQL.js +54 -5
- package/source/providers/Meadow-Provider-MySQL.js +54 -5
- package/source/providers/Meadow-Provider-PostgreSQL.js +54 -4
- package/source/providers/Meadow-Provider-SQLite.js +54 -4
- package/test/Animal.json +14 -2
- package/test/Meadow-Provider-ALASQL.js +7 -2
- package/test/Meadow-Provider-MSSQL_tests.js +45 -9
- package/test/Meadow-Provider-MongoDB_tests.js +39 -5
- package/test/Meadow-Provider-MySQL_tests.js +45 -9
- package/test/Meadow-Provider-PostgreSQL_tests.js +44 -8
- package/test/Meadow-Provider-RocksDB_tests.js +65 -30
- package/test/Meadow-Provider-SQLiteBrowser-Headless_tests.js +8 -3
- package/test/Meadow-Provider-SQLiteBrowser_tests.js +43 -5
- package/test/Meadow-Provider-SQLite_tests.js +43 -5
package/.quackage.json
ADDED
package/README.md
CHANGED
|
@@ -41,6 +41,7 @@ const meadow = libMeadow.new(libFable, 'Book')
|
|
|
41
41
|
{ Column: 'GUIDBook', Type: 'AutoGUID' },
|
|
42
42
|
{ Column: 'Title', Type: 'String', Size: '255' },
|
|
43
43
|
{ Column: 'Author', Type: 'String', Size: '128' },
|
|
44
|
+
{ Column: 'Metadata', Type: 'JSON' },
|
|
44
45
|
{ Column: 'CreateDate', Type: 'CreateDate' },
|
|
45
46
|
{ Column: 'CreatingIDUser', Type: 'CreateIDUser' },
|
|
46
47
|
{ Column: 'UpdateDate', Type: 'UpdateDate' },
|
|
@@ -92,6 +93,8 @@ meadow.doReads(meadow.query.setCap(25).setBegin(0),
|
|
|
92
93
|
| `DeleteDate` | Auto-populated timestamp on delete |
|
|
93
94
|
| `DeleteIDUser` | Auto-populated user ID on delete |
|
|
94
95
|
| `Deleted` | Soft delete flag (enables logical deletion) |
|
|
96
|
+
| `JSON` | Structured JSON data (stored as `TEXT`, marshaled to/from objects) |
|
|
97
|
+
| `JSONProxy` | JSON stored in a different SQL column (uses `StorageColumn` for SQL, virtual name for objects) |
|
|
95
98
|
|
|
96
99
|
## CRUD Operations
|
|
97
100
|
|
package/docs/_sidebar.md
CHANGED
package/docs/schema/README.md
CHANGED
|
@@ -34,6 +34,8 @@ const tmpSchema =
|
|
|
34
34
|
{ Column: 'InPrint', Type: 'Boolean' },
|
|
35
35
|
{ Column: 'PublishDate', Type: 'DateTime' },
|
|
36
36
|
{ Column: 'Synopsis', Type: 'Text' },
|
|
37
|
+
{ Column: 'Metadata', Type: 'JSON' },
|
|
38
|
+
{ Column: 'Extras', Type: 'JSONProxy', StorageColumn: 'ExtrasJSON' },
|
|
37
39
|
{ Column: 'CreateDate', Type: 'CreateDate' },
|
|
38
40
|
{ Column: 'CreatingIDUser', Type: 'CreateIDUser' },
|
|
39
41
|
{ Column: 'UpdateDate', Type: 'UpdateDate' },
|
|
@@ -59,6 +61,40 @@ meadow.setSchema(tmpSchema);
|
|
|
59
61
|
| `Decimal` | Decimal number | `DECIMAL(precision,scale)` | `0` |
|
|
60
62
|
| `Boolean` | Boolean flag | `INT` / `BOOLEAN` | `0` |
|
|
61
63
|
| `DateTime` | Date/time field | `DATETIME` | `null` |
|
|
64
|
+
| `JSON` | Structured JSON data | `LONGTEXT` (MySQL) / `TEXT` (others) | `{}` |
|
|
65
|
+
| `JSONProxy` | JSON with different SQL column name | `LONGTEXT` / `TEXT` (on `StorageColumn`) | `{}` |
|
|
66
|
+
|
|
67
|
+
### JSON Column Types
|
|
68
|
+
|
|
69
|
+
The `JSON` and `JSONProxy` types store structured data as serialized JSON text in SQL databases. Meadow handles serialization and deserialization automatically in the provider layer.
|
|
70
|
+
|
|
71
|
+
**JSON** -- the SQL column and object property share the same name:
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
{ Column: 'Metadata', Type: 'JSON' }
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
On create/update, the object value is `JSON.stringify`'d into the `Metadata` TEXT column. On read, the text is `JSON.parse`'d back into an object on `record.Metadata`.
|
|
78
|
+
|
|
79
|
+
**JSONProxy** -- the SQL column name differs from the JavaScript property:
|
|
80
|
+
|
|
81
|
+
```javascript
|
|
82
|
+
{ Column: 'Preferences', Type: 'JSONProxy', StorageColumn: 'PreferencesJSON' }
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
On create/update, the value from `record.Preferences` is `JSON.stringify`'d and stored in the `PreferencesJSON` SQL column. On read, `PreferencesJSON` is parsed and mapped to `record.Preferences`. The storage column (`PreferencesJSON`) is never exposed on the marshaled record object.
|
|
86
|
+
|
|
87
|
+
This separation is useful when you want a clean API surface (`record.Preferences`) while keeping a database naming convention that makes the storage format explicit (`PreferencesJSON`).
|
|
88
|
+
|
|
89
|
+
For object-store providers (MongoDB, RocksDB), JSON values are stored and retrieved as native objects -- no serialization is needed.
|
|
90
|
+
|
|
91
|
+
JSON columns support the standard `Column` and `Type` properties, plus:
|
|
92
|
+
|
|
93
|
+
| Property | Required | Description |
|
|
94
|
+
|----------|----------|-------------|
|
|
95
|
+
| `StorageColumn` | JSONProxy only | The actual SQL column name for storage |
|
|
96
|
+
|
|
97
|
+
See [JSON Columns](json-columns.md) for detailed examples and filtering.
|
|
62
98
|
|
|
63
99
|
### Audit Column Types
|
|
64
100
|
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# JSON Columns
|
|
2
|
+
|
|
3
|
+
> Store structured data as JSON in SQL databases with automatic serialization
|
|
4
|
+
|
|
5
|
+
Meadow supports two column types for storing structured JSON data: `JSON` and `JSONProxy`. Both store data as `TEXT` in SQL databases and handle serialization/deserialization automatically. Object-store providers (MongoDB, RocksDB) store these values as native objects.
|
|
6
|
+
|
|
7
|
+
## JSON Type
|
|
8
|
+
|
|
9
|
+
The `JSON` type stores structured data in a column where the SQL name and JavaScript property name are identical.
|
|
10
|
+
|
|
11
|
+
### Schema Definition
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
{ Column: 'Metadata', Type: 'JSON' }
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### MicroDDL Syntax
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
{Metadata
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Behavior
|
|
24
|
+
|
|
25
|
+
| Operation | What Happens |
|
|
26
|
+
|-----------|-------------|
|
|
27
|
+
| **Create** | `record.Metadata` is `JSON.stringify`'d into the `Metadata` TEXT column |
|
|
28
|
+
| **Read** | The `Metadata` TEXT column is `JSON.parse`'d into `record.Metadata` (an object) |
|
|
29
|
+
| **Update** | Same as Create -- the value is serialized before writing |
|
|
30
|
+
| **Default** | `{}` (empty object) |
|
|
31
|
+
|
|
32
|
+
### Example
|
|
33
|
+
|
|
34
|
+
```javascript
|
|
35
|
+
// Schema
|
|
36
|
+
const schema = [
|
|
37
|
+
{ Column: 'IDProduct', Type: 'AutoIdentity' },
|
|
38
|
+
{ Column: 'Name', Type: 'String', Size: '128' },
|
|
39
|
+
{ Column: 'Metadata', Type: 'JSON' },
|
|
40
|
+
// ... audit columns
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
// Create with JSON data
|
|
44
|
+
meadow.doCreate(
|
|
45
|
+
meadow.query.addRecord({
|
|
46
|
+
Name: 'Widget',
|
|
47
|
+
Metadata: { color: 'blue', weight: 1.5, tags: ['sale', 'new'] }
|
|
48
|
+
}),
|
|
49
|
+
(pError, pCreateQuery, pReadQuery, pRecord) =>
|
|
50
|
+
{
|
|
51
|
+
console.log(pRecord.Metadata);
|
|
52
|
+
// => { color: 'blue', weight: 1.5, tags: ['sale', 'new'] }
|
|
53
|
+
console.log(typeof pRecord.Metadata);
|
|
54
|
+
// => 'object'
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## JSONProxy Type
|
|
59
|
+
|
|
60
|
+
The `JSONProxy` type stores JSON in a SQL column with a different name than the JavaScript property. This is useful when you want a clean API surface while keeping an explicit naming convention in the database.
|
|
61
|
+
|
|
62
|
+
### Schema Definition
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
{ Column: 'Preferences', Type: 'JSONProxy', StorageColumn: 'PreferencesJSON' }
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
| Property | Description |
|
|
69
|
+
|----------|-------------|
|
|
70
|
+
| `Column` | The JavaScript property name (virtual) -- what API consumers see |
|
|
71
|
+
| `StorageColumn` | The actual SQL column name -- what the database stores |
|
|
72
|
+
|
|
73
|
+
### MicroDDL Syntax
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
{Preferences PreferencesJSON
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Behavior
|
|
80
|
+
|
|
81
|
+
| Operation | What Happens |
|
|
82
|
+
|-----------|-------------|
|
|
83
|
+
| **Create** | `record.Preferences` is `JSON.stringify`'d into the `PreferencesJSON` SQL column |
|
|
84
|
+
| **Read** | The `PreferencesJSON` column is `JSON.parse`'d and mapped to `record.Preferences`; `PreferencesJSON` is **not** exposed on the result |
|
|
85
|
+
| **Update** | Same as Create -- serialized to the storage column |
|
|
86
|
+
| **Default** | `{}` (empty object) |
|
|
87
|
+
|
|
88
|
+
### Example
|
|
89
|
+
|
|
90
|
+
```javascript
|
|
91
|
+
// Schema
|
|
92
|
+
const schema = [
|
|
93
|
+
{ Column: 'IDUser', Type: 'AutoIdentity' },
|
|
94
|
+
{ Column: 'Name', Type: 'String', Size: '128' },
|
|
95
|
+
{ Column: 'Preferences', Type: 'JSONProxy', StorageColumn: 'PreferencesJSON' },
|
|
96
|
+
// ... audit columns
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
// Create
|
|
100
|
+
meadow.doCreate(
|
|
101
|
+
meadow.query.addRecord({
|
|
102
|
+
Name: 'Alice',
|
|
103
|
+
Preferences: { theme: 'dark', language: 'en', notifications: true }
|
|
104
|
+
}),
|
|
105
|
+
(pError, pCreateQuery, pReadQuery, pRecord) =>
|
|
106
|
+
{
|
|
107
|
+
console.log(pRecord.Preferences);
|
|
108
|
+
// => { theme: 'dark', language: 'en', notifications: true }
|
|
109
|
+
|
|
110
|
+
console.log(pRecord.PreferencesJSON);
|
|
111
|
+
// => undefined (storage column is hidden)
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## SQL Storage
|
|
116
|
+
|
|
117
|
+
In SQL databases, both JSON types are stored as text columns. MySQL uses `LONGTEXT` (up to 4GB) to avoid the 64KB limit of `TEXT`. Other databases use `TEXT` (unlimited in PostgreSQL and SQLite) or `NVARCHAR(MAX)` (MSSQL).
|
|
118
|
+
|
|
119
|
+
| Provider | JSON DDL | JSONProxy DDL |
|
|
120
|
+
|----------|----------|---------------|
|
|
121
|
+
| MySQL | `Metadata LONGTEXT` | `PreferencesJSON LONGTEXT` |
|
|
122
|
+
| PostgreSQL | `"Metadata" TEXT` | `"PreferencesJSON" TEXT` |
|
|
123
|
+
| SQLite | `Metadata TEXT` | `PreferencesJSON TEXT` |
|
|
124
|
+
| MSSQL | `[Metadata] NVARCHAR(MAX)` | `[PreferencesJSON] NVARCHAR(MAX)` |
|
|
125
|
+
|
|
126
|
+
## Filtering on JSON Properties
|
|
127
|
+
|
|
128
|
+
FoxHound supports filtering on nested JSON properties using dot notation:
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
131
|
+
// Filter by a JSON property
|
|
132
|
+
meadow.doReads(
|
|
133
|
+
meadow.query.addFilter('Metadata.color', 'blue'),
|
|
134
|
+
(pError, pQuery, pRecords) =>
|
|
135
|
+
{
|
|
136
|
+
// Returns records where Metadata contains { color: 'blue' }
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Comparison operators work too
|
|
140
|
+
meadow.doReads(
|
|
141
|
+
meadow.query.addFilter('Metadata.weight', 2.0, '>='),
|
|
142
|
+
(pError, pQuery, pRecords) =>
|
|
143
|
+
{
|
|
144
|
+
// Returns records where Metadata.weight >= 2.0
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The generated SQL uses dialect-specific JSON path functions:
|
|
149
|
+
|
|
150
|
+
| Dialect | Generated SQL |
|
|
151
|
+
|---------|---------------|
|
|
152
|
+
| MySQL | `JSON_EXTRACT(Metadata, '$.color') = :Metadata_color_w0` |
|
|
153
|
+
| PostgreSQL | `Metadata->>'color' = :Metadata_color_w0` |
|
|
154
|
+
| SQLite | `json_extract(Metadata, '$.color') = :Metadata_color_w0` |
|
|
155
|
+
| MSSQL | `JSON_VALUE(Metadata, '$.color') = :Metadata_color_w0` |
|
|
156
|
+
|
|
157
|
+
Nested paths are supported (e.g., `Metadata.dimensions.width`).
|
|
158
|
+
|
|
159
|
+
For `JSONProxy` columns, the storage column is used in the SQL expression automatically. Filtering on `Preferences.theme` produces `json_extract(PreferencesJSON, '$.theme')` in SQLite.
|
|
160
|
+
|
|
161
|
+
## Package File Format
|
|
162
|
+
|
|
163
|
+
When using a JSON package file to define your schema:
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
{
|
|
167
|
+
"Scope": "Product",
|
|
168
|
+
"DefaultIdentifier": "IDProduct",
|
|
169
|
+
"Schema": [
|
|
170
|
+
{ "Column": "IDProduct", "Type": "AutoIdentity" },
|
|
171
|
+
{ "Column": "Name", "Type": "String", "Size": "128" },
|
|
172
|
+
{ "Column": "Metadata", "Type": "JSON" },
|
|
173
|
+
{ "Column": "Preferences", "Type": "JSONProxy", "StorageColumn": "PreferencesJSON" }
|
|
174
|
+
],
|
|
175
|
+
"DefaultObject": {
|
|
176
|
+
"IDProduct": null,
|
|
177
|
+
"Name": "",
|
|
178
|
+
"Metadata": {},
|
|
179
|
+
"Preferences": {}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Note that `DefaultObject` uses the virtual property name (`Preferences`), not the storage column name.
|
|
185
|
+
|
|
186
|
+
## Object Store Providers
|
|
187
|
+
|
|
188
|
+
MongoDB and RocksDB store JSON values as native objects -- no serialization or deserialization is needed. The `JSON` and `JSONProxy` types work transparently with these providers; the marshaling layer simply passes the objects through.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "meadow",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.29",
|
|
4
4
|
"description": "A data access library.",
|
|
5
5
|
"main": "source/Meadow.js",
|
|
6
6
|
"scripts": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"test": "npx quack test",
|
|
10
10
|
"tests": "npx quack test -g",
|
|
11
11
|
"build": "npx quack build",
|
|
12
|
+
"prepublishOnly": "npm run build",
|
|
12
13
|
"docker-dev-build": "docker build ./ -f Dockerfile_LUXURYCode -t retold/meadow:local",
|
|
13
14
|
"docker-dev-run": "docker run -it -d --name meadow-dev -p 12342:8080 -p 12106:3306 -v \"$PWD/.config:/home/coder/.config\" -v \"$PWD:/home/coder/meadow\" -u \"$(id -u):$(id -g)\" -e \"DOCKER_USER=$USER\" retold/meadow:local",
|
|
14
15
|
"docker-dev-shell": "docker exec -it meadow-dev /bin/bash",
|
|
@@ -86,22 +87,22 @@
|
|
|
86
87
|
"gulp-util": "^3.0.8",
|
|
87
88
|
"meadow-connection-dgraph": "^1.0.2",
|
|
88
89
|
"meadow-connection-mongodb": "^1.0.2",
|
|
89
|
-
"meadow-connection-mssql": "^1.0.
|
|
90
|
-
"meadow-connection-mysql": "^1.0.
|
|
91
|
-
"meadow-connection-postgresql": "^1.0.
|
|
90
|
+
"meadow-connection-mssql": "^1.0.16",
|
|
91
|
+
"meadow-connection-mysql": "^1.0.14",
|
|
92
|
+
"meadow-connection-postgresql": "^1.0.2",
|
|
92
93
|
"meadow-connection-rocksdb": "^0.0.2",
|
|
93
94
|
"meadow-connection-solr": "^1.0.2",
|
|
94
|
-
"meadow-connection-sqlite": "^1.0.
|
|
95
|
-
"meadow-connection-sqlite-browser": "^1.0.
|
|
95
|
+
"meadow-connection-sqlite": "^1.0.18",
|
|
96
|
+
"meadow-connection-sqlite-browser": "^1.0.1",
|
|
96
97
|
"mongodb": "^6.12.0",
|
|
97
98
|
"mysql2": "^3.18.2",
|
|
98
99
|
"puppeteer": "^24.38.0",
|
|
99
|
-
"quackage": "^1.0.
|
|
100
|
+
"quackage": "^1.0.63",
|
|
100
101
|
"solr-client": "^0.9.0"
|
|
101
102
|
},
|
|
102
103
|
"dependencies": {
|
|
103
104
|
"async": "3.2.6",
|
|
104
|
-
"foxhound": "^2.0.
|
|
105
|
+
"foxhound": "^2.0.25",
|
|
105
106
|
"is-my-json-valid": "2.20.6",
|
|
106
107
|
"simple-get": "^4.0.1"
|
|
107
108
|
}
|
|
@@ -92,6 +92,12 @@ var MeadowProvider = function()
|
|
|
92
92
|
case 'Text':
|
|
93
93
|
tmpCreateStatement += " `"+tmpSchema[j].Column+"` TEXT";
|
|
94
94
|
break;
|
|
95
|
+
case 'JSON':
|
|
96
|
+
tmpCreateStatement += " `"+tmpSchema[j].Column+"` TEXT";
|
|
97
|
+
break;
|
|
98
|
+
case 'JSONProxy':
|
|
99
|
+
tmpCreateStatement += " `"+tmpSchema[j].StorageColumn+"` TEXT";
|
|
100
|
+
break;
|
|
95
101
|
case 'CreateDate':
|
|
96
102
|
case 'UpdateDate':
|
|
97
103
|
case 'DeleteDate':
|
|
@@ -145,14 +151,63 @@ var MeadowProvider = function()
|
|
|
145
151
|
return true;
|
|
146
152
|
};
|
|
147
153
|
|
|
148
|
-
// The Meadow marshaller
|
|
149
|
-
var marshalRecordFromSourceToObject = function(pObject, pRecord)
|
|
154
|
+
// The Meadow marshaller passes in the Schema as the third parameter for JSON/JSONProxy deserialization.
|
|
155
|
+
var marshalRecordFromSourceToObject = function (pObject, pRecord, pSchema)
|
|
150
156
|
{
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
157
|
+
// Build lookups for JSON columns (only if schema is provided)
|
|
158
|
+
var tmpJsonColumns = {};
|
|
159
|
+
var tmpProxyColumns = {};
|
|
160
|
+
if (Array.isArray(pSchema))
|
|
154
161
|
{
|
|
155
|
-
|
|
162
|
+
for (var s = 0; s < pSchema.length; s++)
|
|
163
|
+
{
|
|
164
|
+
if (pSchema[s].Type === 'JSON')
|
|
165
|
+
{
|
|
166
|
+
tmpJsonColumns[pSchema[s].Column] = true;
|
|
167
|
+
}
|
|
168
|
+
else if (pSchema[s].Type === 'JSONProxy' && pSchema[s].StorageColumn)
|
|
169
|
+
{
|
|
170
|
+
tmpProxyColumns[pSchema[s].StorageColumn] = pSchema[s].Column;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
for (var tmpColumn in pRecord)
|
|
176
|
+
{
|
|
177
|
+
if (tmpJsonColumns[tmpColumn])
|
|
178
|
+
{
|
|
179
|
+
// JSON type: parse string from DB into object on the same column name
|
|
180
|
+
try
|
|
181
|
+
{
|
|
182
|
+
pObject[tmpColumn] = (typeof pRecord[tmpColumn] === 'string')
|
|
183
|
+
? JSON.parse(pRecord[tmpColumn])
|
|
184
|
+
: (pRecord[tmpColumn] || {});
|
|
185
|
+
}
|
|
186
|
+
catch (pParseError)
|
|
187
|
+
{
|
|
188
|
+
pObject[tmpColumn] = {};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
else if (tmpProxyColumns.hasOwnProperty(tmpColumn))
|
|
192
|
+
{
|
|
193
|
+
// JSONProxy: storage column -> parse and assign to virtual column name
|
|
194
|
+
var tmpVirtualColumn = tmpProxyColumns[tmpColumn];
|
|
195
|
+
try
|
|
196
|
+
{
|
|
197
|
+
pObject[tmpVirtualColumn] = (typeof pRecord[tmpColumn] === 'string')
|
|
198
|
+
? JSON.parse(pRecord[tmpColumn])
|
|
199
|
+
: (pRecord[tmpColumn] || {});
|
|
200
|
+
}
|
|
201
|
+
catch (pParseError)
|
|
202
|
+
{
|
|
203
|
+
pObject[tmpVirtualColumn] = {};
|
|
204
|
+
}
|
|
205
|
+
// Do NOT copy the storage column to the output object
|
|
206
|
+
}
|
|
207
|
+
else
|
|
208
|
+
{
|
|
209
|
+
pObject[tmpColumn] = pRecord[tmpColumn];
|
|
210
|
+
}
|
|
156
211
|
}
|
|
157
212
|
};
|
|
158
213
|
|
|
@@ -43,14 +43,63 @@ var MeadowProvider = function ()
|
|
|
43
43
|
return false;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
// The Meadow marshaller
|
|
47
|
-
var marshalRecordFromSourceToObject = function (pObject, pRecord)
|
|
46
|
+
// The Meadow marshaller passes in the Schema as the third parameter for JSON/JSONProxy deserialization.
|
|
47
|
+
var marshalRecordFromSourceToObject = function (pObject, pRecord, pSchema)
|
|
48
48
|
{
|
|
49
|
-
//
|
|
50
|
-
|
|
49
|
+
// Build lookups for JSON columns (only if schema is provided)
|
|
50
|
+
var tmpJsonColumns = {};
|
|
51
|
+
var tmpProxyColumns = {};
|
|
52
|
+
if (Array.isArray(pSchema))
|
|
53
|
+
{
|
|
54
|
+
for (var s = 0; s < pSchema.length; s++)
|
|
55
|
+
{
|
|
56
|
+
if (pSchema[s].Type === 'JSON')
|
|
57
|
+
{
|
|
58
|
+
tmpJsonColumns[pSchema[s].Column] = true;
|
|
59
|
+
}
|
|
60
|
+
else if (pSchema[s].Type === 'JSONProxy' && pSchema[s].StorageColumn)
|
|
61
|
+
{
|
|
62
|
+
tmpProxyColumns[pSchema[s].StorageColumn] = pSchema[s].Column;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
51
67
|
for (var tmpColumn in pRecord)
|
|
52
68
|
{
|
|
53
|
-
|
|
69
|
+
if (tmpJsonColumns[tmpColumn])
|
|
70
|
+
{
|
|
71
|
+
// JSON type: parse string from DB into object on the same column name
|
|
72
|
+
try
|
|
73
|
+
{
|
|
74
|
+
pObject[tmpColumn] = (typeof pRecord[tmpColumn] === 'string')
|
|
75
|
+
? JSON.parse(pRecord[tmpColumn])
|
|
76
|
+
: (pRecord[tmpColumn] || {});
|
|
77
|
+
}
|
|
78
|
+
catch (pParseError)
|
|
79
|
+
{
|
|
80
|
+
pObject[tmpColumn] = {};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else if (tmpProxyColumns.hasOwnProperty(tmpColumn))
|
|
84
|
+
{
|
|
85
|
+
// JSONProxy: storage column -> parse and assign to virtual column name
|
|
86
|
+
var tmpVirtualColumn = tmpProxyColumns[tmpColumn];
|
|
87
|
+
try
|
|
88
|
+
{
|
|
89
|
+
pObject[tmpVirtualColumn] = (typeof pRecord[tmpColumn] === 'string')
|
|
90
|
+
? JSON.parse(pRecord[tmpColumn])
|
|
91
|
+
: (pRecord[tmpColumn] || {});
|
|
92
|
+
}
|
|
93
|
+
catch (pParseError)
|
|
94
|
+
{
|
|
95
|
+
pObject[tmpVirtualColumn] = {};
|
|
96
|
+
}
|
|
97
|
+
// Do NOT copy the storage column to the output object
|
|
98
|
+
}
|
|
99
|
+
else
|
|
100
|
+
{
|
|
101
|
+
pObject[tmpColumn] = pRecord[tmpColumn];
|
|
102
|
+
}
|
|
54
103
|
}
|
|
55
104
|
};
|
|
56
105
|
|
|
@@ -57,14 +57,63 @@ var MeadowProvider = function ()
|
|
|
57
57
|
return false;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
// The Meadow marshaller
|
|
61
|
-
var marshalRecordFromSourceToObject = function (pObject, pRecord)
|
|
60
|
+
// The Meadow marshaller passes in the Schema as the third parameter for JSON/JSONProxy deserialization.
|
|
61
|
+
var marshalRecordFromSourceToObject = function (pObject, pRecord, pSchema)
|
|
62
62
|
{
|
|
63
|
-
//
|
|
64
|
-
|
|
63
|
+
// Build lookups for JSON columns (only if schema is provided)
|
|
64
|
+
var tmpJsonColumns = {};
|
|
65
|
+
var tmpProxyColumns = {};
|
|
66
|
+
if (Array.isArray(pSchema))
|
|
67
|
+
{
|
|
68
|
+
for (var s = 0; s < pSchema.length; s++)
|
|
69
|
+
{
|
|
70
|
+
if (pSchema[s].Type === 'JSON')
|
|
71
|
+
{
|
|
72
|
+
tmpJsonColumns[pSchema[s].Column] = true;
|
|
73
|
+
}
|
|
74
|
+
else if (pSchema[s].Type === 'JSONProxy' && pSchema[s].StorageColumn)
|
|
75
|
+
{
|
|
76
|
+
tmpProxyColumns[pSchema[s].StorageColumn] = pSchema[s].Column;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
65
81
|
for (var tmpColumn in pRecord)
|
|
66
82
|
{
|
|
67
|
-
|
|
83
|
+
if (tmpJsonColumns[tmpColumn])
|
|
84
|
+
{
|
|
85
|
+
// JSON type: parse string from DB into object on the same column name
|
|
86
|
+
try
|
|
87
|
+
{
|
|
88
|
+
pObject[tmpColumn] = (typeof pRecord[tmpColumn] === 'string')
|
|
89
|
+
? JSON.parse(pRecord[tmpColumn])
|
|
90
|
+
: (pRecord[tmpColumn] || {});
|
|
91
|
+
}
|
|
92
|
+
catch (pParseError)
|
|
93
|
+
{
|
|
94
|
+
pObject[tmpColumn] = {};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else if (tmpProxyColumns.hasOwnProperty(tmpColumn))
|
|
98
|
+
{
|
|
99
|
+
// JSONProxy: storage column -> parse and assign to virtual column name
|
|
100
|
+
var tmpVirtualColumn = tmpProxyColumns[tmpColumn];
|
|
101
|
+
try
|
|
102
|
+
{
|
|
103
|
+
pObject[tmpVirtualColumn] = (typeof pRecord[tmpColumn] === 'string')
|
|
104
|
+
? JSON.parse(pRecord[tmpColumn])
|
|
105
|
+
: (pRecord[tmpColumn] || {});
|
|
106
|
+
}
|
|
107
|
+
catch (pParseError)
|
|
108
|
+
{
|
|
109
|
+
pObject[tmpVirtualColumn] = {};
|
|
110
|
+
}
|
|
111
|
+
// Do NOT copy the storage column to the output object
|
|
112
|
+
}
|
|
113
|
+
else
|
|
114
|
+
{
|
|
115
|
+
pObject[tmpColumn] = pRecord[tmpColumn];
|
|
116
|
+
}
|
|
68
117
|
}
|
|
69
118
|
};
|
|
70
119
|
|
|
@@ -104,13 +104,63 @@ var MeadowProvider = function ()
|
|
|
104
104
|
return false;
|
|
105
105
|
};
|
|
106
106
|
|
|
107
|
-
// The Meadow marshaller
|
|
108
|
-
var marshalRecordFromSourceToObject = function (pObject, pRecord)
|
|
107
|
+
// The Meadow marshaller passes in the Schema as the third parameter for JSON/JSONProxy deserialization.
|
|
108
|
+
var marshalRecordFromSourceToObject = function (pObject, pRecord, pSchema)
|
|
109
109
|
{
|
|
110
|
-
//
|
|
110
|
+
// Build lookups for JSON columns (only if schema is provided)
|
|
111
|
+
var tmpJsonColumns = {};
|
|
112
|
+
var tmpProxyColumns = {};
|
|
113
|
+
if (Array.isArray(pSchema))
|
|
114
|
+
{
|
|
115
|
+
for (var s = 0; s < pSchema.length; s++)
|
|
116
|
+
{
|
|
117
|
+
if (pSchema[s].Type === 'JSON')
|
|
118
|
+
{
|
|
119
|
+
tmpJsonColumns[pSchema[s].Column] = true;
|
|
120
|
+
}
|
|
121
|
+
else if (pSchema[s].Type === 'JSONProxy' && pSchema[s].StorageColumn)
|
|
122
|
+
{
|
|
123
|
+
tmpProxyColumns[pSchema[s].StorageColumn] = pSchema[s].Column;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
111
128
|
for (var tmpColumn in pRecord)
|
|
112
129
|
{
|
|
113
|
-
|
|
130
|
+
if (tmpJsonColumns[tmpColumn])
|
|
131
|
+
{
|
|
132
|
+
// JSON type: parse string from DB into object on the same column name
|
|
133
|
+
try
|
|
134
|
+
{
|
|
135
|
+
pObject[tmpColumn] = (typeof pRecord[tmpColumn] === 'string')
|
|
136
|
+
? JSON.parse(pRecord[tmpColumn])
|
|
137
|
+
: (pRecord[tmpColumn] || {});
|
|
138
|
+
}
|
|
139
|
+
catch (pParseError)
|
|
140
|
+
{
|
|
141
|
+
pObject[tmpColumn] = {};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else if (tmpProxyColumns.hasOwnProperty(tmpColumn))
|
|
145
|
+
{
|
|
146
|
+
// JSONProxy: storage column -> parse and assign to virtual column name
|
|
147
|
+
var tmpVirtualColumn = tmpProxyColumns[tmpColumn];
|
|
148
|
+
try
|
|
149
|
+
{
|
|
150
|
+
pObject[tmpVirtualColumn] = (typeof pRecord[tmpColumn] === 'string')
|
|
151
|
+
? JSON.parse(pRecord[tmpColumn])
|
|
152
|
+
: (pRecord[tmpColumn] || {});
|
|
153
|
+
}
|
|
154
|
+
catch (pParseError)
|
|
155
|
+
{
|
|
156
|
+
pObject[tmpVirtualColumn] = {};
|
|
157
|
+
}
|
|
158
|
+
// Do NOT copy the storage column to the output object
|
|
159
|
+
}
|
|
160
|
+
else
|
|
161
|
+
{
|
|
162
|
+
pObject[tmpColumn] = pRecord[tmpColumn];
|
|
163
|
+
}
|
|
114
164
|
}
|
|
115
165
|
};
|
|
116
166
|
|
|
@@ -105,13 +105,63 @@ var MeadowProvider = function ()
|
|
|
105
105
|
return pParams;
|
|
106
106
|
};
|
|
107
107
|
|
|
108
|
-
// The Meadow marshaller
|
|
109
|
-
var marshalRecordFromSourceToObject = function (pObject, pRecord)
|
|
108
|
+
// The Meadow marshaller passes in the Schema as the third parameter for JSON/JSONProxy deserialization.
|
|
109
|
+
var marshalRecordFromSourceToObject = function (pObject, pRecord, pSchema)
|
|
110
110
|
{
|
|
111
|
-
//
|
|
111
|
+
// Build lookups for JSON columns (only if schema is provided)
|
|
112
|
+
var tmpJsonColumns = {};
|
|
113
|
+
var tmpProxyColumns = {};
|
|
114
|
+
if (Array.isArray(pSchema))
|
|
115
|
+
{
|
|
116
|
+
for (var s = 0; s < pSchema.length; s++)
|
|
117
|
+
{
|
|
118
|
+
if (pSchema[s].Type === 'JSON')
|
|
119
|
+
{
|
|
120
|
+
tmpJsonColumns[pSchema[s].Column] = true;
|
|
121
|
+
}
|
|
122
|
+
else if (pSchema[s].Type === 'JSONProxy' && pSchema[s].StorageColumn)
|
|
123
|
+
{
|
|
124
|
+
tmpProxyColumns[pSchema[s].StorageColumn] = pSchema[s].Column;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
112
129
|
for (var tmpColumn in pRecord)
|
|
113
130
|
{
|
|
114
|
-
|
|
131
|
+
if (tmpJsonColumns[tmpColumn])
|
|
132
|
+
{
|
|
133
|
+
// JSON type: parse string from DB into object on the same column name
|
|
134
|
+
try
|
|
135
|
+
{
|
|
136
|
+
pObject[tmpColumn] = (typeof pRecord[tmpColumn] === 'string')
|
|
137
|
+
? JSON.parse(pRecord[tmpColumn])
|
|
138
|
+
: (pRecord[tmpColumn] || {});
|
|
139
|
+
}
|
|
140
|
+
catch (pParseError)
|
|
141
|
+
{
|
|
142
|
+
pObject[tmpColumn] = {};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else if (tmpProxyColumns.hasOwnProperty(tmpColumn))
|
|
146
|
+
{
|
|
147
|
+
// JSONProxy: storage column -> parse and assign to virtual column name
|
|
148
|
+
var tmpVirtualColumn = tmpProxyColumns[tmpColumn];
|
|
149
|
+
try
|
|
150
|
+
{
|
|
151
|
+
pObject[tmpVirtualColumn] = (typeof pRecord[tmpColumn] === 'string')
|
|
152
|
+
? JSON.parse(pRecord[tmpColumn])
|
|
153
|
+
: (pRecord[tmpColumn] || {});
|
|
154
|
+
}
|
|
155
|
+
catch (pParseError)
|
|
156
|
+
{
|
|
157
|
+
pObject[tmpVirtualColumn] = {};
|
|
158
|
+
}
|
|
159
|
+
// Do NOT copy the storage column to the output object
|
|
160
|
+
}
|
|
161
|
+
else
|
|
162
|
+
{
|
|
163
|
+
pObject[tmpColumn] = pRecord[tmpColumn];
|
|
164
|
+
}
|
|
115
165
|
}
|
|
116
166
|
};
|
|
117
167
|
|