orange-orm 3.10.2 → 4.0.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/README.md +70 -14
- package/docs/changelog.md +8 -4
- package/docs/diagram.svg +12 -0
- package/package.json +1 -1
- package/src/client/index.js +42 -34
- package/src/client/index.mjs +42 -34
- package/src/getTSDefinition.js +16 -12
- package/src/hostExpress/executePath.js +74 -8
- package/src/hostExpress/getMeta.js +2 -9
- package/src/map.d.ts +75 -27
- package/src/validateDeleteConflict.js +2 -2
- package/docs/relations.png +0 -0
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
The ultimate Object Relational Mapper for Node.js and Typescript, offering seamless integration with a variety of popular databases. Whether you're building applications in TypeScript or JavaScript (including both CommonJS and ECMAScript), Orange ORM has got you covered.
|
|
6
6
|
|
|
7
|
-
[](https://www.npmjs.org/package/orange-orm)
|
|
8
8
|
[](https://github.com/alfateam/orange-orm/actions)
|
|
9
9
|
[](https://github.com/alfateam/orange-orm/actions)
|
|
10
10
|
[](https://github.com/alfateam/orange-orm)
|
|
@@ -46,7 +46,7 @@ $ npm install orange-orm
|
|
|
46
46
|
## Example
|
|
47
47
|
Watch the [tutorial video on YouTube](https://youtu.be/1IwwjPr2lMs)
|
|
48
48
|
|
|
49
|
-

|
|
50
50
|
|
|
51
51
|
Here we choose SQLite.
|
|
52
52
|
```bash
|
|
@@ -77,6 +77,12 @@ const map = orange.map(x => ({
|
|
|
77
77
|
amount: column('amount').numeric(),
|
|
78
78
|
})),
|
|
79
79
|
|
|
80
|
+
package: x.table('package').map(({ column }) => ({
|
|
81
|
+
id: column('packageId').numeric().primary().notNullExceptInsert(),
|
|
82
|
+
lineId: column('lineId').numeric().notNullExceptInsert(),
|
|
83
|
+
sscc: column('sscc').string() //the barcode
|
|
84
|
+
})),
|
|
85
|
+
|
|
80
86
|
deliveryAddress: x.table('deliveryAddress').map(({ column }) => ({
|
|
81
87
|
id: column('id').numeric().primary(),
|
|
82
88
|
orderId: column('orderId').numeric(),
|
|
@@ -87,6 +93,10 @@ const map = orange.map(x => ({
|
|
|
87
93
|
countryCode: column('countryCode').string(),
|
|
88
94
|
}))
|
|
89
95
|
|
|
96
|
+
})).map(x => ({
|
|
97
|
+
orderLine: x.orderLine.map(({ hasMany }) => ({
|
|
98
|
+
packages: hasMany(x.package).by('lineId')
|
|
99
|
+
}))
|
|
90
100
|
})).map(x => ({
|
|
91
101
|
order: x.order.map(v => ({
|
|
92
102
|
customer: v.references(x.customer).by('customerId'),
|
|
@@ -130,8 +140,10 @@ async function getRows() {
|
|
|
130
140
|
const orders = await db.order.getAll({
|
|
131
141
|
where: x => x.lines.any(line => line.product.contains('broomstick'))
|
|
132
142
|
.and(db.order.customer.name.startsWith('Harry')),
|
|
133
|
-
lines:
|
|
134
|
-
|
|
143
|
+
lines: {
|
|
144
|
+
packages: true
|
|
145
|
+
},
|
|
146
|
+
deliveryAddress: true,
|
|
135
147
|
customer: true
|
|
136
148
|
});
|
|
137
149
|
}
|
|
@@ -145,7 +157,7 @@ async function getRows() {
|
|
|
145
157
|
|
|
146
158
|
Each column within your database table is designated by using the <strong><i>column()</i></strong> method, in which you specify its name. This action generates a reference to a column object that enables you to articulate further column properties like its data type or if it serves as a primary key.
|
|
147
159
|
|
|
148
|
-
Relationships between tables can also be outlined. By using methods like <strong><i>hasOne</i></strong>, <strong><i>hasMany</i></strong>, and <strong><i>references</i></strong>, you can establish connections that reflect the relationships in your data schema. In the example below, an 'order' is linked to a 'customer' reference, a 'deliveryAddress', and multiple 'lines'. The hasMany and hasOne relations represents ownership - the tables 'deliveryAddress' and 'orderLine' are owned by the 'order' table, and therefore, they contain the 'orderId' column referring to their parent table, which is 'order'. Conversely, the customer table is independent and can exist without any knowledge of the 'order' table. Therefore we say that the order table <i>references</i> the customer table - necessitating the existence of a 'customerId' column in the 'order' table.</p>
|
|
160
|
+
Relationships between tables can also be outlined. By using methods like <strong><i>hasOne</i></strong>, <strong><i>hasMany</i></strong>, and <strong><i>references</i></strong>, you can establish connections that reflect the relationships in your data schema. In the example below, an 'order' is linked to a 'customer' reference, a 'deliveryAddress', and multiple 'lines'. The hasMany and hasOne relations represents ownership - the tables 'deliveryAddress' and 'orderLine' are owned by the 'order' table, and therefore, they contain the 'orderId' column referring to their parent table, which is 'order'. The similar relationship exists between orderLine and package - hence the packages are owned by the orderLine. Conversely, the customer table is independent and can exist without any knowledge of the 'order' table. Therefore we say that the order table <i>references</i> the customer table - necessitating the existence of a 'customerId' column in the 'order' table.</p>
|
|
149
161
|
|
|
150
162
|
<sub>📄 map.ts</sub>
|
|
151
163
|
```javascript
|
|
@@ -171,6 +183,12 @@ const map = orange.map(x => ({
|
|
|
171
183
|
product: column('product').string(),
|
|
172
184
|
})),
|
|
173
185
|
|
|
186
|
+
package: x.table('package').map(({ column }) => ({
|
|
187
|
+
id: column('packageId').numeric().primary().notNullExceptInsert(),
|
|
188
|
+
lineId: column('lineId').numeric().notNullExceptInsert(),
|
|
189
|
+
sscc: column('sscc').string() //the barcode
|
|
190
|
+
})),
|
|
191
|
+
|
|
174
192
|
deliveryAddress: x.table('deliveryAddress').map(({ column }) => ({
|
|
175
193
|
id: column('id').numeric().primary(),
|
|
176
194
|
orderId: column('orderId').numeric(),
|
|
@@ -181,6 +199,10 @@ const map = orange.map(x => ({
|
|
|
181
199
|
countryCode: column('countryCode').string(),
|
|
182
200
|
}))
|
|
183
201
|
|
|
202
|
+
})).map(x => ({
|
|
203
|
+
orderLine: x.orderLine.map(({ hasMany }) => ({
|
|
204
|
+
packages: hasMany(x.package).by('lineId')
|
|
205
|
+
}))
|
|
184
206
|
})).map(x => ({
|
|
185
207
|
order: x.order.map(({ hasOne, hasMany, references }) => ({
|
|
186
208
|
customer: references(x.customer).by('customerId'),
|
|
@@ -225,6 +247,12 @@ CREATE TABLE orderLine (
|
|
|
225
247
|
amount NUMERIC(10,2)
|
|
226
248
|
);
|
|
227
249
|
|
|
250
|
+
CREATE TABLE package (
|
|
251
|
+
packageId INTEGER PRIMARY KEY,
|
|
252
|
+
lineId INTEGER REFERENCES orderLine,
|
|
253
|
+
sscc TEXT
|
|
254
|
+
);
|
|
255
|
+
|
|
228
256
|
CREATE TABLE deliveryAddress (
|
|
229
257
|
id INTEGER PRIMARY KEY,
|
|
230
258
|
orderId INTEGER REFERENCES _order,
|
|
@@ -329,7 +357,7 @@ $ npm install pg
|
|
|
329
357
|
```
|
|
330
358
|
```javascript
|
|
331
359
|
import map from './map';
|
|
332
|
-
const db = map.
|
|
360
|
+
const db = map.postgres('postgres://postgres:postgres@postgres/postgres');
|
|
333
361
|
```
|
|
334
362
|
__Oracle__
|
|
335
363
|
```bash
|
|
@@ -497,12 +525,14 @@ async function getRows() {
|
|
|
497
525
|
const orders = await db.order.getAll({
|
|
498
526
|
customer: true,
|
|
499
527
|
deliveryAddress: true,
|
|
500
|
-
lines:
|
|
528
|
+
lines: {
|
|
529
|
+
packages: true
|
|
530
|
+
}
|
|
501
531
|
});
|
|
502
532
|
}
|
|
503
533
|
```
|
|
504
534
|
__Limit, offset and order by__
|
|
505
|
-
This script demonstrates how to fetch orders with customer, lines and deliveryAddress, limiting the results to 10, skipping the first row, and sorting the data based on the orderDate in descending order followed by id. The lines are sorted by product.
|
|
535
|
+
This script demonstrates how to fetch orders with customer, lines, packages and deliveryAddress, limiting the results to 10, skipping the first row, and sorting the data based on the orderDate in descending order followed by id. The lines are sorted by product.
|
|
506
536
|
|
|
507
537
|
```javascript
|
|
508
538
|
import map from './map';
|
|
@@ -518,6 +548,7 @@ async function getRows() {
|
|
|
518
548
|
customer: true,
|
|
519
549
|
deliveryAddress: true,
|
|
520
550
|
lines: {
|
|
551
|
+
packages: true,
|
|
521
552
|
orderBy: 'product'
|
|
522
553
|
},
|
|
523
554
|
});
|
|
@@ -593,7 +624,7 @@ getRows();
|
|
|
593
624
|
|
|
594
625
|
async function getRows() {
|
|
595
626
|
const order = await db.order.getOne(undefined /* optional filter */, {
|
|
596
|
-
where: x => x.
|
|
627
|
+
where: x => x.customer(customer => customer.isActive.eq(true)
|
|
597
628
|
.and(customer.startsWith('Harr'))),
|
|
598
629
|
customer: true,
|
|
599
630
|
deliveryAddress: true,
|
|
@@ -616,7 +647,6 @@ async function getRows() {
|
|
|
616
647
|
});
|
|
617
648
|
}
|
|
618
649
|
```
|
|
619
|
-
```
|
|
620
650
|
|
|
621
651
|
__Single row by primary key__
|
|
622
652
|
|
|
@@ -710,8 +740,8 @@ async function update() {
|
|
|
710
740
|
await orders.saveChanges();
|
|
711
741
|
}
|
|
712
742
|
```
|
|
713
|
-
|
|
714
|
-
The update method is
|
|
743
|
+
__Selective updates__
|
|
744
|
+
The update method is ideal for updating specific columns and relationships across one or multiple rows. You must provide a where filter to specify which rows to target. If you include a fetching strategy, the affected rows and their related data will be returned; otherwise, no data is returned.
|
|
715
745
|
|
|
716
746
|
```javascript
|
|
717
747
|
import map from './map';
|
|
@@ -721,7 +751,33 @@ update();
|
|
|
721
751
|
|
|
722
752
|
async function update() {
|
|
723
753
|
|
|
754
|
+
const propsToBeModified = {
|
|
755
|
+
orderDate: new Date(),
|
|
756
|
+
customerId: 2,
|
|
757
|
+
lines: [
|
|
758
|
+
{ id: 1, product: 'Bicycle', amount: 250 }, //already existing line
|
|
759
|
+
{ id: 2, product: 'Small guitar', amount: 150 }, //already existing line
|
|
760
|
+
{ product: 'Piano', amount: 800 } //the new line to be inserted
|
|
761
|
+
]
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
const strategy = {customer: true, deliveryAddress: true, lines: true};
|
|
765
|
+
const orders = await db.order.update(propsToBeModified, { where: x => x.id.eq(1) }, strategy);
|
|
766
|
+
}
|
|
767
|
+
```
|
|
768
|
+
__Replacing a row from JSON__
|
|
769
|
+
The replace method is suitable when a complete overwrite is required from a JSON object - typically in a REST API. However, it's important to consider that this method replaces the entire row and it's children, which might not always be desirable in a multi-user environment.
|
|
770
|
+
|
|
771
|
+
```javascript
|
|
772
|
+
import map from './map';
|
|
773
|
+
const db = map.sqlite('demo.db');
|
|
774
|
+
|
|
775
|
+
replace();
|
|
776
|
+
|
|
777
|
+
async function replace() {
|
|
778
|
+
|
|
724
779
|
const modified = {
|
|
780
|
+
id: 1,
|
|
725
781
|
orderDate: '2023-07-14T12:00:00',
|
|
726
782
|
customer: {
|
|
727
783
|
id: 2
|
|
@@ -740,7 +796,7 @@ async function update() {
|
|
|
740
796
|
]
|
|
741
797
|
};
|
|
742
798
|
|
|
743
|
-
const order = await db.order.
|
|
799
|
+
const order = await db.order.replace(modified, {customer: true, deliveryAddress: true, lines: true});
|
|
744
800
|
}
|
|
745
801
|
```
|
|
746
802
|
__Partially updating from JSON__
|
|
@@ -1795,7 +1851,7 @@ async function getRows() {
|
|
|
1795
1851
|
```
|
|
1796
1852
|
</details>
|
|
1797
1853
|
|
|
1798
|
-
<details><summary><strong>Aggregate functions</strong></summary>
|
|
1854
|
+
<details id="aggregates"><summary><strong>Aggregate functions</strong></summary>
|
|
1799
1855
|
|
|
1800
1856
|
You can count records and aggregate numerical columns. This can either be done across rows or separately for each row.
|
|
1801
1857
|
Supported functions include:
|
package/docs/changelog.md
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
## Changelog
|
|
2
|
-
|
|
2
|
+
__4.0.0__
|
|
3
|
+
Changed the behaviour of `update` to accept a `where` filter and only update passed in columns and relations. The previous behaviour of `update` has moved to `replace` method.
|
|
4
|
+
__3.10.3__
|
|
5
|
+
Fix duplicate method signatures for those still using code generation
|
|
6
|
+
__3.10.2__
|
|
3
7
|
Orange ORM was renamed from rdb. New installation url: [npmjs.org/package/orange-orm](https://npmjs.org/package/orange-orm) . Old url was npmjs.org/package/rdb
|
|
4
|
-
__3.10.
|
|
8
|
+
__3.10.1__
|
|
5
9
|
Bugfix: Adding hasOne row to existing parent throws. See [#86](https://github.com/alfateam/orange-orm/issues/86)
|
|
6
|
-
__3.10.
|
|
10
|
+
__3.10.0__
|
|
7
11
|
Aggregate functions
|
|
8
|
-
__3.9.
|
|
12
|
+
__3.9.1__
|
|
9
13
|
Bugfix: Crashing on many relations if foreign key column is omitted in strategy. See [#83](https://github.com/alfateam/orange-orm/issues/83)
|
|
10
14
|
__3.9.0__
|
|
11
15
|
Possible to elevate associated column on a related table to a parent table when fetching. See https://github.com/alfateam/orange-orm/#user-content-aggregate-results
|