@cocreate/postgresql 1.0.0 → 1.2.0
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/CHANGELOG.md +14 -0
- package/README.md +141 -51
- package/package.json +13 -10
- package/src/index.js +70 -42
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [1.2.0](https://github.com/CoCreate-app/CoCreate-postgresql/compare/v1.1.0...v1.2.0) (2026-07-19)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* update README.md to reflect PostgreSQL integration and enhance documentation ([ba33b21](https://github.com/CoCreate-app/CoCreate-postgresql/commit/ba33b211367c47430ef3ec8478a3f6e460fd0679))
|
|
7
|
+
|
|
8
|
+
# [1.1.0](https://github.com/CoCreate-app/CoCreate-postgresql/compare/v1.0.0...v1.1.0) (2026-07-18)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* update package.json description and keywords; migrate to ES modules in index.js ([b91abfa](https://github.com/CoCreate-app/CoCreate-postgresql/commit/b91abfaaf64923dda537fa541e43c1fa8ea6e377))
|
|
14
|
+
|
|
1
15
|
# 1.0.0 (2026-07-18)
|
|
2
16
|
|
|
3
17
|
|
package/README.md
CHANGED
|
@@ -1,88 +1,178 @@
|
|
|
1
|
-
#
|
|
1
|
+
# postgresql
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A highly efficient, enterprise-grade PostgreSQL relational database CRUD wrapper and state-routing backend client for the collaborative CoCreate ecosystem. This wrapper maps PostgreSQL's advanced, extensible, and feature-rich structured SQL capabilities into CoCreate's unified JSON interface.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-

|
|
7
|
-

|
|
5
|
+
## Documentation
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
For a complete guide and working integration examples, refer to the [CoCreate PostgreSQL Documentation](https://www.google.com/search?q=https://cocreatejs.com/docs/postgresql).
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
---
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
## Table of Contents
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
* [The Power of the CoCreate Unified Ecosystem](https://www.google.com/search?q=%23the-power-of-the-cocreate-unified-ecosystem)
|
|
14
|
+
* [Why PostgreSQL?](https://www.google.com/search?q=%23why-postgresql)
|
|
15
|
+
* [Installation](https://www.google.com/search?q=%23installation)
|
|
16
|
+
* [Unified JSON Payload Examples](https://www.google.com/search?q=%23unified-json-payload-examples)
|
|
17
|
+
* [Announcements](https://www.google.com/search?q=%23announcements)
|
|
18
|
+
* [Roadmap](https://www.google.com/search?q=%23roadmap)
|
|
19
|
+
* [How to Contribute](https://www.google.com/search?q=%23how-to-contribute)
|
|
20
|
+
* [About](https://www.google.com/search?q=%23about)
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
<script src="https://cdn.cocreate.app/mongodb/latest/CoCreate-mongodb.min.js"></script>
|
|
19
|
-
```
|
|
22
|
+
---
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
<script src="https://cdn.cocreate.app/mongodb/latest/CoCreate-mongodb.min.css"></script>
|
|
23
|
-
```
|
|
24
|
+
## The Power of the CoCreate Unified Ecosystem
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
This is not just another database driver. By wrapping PostgreSQL into the CoCreate state-routing pipeline, you unlock the full power of a multi-cloud, multi-model data orchestration mesh:
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
* **Unified JSON Interface (MongoDB-like Syntax):** You don't need to write custom SQL strings or manually orchestrate complex object-relational mappings. CoCreate translates standard NoSQL JSON CRUD operations (such as `object.create` or `object.read`) transparently into optimized PostgreSQL relational queries or JSONB operations under the hood.
|
|
29
|
+
* **Parallel Multi-Database Writing:** Write to many target databases simultaneously with a single API call. You can stream transactional data to MongoDB (for instant operational state), vectorize it into Qdrant (for real-time semantic search), and persist it in PostgreSQL (for enterprise-grade relational analytics and safety) in perfect parallel harmony:
|
|
30
|
+
|
|
31
|
+
$$\text{Write Command} \xrightarrow{\text{Parallel Mesh}} \begin{cases} \text{MongoDB} & \text{(ACID Transaction Store)} \\ \text{Qdrant} & \text{(Semantic Vector Search)} \\ \text{PostgreSQL} & \text{(Enterprise Relational Core)} \end{cases}$$
|
|
32
|
+
|
|
33
|
+
* **Dynamic State-Routing:** Isolate multi-tenant environments effortlessly. The routing mesh automatically injects tenant metadata and organization boundaries, ensuring isolated database processing within your shared PostgreSQL infrastructure.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Why PostgreSQL?
|
|
38
|
+
|
|
39
|
+
PostgreSQL is the world's most advanced open-source relational database. Built on over 35 years of active development, it provides unmatched reliability, data integrity, and support for complex transactional workflows. It features first-class support for extensible data types, indexing, and native JSONB structured columns that allow relational tables and unstructured JSON to intersect natively.
|
|
30
40
|
|
|
31
|
-
|
|
41
|
+
By routing operational states, analytical reporting datasets, and secure transactional records to PostgreSQL via this wrapper, you leverage industrial-grade SQL scalability and strict ACID compliance straight through your unified JSON data flow.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
### NPM Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm i @cocreate/postgresql
|
|
32
51
|
|
|
33
|
-
```shell
|
|
34
|
-
$ yarn install @cocreate/mongodb
|
|
35
52
|
```
|
|
36
53
|
|
|
37
|
-
|
|
54
|
+
### Yarn Installation
|
|
38
55
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
- [Roadmap](#roadmap)
|
|
42
|
-
- [How to Contribute](#how-to-contribute)
|
|
43
|
-
- [About](#about)
|
|
44
|
-
- [License](#license)
|
|
56
|
+
```bash
|
|
57
|
+
yarn add @cocreate/postgresql
|
|
45
58
|
|
|
46
|
-
|
|
59
|
+
```
|
|
47
60
|
|
|
48
|
-
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Unified JSON Payload Examples
|
|
64
|
+
|
|
65
|
+
Here are the complete, real-world JSON objects representing raw payloads routed through the CoCreate system. By declaring an array in the `storage` key, CoCreate fires the write operations to all specified backends simultaneously in parallel.
|
|
66
|
+
|
|
67
|
+
### 1. Writing a Full Transaction Object (`object.create`)
|
|
68
|
+
|
|
69
|
+
When this operational payload is sent, CoCreate registers it, processes the nested JSON fields, and automatically warehouses it across all listed parallel storage platforms.
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"method": "object.create",
|
|
74
|
+
"storage": ["postgresql", "mongodb", "qdrant"], // Array of storages to store in parallel!
|
|
75
|
+
"database": "sales_analytics",
|
|
76
|
+
"array": "order_events",
|
|
77
|
+
"object": {
|
|
78
|
+
"_id": "order_77c8f2a9_3b01",
|
|
79
|
+
"customer": {
|
|
80
|
+
"id": "cust_88301",
|
|
81
|
+
"email": "customer@example.com",
|
|
82
|
+
"region": "North America",
|
|
83
|
+
"acquisition_channel": "Google Search"
|
|
84
|
+
},
|
|
85
|
+
"transaction": {
|
|
86
|
+
"payment_method": "Credit Card",
|
|
87
|
+
"currency": "USD",
|
|
88
|
+
"subtotal": 249.99,
|
|
89
|
+
"discount": 15.00,
|
|
90
|
+
"tax": 18.80,
|
|
91
|
+
"total": 253.79
|
|
92
|
+
},
|
|
93
|
+
"items": [
|
|
94
|
+
{
|
|
95
|
+
"product_id": "prod_head_009",
|
|
96
|
+
"name": "Wireless Noise-Cancelling Headphones",
|
|
97
|
+
"category": "Electronics",
|
|
98
|
+
"quantity": 1,
|
|
99
|
+
"unit_price": 199.99
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"product_id": "prod_stand_012",
|
|
103
|
+
"name": "Aluminum Headphone Stand",
|
|
104
|
+
"category": "Accessories",
|
|
105
|
+
"quantity": 2,
|
|
106
|
+
"unit_price": 25.00
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
"shipping": {
|
|
110
|
+
"carrier": "FedEx",
|
|
111
|
+
"service": "2-Day Air",
|
|
112
|
+
"estimated_delivery": "2026-07-20T12:00:00.000Z"
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"organization_id": "org_enterprise_99x",
|
|
116
|
+
"user_id": "usr_sales_runner_402",
|
|
117
|
+
"timeStamp": "2026-07-18T18:40:43.000Z"
|
|
118
|
+
}
|
|
49
119
|
|
|
50
|
-
|
|
120
|
+
```
|
|
51
121
|
|
|
52
|
-
|
|
122
|
+
### 2. Reading and Filtering Warehoused Data (`object.read`)
|
|
123
|
+
|
|
124
|
+
Querying targets a single database engine via a string filter syntax. The query below retrieves high-value orders handled by a specific shipping carrier from PostgreSQL, sorting them by total sale volume.
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"method": "object.read",
|
|
129
|
+
"storage": "postgresql",
|
|
130
|
+
"database": "sales_analytics",
|
|
131
|
+
"array": "order_events",
|
|
132
|
+
"$filter": {
|
|
133
|
+
"query": {
|
|
134
|
+
"transaction.total": { "$gte": 100.00 },
|
|
135
|
+
"shipping.carrier": "FedEx"
|
|
136
|
+
},
|
|
137
|
+
"sort": [
|
|
138
|
+
{ "key": "transaction.total", "direction": "desc" }
|
|
139
|
+
],
|
|
140
|
+
"limit": 50,
|
|
141
|
+
"index": 0
|
|
142
|
+
},
|
|
143
|
+
"organization_id": "org_enterprise_99x"
|
|
144
|
+
}
|
|
53
145
|
|
|
54
|
-
|
|
146
|
+
```
|
|
55
147
|
|
|
56
|
-
|
|
148
|
+
---
|
|
57
149
|
|
|
58
|
-
|
|
150
|
+
## Announcements
|
|
59
151
|
|
|
60
|
-
|
|
152
|
+
All updates to this library are documented in our `CHANGELOG` and releases. You may also subscribe to email for releases and breaking changes.
|
|
61
153
|
|
|
62
|
-
|
|
154
|
+
---
|
|
63
155
|
|
|
64
|
-
|
|
156
|
+
## Roadmap
|
|
65
157
|
|
|
66
|
-
|
|
158
|
+
If you are interested in the future direction of this project, please take a look at our open issues and pull requests. We would love to hear your feedback.
|
|
67
159
|
|
|
68
|
-
|
|
160
|
+
---
|
|
69
161
|
|
|
70
|
-
|
|
162
|
+
## How to Contribute
|
|
71
163
|
|
|
72
|
-
We encourage contribution to our libraries (you might even score some nifty swag), please see our
|
|
164
|
+
We encourage contribution to our libraries (you might even score some nifty swag), please see our `CONTRIBUTING` guide for details.
|
|
73
165
|
|
|
74
|
-
We want this library to be community-driven, and CoCreate led. We need your help to realize this goal. To help make sure we are building the right things in the right order, we ask that you create
|
|
166
|
+
We want this library to be community-driven, and CoCreate led. We need your help to realize this goal. To help make sure we are building the right things in the right order, we ask that you create issues and pull requests or merely upvote or comment on existing issues or pull requests.
|
|
75
167
|
|
|
76
168
|
We appreciate your continued support, thank you!
|
|
77
169
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
# License
|
|
170
|
+
---
|
|
81
171
|
|
|
82
|
-
|
|
172
|
+
## About
|
|
83
173
|
|
|
84
|
-
-
|
|
174
|
+
`CoCreate-postgresql` is guided and supported by the **CoCreate Developer Experience Team**.
|
|
85
175
|
|
|
86
|
-
|
|
176
|
+
> Please Email the Developer Experience Team here in case of any queries.
|
|
87
177
|
|
|
88
|
-
|
|
178
|
+
`CoCreate-postgresql` is maintained and funded by CoCreate. The names and logos for CoCreate are trademarks of CoCreate, LLC.
|
package/package.json
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cocreate/postgresql",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "Lightweight PostgreSQL CRUD wrapper and multi-tenant state routing client for the CoCreate ecosystem.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"postgresql",
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
7
|
+
"postgres",
|
|
8
|
+
"sql",
|
|
9
|
+
"jsonb",
|
|
10
|
+
"crud-api",
|
|
11
|
+
"abstract-crud",
|
|
12
|
+
"database-abstraction",
|
|
13
|
+
"multi-tenant",
|
|
14
|
+
"multi-tenant-routing",
|
|
15
|
+
"realtime-sync",
|
|
16
|
+
"state-synchronization"
|
|
14
17
|
],
|
|
15
18
|
"publishConfig": {
|
|
16
19
|
"access": "public"
|
|
@@ -38,6 +41,6 @@
|
|
|
38
41
|
"main": "./src/index.js",
|
|
39
42
|
"dependencies": {
|
|
40
43
|
"@cocreate/utils": "^1.44.0",
|
|
41
|
-
"
|
|
44
|
+
"pg": "^8.22.0"
|
|
42
45
|
}
|
|
43
46
|
}
|
package/src/index.js
CHANGED
|
@@ -1,11 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (C) 2026 CoCreate and Contributors.
|
|
3
|
+
*
|
|
4
|
+
* This program is free software: you can redistribute it and/or modify
|
|
5
|
+
* it under the terms of the GNU Affero General Public License as published
|
|
6
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
* (at your option) any later version.
|
|
8
|
+
*
|
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU Affero General Public License for more details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
********************************************************************************/
|
|
17
|
+
|
|
18
|
+
import pg from "pg";
|
|
19
|
+
const { Pool } = pg;
|
|
20
|
+
import {
|
|
3
21
|
dotNotationToObject,
|
|
4
22
|
queryData,
|
|
5
23
|
searchData,
|
|
6
24
|
sortData,
|
|
7
25
|
isValidDate
|
|
8
|
-
}
|
|
26
|
+
} from "@cocreate/utils";
|
|
9
27
|
|
|
10
28
|
const organizations = {};
|
|
11
29
|
|
|
@@ -14,8 +32,9 @@ const organizations = {};
|
|
|
14
32
|
*/
|
|
15
33
|
async function dbClient(data) {
|
|
16
34
|
if (data.storageUrl) {
|
|
17
|
-
if (!organizations[data.organization_id])
|
|
35
|
+
if (!organizations[data.organization_id]) {
|
|
18
36
|
organizations[data.organization_id] = {};
|
|
37
|
+
}
|
|
19
38
|
try {
|
|
20
39
|
if (!organizations[data.organization_id][data.storageUrl]) {
|
|
21
40
|
// Establish PostgreSQL Pool with optimized connection properties
|
|
@@ -29,7 +48,7 @@ async function dbClient(data) {
|
|
|
29
48
|
return organizations[data.organization_id][data.storageUrl];
|
|
30
49
|
} catch (error) {
|
|
31
50
|
console.error(
|
|
32
|
-
`${data.organization_id}: storageName ${data.storageName} failed to connect to PostgreSQL`
|
|
51
|
+
`${data.organization_id}: storageName ${data.storageName || "postgresql"} failed to connect to PostgreSQL`
|
|
33
52
|
);
|
|
34
53
|
errorHandler(data, error);
|
|
35
54
|
return { status: false };
|
|
@@ -40,10 +59,41 @@ async function dbClient(data) {
|
|
|
40
59
|
return;
|
|
41
60
|
}
|
|
42
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Global Event Listener: orgDeleted
|
|
64
|
+
* Gracefully terminates active PostgreSQL connection pools.
|
|
65
|
+
*/
|
|
66
|
+
process.on("orgDeleted", (organization_id) => {
|
|
67
|
+
const orgPools = organizations[organization_id];
|
|
68
|
+
if (orgPools) {
|
|
69
|
+
console.log(`[PostgreSQL Utilities] Cleanup Scheduled: Initiating 5-second grace period for Org: ${organization_id}`);
|
|
70
|
+
setTimeout(async () => {
|
|
71
|
+
const currentPools = organizations[organization_id];
|
|
72
|
+
if (!currentPools) return;
|
|
73
|
+
|
|
74
|
+
console.log(`[PostgreSQL Utilities] Cleanup Executing: Closing connection pools for Org: ${organization_id}`);
|
|
75
|
+
const poolList = Object.values(currentPools);
|
|
76
|
+
|
|
77
|
+
for (const pool of poolList) {
|
|
78
|
+
try {
|
|
79
|
+
if (pool && typeof pool.end === "function") {
|
|
80
|
+
await pool.end();
|
|
81
|
+
}
|
|
82
|
+
} catch (err) {
|
|
83
|
+
console.error(`[PostgreSQL Utilities] Error closing connection pool for Org ${organization_id}:`, err);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
delete organizations[organization_id];
|
|
88
|
+
console.log(`[PostgreSQL Utilities] Memory Cleared: Registry freed for Org: ${organization_id}`);
|
|
89
|
+
}, 5000);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
43
93
|
/**
|
|
44
94
|
* Universal router matching the exact entry-point signature of the MongoDB wrapper.
|
|
45
95
|
*/
|
|
46
|
-
function send(data) {
|
|
96
|
+
export function send(data) {
|
|
47
97
|
let [type, method] = data.method.split(".");
|
|
48
98
|
if (type === "database") return database(method, data);
|
|
49
99
|
if (type === "array") return array(method, data);
|
|
@@ -61,7 +111,7 @@ function database(method, data) {
|
|
|
61
111
|
|
|
62
112
|
try {
|
|
63
113
|
const pool = await dbClient(data);
|
|
64
|
-
if (!pool || pool.status === false) return data;
|
|
114
|
+
if (!pool || pool.status === false) return resolve(data);
|
|
65
115
|
|
|
66
116
|
if (method === "read") {
|
|
67
117
|
const sql = "SELECT datname FROM pg_database WHERE datistemplate = false;";
|
|
@@ -87,12 +137,12 @@ function database(method, data) {
|
|
|
87
137
|
if (isFilter)
|
|
88
138
|
databaseArray.push({
|
|
89
139
|
database: dbObj,
|
|
90
|
-
storage: data.storageName
|
|
140
|
+
storage: data.storageName || "postgresql"
|
|
91
141
|
});
|
|
92
142
|
} else {
|
|
93
143
|
databaseArray.push({
|
|
94
144
|
database: dbObj,
|
|
95
|
-
storage: data.storageName
|
|
145
|
+
storage: data.storageName || "postgresql"
|
|
96
146
|
});
|
|
97
147
|
}
|
|
98
148
|
}
|
|
@@ -122,9 +172,6 @@ function database(method, data) {
|
|
|
122
172
|
console.log(method, "error", error);
|
|
123
173
|
resolve(data);
|
|
124
174
|
}
|
|
125
|
-
},
|
|
126
|
-
(error) => {
|
|
127
|
-
errorHandler(data, error);
|
|
128
175
|
}
|
|
129
176
|
);
|
|
130
177
|
}
|
|
@@ -140,7 +187,7 @@ function array(method, data) {
|
|
|
140
187
|
|
|
141
188
|
try {
|
|
142
189
|
const pool = await dbClient(data);
|
|
143
|
-
if (!pool || pool.status === false) return data;
|
|
190
|
+
if (!pool || pool.status === false) return resolve(data);
|
|
144
191
|
|
|
145
192
|
if (data.request) data.array = data.request;
|
|
146
193
|
|
|
@@ -178,13 +225,13 @@ function array(method, data) {
|
|
|
178
225
|
arrayArray.push({
|
|
179
226
|
name: row.table_name,
|
|
180
227
|
database,
|
|
181
|
-
storage: data.storageName
|
|
228
|
+
storage: data.storageName || "postgresql"
|
|
182
229
|
});
|
|
183
230
|
} else {
|
|
184
231
|
arrayArray.push({
|
|
185
232
|
name: row.table_name,
|
|
186
233
|
database,
|
|
187
|
-
storage: data.storageName
|
|
234
|
+
storage: data.storageName || "postgresql"
|
|
188
235
|
});
|
|
189
236
|
}
|
|
190
237
|
}
|
|
@@ -222,7 +269,7 @@ function array(method, data) {
|
|
|
222
269
|
arrayArray.push({
|
|
223
270
|
name: array,
|
|
224
271
|
database,
|
|
225
|
-
storage: data.storageName
|
|
272
|
+
storage: data.storageName || "postgresql"
|
|
226
273
|
});
|
|
227
274
|
|
|
228
275
|
arraysLength -= 1;
|
|
@@ -247,7 +294,7 @@ function array(method, data) {
|
|
|
247
294
|
name: newName,
|
|
248
295
|
oldName: oldName,
|
|
249
296
|
database,
|
|
250
|
-
storage: data.storageName
|
|
297
|
+
storage: data.storageName || "postgresql"
|
|
251
298
|
});
|
|
252
299
|
|
|
253
300
|
arraysLength -= 1;
|
|
@@ -271,7 +318,7 @@ function array(method, data) {
|
|
|
271
318
|
arrayArray.push({
|
|
272
319
|
name: array,
|
|
273
320
|
database,
|
|
274
|
-
storage: data.storageName
|
|
321
|
+
storage: data.storageName || "postgresql"
|
|
275
322
|
});
|
|
276
323
|
|
|
277
324
|
arraysLength -= 1;
|
|
@@ -290,9 +337,6 @@ function array(method, data) {
|
|
|
290
337
|
console.log(method, "error", error);
|
|
291
338
|
resolve(data);
|
|
292
339
|
}
|
|
293
|
-
},
|
|
294
|
-
(error) => {
|
|
295
|
-
errorHandler(data, error);
|
|
296
340
|
}
|
|
297
341
|
);
|
|
298
342
|
}
|
|
@@ -305,7 +349,7 @@ function object(method, data) {
|
|
|
305
349
|
async (resolve, reject) => {
|
|
306
350
|
try {
|
|
307
351
|
const pool = await dbClient(data);
|
|
308
|
-
if (!pool || pool.status === false) return data;
|
|
352
|
+
if (!pool || pool.status === false) return resolve(data);
|
|
309
353
|
|
|
310
354
|
let type = "object";
|
|
311
355
|
let documents = [];
|
|
@@ -322,7 +366,7 @@ function object(method, data) {
|
|
|
322
366
|
|
|
323
367
|
for (let array of arrays) {
|
|
324
368
|
const reference = {
|
|
325
|
-
$storage: data.storageName,
|
|
369
|
+
$storage: data.storageName || "postgresql",
|
|
326
370
|
$database: database,
|
|
327
371
|
$array: array
|
|
328
372
|
};
|
|
@@ -347,7 +391,7 @@ function object(method, data) {
|
|
|
347
391
|
if (!Array.isArray($database)) $database = [data[type][i].$database];
|
|
348
392
|
if (!Array.isArray($array)) $array = [data[type][i].$array];
|
|
349
393
|
|
|
350
|
-
if (!$storage.includes(data.storageName)) $storage.push(data.storageName);
|
|
394
|
+
if (!$storage.includes(data.storageName || "postgresql")) $storage.push(data.storageName || "postgresql");
|
|
351
395
|
if (!$database.includes(database)) $database.push(database);
|
|
352
396
|
if (!$array.includes(array)) $array.push(array);
|
|
353
397
|
|
|
@@ -439,7 +483,6 @@ function object(method, data) {
|
|
|
439
483
|
if (isFilter) {
|
|
440
484
|
for (let row of result.rows) {
|
|
441
485
|
const parsedRow = parseSqlRow(row);
|
|
442
|
-
// Perform dynamic text searching if requested
|
|
443
486
|
if (data.$filter && data.$filter.search) {
|
|
444
487
|
let isMatch = searchData(parsedRow, data.$filter.search);
|
|
445
488
|
if (!isMatch) continue;
|
|
@@ -452,18 +495,15 @@ function object(method, data) {
|
|
|
452
495
|
} else if (result.rows.length > 0) {
|
|
453
496
|
let dbRecord = parseSqlRow(result.rows[0]);
|
|
454
497
|
|
|
455
|
-
// Offline synchronization logic
|
|
456
498
|
if ($storage.length && data[type][i].modified && data[type][i].modified.on) {
|
|
457
499
|
let clientTime = new Date(data[type][i].modified.on);
|
|
458
500
|
let dbTime = new Date(dbRecord.modified ? dbRecord.modified.on : 0);
|
|
459
501
|
|
|
460
502
|
if (clientTime > dbTime) {
|
|
461
|
-
// Client modified newer: perform a full synchronization merge and commit write
|
|
462
503
|
const updatedRecord = { ...dbRecord, ...data[type][i] };
|
|
463
504
|
const savedRecord = await executeUpdateTransaction(pool, array, data[type][i]._id, updatedRecord, data);
|
|
464
505
|
data[type][i] = { ...savedRecord, $storage, $database, $array };
|
|
465
506
|
} else {
|
|
466
|
-
// DB has newer or identical stamp: resolve with database state
|
|
467
507
|
data[type][i] = { ...data[type][i], ...dbRecord, $storage, $database, $array };
|
|
468
508
|
}
|
|
469
509
|
} else {
|
|
@@ -489,7 +529,6 @@ function object(method, data) {
|
|
|
489
529
|
data[type][i] = { ...updatedDoc, $storage, $database, $array };
|
|
490
530
|
}
|
|
491
531
|
} else if (isFilter) {
|
|
492
|
-
// Read target row IDs first for consistent processing
|
|
493
532
|
const selectSql = `SELECT _id FROM "${array}" ${sqlWhere}`;
|
|
494
533
|
process.emit("usage", {
|
|
495
534
|
type: "egress",
|
|
@@ -550,9 +589,6 @@ function object(method, data) {
|
|
|
550
589
|
console.log(method, "error", error);
|
|
551
590
|
resolve(data);
|
|
552
591
|
}
|
|
553
|
-
},
|
|
554
|
-
(error) => {
|
|
555
|
-
errorHandler(data, error);
|
|
556
592
|
}
|
|
557
593
|
);
|
|
558
594
|
}
|
|
@@ -565,7 +601,6 @@ async function executeUpdateTransaction(pool, arrayName, docId, rawUpdateInput,
|
|
|
565
601
|
try {
|
|
566
602
|
await client.query("BEGIN;");
|
|
567
603
|
|
|
568
|
-
// Row lock the targeted document preventing race conditions
|
|
569
604
|
const selectSql = `SELECT * FROM "${arrayName}" WHERE _id = $1 AND organization_id = $2 FOR UPDATE;`;
|
|
570
605
|
const selectParams = [docId, requestData.organization_id];
|
|
571
606
|
|
|
@@ -581,16 +616,13 @@ async function executeUpdateTransaction(pool, arrayName, docId, rawUpdateInput,
|
|
|
581
616
|
}
|
|
582
617
|
}
|
|
583
618
|
|
|
584
|
-
// Compute updated structural fields and payload blocks
|
|
585
619
|
const modified = {
|
|
586
620
|
on: new Date(requestData.timeStamp),
|
|
587
621
|
by: requestData.user_id || requestData.clientId
|
|
588
622
|
};
|
|
589
623
|
|
|
590
|
-
// Run deep JavaScript nested update mutator reproducing MongoDB operator behavior
|
|
591
624
|
const updatedPayload = applyMongoUpdate(currentRecord, rawUpdateInput);
|
|
592
625
|
|
|
593
|
-
// Clean out system structures before committing payload to SQL database JSONB field
|
|
594
626
|
const savePayload = { ...updatedPayload };
|
|
595
627
|
delete savePayload._id;
|
|
596
628
|
delete savePayload.organization_id;
|
|
@@ -637,15 +669,13 @@ async function executeUpdateTransaction(pool, arrayName, docId, rawUpdateInput,
|
|
|
637
669
|
function applyMongoUpdate(record, updatePayload) {
|
|
638
670
|
let target = { ...record };
|
|
639
671
|
|
|
640
|
-
// Process set operators
|
|
641
672
|
Object.keys(updatePayload).forEach((rawKey) => {
|
|
642
|
-
if (rawKey.startsWith("$")) return;
|
|
673
|
+
if (rawKey.startsWith("$")) return;
|
|
643
674
|
|
|
644
675
|
let cleanKey = rawKey.replace(/\[(\d+)\]/g, ".$1");
|
|
645
676
|
setNestedValue(target, cleanKey, updatePayload[rawKey]);
|
|
646
677
|
});
|
|
647
678
|
|
|
648
|
-
// Process Mongo Operators: $set, $unset, $push, $pull, $addToSet, $inc
|
|
649
679
|
if (updatePayload.$set) {
|
|
650
680
|
Object.keys(updatePayload.$set).forEach((key) => {
|
|
651
681
|
let cleanKey = key.replace(/\[(\d+)\]/g, ".$1");
|
|
@@ -736,7 +766,6 @@ function compileFilterToSQL(filter, organizationId) {
|
|
|
736
766
|
const parseConditionGroup = (queryObj, isOrJoin = false) => {
|
|
737
767
|
let localClauses = [];
|
|
738
768
|
|
|
739
|
-
// Special Handling for nested boolean operators ($or, $and)
|
|
740
769
|
if (queryObj.$or && Array.isArray(queryObj.$or)) {
|
|
741
770
|
let orClauses = queryObj.$or.map(subQuery => parseConditionGroup(subQuery)).filter(c => c.length);
|
|
742
771
|
if (orClauses.length) {
|
|
@@ -750,7 +779,6 @@ function compileFilterToSQL(filter, organizationId) {
|
|
|
750
779
|
}
|
|
751
780
|
}
|
|
752
781
|
|
|
753
|
-
// Process fields recursively supporting dot path targets
|
|
754
782
|
for (let key in queryObj) {
|
|
755
783
|
if (["$or", "$and"].includes(key)) continue;
|
|
756
784
|
|
|
@@ -890,4 +918,4 @@ function errorHandler(data, error, database, array) {
|
|
|
890
918
|
}
|
|
891
919
|
}
|
|
892
920
|
|
|
893
|
-
|
|
921
|
+
export default { send };
|