pqb 0.27.1 → 0.27.3
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/dist/index.d.ts +189 -176
- package/dist/index.js +1916 -1742
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1916 -1742
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -102,25 +102,25 @@ function sqlQueryArgsToExpression(args) {
|
|
|
102
102
|
return Array.isArray(args[0]) ? new RawSQL(args) : args[0];
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
var __defProp$
|
|
106
|
-
var __defProps$
|
|
107
|
-
var __getOwnPropDescs$
|
|
108
|
-
var __getOwnPropSymbols$
|
|
109
|
-
var __hasOwnProp$
|
|
110
|
-
var __propIsEnum$
|
|
111
|
-
var __defNormalProp$
|
|
112
|
-
var __spreadValues$
|
|
105
|
+
var __defProp$h = Object.defineProperty;
|
|
106
|
+
var __defProps$b = Object.defineProperties;
|
|
107
|
+
var __getOwnPropDescs$b = Object.getOwnPropertyDescriptors;
|
|
108
|
+
var __getOwnPropSymbols$i = Object.getOwnPropertySymbols;
|
|
109
|
+
var __hasOwnProp$i = Object.prototype.hasOwnProperty;
|
|
110
|
+
var __propIsEnum$i = Object.prototype.propertyIsEnumerable;
|
|
111
|
+
var __defNormalProp$h = (obj, key, value) => key in obj ? __defProp$h(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
112
|
+
var __spreadValues$h = (a, b) => {
|
|
113
113
|
for (var prop in b || (b = {}))
|
|
114
|
-
if (__hasOwnProp$
|
|
115
|
-
__defNormalProp$
|
|
116
|
-
if (__getOwnPropSymbols$
|
|
117
|
-
for (var prop of __getOwnPropSymbols$
|
|
118
|
-
if (__propIsEnum$
|
|
119
|
-
__defNormalProp$
|
|
114
|
+
if (__hasOwnProp$i.call(b, prop))
|
|
115
|
+
__defNormalProp$h(a, prop, b[prop]);
|
|
116
|
+
if (__getOwnPropSymbols$i)
|
|
117
|
+
for (var prop of __getOwnPropSymbols$i(b)) {
|
|
118
|
+
if (__propIsEnum$i.call(b, prop))
|
|
119
|
+
__defNormalProp$h(a, prop, b[prop]);
|
|
120
120
|
}
|
|
121
121
|
return a;
|
|
122
122
|
};
|
|
123
|
-
var __spreadProps$
|
|
123
|
+
var __spreadProps$b = (a, b) => __defProps$b(a, __getOwnPropDescs$b(b));
|
|
124
124
|
class ColumnType extends orchidCore.ColumnTypeBase {
|
|
125
125
|
/**
|
|
126
126
|
* Mark the column as a primary key.
|
|
@@ -146,7 +146,7 @@ class ColumnType extends orchidCore.ColumnTypeBase {
|
|
|
146
146
|
return orchidCore.setColumnData(this, "isPrimaryKey", true);
|
|
147
147
|
}
|
|
148
148
|
foreignKey(fnOrTable, column, options = orchidCore.emptyObject) {
|
|
149
|
-
const item = typeof fnOrTable === "string" ? __spreadValues$
|
|
149
|
+
const item = typeof fnOrTable === "string" ? __spreadValues$h({ table: fnOrTable, columns: [column] }, options) : __spreadValues$h({ fn: fnOrTable, columns: [column] }, options);
|
|
150
150
|
return orchidCore.pushColumnData(this, "foreignKeys", item);
|
|
151
151
|
}
|
|
152
152
|
toSQL() {
|
|
@@ -203,10 +203,10 @@ class ColumnType extends orchidCore.ColumnTypeBase {
|
|
|
203
203
|
* @param options - index options
|
|
204
204
|
*/
|
|
205
205
|
searchIndex(options) {
|
|
206
|
-
return orchidCore.pushColumnData(this, "indexes", __spreadValues$
|
|
206
|
+
return orchidCore.pushColumnData(this, "indexes", __spreadValues$h(__spreadValues$h({}, options), this.dataType === "tsvector" ? { using: "GIN" } : { tsVector: true }));
|
|
207
207
|
}
|
|
208
208
|
unique(options = {}) {
|
|
209
|
-
return orchidCore.pushColumnData(this, "indexes", __spreadProps$
|
|
209
|
+
return orchidCore.pushColumnData(this, "indexes", __spreadProps$b(__spreadValues$h({}, options), { unique: true }));
|
|
210
210
|
}
|
|
211
211
|
comment(comment) {
|
|
212
212
|
return orchidCore.setColumnData(this, "comment", comment);
|
|
@@ -244,25 +244,25 @@ class ColumnType extends orchidCore.ColumnTypeBase {
|
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
-
var __defProp$
|
|
248
|
-
var __defProps$
|
|
249
|
-
var __getOwnPropDescs$
|
|
250
|
-
var __getOwnPropSymbols$
|
|
251
|
-
var __hasOwnProp$
|
|
252
|
-
var __propIsEnum$
|
|
253
|
-
var __defNormalProp$
|
|
254
|
-
var __spreadValues$
|
|
247
|
+
var __defProp$g = Object.defineProperty;
|
|
248
|
+
var __defProps$a = Object.defineProperties;
|
|
249
|
+
var __getOwnPropDescs$a = Object.getOwnPropertyDescriptors;
|
|
250
|
+
var __getOwnPropSymbols$h = Object.getOwnPropertySymbols;
|
|
251
|
+
var __hasOwnProp$h = Object.prototype.hasOwnProperty;
|
|
252
|
+
var __propIsEnum$h = Object.prototype.propertyIsEnumerable;
|
|
253
|
+
var __defNormalProp$g = (obj, key, value) => key in obj ? __defProp$g(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
254
|
+
var __spreadValues$g = (a, b) => {
|
|
255
255
|
for (var prop in b || (b = {}))
|
|
256
|
-
if (__hasOwnProp$
|
|
257
|
-
__defNormalProp$
|
|
258
|
-
if (__getOwnPropSymbols$
|
|
259
|
-
for (var prop of __getOwnPropSymbols$
|
|
260
|
-
if (__propIsEnum$
|
|
261
|
-
__defNormalProp$
|
|
256
|
+
if (__hasOwnProp$h.call(b, prop))
|
|
257
|
+
__defNormalProp$g(a, prop, b[prop]);
|
|
258
|
+
if (__getOwnPropSymbols$h)
|
|
259
|
+
for (var prop of __getOwnPropSymbols$h(b)) {
|
|
260
|
+
if (__propIsEnum$h.call(b, prop))
|
|
261
|
+
__defNormalProp$g(a, prop, b[prop]);
|
|
262
262
|
}
|
|
263
263
|
return a;
|
|
264
264
|
};
|
|
265
|
-
var __spreadProps$
|
|
265
|
+
var __spreadProps$a = (a, b) => __defProps$a(a, __getOwnPropDescs$a(b));
|
|
266
266
|
const knownDefaults = {
|
|
267
267
|
current_timestamp: "now()",
|
|
268
268
|
"transaction_timestamp()": "now()"
|
|
@@ -276,7 +276,7 @@ const simplifyColumnDefault = (value) => {
|
|
|
276
276
|
};
|
|
277
277
|
const instantiateColumn = (typeFn, params) => {
|
|
278
278
|
const column = typeFn();
|
|
279
|
-
Object.assign(column.data, __spreadProps$
|
|
279
|
+
Object.assign(column.data, __spreadProps$a(__spreadValues$g({}, params), {
|
|
280
280
|
default: simplifyColumnDefault(params.default)
|
|
281
281
|
}));
|
|
282
282
|
return column;
|
|
@@ -993,14 +993,14 @@ const processWhere = (ands, ctx, table, query, data, quotedAs) => {
|
|
|
993
993
|
const joinItems = Array.isArray(value[0]) ? value : [value];
|
|
994
994
|
const joinSet = joinItems.length > 1 ? /* @__PURE__ */ new Set() : null;
|
|
995
995
|
for (const args of joinItems) {
|
|
996
|
-
const { target,
|
|
996
|
+
const { target, on } = processJoinItem(
|
|
997
997
|
ctx,
|
|
998
998
|
table,
|
|
999
999
|
query,
|
|
1000
1000
|
args,
|
|
1001
1001
|
quotedAs
|
|
1002
1002
|
);
|
|
1003
|
-
const sql = `EXISTS (SELECT 1 FROM ${target} WHERE ${
|
|
1003
|
+
const sql = `EXISTS (SELECT 1 FROM ${target}${on ? ` WHERE ${on}` : ""})`;
|
|
1004
1004
|
if (joinSet) {
|
|
1005
1005
|
if (joinSet.has(sql))
|
|
1006
1006
|
continue;
|
|
@@ -1091,190 +1091,144 @@ const pushIn = (ctx, query, ands, quotedAs, arg) => {
|
|
|
1091
1091
|
ands.push(`${multiple ? `(${columnsSql})` : columnsSql} IN ${value}`);
|
|
1092
1092
|
};
|
|
1093
1093
|
|
|
1094
|
-
var __defProp$
|
|
1095
|
-
var __defProps$
|
|
1096
|
-
var __getOwnPropDescs$
|
|
1097
|
-
var __getOwnPropSymbols$
|
|
1098
|
-
var __hasOwnProp$
|
|
1099
|
-
var __propIsEnum$
|
|
1100
|
-
var __defNormalProp$
|
|
1101
|
-
var __spreadValues$
|
|
1094
|
+
var __defProp$f = Object.defineProperty;
|
|
1095
|
+
var __defProps$9 = Object.defineProperties;
|
|
1096
|
+
var __getOwnPropDescs$9 = Object.getOwnPropertyDescriptors;
|
|
1097
|
+
var __getOwnPropSymbols$g = Object.getOwnPropertySymbols;
|
|
1098
|
+
var __hasOwnProp$g = Object.prototype.hasOwnProperty;
|
|
1099
|
+
var __propIsEnum$g = Object.prototype.propertyIsEnumerable;
|
|
1100
|
+
var __defNormalProp$f = (obj, key, value) => key in obj ? __defProp$f(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1101
|
+
var __spreadValues$f = (a, b) => {
|
|
1102
1102
|
for (var prop in b || (b = {}))
|
|
1103
|
-
if (__hasOwnProp$
|
|
1104
|
-
__defNormalProp$
|
|
1105
|
-
if (__getOwnPropSymbols$
|
|
1106
|
-
for (var prop of __getOwnPropSymbols$
|
|
1107
|
-
if (__propIsEnum$
|
|
1108
|
-
__defNormalProp$
|
|
1103
|
+
if (__hasOwnProp$g.call(b, prop))
|
|
1104
|
+
__defNormalProp$f(a, prop, b[prop]);
|
|
1105
|
+
if (__getOwnPropSymbols$g)
|
|
1106
|
+
for (var prop of __getOwnPropSymbols$g(b)) {
|
|
1107
|
+
if (__propIsEnum$g.call(b, prop))
|
|
1108
|
+
__defNormalProp$f(a, prop, b[prop]);
|
|
1109
1109
|
}
|
|
1110
1110
|
return a;
|
|
1111
1111
|
};
|
|
1112
|
-
var __spreadProps$
|
|
1113
|
-
const processJoinItem = (ctx, table, query,
|
|
1112
|
+
var __spreadProps$9 = (a, b) => __defProps$9(a, __getOwnPropDescs$9(b));
|
|
1113
|
+
const processJoinItem = (ctx, table, query, args, quotedAs) => {
|
|
1114
1114
|
let target;
|
|
1115
|
-
let
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
).q;
|
|
1141
|
-
if (arg.and)
|
|
1142
|
-
queryData.and.push(...arg.and);
|
|
1143
|
-
if (arg.or)
|
|
1144
|
-
queryData.or.push(...arg.or);
|
|
1115
|
+
let on;
|
|
1116
|
+
if ("j" in args) {
|
|
1117
|
+
const { j, s, r } = args;
|
|
1118
|
+
const tableName = typeof j.q.from === "string" ? j.q.from : j.table;
|
|
1119
|
+
const quotedTable = quoteSchemaAndTable(j.q.schema, tableName);
|
|
1120
|
+
target = quotedTable;
|
|
1121
|
+
const as = j.q.as;
|
|
1122
|
+
const joinAs = `"${as}"`;
|
|
1123
|
+
if (as !== tableName) {
|
|
1124
|
+
target += ` AS ${joinAs}`;
|
|
1125
|
+
}
|
|
1126
|
+
if (r && s) {
|
|
1127
|
+
target = `LATERAL ${subJoinToSql(ctx, j, quotedTable, joinAs, true)}`;
|
|
1128
|
+
} else {
|
|
1129
|
+
on = whereToSql(ctx, j, j.q, joinAs);
|
|
1130
|
+
}
|
|
1131
|
+
} else if ("w" in args) {
|
|
1132
|
+
const { w } = args;
|
|
1133
|
+
target = `"${w}"`;
|
|
1134
|
+
if ("r" in args) {
|
|
1135
|
+
const { s, r } = args;
|
|
1136
|
+
if (s) {
|
|
1137
|
+
target = `LATERAL ${subJoinToSql(ctx, r, target, target)}`;
|
|
1138
|
+
} else {
|
|
1139
|
+
on = whereToSql(ctx, r, r.q, target);
|
|
1145
1140
|
}
|
|
1146
|
-
conditions = whereToSql(ctx, jq, queryData, joinAs);
|
|
1147
1141
|
} else {
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
conditions = processArgs(
|
|
1151
|
-
args,
|
|
1142
|
+
on = processArgs(
|
|
1143
|
+
args.a,
|
|
1152
1144
|
ctx,
|
|
1153
|
-
table,
|
|
1154
1145
|
query,
|
|
1155
|
-
first,
|
|
1156
1146
|
target,
|
|
1157
|
-
|
|
1147
|
+
query.joinedShapes[w],
|
|
1158
1148
|
quotedAs
|
|
1159
1149
|
);
|
|
1160
1150
|
}
|
|
1161
1151
|
} else {
|
|
1162
|
-
const
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
});
|
|
1173
|
-
target = `(${subQuery.text}) ${qAs || joinAs}`;
|
|
1174
|
-
if (addAs)
|
|
1175
|
-
joinAs = qAs;
|
|
1152
|
+
const { q, s } = args;
|
|
1153
|
+
let joinAs;
|
|
1154
|
+
if ("r" in args) {
|
|
1155
|
+
const { r } = args;
|
|
1156
|
+
const res = getArgQueryTarget(ctx, q, s, s);
|
|
1157
|
+
target = s ? `LATERAL ${res.target}` : res.target;
|
|
1158
|
+
joinAs = res.joinAs;
|
|
1159
|
+
if (!s) {
|
|
1160
|
+
on = whereToSql(ctx, r, r.q, joinAs);
|
|
1161
|
+
}
|
|
1176
1162
|
} else {
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1163
|
+
const res = getArgQueryTarget(ctx, q, s);
|
|
1164
|
+
target = res.target;
|
|
1165
|
+
joinAs = res.joinAs;
|
|
1166
|
+
if ("a" in args) {
|
|
1167
|
+
on = processArgs(args.a, ctx, query, joinAs, q.shape, quotedAs);
|
|
1180
1168
|
}
|
|
1181
1169
|
}
|
|
1182
|
-
|
|
1183
|
-
args,
|
|
1184
|
-
ctx,
|
|
1185
|
-
table,
|
|
1186
|
-
query,
|
|
1187
|
-
first,
|
|
1188
|
-
joinAs,
|
|
1189
|
-
joinedShape,
|
|
1190
|
-
quotedAs
|
|
1191
|
-
);
|
|
1192
|
-
if (!item.isSubQuery) {
|
|
1170
|
+
if (!s) {
|
|
1193
1171
|
const whereSql = whereToSql(
|
|
1194
1172
|
ctx,
|
|
1195
|
-
|
|
1196
|
-
__spreadProps$
|
|
1197
|
-
joinedShapes: __spreadProps$
|
|
1173
|
+
q,
|
|
1174
|
+
__spreadProps$9(__spreadValues$f({}, q.q), {
|
|
1175
|
+
joinedShapes: __spreadProps$9(__spreadValues$f(__spreadValues$f({}, query.joinedShapes), q.q.joinedShapes), {
|
|
1198
1176
|
[table.q.as || table.table]: table.q.shape
|
|
1199
1177
|
})
|
|
1200
1178
|
}),
|
|
1201
1179
|
joinAs
|
|
1202
1180
|
);
|
|
1203
1181
|
if (whereSql) {
|
|
1204
|
-
if (
|
|
1205
|
-
|
|
1182
|
+
if (on)
|
|
1183
|
+
on += ` AND ${whereSql}`;
|
|
1206
1184
|
else
|
|
1207
|
-
|
|
1185
|
+
on = whereSql;
|
|
1208
1186
|
}
|
|
1209
1187
|
}
|
|
1210
1188
|
}
|
|
1211
|
-
return { target,
|
|
1189
|
+
return { target, on };
|
|
1212
1190
|
};
|
|
1213
|
-
const
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
}
|
|
1230
|
-
q = Object.create(table);
|
|
1231
|
-
q.q = {
|
|
1232
|
-
type: void 0,
|
|
1233
|
-
shape,
|
|
1234
|
-
adapter: query2.adapter,
|
|
1235
|
-
handleResult: query2.handleResult,
|
|
1236
|
-
returnType: "all",
|
|
1237
|
-
logger: query2.logger
|
|
1238
|
-
};
|
|
1239
|
-
data = { shape, joinedShapes };
|
|
1240
|
-
} else {
|
|
1241
|
-
q = first;
|
|
1242
|
-
if (first.joinQueryAfterCallback) {
|
|
1243
|
-
let base = q.baseQuery;
|
|
1244
|
-
if (q.q.as) {
|
|
1245
|
-
base = base.as(q.q.as);
|
|
1246
|
-
}
|
|
1247
|
-
const { q: query2 } = first.joinQueryAfterCallback(
|
|
1248
|
-
base,
|
|
1249
|
-
table
|
|
1250
|
-
);
|
|
1251
|
-
if (query2.and) {
|
|
1252
|
-
pushQueryArray(q, "and", query2.and);
|
|
1253
|
-
}
|
|
1254
|
-
if (query2.or) {
|
|
1255
|
-
pushQueryArray(q, "or", query2.or);
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
|
-
data = __spreadProps$8(__spreadValues$e({}, first.q), {
|
|
1259
|
-
joinedShapes: __spreadValues$e(__spreadValues$e({}, first.q.joinedShapes), joinedShapes)
|
|
1260
|
-
});
|
|
1261
|
-
}
|
|
1262
|
-
const jq = arg(new ctx.queryBuilder.onQueryBuilder(q, data, table));
|
|
1263
|
-
if (jq.q.joinedShapes !== joinedShapes) {
|
|
1264
|
-
jq.q.joinedShapes = __spreadValues$e(__spreadValues$e({}, jq.q.joinedShapes), joinedShapes);
|
|
1265
|
-
}
|
|
1266
|
-
return whereToSql(ctx, jq, jq.q, joinAs);
|
|
1267
|
-
} else {
|
|
1268
|
-
return getObjectOrRawConditions(
|
|
1269
|
-
ctx,
|
|
1270
|
-
query,
|
|
1271
|
-
arg,
|
|
1272
|
-
quotedAs,
|
|
1273
|
-
joinAs,
|
|
1274
|
-
joinShape
|
|
1275
|
-
);
|
|
1191
|
+
const getArgQueryTarget = (ctx, first, joinSubQuery, cloned) => {
|
|
1192
|
+
const joinQuery = first.q;
|
|
1193
|
+
const quotedFrom = typeof joinQuery.from === "string" ? `"${joinQuery.from}"` : void 0;
|
|
1194
|
+
let joinAs = quotedFrom || `"${first.table}"`;
|
|
1195
|
+
const qAs = joinQuery.as ? `"${joinQuery.as}"` : void 0;
|
|
1196
|
+
const addAs = qAs && qAs !== joinAs;
|
|
1197
|
+
if (joinSubQuery) {
|
|
1198
|
+
return {
|
|
1199
|
+
target: subJoinToSql(ctx, first, joinAs, qAs, cloned),
|
|
1200
|
+
joinAs: addAs ? qAs : joinAs
|
|
1201
|
+
};
|
|
1202
|
+
} else {
|
|
1203
|
+
let target = quotedFrom || quoteSchemaAndTable(joinQuery.schema, first.table);
|
|
1204
|
+
if (addAs) {
|
|
1205
|
+
joinAs = qAs;
|
|
1206
|
+
target += ` AS ${qAs}`;
|
|
1276
1207
|
}
|
|
1277
|
-
|
|
1208
|
+
return { target, joinAs };
|
|
1209
|
+
}
|
|
1210
|
+
};
|
|
1211
|
+
const subJoinToSql = (ctx, jq, innerAs, outerAs, cloned) => {
|
|
1212
|
+
if (!jq.q.select && jq.internal.columnsForSelectAll) {
|
|
1213
|
+
if (!cloned)
|
|
1214
|
+
jq = jq.clone();
|
|
1215
|
+
jq.q.select = [new RawSQL(`${innerAs}.*`)];
|
|
1216
|
+
}
|
|
1217
|
+
return `(${jq.toSQL({
|
|
1218
|
+
values: ctx.values
|
|
1219
|
+
}).text}) ${outerAs || innerAs}`;
|
|
1220
|
+
};
|
|
1221
|
+
const processArgs = (args, ctx, query, joinAs, joinShape, quotedAs) => {
|
|
1222
|
+
if (args.length === 1) {
|
|
1223
|
+
return getObjectOrRawConditions(
|
|
1224
|
+
ctx,
|
|
1225
|
+
query,
|
|
1226
|
+
args[0],
|
|
1227
|
+
quotedAs,
|
|
1228
|
+
joinAs,
|
|
1229
|
+
joinShape
|
|
1230
|
+
);
|
|
1231
|
+
} else {
|
|
1278
1232
|
return getConditionsFor3Or4LengthItem(
|
|
1279
1233
|
ctx,
|
|
1280
1234
|
query,
|
|
@@ -1284,7 +1238,6 @@ const processArgs = (args, ctx, table, query, first, joinAs, joinShape, quotedAs
|
|
|
1284
1238
|
joinShape
|
|
1285
1239
|
);
|
|
1286
1240
|
}
|
|
1287
|
-
return void 0;
|
|
1288
1241
|
};
|
|
1289
1242
|
const getConditionsFor3Or4LengthItem = (ctx, query, target, quotedAs, args, joinShape) => {
|
|
1290
1243
|
const [leftColumn, opOrRightColumn, maybeRightColumn] = args;
|
|
@@ -1334,14 +1287,14 @@ const pushJoinSql = (ctx, table, query, quotedAs) => {
|
|
|
1334
1287
|
sql = `${item[0]} LATERAL (${q.toSQL(ctx).text}) "${((_a = query.joinOverrides) == null ? void 0 : _a[as]) || as}" ON true`;
|
|
1335
1288
|
ctx.aliasValue = aliasValue;
|
|
1336
1289
|
} else {
|
|
1337
|
-
const { target,
|
|
1290
|
+
const { target, on = "true" } = processJoinItem(
|
|
1338
1291
|
ctx,
|
|
1339
1292
|
table,
|
|
1340
1293
|
query,
|
|
1341
|
-
item,
|
|
1294
|
+
item.args,
|
|
1342
1295
|
quotedAs
|
|
1343
1296
|
);
|
|
1344
|
-
sql =
|
|
1297
|
+
sql = `${item.type} ${target} ON ${on}`;
|
|
1345
1298
|
}
|
|
1346
1299
|
if (joinSet) {
|
|
1347
1300
|
if (joinSet.has(sql))
|
|
@@ -1362,15 +1315,123 @@ const skipQueryKeysForSubQuery = {
|
|
|
1362
1315
|
joinedShapes: true,
|
|
1363
1316
|
returnsOne: true
|
|
1364
1317
|
};
|
|
1365
|
-
const getIsJoinSubQuery = (query
|
|
1366
|
-
|
|
1367
|
-
|
|
1318
|
+
const getIsJoinSubQuery = (query) => {
|
|
1319
|
+
const {
|
|
1320
|
+
q,
|
|
1321
|
+
baseQuery: { q: baseQ }
|
|
1322
|
+
} = query;
|
|
1323
|
+
for (const key in q) {
|
|
1324
|
+
if (!skipQueryKeysForSubQuery[key] && q[key] !== baseQ[key]) {
|
|
1368
1325
|
return true;
|
|
1369
1326
|
}
|
|
1370
1327
|
}
|
|
1371
1328
|
return false;
|
|
1372
1329
|
};
|
|
1373
1330
|
|
|
1331
|
+
var __defProp$e = Object.defineProperty;
|
|
1332
|
+
var __defProps$8 = Object.defineProperties;
|
|
1333
|
+
var __getOwnPropDescs$8 = Object.getOwnPropertyDescriptors;
|
|
1334
|
+
var __getOwnPropSymbols$f = Object.getOwnPropertySymbols;
|
|
1335
|
+
var __hasOwnProp$f = Object.prototype.hasOwnProperty;
|
|
1336
|
+
var __propIsEnum$f = Object.prototype.propertyIsEnumerable;
|
|
1337
|
+
var __defNormalProp$e = (obj, key, value) => key in obj ? __defProp$e(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1338
|
+
var __spreadValues$e = (a, b) => {
|
|
1339
|
+
for (var prop in b || (b = {}))
|
|
1340
|
+
if (__hasOwnProp$f.call(b, prop))
|
|
1341
|
+
__defNormalProp$e(a, prop, b[prop]);
|
|
1342
|
+
if (__getOwnPropSymbols$f)
|
|
1343
|
+
for (var prop of __getOwnPropSymbols$f(b)) {
|
|
1344
|
+
if (__propIsEnum$f.call(b, prop))
|
|
1345
|
+
__defNormalProp$e(a, prop, b[prop]);
|
|
1346
|
+
}
|
|
1347
|
+
return a;
|
|
1348
|
+
};
|
|
1349
|
+
var __spreadProps$8 = (a, b) => __defProps$8(a, __getOwnPropDescs$8(b));
|
|
1350
|
+
const processJoinArgs = (joinTo, first, args, joinSubQuery) => {
|
|
1351
|
+
var _a;
|
|
1352
|
+
if (typeof first === "string") {
|
|
1353
|
+
if (first in joinTo.relations) {
|
|
1354
|
+
const { query: toQuery, joinQuery } = joinTo.relations[first].relationConfig;
|
|
1355
|
+
const j = joinQuery(toQuery, joinTo);
|
|
1356
|
+
if (typeof args[0] === "function") {
|
|
1357
|
+
const r = args[0](
|
|
1358
|
+
makeJoinQueryBuilder(j, j.q.joinedShapes, joinTo)
|
|
1359
|
+
);
|
|
1360
|
+
return { j: j.merge(r), s: joinSubQuery || getIsJoinSubQuery(r), r };
|
|
1361
|
+
}
|
|
1362
|
+
return { j, s: joinSubQuery };
|
|
1363
|
+
} else if (typeof args[0] !== "function") {
|
|
1364
|
+
return { w: first, a: args };
|
|
1365
|
+
} else {
|
|
1366
|
+
const joinToQ = joinTo.q;
|
|
1367
|
+
const shape = (_a = joinToQ.withShapes) == null ? void 0 : _a[first];
|
|
1368
|
+
if (!shape) {
|
|
1369
|
+
throw new Error("Cannot get shape of `with` statement");
|
|
1370
|
+
}
|
|
1371
|
+
const j = joinTo.queryBuilder.baseQuery.clone();
|
|
1372
|
+
j.table = first;
|
|
1373
|
+
j.q = {
|
|
1374
|
+
shape,
|
|
1375
|
+
adapter: joinToQ.adapter,
|
|
1376
|
+
handleResult: joinToQ.handleResult,
|
|
1377
|
+
returnType: "all",
|
|
1378
|
+
logger: joinToQ.logger
|
|
1379
|
+
};
|
|
1380
|
+
j.baseQuery = j;
|
|
1381
|
+
const joinedShapes = __spreadProps$8(__spreadValues$e({}, joinToQ.joinedShapes), {
|
|
1382
|
+
[joinToQ.as || joinTo.table]: joinTo.shape
|
|
1383
|
+
});
|
|
1384
|
+
const r = args[0](
|
|
1385
|
+
makeJoinQueryBuilder(
|
|
1386
|
+
j,
|
|
1387
|
+
j.q.joinedShapes ? __spreadValues$e(__spreadValues$e({}, j.q.joinedShapes), joinedShapes) : joinedShapes,
|
|
1388
|
+
joinTo
|
|
1389
|
+
)
|
|
1390
|
+
);
|
|
1391
|
+
return { w: first, r, s: joinSubQuery || getIsJoinSubQuery(r) };
|
|
1392
|
+
}
|
|
1393
|
+
} else if (typeof args[0] === "function") {
|
|
1394
|
+
const q = first;
|
|
1395
|
+
if (q.joinQueryAfterCallback) {
|
|
1396
|
+
let base = q.baseQuery;
|
|
1397
|
+
if (q.q.as) {
|
|
1398
|
+
base = base.as(q.q.as);
|
|
1399
|
+
}
|
|
1400
|
+
const { q: query } = q.joinQueryAfterCallback(base, joinTo);
|
|
1401
|
+
if (query.and) {
|
|
1402
|
+
pushQueryArray(q, "and", query.and);
|
|
1403
|
+
}
|
|
1404
|
+
if (query.or) {
|
|
1405
|
+
pushQueryArray(q, "or", query.or);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
const joinedShapes = __spreadProps$8(__spreadValues$e({}, joinTo.q.joinedShapes), {
|
|
1409
|
+
[joinTo.q.as || joinTo.table]: joinTo.shape
|
|
1410
|
+
});
|
|
1411
|
+
const r = args[0](
|
|
1412
|
+
makeJoinQueryBuilder(
|
|
1413
|
+
q,
|
|
1414
|
+
q.q.joinedShapes ? __spreadValues$e(__spreadValues$e({}, q.q.joinedShapes), joinedShapes) : joinedShapes,
|
|
1415
|
+
joinTo
|
|
1416
|
+
)
|
|
1417
|
+
);
|
|
1418
|
+
joinSubQuery || (joinSubQuery = getIsJoinSubQuery(r));
|
|
1419
|
+
return { q: joinSubQuery ? q.merge(r) : q, r, s: joinSubQuery };
|
|
1420
|
+
}
|
|
1421
|
+
return {
|
|
1422
|
+
q: first,
|
|
1423
|
+
a: args,
|
|
1424
|
+
s: joinSubQuery
|
|
1425
|
+
};
|
|
1426
|
+
};
|
|
1427
|
+
const makeJoinQueryBuilder = (joinedQuery, joinedShapes, joinTo) => {
|
|
1428
|
+
const q = joinedQuery.baseQuery.clone();
|
|
1429
|
+
q.q.joinedShapes = joinedShapes;
|
|
1430
|
+
q.baseQuery = q;
|
|
1431
|
+
q.q.joinTo = joinTo;
|
|
1432
|
+
return q;
|
|
1433
|
+
};
|
|
1434
|
+
|
|
1374
1435
|
var __defProp$d = Object.defineProperty;
|
|
1375
1436
|
var __defProps$7 = Object.defineProperties;
|
|
1376
1437
|
var __getOwnPropDescs$7 = Object.getOwnPropertyDescriptors;
|
|
@@ -1391,23 +1452,24 @@ var __spreadValues$d = (a, b) => {
|
|
|
1391
1452
|
};
|
|
1392
1453
|
var __spreadProps$7 = (a, b) => __defProps$7(a, __getOwnPropDescs$7(b));
|
|
1393
1454
|
const _join = (q, require2, type, first, args) => {
|
|
1394
|
-
var _a;
|
|
1455
|
+
var _a, _b;
|
|
1395
1456
|
let joinKey;
|
|
1396
1457
|
let shape;
|
|
1397
1458
|
let parsers;
|
|
1398
|
-
let
|
|
1459
|
+
let joinSubQuery = false;
|
|
1399
1460
|
if (typeof first === "function") {
|
|
1400
1461
|
first = first(q.relations);
|
|
1401
1462
|
first.joinQueryAfterCallback = first.joinQuery;
|
|
1402
1463
|
}
|
|
1403
1464
|
if (typeof first === "object") {
|
|
1404
|
-
|
|
1405
|
-
|
|
1465
|
+
const q2 = first;
|
|
1466
|
+
joinSubQuery = getIsJoinSubQuery(q2);
|
|
1467
|
+
joinKey = q2.q.as || q2.table;
|
|
1406
1468
|
if (joinKey) {
|
|
1407
|
-
shape = getShapeFromSelect(
|
|
1408
|
-
parsers =
|
|
1409
|
-
if (
|
|
1410
|
-
first =
|
|
1469
|
+
shape = getShapeFromSelect(q2, joinSubQuery);
|
|
1470
|
+
parsers = q2.q.parsers;
|
|
1471
|
+
if (joinSubQuery) {
|
|
1472
|
+
first = q2.clone();
|
|
1411
1473
|
first.shape = shape;
|
|
1412
1474
|
}
|
|
1413
1475
|
}
|
|
@@ -1433,14 +1495,46 @@ const _join = (q, require2, type, first, args) => {
|
|
|
1433
1495
|
}
|
|
1434
1496
|
}
|
|
1435
1497
|
if (joinKey) {
|
|
1436
|
-
setQueryObjectValue(
|
|
1437
|
-
|
|
1498
|
+
setQueryObjectValue(
|
|
1499
|
+
q,
|
|
1500
|
+
"joinedShapes",
|
|
1501
|
+
joinKey,
|
|
1502
|
+
shape
|
|
1503
|
+
);
|
|
1504
|
+
setQueryObjectValue(
|
|
1505
|
+
q,
|
|
1506
|
+
"joinedParsers",
|
|
1507
|
+
joinKey,
|
|
1508
|
+
parsers
|
|
1509
|
+
);
|
|
1438
1510
|
}
|
|
1439
|
-
|
|
1440
|
-
|
|
1511
|
+
const joinArgs = processJoinArgs(
|
|
1512
|
+
q,
|
|
1441
1513
|
first,
|
|
1442
1514
|
args,
|
|
1443
|
-
|
|
1515
|
+
joinSubQuery
|
|
1516
|
+
);
|
|
1517
|
+
if (joinKey && "s" in joinArgs && joinArgs.s) {
|
|
1518
|
+
const j = "j" in joinArgs ? (_b = joinArgs.r) != null ? _b : joinArgs.j : "r" in joinArgs ? joinArgs.r : joinArgs.q;
|
|
1519
|
+
if (j.q.select || !j.internal.columnsForSelectAll) {
|
|
1520
|
+
const shape2 = getShapeFromSelect(j, true);
|
|
1521
|
+
setQueryObjectValue(
|
|
1522
|
+
q,
|
|
1523
|
+
"joinedShapes",
|
|
1524
|
+
joinKey,
|
|
1525
|
+
shape2
|
|
1526
|
+
);
|
|
1527
|
+
setQueryObjectValue(
|
|
1528
|
+
q,
|
|
1529
|
+
"joinedParsers",
|
|
1530
|
+
joinKey,
|
|
1531
|
+
j.q.parsers
|
|
1532
|
+
);
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
return pushQueryValue(q, "join", {
|
|
1536
|
+
type,
|
|
1537
|
+
args: joinArgs
|
|
1444
1538
|
});
|
|
1445
1539
|
};
|
|
1446
1540
|
const _joinLateral = (self, type, arg, cb, as) => {
|
|
@@ -1450,7 +1544,7 @@ const _joinLateral = (self, type, arg, cb, as) => {
|
|
|
1450
1544
|
if (typeof arg === "string") {
|
|
1451
1545
|
relation = q.relations[arg];
|
|
1452
1546
|
if (relation) {
|
|
1453
|
-
arg = relation.relationConfig.query;
|
|
1547
|
+
arg = relation.relationConfig.query.clone();
|
|
1454
1548
|
} else {
|
|
1455
1549
|
const shape = (_a = q.q.withShapes) == null ? void 0 : _a[arg];
|
|
1456
1550
|
if (shape) {
|
|
@@ -3286,7 +3380,7 @@ const processValue = (ctx, table, QueryClass, key, value, quotedAs) => {
|
|
|
3286
3380
|
};
|
|
3287
3381
|
|
|
3288
3382
|
const pushDeleteSql = (ctx, table, query, quotedAs) => {
|
|
3289
|
-
var _a, _b, _c;
|
|
3383
|
+
var _a, _b, _c, _d;
|
|
3290
3384
|
const from = `"${table.table}"`;
|
|
3291
3385
|
ctx.sql.push(`DELETE FROM ${from}`);
|
|
3292
3386
|
if (from !== quotedAs) {
|
|
@@ -3294,28 +3388,40 @@ const pushDeleteSql = (ctx, table, query, quotedAs) => {
|
|
|
3294
3388
|
}
|
|
3295
3389
|
let conditions;
|
|
3296
3390
|
if ((_a = query.join) == null ? void 0 : _a.length) {
|
|
3297
|
-
const
|
|
3391
|
+
const targets = [];
|
|
3392
|
+
const ons = [];
|
|
3298
3393
|
const joinSet = query.join.length > 1 ? /* @__PURE__ */ new Set() : null;
|
|
3299
3394
|
for (const item of query.join) {
|
|
3300
|
-
if (
|
|
3301
|
-
const
|
|
3302
|
-
const
|
|
3395
|
+
if (Array.isArray(item)) {
|
|
3396
|
+
const q = item[1];
|
|
3397
|
+
const { aliasValue } = ctx;
|
|
3398
|
+
ctx.aliasValue = true;
|
|
3399
|
+
const as = item[2];
|
|
3400
|
+
targets.push(
|
|
3401
|
+
`LATERAL (${q.toSQL(ctx).text}) "${((_b = query.joinOverrides) == null ? void 0 : _b[as]) || as}"`
|
|
3402
|
+
);
|
|
3403
|
+
ctx.aliasValue = aliasValue;
|
|
3404
|
+
} else {
|
|
3405
|
+
const join = processJoinItem(ctx, table, query, item.args, quotedAs);
|
|
3406
|
+
const key = `${join.target}${join.on}`;
|
|
3303
3407
|
if (joinSet) {
|
|
3304
3408
|
if (joinSet.has(key))
|
|
3305
3409
|
continue;
|
|
3306
3410
|
joinSet.add(key);
|
|
3307
3411
|
}
|
|
3308
|
-
|
|
3412
|
+
targets.push(join.target);
|
|
3413
|
+
if (join.on)
|
|
3414
|
+
ons.push(join.on);
|
|
3309
3415
|
}
|
|
3310
3416
|
}
|
|
3311
|
-
if (
|
|
3312
|
-
ctx.sql.push(`USING ${
|
|
3313
|
-
conditions = items.map((item) => item.conditions).filter(Boolean).join(" AND ");
|
|
3417
|
+
if (targets.length) {
|
|
3418
|
+
ctx.sql.push(`USING ${targets.join(", ")}`);
|
|
3314
3419
|
}
|
|
3420
|
+
conditions = ons.join(" AND ");
|
|
3315
3421
|
}
|
|
3316
3422
|
pushWhereStatementSql(ctx, table, query, quotedAs);
|
|
3317
|
-
if (conditions
|
|
3318
|
-
if (((
|
|
3423
|
+
if (conditions) {
|
|
3424
|
+
if (((_c = query.and) == null ? void 0 : _c.length) || ((_d = query.or) == null ? void 0 : _d.length)) {
|
|
3319
3425
|
ctx.sql.push("AND", conditions);
|
|
3320
3426
|
} else {
|
|
3321
3427
|
ctx.sql.push("WHERE", conditions);
|
|
@@ -4739,12 +4845,7 @@ const makeColumnTypes = (schema) => {
|
|
|
4739
4845
|
});
|
|
4740
4846
|
return orchidCore.emptyObject;
|
|
4741
4847
|
}
|
|
4742
|
-
}, orchidCore.makeTimestampsHelpers(
|
|
4743
|
-
makeRegexToFindInSql('\\bupdatedAt\\b"?\\s*='),
|
|
4744
|
-
'"updatedAt"',
|
|
4745
|
-
makeRegexToFindInSql('\\bupdated_at\\b"?\\s*='),
|
|
4746
|
-
'"updated_at"'
|
|
4747
|
-
));
|
|
4848
|
+
}, orchidCore.makeTimestampsHelpers(makeRegexToFindInSql));
|
|
4748
4849
|
};
|
|
4749
4850
|
RawSQL.prototype.columnTypes = makeColumnTypes;
|
|
4750
4851
|
|
|
@@ -6881,1944 +6982,1996 @@ class QueryHooks {
|
|
|
6881
6982
|
}
|
|
6882
6983
|
}
|
|
6883
6984
|
|
|
6884
|
-
class
|
|
6885
|
-
constructor() {
|
|
6886
|
-
this.q = {};
|
|
6887
|
-
}
|
|
6985
|
+
class Join {
|
|
6888
6986
|
/**
|
|
6889
|
-
*
|
|
6987
|
+
* ## Select relation
|
|
6890
6988
|
*
|
|
6891
|
-
*
|
|
6892
|
-
*/
|
|
6893
|
-
clone() {
|
|
6894
|
-
const cloned = Object.create(this.baseQuery);
|
|
6895
|
-
cloned.q = getClonedQueryData(this.q);
|
|
6896
|
-
return cloned;
|
|
6897
|
-
}
|
|
6898
|
-
}
|
|
6899
|
-
|
|
6900
|
-
const _queryWhere = (q, args) => {
|
|
6901
|
-
return pushQueryArray(
|
|
6902
|
-
q,
|
|
6903
|
-
"and",
|
|
6904
|
-
args
|
|
6905
|
-
);
|
|
6906
|
-
};
|
|
6907
|
-
const _queryWhereSql = (q, args) => {
|
|
6908
|
-
return pushQueryValue(
|
|
6909
|
-
q,
|
|
6910
|
-
"and",
|
|
6911
|
-
sqlQueryArgsToExpression(args)
|
|
6912
|
-
);
|
|
6913
|
-
};
|
|
6914
|
-
const _queryWhereNot = (q, args) => {
|
|
6915
|
-
return pushQueryValue(q, "and", {
|
|
6916
|
-
NOT: args
|
|
6917
|
-
});
|
|
6918
|
-
};
|
|
6919
|
-
const _queryWhereNotSql = (q, args) => {
|
|
6920
|
-
return pushQueryValue(q, "and", {
|
|
6921
|
-
NOT: sqlQueryArgsToExpression(args)
|
|
6922
|
-
});
|
|
6923
|
-
};
|
|
6924
|
-
const _queryOr = (q, args) => {
|
|
6925
|
-
return pushQueryArray(
|
|
6926
|
-
q,
|
|
6927
|
-
"or",
|
|
6928
|
-
args.map((item) => [item])
|
|
6929
|
-
);
|
|
6930
|
-
};
|
|
6931
|
-
const _queryOrNot = (q, args) => {
|
|
6932
|
-
return pushQueryArray(
|
|
6933
|
-
q,
|
|
6934
|
-
"or",
|
|
6935
|
-
args.map((item) => [{ NOT: item }])
|
|
6936
|
-
);
|
|
6937
|
-
};
|
|
6938
|
-
const _queryWhereIn = (q, and, arg, values, not) => {
|
|
6939
|
-
let item;
|
|
6940
|
-
if (values) {
|
|
6941
|
-
if (Array.isArray(arg)) {
|
|
6942
|
-
item = {
|
|
6943
|
-
IN: {
|
|
6944
|
-
columns: arg,
|
|
6945
|
-
values
|
|
6946
|
-
}
|
|
6947
|
-
};
|
|
6948
|
-
} else {
|
|
6949
|
-
item = { [arg]: { in: values } };
|
|
6950
|
-
}
|
|
6951
|
-
} else {
|
|
6952
|
-
item = {};
|
|
6953
|
-
for (const key in arg) {
|
|
6954
|
-
item[key] = { in: arg[key] };
|
|
6955
|
-
}
|
|
6956
|
-
}
|
|
6957
|
-
if (not)
|
|
6958
|
-
item = { NOT: item };
|
|
6959
|
-
if (and) {
|
|
6960
|
-
pushQueryValue(q, "and", item);
|
|
6961
|
-
} else {
|
|
6962
|
-
pushQueryValue(q, "or", [item]);
|
|
6963
|
-
}
|
|
6964
|
-
return q;
|
|
6965
|
-
};
|
|
6966
|
-
const existsArgs = (q, args) => {
|
|
6967
|
-
let isSubQuery;
|
|
6968
|
-
if (typeof q === "object") {
|
|
6969
|
-
isSubQuery = getIsJoinSubQuery(q.q, q.baseQuery.q);
|
|
6970
|
-
if (isSubQuery) {
|
|
6971
|
-
q = q.clone();
|
|
6972
|
-
q.shape = getShapeFromSelect(q, true);
|
|
6973
|
-
}
|
|
6974
|
-
} else {
|
|
6975
|
-
isSubQuery = false;
|
|
6976
|
-
}
|
|
6977
|
-
return [
|
|
6978
|
-
{
|
|
6979
|
-
EXISTS: {
|
|
6980
|
-
first: q,
|
|
6981
|
-
args,
|
|
6982
|
-
isSubQuery
|
|
6983
|
-
}
|
|
6984
|
-
}
|
|
6985
|
-
];
|
|
6986
|
-
};
|
|
6987
|
-
class Where {
|
|
6988
|
-
/**
|
|
6989
|
-
* Constructing `WHERE` conditions:
|
|
6989
|
+
* Before joining a table, consider if selecting a relation is enough for your case:
|
|
6990
6990
|
*
|
|
6991
6991
|
* ```ts
|
|
6992
|
-
*
|
|
6993
|
-
*
|
|
6994
|
-
*
|
|
6995
|
-
*
|
|
6996
|
-
*
|
|
6997
|
-
* 'table.lastName': 'Johnsonuk',
|
|
6992
|
+
* // select users with profiles
|
|
6993
|
+
* // result type is Array<{ name: string, profile: Profile }>
|
|
6994
|
+
* await db.user.select('name', {
|
|
6995
|
+
* profile: (q) => q.profile,
|
|
6996
|
+
* });
|
|
6998
6997
|
*
|
|
6999
|
-
*
|
|
7000
|
-
*
|
|
7001
|
-
*
|
|
7002
|
-
*
|
|
7003
|
-
*
|
|
6998
|
+
* // select posts with counts of comments, order by comments count
|
|
6999
|
+
* // result type is Array<Post & { commentsCount: number }>
|
|
7000
|
+
* await db.post
|
|
7001
|
+
* .select('*', {
|
|
7002
|
+
* commentsCount: (q) => q.comments.count(),
|
|
7003
|
+
* })
|
|
7004
|
+
* .order({
|
|
7005
|
+
* commentsCount: 'DESC',
|
|
7006
|
+
* });
|
|
7004
7007
|
*
|
|
7005
|
-
*
|
|
7006
|
-
*
|
|
7008
|
+
* // select authors with array of their book titles
|
|
7009
|
+
* // result type is Array<Author & { books: string[] }>
|
|
7010
|
+
* await db.author.select('*', {
|
|
7011
|
+
* books: (q) => q.books.pluck('title'),
|
|
7007
7012
|
* });
|
|
7008
7013
|
* ```
|
|
7009
7014
|
*
|
|
7010
|
-
*
|
|
7015
|
+
* Internally, such selects will use `LEFT JOIN LATERAL` to join a relation.
|
|
7016
|
+
* If you're loading users with profiles (one-to-one relation), and some users don't have a profile, `profile` property will have `NULL` for such users.
|
|
7017
|
+
* If you want to load only users that have profiles, and filter out the rest, add `.join()` method to the relation without arguments:
|
|
7011
7018
|
*
|
|
7012
7019
|
* ```ts
|
|
7013
|
-
*
|
|
7014
|
-
*
|
|
7020
|
+
* // load only users who have a profile
|
|
7021
|
+
* await db.user.select('*', {
|
|
7022
|
+
* profile: (q) => q.profile.join(),
|
|
7023
|
+
* });
|
|
7015
7024
|
*
|
|
7016
|
-
*
|
|
7017
|
-
*
|
|
7025
|
+
* // load only users who have a specific profile
|
|
7026
|
+
* await db.user.select('*', {
|
|
7027
|
+
* profile: (q) => q.profile.join().where({ age: { gt: 20 } }),
|
|
7028
|
+
* });
|
|
7018
7029
|
* ```
|
|
7019
7030
|
*
|
|
7020
|
-
* `
|
|
7031
|
+
* You can also use this `.join()` method on the one-to-many relations, and records with empty array will be filtered out:
|
|
7021
7032
|
*
|
|
7022
7033
|
* ```ts
|
|
7023
|
-
*
|
|
7024
|
-
*
|
|
7025
|
-
*
|
|
7026
|
-
*
|
|
7027
|
-
*
|
|
7028
|
-
* const loadRecords = async (params: Params) => {
|
|
7029
|
-
* // this will load all records if params is an empty object
|
|
7030
|
-
* const records = await db.table.where(params);
|
|
7031
|
-
* };
|
|
7034
|
+
* // posts that have no tags won't be loaded
|
|
7035
|
+
* // result type is Array<Post & { tags: Tag[] }>
|
|
7036
|
+
* db.post.select('*', {
|
|
7037
|
+
* tags: (q) => q.tags.join(),
|
|
7038
|
+
* });
|
|
7032
7039
|
* ```
|
|
7033
7040
|
*
|
|
7034
|
-
*
|
|
7041
|
+
* # Joins
|
|
7035
7042
|
*
|
|
7036
|
-
*
|
|
7037
|
-
* db.table.where({
|
|
7038
|
-
* // compare `someColumn` in one table with the `column` value returned from another query.
|
|
7039
|
-
* someColumn: db.otherTable.where(...conditions).get('column'),
|
|
7040
|
-
* });
|
|
7041
|
-
* ```
|
|
7043
|
+
* `join` methods allows to join other tables, relations by name, [with](/guide/advanced-queries#with) statements, sub queries.
|
|
7042
7044
|
*
|
|
7043
|
-
* `
|
|
7045
|
+
* All the `join` methods accept the same arguments, but returning type is different because with `join` it's guaranteed to load joined table, and with `leftJoin` the joined table columns may be `NULL` when no matching record was found.
|
|
7046
|
+
*
|
|
7047
|
+
* For the following examples, imagine we have a `User` table with `id` and `name`, and `Message` table with `id`, `text`, messages belongs to user via `userId` column:
|
|
7044
7048
|
*
|
|
7045
7049
|
* ```ts
|
|
7046
|
-
*
|
|
7050
|
+
* export class UserTable extends BaseTable {
|
|
7051
|
+
* readonly table = 'user';
|
|
7052
|
+
* columns = this.setColumns((t) => ({
|
|
7053
|
+
* id: t.identity().primaryKey(),
|
|
7054
|
+
* name: t.text(),
|
|
7055
|
+
* }));
|
|
7047
7056
|
*
|
|
7048
|
-
*
|
|
7049
|
-
*
|
|
7057
|
+
* relations = {
|
|
7058
|
+
* messages: this.hasMany(() => MessageTable, {
|
|
7059
|
+
* primaryKey: 'id',
|
|
7060
|
+
* foreignKey: 'userId',
|
|
7061
|
+
* }),
|
|
7062
|
+
* };
|
|
7063
|
+
* }
|
|
7064
|
+
*
|
|
7065
|
+
* export class MessageTable extends BaseTable {
|
|
7066
|
+
* readonly table = 'message';
|
|
7067
|
+
* columns = this.setColumns((t) => ({
|
|
7068
|
+
* id: t.identity().primaryKey(),
|
|
7069
|
+
* text: t.text(),
|
|
7070
|
+
* ...t.timestamps(),
|
|
7071
|
+
* }));
|
|
7072
|
+
*
|
|
7073
|
+
* relations = {
|
|
7074
|
+
* user: this.belongsTo(() => UserTable, {
|
|
7075
|
+
* primaryKey: 'id',
|
|
7076
|
+
* foreignKey: 'userId',
|
|
7077
|
+
* }),
|
|
7078
|
+
* };
|
|
7079
|
+
* }
|
|
7050
7080
|
* ```
|
|
7051
7081
|
*
|
|
7052
|
-
*
|
|
7082
|
+
* ## join
|
|
7053
7083
|
*
|
|
7054
|
-
*
|
|
7055
|
-
* db.table.where(db.table.sql`a = b`);
|
|
7084
|
+
* `join` is a method for SQL `JOIN`, which is equivalent to `INNER JOIN`, `LEFT INNERT JOIN`.
|
|
7056
7085
|
*
|
|
7057
|
-
*
|
|
7058
|
-
* import { raw } from 'orchid-orm';
|
|
7086
|
+
* When no matching record is found, it will skip records of the main table.
|
|
7059
7087
|
*
|
|
7060
|
-
*
|
|
7088
|
+
* When joining the same table with the same condition more than once, duplicated joins will be ignored:
|
|
7089
|
+
*
|
|
7090
|
+
* ```ts
|
|
7091
|
+
* // joining a relation
|
|
7092
|
+
* db.post.join('comments').join('comments');
|
|
7093
|
+
*
|
|
7094
|
+
* // joining a table with a condition
|
|
7095
|
+
* db.post
|
|
7096
|
+
* .join('comments', 'comments.postId', 'post.id')
|
|
7097
|
+
* .join('comments', 'comments.postId', 'post.id');
|
|
7061
7098
|
* ```
|
|
7062
7099
|
*
|
|
7063
|
-
*
|
|
7100
|
+
* Both queries will produce SQL with only 1 join
|
|
7064
7101
|
*
|
|
7065
|
-
* ```
|
|
7066
|
-
*
|
|
7067
|
-
* q
|
|
7068
|
-
* .where({ name: 'Name' })
|
|
7069
|
-
* .orWhere({ id: 1 }, { id: 2 })
|
|
7070
|
-
* .whereIn('letter', ['a', 'b', 'c'])
|
|
7071
|
-
* .whereExists(Message, 'authorId', 'id'),
|
|
7072
|
-
* );
|
|
7102
|
+
* ```sql
|
|
7103
|
+
* SELECT * FROM post JOIN comments ON comments.postId = post.id
|
|
7073
7104
|
* ```
|
|
7074
7105
|
*
|
|
7075
|
-
*
|
|
7106
|
+
* However, this is only possible if the join has no dynamic values:
|
|
7076
7107
|
*
|
|
7077
7108
|
* ```ts
|
|
7078
|
-
* db.
|
|
7079
|
-
* {
|
|
7080
|
-
*
|
|
7081
|
-
* db.table.sql`a = b`,
|
|
7082
|
-
* );
|
|
7109
|
+
* db.post
|
|
7110
|
+
* .join('comments', (q) => q.where({ rating: { gt: 5 } }))
|
|
7111
|
+
* .join('comments', (q) => q.where({ rating: { gt: 5 } }));
|
|
7083
7112
|
* ```
|
|
7084
7113
|
*
|
|
7085
|
-
*
|
|
7114
|
+
* Both joins above have the same `{ gt: 5 }`, but still, the `5` is a dynamic value and in this case joins will be duplicated,
|
|
7115
|
+
* resulting in a database error.
|
|
7086
7116
|
*
|
|
7087
|
-
*
|
|
7117
|
+
* ### join relation
|
|
7088
7118
|
*
|
|
7089
|
-
*
|
|
7119
|
+
* When relations are defined between the tables, you can join them by a relation name.
|
|
7120
|
+
* Joined table can be references from `where` and `select` by a relation name.
|
|
7090
7121
|
*
|
|
7091
7122
|
* ```ts
|
|
7092
|
-
* const
|
|
7123
|
+
* const result = await db.user
|
|
7124
|
+
* .join('messages')
|
|
7125
|
+
* // after joining a table, we can use it in `where` conditions:
|
|
7126
|
+
* .where({ 'messages.text': { startsWith: 'Hi' } })
|
|
7127
|
+
* .select(
|
|
7128
|
+
* 'name', // name is User column, table name may be omitted
|
|
7129
|
+
* 'messages.text', // text is the Message column, and the table name is required
|
|
7130
|
+
* );
|
|
7093
7131
|
*
|
|
7094
|
-
*
|
|
7095
|
-
*
|
|
7096
|
-
* post.tags // query tags of the post
|
|
7097
|
-
* .whereIn('tagName', givenTags) // where name of the tag is inside array
|
|
7098
|
-
* .count() // count how many such tags were found
|
|
7099
|
-
* .equals(wantedTags.length), // the count must be exactly the length of array
|
|
7100
|
-
* // if the post has ony `typescript` tag but not the `node.js` it will be omitted
|
|
7101
|
-
* );
|
|
7132
|
+
* // result has the following type:
|
|
7133
|
+
* const ok: { name: string; text: string }[] = result;
|
|
7102
7134
|
* ```
|
|
7103
7135
|
*
|
|
7104
|
-
*
|
|
7136
|
+
* The first argument can also be a callback, where instead of relation name as a string we're picking it as a property of `q`.
|
|
7137
|
+
* In such a way, we can alias the relation with `as`, add `where` conditions, use other query methods.
|
|
7105
7138
|
*
|
|
7106
|
-
* ```
|
|
7107
|
-
*
|
|
7108
|
-
*
|
|
7109
|
-
*
|
|
7110
|
-
* FROM "tag" AS "tags"
|
|
7111
|
-
* WHERE "tag"."tagName" IN ('typescript', 'node.js')
|
|
7112
|
-
* -- join tags to the post via "postTag" table
|
|
7113
|
-
* AND EXISTS (
|
|
7114
|
-
* SELECT 1 FROM "postTag"
|
|
7115
|
-
* WHERE "postTag"."postId" = "post"."id"
|
|
7116
|
-
* AND "postTag"."tagId" = "tag"."id"
|
|
7117
|
-
* )
|
|
7118
|
-
* )
|
|
7139
|
+
* ```ts
|
|
7140
|
+
* const result = await db.user.join((q) =>
|
|
7141
|
+
* q.messages.as('m').where({ text: 'some text' }),
|
|
7142
|
+
* );
|
|
7119
7143
|
* ```
|
|
7120
7144
|
*
|
|
7121
|
-
*
|
|
7145
|
+
* Optionally, you can pass a second callback argument, it makes `on` and `orOn` methods available.
|
|
7122
7146
|
*
|
|
7123
|
-
*
|
|
7147
|
+
* But remember that when joining a relation, the relevant `ON` conditions are already handled automatically.
|
|
7124
7148
|
*
|
|
7125
|
-
*
|
|
7149
|
+
* ```ts
|
|
7150
|
+
* const result = await db.user.join(
|
|
7151
|
+
* (q) => q.messages.as('m'),
|
|
7152
|
+
* (q) =>
|
|
7153
|
+
* q
|
|
7154
|
+
* .on('text', 'name') // additionally, match message with user name
|
|
7155
|
+
* .where({ text: 'some text' }), // you can add `where` in a second callback as well.
|
|
7156
|
+
* );
|
|
7157
|
+
* ```
|
|
7126
7158
|
*
|
|
7127
|
-
*
|
|
7159
|
+
* ### Selecting full joined records
|
|
7128
7160
|
*
|
|
7129
|
-
*
|
|
7161
|
+
* `select` supports selecting a full record of a previously joined table by passing a table name with `.*` at the end:
|
|
7130
7162
|
*
|
|
7131
7163
|
* ```ts
|
|
7132
|
-
* db.
|
|
7133
|
-
*
|
|
7134
|
-
* OR: [{ name: 'a' }, { name: 'b' }],
|
|
7135
|
-
* IN: {
|
|
7136
|
-
* columns: ['id', 'name'],
|
|
7137
|
-
* values: [
|
|
7138
|
-
* [1, 'a'],
|
|
7139
|
-
* [2, 'b'],
|
|
7140
|
-
* ],
|
|
7141
|
-
* },
|
|
7164
|
+
* const result = await db.book.join('author').select('title', {
|
|
7165
|
+
* author: 'author.*',
|
|
7142
7166
|
* });
|
|
7167
|
+
*
|
|
7168
|
+
* // result has the following type:
|
|
7169
|
+
* const ok: {
|
|
7170
|
+
* // title of the book
|
|
7171
|
+
* title: string;
|
|
7172
|
+
* // a full author record is included:
|
|
7173
|
+
* author: { id: number; name: string; updatedAt: Date; createdAt: Date };
|
|
7174
|
+
* }[] = result;
|
|
7143
7175
|
* ```
|
|
7144
7176
|
*
|
|
7145
|
-
*
|
|
7177
|
+
* It works fine for `1:1` (`belongsTo`, `hasOne`) relations, but it may have an unexpected result for `1:M` or `M:M` (`hasMany`, `hasAndBelongsToMany`) relations.
|
|
7178
|
+
* For any kind of relation, it results in one main table record with data of exactly one joined table record, i.e. when selecting in this way, the records **won't** be collected into arrays.
|
|
7146
7179
|
*
|
|
7147
7180
|
* ```ts
|
|
7148
|
-
* db.
|
|
7149
|
-
*
|
|
7150
|
-
*
|
|
7151
|
-
*
|
|
7152
|
-
* NOT: [{ id: 1 }, { id: 2 }],
|
|
7153
|
-
*
|
|
7154
|
-
* // see .orWhere
|
|
7155
|
-
* OR: [{ name: 'a' }, { name: 'b' }],
|
|
7156
|
-
* // can be an array:
|
|
7157
|
-
* // this will give id = 1 AND id = 2 OR id = 3 AND id = 4
|
|
7158
|
-
* OR: [
|
|
7159
|
-
* [{ id: 1 }, { id: 2 }],
|
|
7160
|
-
* [{ id: 3 }, { id: 4 }],
|
|
7161
|
-
* ],
|
|
7181
|
+
* const result = await db.user
|
|
7182
|
+
* .join('messages')
|
|
7183
|
+
* .where({ 'messages.text': { startsWith: 'Hi' } })
|
|
7184
|
+
* .select('name', { messages: 'messages.*' });
|
|
7162
7185
|
*
|
|
7163
|
-
*
|
|
7164
|
-
*
|
|
7165
|
-
*
|
|
7166
|
-
*
|
|
7167
|
-
*
|
|
7168
|
-
*
|
|
7169
|
-
* ],
|
|
7170
|
-
* },
|
|
7171
|
-
* // can be an array:
|
|
7172
|
-
* IN: [
|
|
7173
|
-
* {
|
|
7174
|
-
* columns: ['id', 'name'],
|
|
7175
|
-
* values: [
|
|
7176
|
-
* [1, 'a'],
|
|
7177
|
-
* [2, 'b'],
|
|
7178
|
-
* ],
|
|
7179
|
-
* },
|
|
7180
|
-
* { columns: ['someColumn'], values: [['foo', 'bar']] },
|
|
7181
|
-
* ],
|
|
7182
|
-
* });
|
|
7186
|
+
* // result has the following type:
|
|
7187
|
+
* const ok: {
|
|
7188
|
+
* name: string;
|
|
7189
|
+
* // full message is included:
|
|
7190
|
+
* messages: { id: number; text: string; updatedAt: Date; createdAt: Date };
|
|
7191
|
+
* }[] = result;
|
|
7183
7192
|
* ```
|
|
7184
7193
|
*
|
|
7185
|
-
*
|
|
7194
|
+
* Because it's a one-to-many relation, one user has many messages, the user data will be duplicated for different messages data:
|
|
7186
7195
|
*
|
|
7187
|
-
*
|
|
7196
|
+
* | name | msg |
|
|
7197
|
+
* | ------ | ------------------------------ |
|
|
7198
|
+
* | user 1 | `{ id: 1, text: 'message 1' }` |
|
|
7199
|
+
* | user 1 | `{ id: 2, text: 'message 2' }` |
|
|
7200
|
+
* | user 1 | `{ id: 3, text: 'message 3' }` |
|
|
7188
7201
|
*
|
|
7189
|
-
*
|
|
7202
|
+
* ### join table
|
|
7190
7203
|
*
|
|
7191
|
-
*
|
|
7204
|
+
* If relation wasn't defined, provide a `db.table` instance and specify columns for the join.
|
|
7205
|
+
* Joined table can be references from `where` and `select` by a table name.
|
|
7192
7206
|
*
|
|
7193
7207
|
* ```ts
|
|
7194
|
-
*
|
|
7195
|
-
*
|
|
7196
|
-
*
|
|
7197
|
-
*
|
|
7198
|
-
*
|
|
7199
|
-
* lt: 5,
|
|
7200
|
-
*
|
|
7201
|
-
* // lower than the value returned by sub-query
|
|
7202
|
-
* lt: OtherTable.select('someNumber').take(),
|
|
7203
|
-
*
|
|
7204
|
-
* // raw SQL expression produces WHERE "numericColumn" < "otherColumn" + 10
|
|
7205
|
-
* lt: sql`"otherColumn" + 10`,
|
|
7206
|
-
* },
|
|
7207
|
-
* });
|
|
7208
|
+
* // Join message where userId = id:
|
|
7209
|
+
* db.user
|
|
7210
|
+
* .join(db.message, 'userId', 'id')
|
|
7211
|
+
* .where({ 'message.text': { startsWith: 'Hi' } })
|
|
7212
|
+
* .select('name', 'message.text');
|
|
7208
7213
|
* ```
|
|
7209
7214
|
*
|
|
7210
|
-
*
|
|
7211
|
-
*
|
|
7212
|
-
* `equals` is a simple `=` operator, it may be useful for comparing column value with JSON object:
|
|
7215
|
+
* Columns in the join list may be prefixed with table names for clarity:
|
|
7213
7216
|
*
|
|
7214
7217
|
* ```ts
|
|
7215
|
-
* db.
|
|
7216
|
-
* // when searching for an exact same JSON value, this won't work:
|
|
7217
|
-
* jsonColumn: someObject,
|
|
7218
|
-
*
|
|
7219
|
-
* // use `{ equals: ... }` instead:
|
|
7220
|
-
* jsonColumn: { equals: someObject },
|
|
7221
|
-
* });
|
|
7218
|
+
* db.user.join(db.message, 'message.userId', 'user.id');
|
|
7222
7219
|
* ```
|
|
7223
7220
|
*
|
|
7224
|
-
*
|
|
7221
|
+
* Joined table can have an alias for referencing it further:
|
|
7225
7222
|
*
|
|
7226
7223
|
* ```ts
|
|
7227
|
-
* db.
|
|
7228
|
-
*
|
|
7229
|
-
* })
|
|
7224
|
+
* db.user
|
|
7225
|
+
* .join(db.message.as('m'), 'message.userId', 'user.id')
|
|
7226
|
+
* .where({ 'm.text': { startsWith: 'Hi' } })
|
|
7227
|
+
* .select('name', 'm.text');
|
|
7230
7228
|
* ```
|
|
7231
7229
|
*
|
|
7232
|
-
*
|
|
7233
|
-
*
|
|
7234
|
-
* Takes an array of the same type as a column, a sub-query that returns a list of values, or a raw SQL expression that returns a list.
|
|
7230
|
+
* Joined table can be selected as an object as well as the relation join above:
|
|
7235
7231
|
*
|
|
7236
7232
|
* ```ts
|
|
7237
|
-
* db.
|
|
7238
|
-
*
|
|
7239
|
-
*
|
|
7240
|
-
*
|
|
7241
|
-
* // WHERE "column" IN (SELECT "column" FROM "otherTable")
|
|
7242
|
-
* in: OtherTable.select('column'),
|
|
7233
|
+
* const result = await db.user
|
|
7234
|
+
* .join(db.message.as('m'), 'message.userId', 'user.id')
|
|
7235
|
+
* .where({ 'm.text': { startsWith: 'Hi' } })
|
|
7236
|
+
* .select('name', { msg: 'm.*' });
|
|
7243
7237
|
*
|
|
7244
|
-
*
|
|
7245
|
-
*
|
|
7246
|
-
*
|
|
7238
|
+
* // result has the following type:
|
|
7239
|
+
* const ok: {
|
|
7240
|
+
* name: string;
|
|
7241
|
+
* // full message is included as msg:
|
|
7242
|
+
* msg: { id: number; text: string; updatedAt: Date; createdAt: Date };
|
|
7243
|
+
* }[] = result;
|
|
7247
7244
|
* ```
|
|
7248
7245
|
*
|
|
7249
|
-
*
|
|
7250
|
-
*
|
|
7251
|
-
* ### Numeric, Date, and Time column operators
|
|
7252
|
-
*
|
|
7253
|
-
* To compare numbers, dates, and times.
|
|
7254
|
-
*
|
|
7255
|
-
* `lt` is for `<` (lower than)
|
|
7256
|
-
*
|
|
7257
|
-
* `lte` is for `<=` (lower than or equal)
|
|
7258
|
-
*
|
|
7259
|
-
* `gt` is for `>` (greater than)
|
|
7260
|
-
*
|
|
7261
|
-
* `gte` is for `>=` (greater than or equal)
|
|
7246
|
+
* You can provide a custom comparison operator
|
|
7262
7247
|
*
|
|
7263
7248
|
* ```ts
|
|
7264
|
-
* db.
|
|
7265
|
-
*
|
|
7266
|
-
* gt: 5,
|
|
7267
|
-
* lt: 10,
|
|
7268
|
-
* },
|
|
7249
|
+
* db.user.join(db.message, 'userId', '!=', 'id');
|
|
7250
|
+
* ```
|
|
7269
7251
|
*
|
|
7270
|
-
*
|
|
7271
|
-
* lte: new Date(),
|
|
7272
|
-
* },
|
|
7252
|
+
* Join can accept raw SQL for the `ON` part of join:
|
|
7273
7253
|
*
|
|
7274
|
-
*
|
|
7275
|
-
*
|
|
7276
|
-
*
|
|
7277
|
-
*
|
|
7254
|
+
* ```ts
|
|
7255
|
+
* db.user.join(
|
|
7256
|
+
* db.message,
|
|
7257
|
+
* db.user.sql`lower("message"."text") = lower("user"."name")`,
|
|
7258
|
+
* );
|
|
7278
7259
|
* ```
|
|
7279
7260
|
*
|
|
7280
|
-
*
|
|
7281
|
-
*
|
|
7282
|
-
* Both elements can be of the same type as a column, a sub-query, or a raw SQL expression.
|
|
7261
|
+
* Join can accept raw SQL instead of columns:
|
|
7283
7262
|
*
|
|
7284
7263
|
* ```ts
|
|
7285
|
-
* db.
|
|
7286
|
-
*
|
|
7287
|
-
*
|
|
7288
|
-
*
|
|
7264
|
+
* db.user.join(
|
|
7265
|
+
* db.message,
|
|
7266
|
+
* db.user.sql`lower("message"."text")`,
|
|
7267
|
+
* db.user.sql`lower("user"."name")`,
|
|
7268
|
+
* );
|
|
7289
7269
|
*
|
|
7290
|
-
*
|
|
7291
|
-
*
|
|
7292
|
-
*
|
|
7293
|
-
*
|
|
7270
|
+
* // with operator:
|
|
7271
|
+
* db.user.join(
|
|
7272
|
+
* db.message,
|
|
7273
|
+
* db.user.sql`lower("message"."text")`,
|
|
7274
|
+
* '!=',
|
|
7275
|
+
* db.user.sql`lower("user"."name")`,
|
|
7276
|
+
* );
|
|
7294
7277
|
* ```
|
|
7295
7278
|
*
|
|
7296
|
-
*
|
|
7297
|
-
*
|
|
7298
|
-
* For `text`, `char`, `varchar`, and `json` columns.
|
|
7279
|
+
* To join based on multiple columns, you can provide an object where keys are joining table columns, and values are main table columns or a raw SQL:
|
|
7299
7280
|
*
|
|
7300
|
-
*
|
|
7281
|
+
* ```ts
|
|
7282
|
+
* db.user.join(db.message, {
|
|
7283
|
+
* userId: 'id',
|
|
7301
7284
|
*
|
|
7302
|
-
*
|
|
7285
|
+
* // with table names:
|
|
7286
|
+
* 'message.userId': 'user.id',
|
|
7303
7287
|
*
|
|
7304
|
-
*
|
|
7305
|
-
* db.
|
|
7306
|
-
* textColumn: {
|
|
7307
|
-
* // WHERE "textColumn" LIKE '%string%'
|
|
7308
|
-
* contains: 'string',
|
|
7309
|
-
* // WHERE "textColumn" ILIKE '%string%'
|
|
7310
|
-
* containsInsensitive: 'string',
|
|
7311
|
-
* // WHERE "textColumn" LIKE 'string%'
|
|
7312
|
-
* startsWith: 'string',
|
|
7313
|
-
* // WHERE "textColumn" ILIKE 'string%'
|
|
7314
|
-
* startsWithInsensitive: 'string',
|
|
7315
|
-
* // WHERE "textColumn" LIKE '%string'
|
|
7316
|
-
* endsWith: 'string',
|
|
7317
|
-
* // WHERE "textColumn" ILIKE '%string'
|
|
7318
|
-
* endsWithInsensitive: 'string',
|
|
7319
|
-
* },
|
|
7288
|
+
* // value can be a raw SQL expression:
|
|
7289
|
+
* text: db.user.sql`lower("user"."name")`,
|
|
7320
7290
|
* });
|
|
7321
7291
|
* ```
|
|
7322
7292
|
*
|
|
7323
|
-
*
|
|
7324
|
-
*
|
|
7325
|
-
* For the `jsonb` column, note that the `json` type has text operators instead.
|
|
7326
|
-
*
|
|
7327
|
-
* `jsonPath` operator: compare a column value under a given JSON path with the provided value.
|
|
7328
|
-
*
|
|
7329
|
-
* Value can be of any type to compare with JSON value, or it can be a sub-query or a raw SQL expression.
|
|
7293
|
+
* Join all records without conditions by providing `true`:
|
|
7330
7294
|
*
|
|
7331
7295
|
* ```ts
|
|
7332
|
-
* db.
|
|
7333
|
-
* jsonbColumn: {
|
|
7334
|
-
* jsonPath: [
|
|
7335
|
-
* '$.name', // first element is JSON path
|
|
7336
|
-
* '=', // second argument is comparison operator
|
|
7337
|
-
* 'value', // third argument is a value to compare with
|
|
7338
|
-
* ],
|
|
7339
|
-
* },
|
|
7340
|
-
* });
|
|
7296
|
+
* db.user.join(db.message, true);
|
|
7341
7297
|
* ```
|
|
7342
7298
|
*
|
|
7343
|
-
*
|
|
7344
|
-
*
|
|
7345
|
-
* For instance, it is true if the column has JSON `{ "a": 1, "b": 2 }` and provided value is `{ "a": 1 }`.
|
|
7346
|
-
*
|
|
7347
|
-
* Takes the value of any type, or sub query which returns a single value, or a raw SQL expression.
|
|
7299
|
+
* Join methods can accept a callback with a special query builder that has `on` and `orOn` methods for handling advanced cases:
|
|
7348
7300
|
*
|
|
7349
7301
|
* ```ts
|
|
7350
|
-
* db.
|
|
7351
|
-
*
|
|
7352
|
-
*
|
|
7353
|
-
*
|
|
7354
|
-
*
|
|
7355
|
-
*
|
|
7356
|
-
*
|
|
7357
|
-
*
|
|
7358
|
-
*
|
|
7359
|
-
*
|
|
7360
|
-
*
|
|
7361
|
-
*
|
|
7362
|
-
*
|
|
7363
|
-
*
|
|
7364
|
-
*
|
|
7365
|
-
*
|
|
7366
|
-
* jsonSupersetOf: { a: 1 },
|
|
7367
|
-
* },
|
|
7368
|
-
* });
|
|
7302
|
+
* db.user.join(
|
|
7303
|
+
* db.message,
|
|
7304
|
+
* (q) =>
|
|
7305
|
+
* q
|
|
7306
|
+
* // left column is the db.message column, right column is the db.user column
|
|
7307
|
+
* .on('userId', 'id')
|
|
7308
|
+
* // table names can be provided:
|
|
7309
|
+
* .on('message.userId', 'user.id')
|
|
7310
|
+
* // operator can be specified:
|
|
7311
|
+
* .on('userId', '!=', 'id')
|
|
7312
|
+
* // operator can be specified with table names as well:
|
|
7313
|
+
* .on('message.userId', '!=', 'user.id')
|
|
7314
|
+
* // `.orOn` takes the same arguments as `.on` and acts like `.or`:
|
|
7315
|
+
* .on('userId', 'id') // where message.userId = user.id
|
|
7316
|
+
* .orOn('text', 'name'), // or message.text = user.name
|
|
7317
|
+
* );
|
|
7369
7318
|
* ```
|
|
7370
7319
|
*
|
|
7371
|
-
*
|
|
7372
|
-
*/
|
|
7373
|
-
where(...args) {
|
|
7374
|
-
return _queryWhere(
|
|
7375
|
-
this.clone(),
|
|
7376
|
-
args
|
|
7377
|
-
);
|
|
7378
|
-
}
|
|
7379
|
-
/**
|
|
7380
|
-
* Use a custom SQL expression in `WHERE` statement:
|
|
7320
|
+
* Column names in the where conditions are applied for the joined table, but you can specify a table name to add a condition for the main table.
|
|
7381
7321
|
*
|
|
7382
7322
|
* ```ts
|
|
7383
|
-
* db.
|
|
7384
|
-
*
|
|
7385
|
-
*
|
|
7386
|
-
*
|
|
7323
|
+
* db.user.join(db.message, (q) =>
|
|
7324
|
+
* q
|
|
7325
|
+
* .on('userId', 'id')
|
|
7326
|
+
* .where({
|
|
7327
|
+
* // not prefixed column name is for joined table:
|
|
7328
|
+
* text: { startsWith: 'hello' },
|
|
7329
|
+
* // specify a table name to set condition on the main table:
|
|
7330
|
+
* 'user.name': 'Bob',
|
|
7331
|
+
* })
|
|
7332
|
+
* // id is a column of a joined table Message
|
|
7333
|
+
* .whereIn('id', [1, 2, 3])
|
|
7334
|
+
* // condition for id of a user
|
|
7335
|
+
* .whereIn('user.id', [4, 5, 6]),
|
|
7336
|
+
* );
|
|
7337
|
+
* ```
|
|
7387
7338
|
*
|
|
7388
|
-
*
|
|
7389
|
-
* import { raw } from 'orchid-orm';
|
|
7339
|
+
* The query above will generate the following SQL (simplified):
|
|
7390
7340
|
*
|
|
7391
|
-
*
|
|
7341
|
+
* ```sql
|
|
7342
|
+
* SELECT * FROM "user"
|
|
7343
|
+
* JOIN "message"
|
|
7344
|
+
* ON "message"."userId" = "user"."id"
|
|
7345
|
+
* AND "message"."text" ILIKE 'hello%'
|
|
7346
|
+
* AND "user"."name" = 'Bob'
|
|
7347
|
+
* AND "message"."id" IN (1, 2, 3)
|
|
7348
|
+
* AND "user"."id" IN (4, 5, 6)
|
|
7392
7349
|
* ```
|
|
7393
7350
|
*
|
|
7394
|
-
*
|
|
7395
|
-
*/
|
|
7396
|
-
whereSql(...args) {
|
|
7397
|
-
return _queryWhereSql(
|
|
7398
|
-
this.clone(),
|
|
7399
|
-
args
|
|
7400
|
-
);
|
|
7401
|
-
}
|
|
7402
|
-
/**
|
|
7403
|
-
* `whereNot` takes the same argument as `where`,
|
|
7404
|
-
* multiple conditions are combined with `AND`,
|
|
7405
|
-
* the whole group of conditions is negated with `NOT`.
|
|
7351
|
+
* The join argument can be a query with `select`, `where`, and other methods. In such case, it will be handled as a sub query:
|
|
7406
7352
|
*
|
|
7407
7353
|
* ```ts
|
|
7408
|
-
*
|
|
7409
|
-
*
|
|
7410
|
-
*
|
|
7411
|
-
*
|
|
7412
|
-
*
|
|
7354
|
+
* db.user.join(
|
|
7355
|
+
* db.message
|
|
7356
|
+
* .select('id', 'userId', 'text')
|
|
7357
|
+
* .where({ text: { startsWith: 'Hi' } })
|
|
7358
|
+
* .as('t'),
|
|
7359
|
+
* 'userId',
|
|
7360
|
+
* 'id',
|
|
7361
|
+
* );
|
|
7413
7362
|
* ```
|
|
7414
7363
|
*
|
|
7415
|
-
*
|
|
7416
|
-
*/
|
|
7417
|
-
whereNot(...args) {
|
|
7418
|
-
return _queryWhereNot(
|
|
7419
|
-
this.clone(),
|
|
7420
|
-
args
|
|
7421
|
-
);
|
|
7422
|
-
}
|
|
7423
|
-
/**
|
|
7424
|
-
* `whereNot` version accepting SQL expression:
|
|
7364
|
+
* It will produce such SQL:
|
|
7425
7365
|
*
|
|
7426
|
-
* ```
|
|
7427
|
-
*
|
|
7366
|
+
* ```sql
|
|
7367
|
+
* SELECT * FROM "user"
|
|
7368
|
+
* JOIN (
|
|
7369
|
+
* SELECT "t"."id", "t"."userId", "t"."text"
|
|
7370
|
+
* FROM "message" AS "t"
|
|
7371
|
+
* ) "t" ON "t"."userId" = "user"."id"
|
|
7428
7372
|
* ```
|
|
7429
7373
|
*
|
|
7430
|
-
*
|
|
7431
|
-
*/
|
|
7432
|
-
whereNotSql(...args) {
|
|
7433
|
-
return _queryWhereNotSql(this.clone(), args);
|
|
7434
|
-
}
|
|
7435
|
-
/**
|
|
7436
|
-
* `orWhere` is accepting the same arguments as {@link where}, joining arguments with `OR`.
|
|
7374
|
+
* ## implicit join lateral
|
|
7437
7375
|
*
|
|
7438
|
-
*
|
|
7376
|
+
* `JOIN`'s source expression that comes before `ON` cannot access other tables, but in some cases this may be needed.
|
|
7439
7377
|
*
|
|
7440
|
-
*
|
|
7378
|
+
* For example, let's consider joining last 10 messages of a user:
|
|
7441
7379
|
*
|
|
7442
7380
|
* ```ts
|
|
7443
|
-
* db.
|
|
7444
|
-
* // equivalent:
|
|
7445
|
-
* db.table.orWhere({ id: 1, color: 'red' }, { id: 2, color: 'blue' });
|
|
7381
|
+
* await db.user.join('messages', (q) => q.order({ createdAt: 'DESC' }).limit(10));
|
|
7446
7382
|
* ```
|
|
7447
7383
|
*
|
|
7448
|
-
*
|
|
7384
|
+
* When the `join`'s callback returns a more complex query than the one that simply applies certain conditions,
|
|
7385
|
+
* it will implicitly generate a `JOIN LATERAL` SQL query, as the following:
|
|
7449
7386
|
*
|
|
7450
7387
|
* ```sql
|
|
7451
|
-
* SELECT *
|
|
7452
|
-
*
|
|
7453
|
-
*
|
|
7388
|
+
* SELECT *
|
|
7389
|
+
* FROM "user"
|
|
7390
|
+
* JOIN LATERAL (
|
|
7391
|
+
* SELECT *
|
|
7392
|
+
* FROM "message" AS "messages"
|
|
7393
|
+
* WHERE "message"."userId" = "user"."id"
|
|
7394
|
+
* ORDER BY "message"."createdAt" DESC
|
|
7395
|
+
* LIMIT 10
|
|
7396
|
+
* ) "messages" ON true
|
|
7454
7397
|
* ```
|
|
7455
7398
|
*
|
|
7456
|
-
* @param
|
|
7457
|
-
|
|
7458
|
-
orWhere(...args) {
|
|
7459
|
-
return _queryOr(this.clone(), args);
|
|
7460
|
-
}
|
|
7461
|
-
/**
|
|
7462
|
-
* `orWhereNot` takes the same arguments as {@link orWhere}, and prepends each condition with `NOT` just as {@link whereNot} does.
|
|
7463
|
-
*
|
|
7464
|
-
* @param args - {@link WhereArgs} will be prefixed with `NOT` and joined with `OR`
|
|
7399
|
+
* @param arg - {@link JoinFirstArg}
|
|
7400
|
+
* @param args - {@link JoinArgs}
|
|
7465
7401
|
*/
|
|
7466
|
-
|
|
7467
|
-
return
|
|
7402
|
+
join(arg, ...args) {
|
|
7403
|
+
return _join(
|
|
7468
7404
|
this.clone(),
|
|
7405
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7406
|
+
true,
|
|
7407
|
+
"JOIN",
|
|
7408
|
+
arg,
|
|
7469
7409
|
args
|
|
7410
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7470
7411
|
);
|
|
7471
7412
|
}
|
|
7472
7413
|
/**
|
|
7473
|
-
* `
|
|
7474
|
-
*
|
|
7475
|
-
* When used with a single column it works equivalent to the `in` column operator:
|
|
7414
|
+
* `leftJoin` is a method for SQL `LEFT JOIN`, which is equivalent to `OUTER JOIN`, `LEFT OUTER JOIN`.
|
|
7476
7415
|
*
|
|
7477
|
-
*
|
|
7478
|
-
* db.table.whereIn('column', [1, 2, 3]);
|
|
7479
|
-
* // the same as:
|
|
7480
|
-
* db.table.where({ column: [1, 2, 3] });
|
|
7481
|
-
* ```
|
|
7416
|
+
* When no matching record is found, it will fill joined table columns with `NULL` values in the result rows.
|
|
7482
7417
|
*
|
|
7483
|
-
*
|
|
7418
|
+
* Works just like `join`, except for result type that may have `null`:
|
|
7484
7419
|
*
|
|
7485
7420
|
* ```ts
|
|
7486
|
-
* db.
|
|
7487
|
-
*
|
|
7488
|
-
*
|
|
7489
|
-
* [1, 'Alice'],
|
|
7490
|
-
* [2, 'Bob'],
|
|
7491
|
-
* ],
|
|
7492
|
-
* );
|
|
7493
|
-
* ```
|
|
7421
|
+
* const result = await db.user
|
|
7422
|
+
* .leftJoin('messages')
|
|
7423
|
+
* .select('name', 'messages.text');
|
|
7494
7424
|
*
|
|
7495
|
-
*
|
|
7425
|
+
* // the same query, but joining table explicitly
|
|
7426
|
+
* const result2: typeof result = await db.user
|
|
7427
|
+
* .leftJoin(db.message, 'userId', 'id')
|
|
7428
|
+
* .select('name', 'message.text');
|
|
7496
7429
|
*
|
|
7497
|
-
*
|
|
7498
|
-
*
|
|
7430
|
+
* // result has the following type:
|
|
7431
|
+
* const ok: { name: string; text: string | null }[] = result;
|
|
7499
7432
|
* ```
|
|
7500
7433
|
*
|
|
7501
|
-
*
|
|
7502
|
-
*
|
|
7503
|
-
* ```ts
|
|
7504
|
-
* db.table.whereIn(['id', 'name'], db.table.sql`((1, 'one'), (2, 'two'))`);
|
|
7505
|
-
* ```
|
|
7434
|
+
* @param arg - {@link JoinFirstArg}
|
|
7435
|
+
* @param args - {@link JoinArgs}
|
|
7506
7436
|
*/
|
|
7507
|
-
|
|
7508
|
-
return
|
|
7437
|
+
leftJoin(arg, ...args) {
|
|
7438
|
+
return _join(
|
|
7509
7439
|
this.clone(),
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7440
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7441
|
+
false,
|
|
7442
|
+
"LEFT JOIN",
|
|
7443
|
+
arg,
|
|
7444
|
+
args
|
|
7445
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7513
7446
|
);
|
|
7514
7447
|
}
|
|
7515
7448
|
/**
|
|
7516
|
-
*
|
|
7517
|
-
*
|
|
7449
|
+
* `rightJoin` is a method for SQL `RIGHT JOIN`, which is equivalent to `RIGHT OUTER JOIN`.
|
|
7450
|
+
*
|
|
7451
|
+
* Takes the same arguments as `json`.
|
|
7452
|
+
*
|
|
7453
|
+
* It will load all records from the joining table, and fill the main table columns with `null` when no match is found.
|
|
7454
|
+
*
|
|
7455
|
+
* The columns of the table you're joining to are becoming nullable when using `rightJoin`.
|
|
7518
7456
|
*
|
|
7519
7457
|
* ```ts
|
|
7520
|
-
*
|
|
7458
|
+
* const result = await db.user
|
|
7459
|
+
* .rightJoin('messages')
|
|
7460
|
+
* .select('name', 'messages.text');
|
|
7461
|
+
*
|
|
7462
|
+
* // even though name is not a nullable column, it becomes nullable after using rightJoin
|
|
7463
|
+
* const ok: { name: string | null; text: string }[] = result;
|
|
7521
7464
|
* ```
|
|
7465
|
+
*
|
|
7466
|
+
* @param arg - {@link JoinFirstArg}
|
|
7467
|
+
* @param args - {@link JoinArgs}
|
|
7522
7468
|
*/
|
|
7523
|
-
|
|
7524
|
-
return
|
|
7469
|
+
rightJoin(arg, ...args) {
|
|
7470
|
+
return _join(
|
|
7525
7471
|
this.clone(),
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7472
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7473
|
+
true,
|
|
7474
|
+
"RIGHT JOIN",
|
|
7475
|
+
arg,
|
|
7476
|
+
args
|
|
7477
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7529
7478
|
);
|
|
7530
7479
|
}
|
|
7531
7480
|
/**
|
|
7532
|
-
*
|
|
7481
|
+
* `fullJoin` is a method for SQL `FULL JOIN`, which is equivalent to `FULL OUTER JOIN`.
|
|
7482
|
+
*
|
|
7483
|
+
* Takes the same arguments as `json`.
|
|
7484
|
+
*
|
|
7485
|
+
* It will load all records from the joining table, both sides of the join may result in `null` values when there is no match.
|
|
7486
|
+
*
|
|
7487
|
+
* All columns become nullable after using `fullJoin`.
|
|
7533
7488
|
*
|
|
7534
7489
|
* ```ts
|
|
7535
|
-
*
|
|
7490
|
+
* const result = await db.user
|
|
7491
|
+
* .rightJoin('messages')
|
|
7492
|
+
* .select('name', 'messages.text');
|
|
7493
|
+
*
|
|
7494
|
+
* // all columns can be null
|
|
7495
|
+
* const ok: { name: string | null; text: string | null }[] = result;
|
|
7536
7496
|
* ```
|
|
7497
|
+
*
|
|
7498
|
+
* @param arg - {@link JoinFirstArg}
|
|
7499
|
+
* @param args - {@link JoinArgs}
|
|
7537
7500
|
*/
|
|
7538
|
-
|
|
7539
|
-
return
|
|
7501
|
+
fullJoin(arg, ...args) {
|
|
7502
|
+
return _join(
|
|
7540
7503
|
this.clone(),
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7504
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7505
|
+
false,
|
|
7506
|
+
"FULL JOIN",
|
|
7507
|
+
arg,
|
|
7508
|
+
args
|
|
7509
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7545
7510
|
);
|
|
7546
7511
|
}
|
|
7547
7512
|
/**
|
|
7548
|
-
*
|
|
7513
|
+
* `joinLateral` allows joining a table with a sub-query that can reference the main table of current query and the other joined tables.
|
|
7549
7514
|
*
|
|
7550
|
-
*
|
|
7551
|
-
* db.table.whereNotIn('a', [1, 2, 3]).orWhereNoIn('b', ['one', 'two']);
|
|
7552
|
-
* ```
|
|
7553
|
-
*/
|
|
7554
|
-
orWhereNotIn(...args) {
|
|
7555
|
-
return _queryWhereIn(
|
|
7556
|
-
this.clone(),
|
|
7557
|
-
false,
|
|
7558
|
-
args[0],
|
|
7559
|
-
args[1],
|
|
7560
|
-
true
|
|
7561
|
-
);
|
|
7562
|
-
}
|
|
7563
|
-
/**
|
|
7564
|
-
* `whereExists` is for support of the `WHERE EXISTS (query)` clause.
|
|
7515
|
+
* First argument is the other table you want to join, or a name of relation, or a name of `with` defined table.
|
|
7565
7516
|
*
|
|
7566
|
-
*
|
|
7517
|
+
* Second argument is a callback where you can reference other tables using `on` and `orOn`, select columns, do `where` conditions, and use any other query methods to build a sub-query.
|
|
7518
|
+
*
|
|
7519
|
+
* Note that the regular `join` will also generate `JOIN LATERAL` SQL expression when the query returned from callback is complex enough (see the bottom of {@link join} description).
|
|
7567
7520
|
*
|
|
7568
7521
|
* ```ts
|
|
7569
|
-
* //
|
|
7570
|
-
* //
|
|
7571
|
-
*
|
|
7522
|
+
* // joinLateral a Message table, alias it as `m`
|
|
7523
|
+
* // without aliasing you can refer to the message by a table name
|
|
7524
|
+
* User.joinLateral(Message.as('m'), (q) =>
|
|
7525
|
+
* q
|
|
7526
|
+
* // select message columns
|
|
7527
|
+
* .select('text')
|
|
7528
|
+
* // join the message to the user, column names can be prefixed with table names
|
|
7529
|
+
* .on('authorId', 'id')
|
|
7530
|
+
* // message columns are available without prefixing,
|
|
7531
|
+
* // outer table columns are available with a table name
|
|
7532
|
+
* .where({ text: 'some text', 'user.name': 'name' })
|
|
7533
|
+
* .order({ createdAt: 'DESC' }),
|
|
7534
|
+
* )
|
|
7535
|
+
* // only selected message columns are available in select and where
|
|
7536
|
+
* .select('id', 'name', 'm.text')
|
|
7537
|
+
* .where({ 'm.text': messageData.text });
|
|
7538
|
+
* ```
|
|
7572
7539
|
*
|
|
7573
|
-
*
|
|
7574
|
-
* db.user.whereExists(db.account, 'account.id', 'user.id');
|
|
7540
|
+
* As well as simple `join`, `joinLateral` can select an object of full joined record:
|
|
7575
7541
|
*
|
|
7576
|
-
*
|
|
7577
|
-
*
|
|
7542
|
+
* ```ts
|
|
7543
|
+
* // join by relation name
|
|
7544
|
+
* const result = await User.joinLateral(
|
|
7545
|
+
* 'messages',
|
|
7546
|
+
* (q) => q.as('message'), // alias to 'message'
|
|
7547
|
+
* ).select('name', { message: 'message.*' });
|
|
7548
|
+
*
|
|
7549
|
+
* // result has the following type:
|
|
7550
|
+
* const ok: {
|
|
7551
|
+
* name: string;
|
|
7552
|
+
* // full message is included:
|
|
7553
|
+
* message: { id: number; text: string; updatedAt: Date; createdAt: Date };
|
|
7554
|
+
* }[] = result;
|
|
7555
|
+
* ```
|
|
7556
|
+
*
|
|
7557
|
+
* `message` can be aliased withing the `select` as well as in case of a simple `join`:
|
|
7558
|
+
*
|
|
7559
|
+
* ```ts
|
|
7560
|
+
* // join by relation name
|
|
7561
|
+
* const result = await User.joinLateral(
|
|
7562
|
+
* 'messages',
|
|
7563
|
+
* (q) => q.as('message'), // alias to 'message'
|
|
7564
|
+
* ).select('name', { msg: 'message.*' });
|
|
7565
|
+
*
|
|
7566
|
+
* // result has the following type:
|
|
7567
|
+
* const ok: {
|
|
7568
|
+
* name: string;
|
|
7569
|
+
* // full message is included as msg:
|
|
7570
|
+
* msg: { id: number; text: string; updatedAt: Date; createdAt: Date };
|
|
7571
|
+
* }[] = result;
|
|
7578
7572
|
* ```
|
|
7573
|
+
*
|
|
7574
|
+
* @param arg - {@link JoinFirstArg}
|
|
7575
|
+
* @param cb - {@link JoinLateralCallback}
|
|
7579
7576
|
*/
|
|
7580
|
-
|
|
7581
|
-
return
|
|
7577
|
+
joinLateral(arg, cb) {
|
|
7578
|
+
return _joinLateral(
|
|
7582
7579
|
this.clone(),
|
|
7583
|
-
|
|
7580
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7581
|
+
"JOIN",
|
|
7582
|
+
arg,
|
|
7583
|
+
cb
|
|
7584
7584
|
);
|
|
7585
7585
|
}
|
|
7586
7586
|
/**
|
|
7587
|
-
*
|
|
7587
|
+
* The same as {@link joinLateral}, but when no records found for the join it will result in `null`:
|
|
7588
7588
|
*
|
|
7589
7589
|
* ```ts
|
|
7590
|
-
*
|
|
7591
|
-
*
|
|
7592
|
-
*
|
|
7590
|
+
* const result = await db.user
|
|
7591
|
+
* .leftJoinLateral('messages', (q) => q.as('message'))
|
|
7592
|
+
* .select('name', 'message.text');
|
|
7593
|
+
*
|
|
7594
|
+
* // result has the following type:
|
|
7595
|
+
* const ok: { name: string; text: string | null }[] = result;
|
|
7593
7596
|
* ```
|
|
7597
|
+
*
|
|
7598
|
+
* @param arg - {@link JoinFirstArg}
|
|
7599
|
+
* @param cb - {@link JoinLateralCallback}
|
|
7594
7600
|
*/
|
|
7595
|
-
|
|
7596
|
-
return
|
|
7601
|
+
leftJoinLateral(arg, cb) {
|
|
7602
|
+
return _joinLateral(
|
|
7597
7603
|
this.clone(),
|
|
7598
|
-
|
|
7604
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7605
|
+
"LEFT JOIN",
|
|
7606
|
+
arg,
|
|
7607
|
+
cb
|
|
7599
7608
|
);
|
|
7600
7609
|
}
|
|
7610
|
+
}
|
|
7611
|
+
const makeOnItem = (joinTo, joinFrom, args) => {
|
|
7612
|
+
return {
|
|
7613
|
+
ON: {
|
|
7614
|
+
joinTo,
|
|
7615
|
+
joinFrom,
|
|
7616
|
+
on: args
|
|
7617
|
+
}
|
|
7618
|
+
};
|
|
7619
|
+
};
|
|
7620
|
+
const pushQueryOn = (q, joinFrom, joinTo, ...on) => {
|
|
7621
|
+
return pushQueryValue(
|
|
7622
|
+
q,
|
|
7623
|
+
"and",
|
|
7624
|
+
makeOnItem(joinFrom, joinTo, on)
|
|
7625
|
+
);
|
|
7626
|
+
};
|
|
7627
|
+
const pushQueryOrOn = (q, joinFrom, joinTo, ...on) => {
|
|
7628
|
+
return pushQueryValue(q, "or", [
|
|
7629
|
+
makeOnItem(joinFrom, joinTo, on)
|
|
7630
|
+
]);
|
|
7631
|
+
};
|
|
7632
|
+
const addQueryOn = (q, joinFrom, joinTo, ...args) => {
|
|
7633
|
+
const cloned = q.clone();
|
|
7634
|
+
setQueryObjectValue(
|
|
7635
|
+
cloned,
|
|
7636
|
+
"joinedShapes",
|
|
7637
|
+
joinFrom.q.as || joinFrom.table,
|
|
7638
|
+
joinFrom.q.shape
|
|
7639
|
+
);
|
|
7640
|
+
return pushQueryOn(cloned, joinFrom, joinTo, ...args);
|
|
7641
|
+
};
|
|
7642
|
+
const _queryJoinOn = (q, args) => {
|
|
7643
|
+
return pushQueryOn(
|
|
7644
|
+
q,
|
|
7645
|
+
q.q.joinTo,
|
|
7646
|
+
q,
|
|
7647
|
+
...args
|
|
7648
|
+
);
|
|
7649
|
+
};
|
|
7650
|
+
const _queryJoinOrOn = (q, args) => {
|
|
7651
|
+
return pushQueryOrOn(
|
|
7652
|
+
q,
|
|
7653
|
+
q.q.joinTo,
|
|
7654
|
+
q,
|
|
7655
|
+
...args
|
|
7656
|
+
);
|
|
7657
|
+
};
|
|
7658
|
+
const _queryJoinOnJsonPathEquals = (q, args) => {
|
|
7659
|
+
return pushQueryValue(q, "and", {
|
|
7660
|
+
ON: args
|
|
7661
|
+
});
|
|
7662
|
+
};
|
|
7663
|
+
class OnMethods {
|
|
7601
7664
|
/**
|
|
7602
|
-
*
|
|
7665
|
+
* Use `on` to specify columns to join records.
|
|
7603
7666
|
*
|
|
7604
7667
|
* ```ts
|
|
7605
|
-
*
|
|
7606
|
-
*
|
|
7607
|
-
*
|
|
7668
|
+
* q
|
|
7669
|
+
* // left column is the db.message column, right column is the db.user column
|
|
7670
|
+
* .on('userId', 'id')
|
|
7671
|
+
* // table names can be provided:
|
|
7672
|
+
* .on('message.userId', 'user.id')
|
|
7673
|
+
* // operator can be specified:
|
|
7674
|
+
* .on('userId', '!=', 'id')
|
|
7675
|
+
* // operator can be specified with table names as well:
|
|
7676
|
+
* .on('message.userId', '!=', 'user.id')
|
|
7677
|
+
* // `.orOn` takes the same arguments as `.on` and acts like `.or`:
|
|
7678
|
+
* .on('userId', 'id') // where message.userId = user.id
|
|
7608
7679
|
* ```
|
|
7609
7680
|
*
|
|
7610
|
-
* @param
|
|
7611
|
-
* @param args - no arguments needed when the first argument is a relation name, or conditions to join the table with.
|
|
7681
|
+
* @param args - columns to join with
|
|
7612
7682
|
*/
|
|
7613
|
-
|
|
7614
|
-
return
|
|
7615
|
-
this.clone(),
|
|
7616
|
-
existsArgs(arg, args)
|
|
7617
|
-
);
|
|
7683
|
+
on(...args) {
|
|
7684
|
+
return _queryJoinOn(this.clone(), args);
|
|
7618
7685
|
}
|
|
7619
7686
|
/**
|
|
7620
|
-
*
|
|
7687
|
+
* Works as {@link on}, but the added conditions will be separated from previous with `OR`.
|
|
7688
|
+
*
|
|
7689
|
+
* @param args - columns to join with
|
|
7690
|
+
*/
|
|
7691
|
+
orOn(...args) {
|
|
7692
|
+
return _queryJoinOrOn(this.clone(), args);
|
|
7693
|
+
}
|
|
7694
|
+
/**
|
|
7695
|
+
* Use `onJsonPathEquals` to join record based on a field of their JSON column:
|
|
7621
7696
|
*
|
|
7622
7697
|
* ```ts
|
|
7623
|
-
*
|
|
7624
|
-
*
|
|
7625
|
-
*
|
|
7698
|
+
* db.table.join(db.otherTable, (q) =>
|
|
7699
|
+
* // '$.key' is a JSON path
|
|
7700
|
+
* q.onJsonPathEquals('otherTable.data', '$.key', 'table.data', '$.key'),
|
|
7701
|
+
* );
|
|
7626
7702
|
* ```
|
|
7703
|
+
*
|
|
7704
|
+
* @param args - columns and JSON paths to join with.
|
|
7627
7705
|
*/
|
|
7628
|
-
|
|
7629
|
-
return
|
|
7630
|
-
this.clone(),
|
|
7631
|
-
existsArgs(arg, args)
|
|
7632
|
-
);
|
|
7706
|
+
onJsonPathEquals(...args) {
|
|
7707
|
+
return _queryJoinOnJsonPathEquals(this.clone(), args);
|
|
7633
7708
|
}
|
|
7634
7709
|
}
|
|
7635
|
-
class WhereQueryBase extends QueryBase {
|
|
7636
|
-
}
|
|
7637
|
-
orchidCore.applyMixins(WhereQueryBase, [Where]);
|
|
7638
7710
|
|
|
7639
|
-
class
|
|
7711
|
+
class JsonModifiers {
|
|
7640
7712
|
/**
|
|
7641
|
-
*
|
|
7642
|
-
*
|
|
7643
|
-
* Before joining a table, consider if selecting a relation is enough for your case:
|
|
7644
|
-
*
|
|
7645
|
-
* ```ts
|
|
7646
|
-
* // select users with profiles
|
|
7647
|
-
* // result type is Array<{ name: string, profile: Profile }>
|
|
7648
|
-
* await db.user.select('name', {
|
|
7649
|
-
* profile: (q) => q.profile,
|
|
7650
|
-
* });
|
|
7651
|
-
*
|
|
7652
|
-
* // select posts with counts of comments, order by comments count
|
|
7653
|
-
* // result type is Array<Post & { commentsCount: number }>
|
|
7654
|
-
* await db.post
|
|
7655
|
-
* .select('*', {
|
|
7656
|
-
* commentsCount: (q) => q.comments.count(),
|
|
7657
|
-
* })
|
|
7658
|
-
* .order({
|
|
7659
|
-
* commentsCount: 'DESC',
|
|
7660
|
-
* });
|
|
7661
|
-
*
|
|
7662
|
-
* // select authors with array of their book titles
|
|
7663
|
-
* // result type is Array<Author & { books: string[] }>
|
|
7664
|
-
* await db.author.select('*', {
|
|
7665
|
-
* books: (q) => q.books.pluck('title'),
|
|
7666
|
-
* });
|
|
7667
|
-
* ```
|
|
7713
|
+
* Return a JSON value/object/array where a given value is set at the given path.
|
|
7714
|
+
* The path is an array of keys to access the value.
|
|
7668
7715
|
*
|
|
7669
|
-
*
|
|
7670
|
-
* If you're loading users with profiles (one-to-one relation), and some users don't have a profile, `profile` property will have `NULL` for such users.
|
|
7671
|
-
* If you want to load only users that have profiles, and filter out the rest, add `.join()` method to the relation without arguments:
|
|
7716
|
+
* Can be used in `update` callback.
|
|
7672
7717
|
*
|
|
7673
7718
|
* ```ts
|
|
7674
|
-
*
|
|
7675
|
-
* await db.user.select('*', {
|
|
7676
|
-
* profile: (q) => q.profile.join(),
|
|
7677
|
-
* });
|
|
7719
|
+
* const result = await db.table.jsonSet('data', ['name'], 'new value').take();
|
|
7678
7720
|
*
|
|
7679
|
-
*
|
|
7680
|
-
* await db.user.select('*', {
|
|
7681
|
-
* profile: (q) => q.profile.join().where({ age: { gt: 20 } }),
|
|
7682
|
-
* });
|
|
7721
|
+
* expect(result.data).toEqual({ name: 'new value' });
|
|
7683
7722
|
* ```
|
|
7684
7723
|
*
|
|
7685
|
-
*
|
|
7724
|
+
* Optionally takes parameters of type `{ as?: string, createIfMissing?: boolean }`
|
|
7686
7725
|
*
|
|
7687
7726
|
* ```ts
|
|
7688
|
-
*
|
|
7689
|
-
* //
|
|
7690
|
-
*
|
|
7691
|
-
* tags: (q) => q.tags.join(),
|
|
7727
|
+
* await db.table.jsonSet('data', ['name'], 'new value', {
|
|
7728
|
+
* as: 'alias', // select data as `alias`
|
|
7729
|
+
* createIfMissing: true, // ignored if missing by default
|
|
7692
7730
|
* });
|
|
7693
7731
|
* ```
|
|
7694
7732
|
*
|
|
7695
|
-
*
|
|
7696
|
-
*
|
|
7697
|
-
*
|
|
7698
|
-
*
|
|
7699
|
-
|
|
7700
|
-
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
|
|
7733
|
+
* @param column - name of JSON column, or a result of a nested json method
|
|
7734
|
+
* @param path - path to value inside the json
|
|
7735
|
+
* @param value - value to set into the json
|
|
7736
|
+
* @param options - `as` to alias the json value when selecting, `createIfMissing: true` will create a new JSON property if it didn't exist before
|
|
7737
|
+
*/
|
|
7738
|
+
jsonSet(column, path, value, options) {
|
|
7739
|
+
var _a;
|
|
7740
|
+
const q = this.clone();
|
|
7741
|
+
const json = {
|
|
7742
|
+
__json: [
|
|
7743
|
+
"set",
|
|
7744
|
+
(_a = options == null ? void 0 : options.as) != null ? _a : typeof column === "string" ? column : column.__json[1],
|
|
7745
|
+
typeof column === "string" ? q.q.shape[column] : column.__json[2],
|
|
7746
|
+
column,
|
|
7747
|
+
path,
|
|
7748
|
+
value,
|
|
7749
|
+
options
|
|
7750
|
+
]
|
|
7751
|
+
};
|
|
7752
|
+
return Object.assign(
|
|
7753
|
+
pushQueryValue(q, "select", json),
|
|
7754
|
+
json
|
|
7755
|
+
);
|
|
7756
|
+
}
|
|
7757
|
+
/**
|
|
7758
|
+
* Return a JSON value/object/array where a given value is inserted at the given JSON path. Value can be a single value or JSON object. If a value exists at the given path, the value is not replaced.
|
|
7710
7759
|
*
|
|
7711
|
-
*
|
|
7712
|
-
* messages: this.hasMany(() => MessageTable, {
|
|
7713
|
-
* primaryKey: 'id',
|
|
7714
|
-
* foreignKey: 'userId',
|
|
7715
|
-
* }),
|
|
7716
|
-
* };
|
|
7717
|
-
* }
|
|
7760
|
+
* Can be used in `update` callback.
|
|
7718
7761
|
*
|
|
7719
|
-
*
|
|
7720
|
-
*
|
|
7721
|
-
*
|
|
7722
|
-
* id: t.identity().primaryKey(),
|
|
7723
|
-
* text: t.text(),
|
|
7724
|
-
* ...t.timestamps(),
|
|
7725
|
-
* }));
|
|
7762
|
+
* ```ts
|
|
7763
|
+
* // imagine user has data = { tags: ['two'] }
|
|
7764
|
+
* const result = await db.table.jsonInsert('data', ['tags', 0], 'one').take();
|
|
7726
7765
|
*
|
|
7727
|
-
*
|
|
7728
|
-
*
|
|
7729
|
-
* primaryKey: 'id',
|
|
7730
|
-
* foreignKey: 'userId',
|
|
7731
|
-
* }),
|
|
7732
|
-
* };
|
|
7733
|
-
* }
|
|
7766
|
+
* // 'one' is inserted to 0 position
|
|
7767
|
+
* expect(result.data).toEqual({ tags: ['one', 'two'] });
|
|
7734
7768
|
* ```
|
|
7735
7769
|
*
|
|
7736
|
-
*
|
|
7737
|
-
*
|
|
7738
|
-
* `join` is a method for SQL `JOIN`, which is equivalent to `INNER JOIN`, `LEFT INNERT JOIN`.
|
|
7739
|
-
*
|
|
7740
|
-
* When no matching record is found, it will skip records of the main table.
|
|
7741
|
-
*
|
|
7742
|
-
* When joining the same table with the same condition more than once, duplicated joins will be ignored:
|
|
7770
|
+
* Optionally takes parameters of type `{ as?: string, insertAfter?: boolean }`
|
|
7743
7771
|
*
|
|
7744
7772
|
* ```ts
|
|
7745
|
-
* //
|
|
7746
|
-
* db.
|
|
7747
|
-
*
|
|
7748
|
-
* //
|
|
7749
|
-
*
|
|
7750
|
-
*
|
|
7751
|
-
* .
|
|
7752
|
-
* ```
|
|
7753
|
-
*
|
|
7754
|
-
* Both queries will produce SQL with only 1 join
|
|
7773
|
+
* // imagine user has data = { tags: ['one'] }
|
|
7774
|
+
* const result = await db.table
|
|
7775
|
+
* .jsonInsert('data', ['tags', 0], 'two', {
|
|
7776
|
+
* as: 'alias', // select as an alias
|
|
7777
|
+
* insertAfter: true, // insert after the specified position
|
|
7778
|
+
* })
|
|
7779
|
+
* .take();
|
|
7755
7780
|
*
|
|
7756
|
-
*
|
|
7757
|
-
*
|
|
7781
|
+
* // 'one' is inserted to 0 position
|
|
7782
|
+
* expect(result.alias).toEqual({ tags: ['one', 'two'] });
|
|
7758
7783
|
* ```
|
|
7784
|
+
* @param column - name of JSON column, or a result of a nested json method
|
|
7785
|
+
* @param path - path to the array inside the json, last path element is index to insert into
|
|
7786
|
+
* @param value - value to insert into the json array
|
|
7787
|
+
* @param options - `as` to alias the json value when selecting, `insertAfter: true` to insert after the specified position
|
|
7788
|
+
*/
|
|
7789
|
+
jsonInsert(column, path, value, options) {
|
|
7790
|
+
var _a;
|
|
7791
|
+
const q = this.clone();
|
|
7792
|
+
const json = {
|
|
7793
|
+
__json: [
|
|
7794
|
+
"insert",
|
|
7795
|
+
(_a = options == null ? void 0 : options.as) != null ? _a : typeof column === "string" ? column : column.__json[1],
|
|
7796
|
+
typeof column === "string" ? q.q.shape[column] : column.__json[2],
|
|
7797
|
+
column,
|
|
7798
|
+
path,
|
|
7799
|
+
value,
|
|
7800
|
+
options
|
|
7801
|
+
]
|
|
7802
|
+
};
|
|
7803
|
+
return Object.assign(
|
|
7804
|
+
pushQueryValue(q, "select", json),
|
|
7805
|
+
json
|
|
7806
|
+
);
|
|
7807
|
+
}
|
|
7808
|
+
/**
|
|
7809
|
+
* Return a JSON value/object/array where a given value is removed at the given JSON path.
|
|
7759
7810
|
*
|
|
7760
|
-
*
|
|
7811
|
+
* Can be used in `update` callback.
|
|
7761
7812
|
*
|
|
7762
7813
|
* ```ts
|
|
7763
|
-
*
|
|
7764
|
-
*
|
|
7765
|
-
* .
|
|
7766
|
-
*
|
|
7767
|
-
*
|
|
7768
|
-
*
|
|
7769
|
-
*
|
|
7814
|
+
* // imagine a user has data = { tags: ['one', 'two'] }
|
|
7815
|
+
* const result = await db.table
|
|
7816
|
+
* .jsonRemove(
|
|
7817
|
+
* 'data',
|
|
7818
|
+
* ['tags', 0],
|
|
7819
|
+
* // optional parameters:
|
|
7820
|
+
* {
|
|
7821
|
+
* as: 'alias', // select as an alias
|
|
7822
|
+
* },
|
|
7823
|
+
* )
|
|
7824
|
+
* .take();
|
|
7770
7825
|
*
|
|
7771
|
-
*
|
|
7826
|
+
* expect(result.alias).toEqual({ tags: ['two'] });
|
|
7827
|
+
* ```
|
|
7772
7828
|
*
|
|
7773
|
-
*
|
|
7774
|
-
*
|
|
7829
|
+
* @param column - name of JSON column, or a result of a nested json method
|
|
7830
|
+
* @param path - path to the array inside the json, last path element is index to remove this element
|
|
7831
|
+
* @param options - `as` to alias the json value when selecting
|
|
7832
|
+
*/
|
|
7833
|
+
jsonRemove(column, path, options) {
|
|
7834
|
+
var _a;
|
|
7835
|
+
const q = this.clone();
|
|
7836
|
+
const json = {
|
|
7837
|
+
__json: [
|
|
7838
|
+
"remove",
|
|
7839
|
+
(_a = options == null ? void 0 : options.as) != null ? _a : typeof column === "string" ? column : column.__json[1],
|
|
7840
|
+
typeof column === "string" ? q.q.shape[column] : column.__json[2],
|
|
7841
|
+
column,
|
|
7842
|
+
path
|
|
7843
|
+
]
|
|
7844
|
+
};
|
|
7845
|
+
return Object.assign(
|
|
7846
|
+
pushQueryValue(q, "select", json),
|
|
7847
|
+
json
|
|
7848
|
+
);
|
|
7849
|
+
}
|
|
7850
|
+
/**
|
|
7851
|
+
* Selects a value from JSON data using a JSON path.
|
|
7775
7852
|
*
|
|
7776
7853
|
* ```ts
|
|
7777
|
-
*
|
|
7778
|
-
* .join('messages')
|
|
7779
|
-
* // after joining a table, we can use it in `where` conditions:
|
|
7780
|
-
* .where({ 'messages.text': { startsWith: 'Hi' } })
|
|
7781
|
-
* .select(
|
|
7782
|
-
* 'name', // name is User column, table name may be omitted
|
|
7783
|
-
* 'messages.text', // text is the Message column, and the table name is required
|
|
7784
|
-
* );
|
|
7785
|
-
*
|
|
7786
|
-
* // result has the following type:
|
|
7787
|
-
* const ok: { name: string; text: string }[] = result;
|
|
7788
|
-
* ```
|
|
7854
|
+
* import { columnTypes } from 'orchid-orm';
|
|
7789
7855
|
*
|
|
7790
|
-
*
|
|
7791
|
-
*
|
|
7856
|
+
* db.table.jsonPathQuery(
|
|
7857
|
+
* columnTypes.text(3, 100), // type of the value
|
|
7858
|
+
* 'data', // name of the JSON column
|
|
7859
|
+
* '$.name', // JSON path
|
|
7860
|
+
* 'name', // select value as name
|
|
7792
7861
|
*
|
|
7793
|
-
*
|
|
7794
|
-
*
|
|
7795
|
-
*
|
|
7862
|
+
* // Optionally supports `vars` and `silent` options
|
|
7863
|
+
* // check Postgres docs for jsonb_path_query for details
|
|
7864
|
+
* {
|
|
7865
|
+
* vars: 'vars',
|
|
7866
|
+
* silent: true,
|
|
7867
|
+
* },
|
|
7796
7868
|
* );
|
|
7797
7869
|
* ```
|
|
7798
7870
|
*
|
|
7799
|
-
*
|
|
7800
|
-
*
|
|
7801
|
-
* But remember that when joining a relation, the needed `ON` conditions are already handled automatically.
|
|
7871
|
+
* Nested JSON operations can be used in place of JSON column name:
|
|
7802
7872
|
*
|
|
7803
7873
|
* ```ts
|
|
7804
|
-
*
|
|
7805
|
-
* (
|
|
7806
|
-
*
|
|
7807
|
-
*
|
|
7808
|
-
*
|
|
7809
|
-
*
|
|
7874
|
+
* db.table.jsonPathQuery(
|
|
7875
|
+
* columnTypes.text(3, 100),
|
|
7876
|
+
* // Available: .jsonSet, .jsonInsert, .jsonRemove
|
|
7877
|
+
* db.table.jsonSet('data', ['key'], 'value'),
|
|
7878
|
+
* '$.name',
|
|
7879
|
+
* 'name',
|
|
7810
7880
|
* );
|
|
7811
7881
|
* ```
|
|
7812
7882
|
*
|
|
7813
|
-
*
|
|
7814
|
-
*
|
|
7815
|
-
*
|
|
7883
|
+
* @param type - provide a column type to have a correct result type
|
|
7884
|
+
* @param column - name of JSON column, or a result of a nested json method
|
|
7885
|
+
* @param path - special JSON path string to reference a JSON value
|
|
7886
|
+
* @param as - optional alias for the selected value
|
|
7887
|
+
* @param options - supports `vars` and `silent`, check Postgres docs of `json_path_query` for these
|
|
7888
|
+
*/
|
|
7889
|
+
jsonPathQuery(type, column, path, as, options) {
|
|
7890
|
+
const q = this.clone();
|
|
7891
|
+
const json = {
|
|
7892
|
+
__json: ["pathQuery", as, type, column, path, options]
|
|
7893
|
+
};
|
|
7894
|
+
return Object.assign(
|
|
7895
|
+
pushQueryValue(q, "select", json),
|
|
7896
|
+
json
|
|
7897
|
+
);
|
|
7898
|
+
}
|
|
7899
|
+
}
|
|
7900
|
+
class JsonMethods {
|
|
7901
|
+
/**
|
|
7902
|
+
* Wraps the query in a way to select a single JSON string.
|
|
7903
|
+
* So that JSON encoding is done on a database side, and the application doesn't have to turn a response to a JSON.
|
|
7904
|
+
* It may be better for performance in some cases.
|
|
7816
7905
|
*
|
|
7817
7906
|
* ```ts
|
|
7818
|
-
*
|
|
7819
|
-
*
|
|
7820
|
-
* });
|
|
7821
|
-
*
|
|
7822
|
-
* // result has the following type:
|
|
7823
|
-
* const ok: {
|
|
7824
|
-
* // title of the book
|
|
7825
|
-
* title: string;
|
|
7826
|
-
* // a full author record is included:
|
|
7827
|
-
* author: { id: number; name: string; updatedAt: Date; createdAt: Date };
|
|
7828
|
-
* }[] = result;
|
|
7907
|
+
* // json is a JSON string that you can directly send as a response.
|
|
7908
|
+
* const json = await db.table.select('id', 'name').json();
|
|
7829
7909
|
* ```
|
|
7830
7910
|
*
|
|
7831
|
-
*
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7911
|
+
* @param coalesce
|
|
7912
|
+
*/
|
|
7913
|
+
json(coalesce) {
|
|
7914
|
+
return queryJson(
|
|
7915
|
+
this.clone(),
|
|
7916
|
+
coalesce
|
|
7917
|
+
);
|
|
7918
|
+
}
|
|
7919
|
+
}
|
|
7920
|
+
|
|
7921
|
+
const logColors = {
|
|
7922
|
+
boldCyanBright: (message) => `\x1B[1m\x1B[96m${message}\x1B[39m\x1B[22m`,
|
|
7923
|
+
boldBlue: (message) => `\x1B[1m\x1B[34m${message}\x1B[39m\x1B[22m`,
|
|
7924
|
+
boldYellow: (message) => `\x1B[1m\x1B[33m${message}\x1B[39m\x1B[22m`,
|
|
7925
|
+
boldMagenta: (message) => `\x1B[1m\x1B[33m${message}\x1B[39m\x1B[22m`,
|
|
7926
|
+
boldRed: (message) => `\x1B[1m\x1B[31m${message}\x1B[39m\x1B[22m`
|
|
7927
|
+
};
|
|
7928
|
+
const makeMessage = (colors, timeColor, time, sqlColor, sql, valuesColor, values) => {
|
|
7929
|
+
const elapsed = process.hrtime(time);
|
|
7930
|
+
const formattedTime = `(${elapsed[0] ? `${elapsed[0]}s ` : ""}${(elapsed[1] / 1e6).toFixed(1)}ms)`;
|
|
7931
|
+
const result = `${colors ? timeColor(formattedTime) : formattedTime} ${colors ? sqlColor(sql) : sql}`;
|
|
7932
|
+
if (!values.length) {
|
|
7933
|
+
return result;
|
|
7934
|
+
}
|
|
7935
|
+
const formattedValues = `[${values.map(quote).join(", ")}]`;
|
|
7936
|
+
return `${result} ${colors ? valuesColor(formattedValues) : formattedValues}`;
|
|
7937
|
+
};
|
|
7938
|
+
const logParamToLogObject = (logger, log) => {
|
|
7939
|
+
if (!log)
|
|
7940
|
+
return;
|
|
7941
|
+
const logObject = Object.assign(
|
|
7942
|
+
{
|
|
7943
|
+
colors: true,
|
|
7944
|
+
beforeQuery() {
|
|
7945
|
+
return process.hrtime();
|
|
7946
|
+
},
|
|
7947
|
+
afterQuery(sql, time) {
|
|
7948
|
+
logger.log(
|
|
7949
|
+
makeMessage(
|
|
7950
|
+
colors,
|
|
7951
|
+
logColors.boldCyanBright,
|
|
7952
|
+
time,
|
|
7953
|
+
logColors.boldBlue,
|
|
7954
|
+
sql.text,
|
|
7955
|
+
logColors.boldYellow,
|
|
7956
|
+
sql.values
|
|
7957
|
+
)
|
|
7958
|
+
);
|
|
7959
|
+
},
|
|
7960
|
+
onError(error, sql, time) {
|
|
7961
|
+
const message = `Error: ${error.message}`;
|
|
7962
|
+
logger.error(
|
|
7963
|
+
`${makeMessage(
|
|
7964
|
+
colors,
|
|
7965
|
+
logColors.boldMagenta,
|
|
7966
|
+
time,
|
|
7967
|
+
logColors.boldRed,
|
|
7968
|
+
sql.text,
|
|
7969
|
+
logColors.boldYellow,
|
|
7970
|
+
sql.values
|
|
7971
|
+
)} ${colors ? logColors.boldRed(message) : message}`
|
|
7972
|
+
);
|
|
7973
|
+
}
|
|
7974
|
+
},
|
|
7975
|
+
log === true ? {} : log
|
|
7976
|
+
);
|
|
7977
|
+
const colors = logObject.colors;
|
|
7978
|
+
return logObject;
|
|
7979
|
+
};
|
|
7980
|
+
class QueryLog {
|
|
7981
|
+
log(log = true) {
|
|
7982
|
+
const q = this.clone();
|
|
7983
|
+
q.q.log = logParamToLogObject(q.q.logger, log);
|
|
7984
|
+
return q;
|
|
7985
|
+
}
|
|
7986
|
+
}
|
|
7987
|
+
|
|
7988
|
+
var __defProp$5 = Object.defineProperty;
|
|
7989
|
+
var __getOwnPropSymbols$5 = Object.getOwnPropertySymbols;
|
|
7990
|
+
var __hasOwnProp$5 = Object.prototype.hasOwnProperty;
|
|
7991
|
+
var __propIsEnum$5 = Object.prototype.propertyIsEnumerable;
|
|
7992
|
+
var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
7993
|
+
var __spreadValues$5 = (a, b) => {
|
|
7994
|
+
for (var prop in b || (b = {}))
|
|
7995
|
+
if (__hasOwnProp$5.call(b, prop))
|
|
7996
|
+
__defNormalProp$5(a, prop, b[prop]);
|
|
7997
|
+
if (__getOwnPropSymbols$5)
|
|
7998
|
+
for (var prop of __getOwnPropSymbols$5(b)) {
|
|
7999
|
+
if (__propIsEnum$5.call(b, prop))
|
|
8000
|
+
__defNormalProp$5(a, prop, b[prop]);
|
|
8001
|
+
}
|
|
8002
|
+
return a;
|
|
8003
|
+
};
|
|
8004
|
+
const mergableObjects = {
|
|
8005
|
+
shape: true,
|
|
8006
|
+
withShapes: true,
|
|
8007
|
+
parsers: true,
|
|
8008
|
+
defaults: true,
|
|
8009
|
+
joinedShapes: true,
|
|
8010
|
+
joinedParsers: true
|
|
8011
|
+
};
|
|
8012
|
+
class MergeQueryMethods {
|
|
8013
|
+
merge(q) {
|
|
8014
|
+
const query = this.clone();
|
|
8015
|
+
const a = query.q;
|
|
8016
|
+
const b = q.q;
|
|
8017
|
+
for (const key in b) {
|
|
8018
|
+
const value = b[key];
|
|
8019
|
+
switch (typeof value) {
|
|
8020
|
+
case "boolean":
|
|
8021
|
+
case "string":
|
|
8022
|
+
case "number":
|
|
8023
|
+
a[key] = value;
|
|
8024
|
+
break;
|
|
8025
|
+
case "object":
|
|
8026
|
+
if (Array.isArray(value)) {
|
|
8027
|
+
a[key] = a[key] ? [...a[key], ...value] : value;
|
|
8028
|
+
} else if (mergableObjects[key]) {
|
|
8029
|
+
a[key] = a[key] ? __spreadValues$5(__spreadValues$5({}, a[key]), value) : value;
|
|
8030
|
+
} else {
|
|
8031
|
+
a[key] = value;
|
|
8032
|
+
}
|
|
8033
|
+
break;
|
|
8034
|
+
}
|
|
8035
|
+
}
|
|
8036
|
+
a[orchidCore.getValueKey] = b[orchidCore.getValueKey];
|
|
8037
|
+
if (b.returnType)
|
|
8038
|
+
a.returnType = b.returnType;
|
|
8039
|
+
return query;
|
|
8040
|
+
}
|
|
8041
|
+
}
|
|
8042
|
+
|
|
8043
|
+
var __defProp$4 = Object.defineProperty;
|
|
8044
|
+
var __defProps$2 = Object.defineProperties;
|
|
8045
|
+
var __getOwnPropDescs$2 = Object.getOwnPropertyDescriptors;
|
|
8046
|
+
var __getOwnPropSymbols$4 = Object.getOwnPropertySymbols;
|
|
8047
|
+
var __hasOwnProp$4 = Object.prototype.hasOwnProperty;
|
|
8048
|
+
var __propIsEnum$4 = Object.prototype.propertyIsEnumerable;
|
|
8049
|
+
var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8050
|
+
var __spreadValues$4 = (a, b) => {
|
|
8051
|
+
for (var prop in b || (b = {}))
|
|
8052
|
+
if (__hasOwnProp$4.call(b, prop))
|
|
8053
|
+
__defNormalProp$4(a, prop, b[prop]);
|
|
8054
|
+
if (__getOwnPropSymbols$4)
|
|
8055
|
+
for (var prop of __getOwnPropSymbols$4(b)) {
|
|
8056
|
+
if (__propIsEnum$4.call(b, prop))
|
|
8057
|
+
__defNormalProp$4(a, prop, b[prop]);
|
|
8058
|
+
}
|
|
8059
|
+
return a;
|
|
8060
|
+
};
|
|
8061
|
+
var __spreadProps$2 = (a, b) => __defProps$2(a, __getOwnPropDescs$2(b));
|
|
8062
|
+
class With {
|
|
8063
|
+
/**
|
|
8064
|
+
* Add Common Table Expression (CTE) to the query.
|
|
7847
8065
|
*
|
|
7848
|
-
*
|
|
8066
|
+
* ```ts
|
|
8067
|
+
* import { columnTypes } from 'orchid-orm';
|
|
8068
|
+
* import { NumberColumn } from './number';
|
|
7849
8069
|
*
|
|
7850
|
-
*
|
|
7851
|
-
*
|
|
7852
|
-
*
|
|
7853
|
-
*
|
|
7854
|
-
*
|
|
8070
|
+
* // .with optionally accepts such options:
|
|
8071
|
+
* type WithOptions = {
|
|
8072
|
+
* // list of columns returned by this WITH statement
|
|
8073
|
+
* // by default all columns from provided column shape will be included
|
|
8074
|
+
* // true is for default behavior
|
|
8075
|
+
* columns?: string[] | boolean;
|
|
7855
8076
|
*
|
|
7856
|
-
*
|
|
8077
|
+
* // Adds RECURSIVE keyword:
|
|
8078
|
+
* recursive?: true;
|
|
7857
8079
|
*
|
|
7858
|
-
*
|
|
7859
|
-
*
|
|
8080
|
+
* // Adds MATERIALIZED keyword:
|
|
8081
|
+
* materialized?: true;
|
|
8082
|
+
*
|
|
8083
|
+
* // Adds NOT MATERIALIZED keyword:
|
|
8084
|
+
* notMaterialized?: true;
|
|
8085
|
+
* };
|
|
8086
|
+
*
|
|
8087
|
+
* // accepts columns shape and a raw expression:
|
|
8088
|
+
* db.table.with(
|
|
8089
|
+
* 'alias',
|
|
8090
|
+
* {
|
|
8091
|
+
* id: columnTypes.integer(),
|
|
8092
|
+
* name: columnTypes.text(3, 100),
|
|
8093
|
+
* },
|
|
8094
|
+
* db.table.sql`SELECT id, name FROM "someTable"`,
|
|
8095
|
+
* );
|
|
8096
|
+
*
|
|
8097
|
+
* // accepts query:
|
|
8098
|
+
* db.table.with('alias', db.table.all());
|
|
8099
|
+
*
|
|
8100
|
+
* // accepts a callback for a query builder:
|
|
8101
|
+
* db.table.with('alias', (qb) =>
|
|
8102
|
+
* qb.select({ one: db.table.sql((t) => t.integer())`1` }),
|
|
8103
|
+
* );
|
|
8104
|
+
*
|
|
8105
|
+
* // All mentioned forms can accept options as a second argument:
|
|
8106
|
+
* db.table.with(
|
|
8107
|
+
* 'alias',
|
|
8108
|
+
* {
|
|
8109
|
+
* recursive: true,
|
|
8110
|
+
* materialized: true,
|
|
8111
|
+
* },
|
|
8112
|
+
* rawOrQueryOrCallback,
|
|
8113
|
+
* );
|
|
8114
|
+
* ```
|
|
8115
|
+
*
|
|
8116
|
+
* Defined `WITH` table can be used in `.from` or `.join` with all the type safeness:
|
|
7860
8117
|
*
|
|
7861
8118
|
* ```ts
|
|
7862
|
-
*
|
|
7863
|
-
*
|
|
7864
|
-
*
|
|
7865
|
-
* .
|
|
7866
|
-
* .
|
|
8119
|
+
* db.table.with('alias', db.table.all()).from('alias').select('alias.id');
|
|
8120
|
+
*
|
|
8121
|
+
* db.table
|
|
8122
|
+
* .with('alias', db.table.all())
|
|
8123
|
+
* .join('alias', 'alias.id', 'user.id')
|
|
8124
|
+
* .select('alias.id');
|
|
7867
8125
|
* ```
|
|
7868
8126
|
*
|
|
7869
|
-
*
|
|
8127
|
+
* @param args - first argument is an alias for this CTE, other arguments can be column shape, query object, or raw SQL.
|
|
8128
|
+
*/
|
|
8129
|
+
with(...args) {
|
|
8130
|
+
const q = this.clone();
|
|
8131
|
+
let options = args.length === 3 && !orchidCore.isExpression(args[2]) || args.length === 4 ? args[1] : void 0;
|
|
8132
|
+
const last = args[args.length - 1];
|
|
8133
|
+
const query = typeof last === "function" ? last(q.queryBuilder) : last;
|
|
8134
|
+
const shape = args.length === 4 ? args[2] : orchidCore.isExpression(query) ? args[1] : query.q.shape;
|
|
8135
|
+
if ((options == null ? void 0 : options.columns) === true) {
|
|
8136
|
+
options = __spreadProps$2(__spreadValues$4({}, options), {
|
|
8137
|
+
columns: Object.keys(shape)
|
|
8138
|
+
});
|
|
8139
|
+
}
|
|
8140
|
+
pushQueryValue(q, "with", [args[0], options || orchidCore.emptyObject, query]);
|
|
8141
|
+
return setQueryObjectValue(q, "withShapes", args[0], shape);
|
|
8142
|
+
}
|
|
8143
|
+
}
|
|
8144
|
+
|
|
8145
|
+
class Union {
|
|
8146
|
+
/**
|
|
8147
|
+
* Creates a union query, taking an array or a list of callbacks, builders, or raw statements to build the union statement, with optional boolean `wrap`.
|
|
8148
|
+
* If the `wrap` parameter is true, the queries will be individually wrapped in parentheses.
|
|
7870
8149
|
*
|
|
7871
8150
|
* ```ts
|
|
7872
|
-
*
|
|
8151
|
+
* SomeTable.select('id', 'name').union(
|
|
8152
|
+
* [
|
|
8153
|
+
* OtherTable.select('id', 'name'),
|
|
8154
|
+
* SomeTable.sql`SELECT id, name FROM "thirdTable"`,
|
|
8155
|
+
* ],
|
|
8156
|
+
* true, // optional wrap parameter
|
|
8157
|
+
* );
|
|
7873
8158
|
* ```
|
|
7874
8159
|
*
|
|
7875
|
-
*
|
|
8160
|
+
* @param args - array of queries or raw SQLs
|
|
8161
|
+
* @param wrap - provide `true` if you want the queries to be wrapped into parentheses
|
|
8162
|
+
*/
|
|
8163
|
+
union(args, wrap) {
|
|
8164
|
+
return pushQueryArray(
|
|
8165
|
+
this.clone(),
|
|
8166
|
+
"union",
|
|
8167
|
+
args.map((arg) => ({ arg, kind: "UNION", wrap }))
|
|
8168
|
+
);
|
|
8169
|
+
}
|
|
8170
|
+
/**
|
|
8171
|
+
* Same as `union`, but allows duplicated rows.
|
|
8172
|
+
*
|
|
8173
|
+
* @param args - array of queries or raw SQLs
|
|
8174
|
+
* @param wrap - provide `true` if you want the queries to be wrapped into parentheses
|
|
8175
|
+
*/
|
|
8176
|
+
unionAll(args, wrap) {
|
|
8177
|
+
return pushQueryArray(
|
|
8178
|
+
this.clone(),
|
|
8179
|
+
"union",
|
|
8180
|
+
args.map((arg) => ({ arg, kind: "UNION ALL", wrap }))
|
|
8181
|
+
);
|
|
8182
|
+
}
|
|
8183
|
+
/**
|
|
8184
|
+
* Same as `union`, but uses a `INTERSECT` SQL keyword instead
|
|
8185
|
+
*
|
|
8186
|
+
* @param args - array of queries or raw SQLs
|
|
8187
|
+
* @param wrap - provide `true` if you want the queries to be wrapped into parentheses
|
|
8188
|
+
*/
|
|
8189
|
+
intersect(args, wrap) {
|
|
8190
|
+
return pushQueryArray(
|
|
8191
|
+
this.clone(),
|
|
8192
|
+
"union",
|
|
8193
|
+
args.map((arg) => ({ arg, kind: "INTERSECT", wrap }))
|
|
8194
|
+
);
|
|
8195
|
+
}
|
|
8196
|
+
/**
|
|
8197
|
+
* Same as `intersect`, but allows duplicated rows.
|
|
8198
|
+
*
|
|
8199
|
+
* @param args - array of queries or raw SQLs
|
|
8200
|
+
* @param wrap - provide `true` if you want the queries to be wrapped into parentheses
|
|
8201
|
+
*/
|
|
8202
|
+
intersectAll(args, wrap) {
|
|
8203
|
+
return pushQueryArray(
|
|
8204
|
+
this.clone(),
|
|
8205
|
+
"union",
|
|
8206
|
+
args.map((arg) => ({ arg, kind: "INTERSECT ALL", wrap }))
|
|
8207
|
+
);
|
|
8208
|
+
}
|
|
8209
|
+
/**
|
|
8210
|
+
* Same as `union`, but uses an `EXCEPT` SQL keyword instead
|
|
8211
|
+
*
|
|
8212
|
+
* @param args - array of queries or raw SQLs
|
|
8213
|
+
* @param wrap - provide `true` if you want the queries to be wrapped into parentheses
|
|
8214
|
+
*/
|
|
8215
|
+
except(args, wrap) {
|
|
8216
|
+
return pushQueryArray(
|
|
8217
|
+
this.clone(),
|
|
8218
|
+
"union",
|
|
8219
|
+
args.map((arg) => ({ arg, kind: "EXCEPT", wrap }))
|
|
8220
|
+
);
|
|
8221
|
+
}
|
|
8222
|
+
/**
|
|
8223
|
+
* Same as `except`, but allows duplicated rows.
|
|
8224
|
+
*
|
|
8225
|
+
* @param args - array of queries or raw SQLs
|
|
8226
|
+
* @param wrap - provide `true` if you want the queries to be wrapped into parentheses
|
|
8227
|
+
*/
|
|
8228
|
+
exceptAll(args, wrap) {
|
|
8229
|
+
return pushQueryArray(
|
|
8230
|
+
this.clone(),
|
|
8231
|
+
"union",
|
|
8232
|
+
args.map((arg) => ({ arg, kind: "EXCEPT ALL", wrap }))
|
|
8233
|
+
);
|
|
8234
|
+
}
|
|
8235
|
+
}
|
|
8236
|
+
|
|
8237
|
+
const _queryWhere = (q, args) => {
|
|
8238
|
+
return pushQueryArray(
|
|
8239
|
+
q,
|
|
8240
|
+
"and",
|
|
8241
|
+
args
|
|
8242
|
+
);
|
|
8243
|
+
};
|
|
8244
|
+
const _queryWhereSql = (q, args) => {
|
|
8245
|
+
return pushQueryValue(
|
|
8246
|
+
q,
|
|
8247
|
+
"and",
|
|
8248
|
+
sqlQueryArgsToExpression(args)
|
|
8249
|
+
);
|
|
8250
|
+
};
|
|
8251
|
+
const _queryWhereNot = (q, args) => {
|
|
8252
|
+
return pushQueryValue(q, "and", {
|
|
8253
|
+
NOT: args
|
|
8254
|
+
});
|
|
8255
|
+
};
|
|
8256
|
+
const _queryWhereNotSql = (q, args) => {
|
|
8257
|
+
return pushQueryValue(q, "and", {
|
|
8258
|
+
NOT: sqlQueryArgsToExpression(args)
|
|
8259
|
+
});
|
|
8260
|
+
};
|
|
8261
|
+
const _queryOr = (q, args) => {
|
|
8262
|
+
return pushQueryArray(
|
|
8263
|
+
q,
|
|
8264
|
+
"or",
|
|
8265
|
+
args.map((item) => [item])
|
|
8266
|
+
);
|
|
8267
|
+
};
|
|
8268
|
+
const _queryOrNot = (q, args) => {
|
|
8269
|
+
return pushQueryArray(
|
|
8270
|
+
q,
|
|
8271
|
+
"or",
|
|
8272
|
+
args.map((item) => [{ NOT: item }])
|
|
8273
|
+
);
|
|
8274
|
+
};
|
|
8275
|
+
const _queryWhereIn = (q, and, arg, values, not) => {
|
|
8276
|
+
let item;
|
|
8277
|
+
if (values) {
|
|
8278
|
+
if (Array.isArray(arg)) {
|
|
8279
|
+
item = {
|
|
8280
|
+
IN: {
|
|
8281
|
+
columns: arg,
|
|
8282
|
+
values
|
|
8283
|
+
}
|
|
8284
|
+
};
|
|
8285
|
+
} else {
|
|
8286
|
+
item = { [arg]: { in: values } };
|
|
8287
|
+
}
|
|
8288
|
+
} else {
|
|
8289
|
+
item = {};
|
|
8290
|
+
for (const key in arg) {
|
|
8291
|
+
item[key] = { in: arg[key] };
|
|
8292
|
+
}
|
|
8293
|
+
}
|
|
8294
|
+
if (not)
|
|
8295
|
+
item = { NOT: item };
|
|
8296
|
+
if (and) {
|
|
8297
|
+
pushQueryValue(q, "and", item);
|
|
8298
|
+
} else {
|
|
8299
|
+
pushQueryValue(q, "or", [item]);
|
|
8300
|
+
}
|
|
8301
|
+
return q;
|
|
8302
|
+
};
|
|
8303
|
+
const existsArgs = (self, q, args) => {
|
|
8304
|
+
let joinSubQuery;
|
|
8305
|
+
if (typeof q === "object") {
|
|
8306
|
+
joinSubQuery = getIsJoinSubQuery(q);
|
|
8307
|
+
if (joinSubQuery) {
|
|
8308
|
+
q = q.clone();
|
|
8309
|
+
q.shape = getShapeFromSelect(
|
|
8310
|
+
q,
|
|
8311
|
+
true
|
|
8312
|
+
);
|
|
8313
|
+
}
|
|
8314
|
+
} else {
|
|
8315
|
+
joinSubQuery = false;
|
|
8316
|
+
}
|
|
8317
|
+
const joinArgs = processJoinArgs(self, q, args, joinSubQuery);
|
|
8318
|
+
return [
|
|
8319
|
+
{
|
|
8320
|
+
EXISTS: joinArgs
|
|
8321
|
+
}
|
|
8322
|
+
];
|
|
8323
|
+
};
|
|
8324
|
+
const _queryWhereExists = (q, arg, args) => {
|
|
8325
|
+
return _queryWhere(
|
|
8326
|
+
q,
|
|
8327
|
+
existsArgs(q, arg, args)
|
|
8328
|
+
);
|
|
8329
|
+
};
|
|
8330
|
+
class Where {
|
|
8331
|
+
/**
|
|
8332
|
+
* Constructing `WHERE` conditions:
|
|
7876
8333
|
*
|
|
7877
8334
|
* ```ts
|
|
7878
|
-
* db.
|
|
7879
|
-
*
|
|
7880
|
-
*
|
|
7881
|
-
* .select('name', 'm.text');
|
|
7882
|
-
* ```
|
|
8335
|
+
* db.table.where({
|
|
8336
|
+
* // column of the current table
|
|
8337
|
+
* name: 'John',
|
|
7883
8338
|
*
|
|
7884
|
-
*
|
|
8339
|
+
* // table name may be specified, it can be the name of a joined table
|
|
8340
|
+
* 'table.lastName': 'Johnsonuk',
|
|
7885
8341
|
*
|
|
7886
|
-
*
|
|
7887
|
-
*
|
|
7888
|
-
*
|
|
7889
|
-
*
|
|
7890
|
-
*
|
|
8342
|
+
* // object with operators, see the "column operators" section to see a full list of them:
|
|
8343
|
+
* age: {
|
|
8344
|
+
* gt: 30,
|
|
8345
|
+
* lt: 70,
|
|
8346
|
+
* },
|
|
7891
8347
|
*
|
|
7892
|
-
*
|
|
7893
|
-
*
|
|
7894
|
-
*
|
|
7895
|
-
* // full message is included as msg:
|
|
7896
|
-
* msg: { id: number; text: string; updatedAt: Date; createdAt: Date };
|
|
7897
|
-
* }[] = result;
|
|
8348
|
+
* // where column equals to raw SQL
|
|
8349
|
+
* column: db.table.sql`sql expression`,
|
|
8350
|
+
* });
|
|
7898
8351
|
* ```
|
|
7899
8352
|
*
|
|
7900
|
-
*
|
|
8353
|
+
* Multiple `where`s are joined with `AND`:
|
|
7901
8354
|
*
|
|
7902
8355
|
* ```ts
|
|
7903
|
-
* db.
|
|
8356
|
+
* db.table.where({ foo: 'foo' }).where({ bar: 'bar' });
|
|
7904
8357
|
* ```
|
|
7905
8358
|
*
|
|
7906
|
-
*
|
|
7907
|
-
*
|
|
7908
|
-
* ```ts
|
|
7909
|
-
* db.user.join(
|
|
7910
|
-
* db.message,
|
|
7911
|
-
* db.user.sql`lower("message"."text") = lower("user"."name")`,
|
|
7912
|
-
* );
|
|
8359
|
+
* ```sql
|
|
8360
|
+
* SELECT * FROM table WHERE foo = 'foo' AND bar = 'bar'
|
|
7913
8361
|
* ```
|
|
7914
8362
|
*
|
|
7915
|
-
*
|
|
8363
|
+
* `undefined` values are ignored, so you can supply a partial object with conditions:
|
|
7916
8364
|
*
|
|
7917
8365
|
* ```ts
|
|
7918
|
-
*
|
|
7919
|
-
*
|
|
7920
|
-
*
|
|
7921
|
-
*
|
|
7922
|
-
* );
|
|
8366
|
+
* type Params = {
|
|
8367
|
+
* // allow providing exact age, or lower or greater than
|
|
8368
|
+
* age?: number | { lt?: number; gt?: number };
|
|
8369
|
+
* };
|
|
7923
8370
|
*
|
|
7924
|
-
*
|
|
7925
|
-
*
|
|
7926
|
-
* db.
|
|
7927
|
-
*
|
|
7928
|
-
* '!=',
|
|
7929
|
-
* db.user.sql`lower("user"."name")`,
|
|
7930
|
-
* );
|
|
8371
|
+
* const loadRecords = async (params: Params) => {
|
|
8372
|
+
* // this will load all records if params is an empty object
|
|
8373
|
+
* const records = await db.table.where(params);
|
|
8374
|
+
* };
|
|
7931
8375
|
* ```
|
|
7932
8376
|
*
|
|
7933
|
-
*
|
|
8377
|
+
* It supports a sub-query that is selecting a single value to compare it with a column:
|
|
7934
8378
|
*
|
|
7935
8379
|
* ```ts
|
|
7936
|
-
* db.
|
|
7937
|
-
*
|
|
7938
|
-
*
|
|
7939
|
-
* // with table names:
|
|
7940
|
-
* 'message.userId': 'user.id',
|
|
7941
|
-
*
|
|
7942
|
-
* // value can be a raw SQL expression:
|
|
7943
|
-
* text: db.user.sql`lower("user"."name")`,
|
|
8380
|
+
* db.table.where({
|
|
8381
|
+
* // compare `someColumn` in one table with the `column` value returned from another query.
|
|
8382
|
+
* someColumn: db.otherTable.where(...conditions).get('column'),
|
|
7944
8383
|
* });
|
|
7945
8384
|
* ```
|
|
7946
8385
|
*
|
|
7947
|
-
*
|
|
7948
|
-
*
|
|
7949
|
-
* ```ts
|
|
7950
|
-
* db.user.join(db.message, true);
|
|
7951
|
-
* ```
|
|
7952
|
-
*
|
|
7953
|
-
* Join methods can accept a callback with a special query builder that has `on` and `orOn` methods for handling advanced cases:
|
|
7954
|
-
*
|
|
7955
|
-
* ```ts
|
|
7956
|
-
* db.user.join(
|
|
7957
|
-
* db.message,
|
|
7958
|
-
* (q) =>
|
|
7959
|
-
* q
|
|
7960
|
-
* // left column is the db.message column, right column is the db.user column
|
|
7961
|
-
* .on('userId', 'id')
|
|
7962
|
-
* // table names can be provided:
|
|
7963
|
-
* .on('message.userId', 'user.id')
|
|
7964
|
-
* // operator can be specified:
|
|
7965
|
-
* .on('userId', '!=', 'id')
|
|
7966
|
-
* // operator can be specified with table names as well:
|
|
7967
|
-
* .on('message.userId', '!=', 'user.id')
|
|
7968
|
-
* // `.orOn` takes the same arguments as `.on` and acts like `.or`:
|
|
7969
|
-
* .on('userId', 'id') // where message.userId = user.id
|
|
7970
|
-
* .orOn('text', 'name'), // or message.text = user.name
|
|
7971
|
-
* );
|
|
7972
|
-
* ```
|
|
7973
|
-
*
|
|
7974
|
-
* Join query builder supports all `where` methods: `.where`, `.whereIn`, `.whereExists`, and all `.or`, `.not`, and `.orNot` forms.
|
|
7975
|
-
*
|
|
7976
|
-
* Column names in the where conditions are applied for the joined table, but you can specify a table name to add a condition for the main table.
|
|
8386
|
+
* `where` can accept other queries and merge their conditions:
|
|
7977
8387
|
*
|
|
7978
8388
|
* ```ts
|
|
7979
|
-
* db.
|
|
7980
|
-
* q
|
|
7981
|
-
* .on('userId', 'id')
|
|
7982
|
-
* .where({
|
|
7983
|
-
* // not prefixed column name is for joined table:
|
|
7984
|
-
* text: { startsWith: 'hello' },
|
|
7985
|
-
* // specify a table name to set condition on the main table:
|
|
7986
|
-
* 'user.name': 'Bob',
|
|
7987
|
-
* })
|
|
7988
|
-
* // id is a column of a joined table Message
|
|
7989
|
-
* .whereIn('id', [1, 2, 3])
|
|
7990
|
-
* // condition for id of a user
|
|
7991
|
-
* .whereIn('user.id', [4, 5, 6]),
|
|
7992
|
-
* );
|
|
7993
|
-
* ```
|
|
7994
|
-
*
|
|
7995
|
-
* The query above will generate the following SQL (simplified):
|
|
8389
|
+
* const otherQuery = db.table.where({ name: 'John' });
|
|
7996
8390
|
*
|
|
7997
|
-
*
|
|
7998
|
-
*
|
|
7999
|
-
* JOIN "message"
|
|
8000
|
-
* ON "message"."userId" = "user"."id"
|
|
8001
|
-
* AND "message"."text" ILIKE 'hello%'
|
|
8002
|
-
* AND "user"."name" = 'Bob'
|
|
8003
|
-
* AND "message"."id" IN (1, 2, 3)
|
|
8004
|
-
* AND "user"."id" IN (4, 5, 6)
|
|
8391
|
+
* db.table.where({ id: 1 }, otherQuery);
|
|
8392
|
+
* // this will produce WHERE "table"."id" = 1 AND "table"."name' = 'John'
|
|
8005
8393
|
* ```
|
|
8006
8394
|
*
|
|
8007
|
-
*
|
|
8395
|
+
* `where` supports raw SQL:
|
|
8008
8396
|
*
|
|
8009
8397
|
* ```ts
|
|
8010
|
-
* db.
|
|
8011
|
-
* db.message
|
|
8012
|
-
* .select('id', 'userId', 'text')
|
|
8013
|
-
* .where({ text: { startsWith: 'Hi' } })
|
|
8014
|
-
* .as('t'),
|
|
8015
|
-
* 'userId',
|
|
8016
|
-
* 'id',
|
|
8017
|
-
* );
|
|
8018
|
-
* ```
|
|
8398
|
+
* db.table.where(db.table.sql`a = b`);
|
|
8019
8399
|
*
|
|
8020
|
-
*
|
|
8400
|
+
* // or
|
|
8401
|
+
* import { raw } from 'orchid-orm';
|
|
8021
8402
|
*
|
|
8022
|
-
*
|
|
8023
|
-
* SELECT * FROM "user"
|
|
8024
|
-
* JOIN (
|
|
8025
|
-
* SELECT "t"."id", "t"."userId", "t"."text"
|
|
8026
|
-
* FROM "message" AS "t"
|
|
8027
|
-
* ) "t" ON "t"."userId" = "user"."id"
|
|
8403
|
+
* db.table.where(raw`a = b`);
|
|
8028
8404
|
* ```
|
|
8029
8405
|
*
|
|
8030
|
-
*
|
|
8031
|
-
* @param args - {@link JoinArgs}
|
|
8032
|
-
*/
|
|
8033
|
-
join(arg, ...args) {
|
|
8034
|
-
return _join(this.clone(), true, "JOIN", arg, args);
|
|
8035
|
-
}
|
|
8036
|
-
/**
|
|
8037
|
-
* `leftJoin` is a method for SQL `LEFT JOIN`, which is equivalent to `OUTER JOIN`, `LEFT OUTER JOIN`.
|
|
8038
|
-
*
|
|
8039
|
-
* When no matching record is found, it will fill joined table columns with `NULL` values in the result rows.
|
|
8040
|
-
*
|
|
8041
|
-
* Works just like `join`, except for result type that may have `null`:
|
|
8406
|
+
* `where` can accept a callback with a specific query builder containing all "where" methods such as `where`, `orWhere`, `whereNot`, `whereIn`, `whereExists`:
|
|
8042
8407
|
*
|
|
8043
8408
|
* ```ts
|
|
8044
|
-
*
|
|
8045
|
-
*
|
|
8046
|
-
*
|
|
8047
|
-
*
|
|
8048
|
-
*
|
|
8049
|
-
*
|
|
8050
|
-
*
|
|
8051
|
-
*
|
|
8052
|
-
*
|
|
8053
|
-
* // result has the following type:
|
|
8054
|
-
* const ok: { name: string; text: string | null }[] = result;
|
|
8055
|
-
* ```
|
|
8056
|
-
*
|
|
8057
|
-
* @param arg - {@link JoinFirstArg}
|
|
8058
|
-
* @param args - {@link JoinArgs}
|
|
8059
|
-
*/
|
|
8060
|
-
leftJoin(arg, ...args) {
|
|
8061
|
-
return _join(this.clone(), false, "LEFT JOIN", arg, args);
|
|
8062
|
-
}
|
|
8063
|
-
/**
|
|
8064
|
-
* `rightJoin` is a method for SQL `RIGHT JOIN`, which is equivalent to `RIGHT OUTER JOIN`.
|
|
8065
|
-
*
|
|
8066
|
-
* Takes the same arguments as `json`.
|
|
8067
|
-
*
|
|
8068
|
-
* It will load all records from the joining table, and fill the main table columns with `null` when no match is found.
|
|
8409
|
+
* db.table.where((q) =>
|
|
8410
|
+
* q
|
|
8411
|
+
* .where({ name: 'Name' })
|
|
8412
|
+
* .orWhere({ id: 1 }, { id: 2 })
|
|
8413
|
+
* .whereIn('letter', ['a', 'b', 'c'])
|
|
8414
|
+
* .whereExists(Message, 'authorId', 'id'),
|
|
8415
|
+
* );
|
|
8416
|
+
* ```
|
|
8069
8417
|
*
|
|
8070
|
-
*
|
|
8418
|
+
* `where` can accept multiple arguments, conditions are joined with `AND`:
|
|
8071
8419
|
*
|
|
8072
8420
|
* ```ts
|
|
8073
|
-
*
|
|
8074
|
-
*
|
|
8075
|
-
* .
|
|
8076
|
-
*
|
|
8077
|
-
*
|
|
8078
|
-
* const ok: { name: string | null; text: string }[] = result;
|
|
8421
|
+
* db.table.where(
|
|
8422
|
+
* { id: 1 },
|
|
8423
|
+
* db.table.where({ name: 'John' }),
|
|
8424
|
+
* db.table.sql`a = b`,
|
|
8425
|
+
* );
|
|
8079
8426
|
* ```
|
|
8080
8427
|
*
|
|
8081
|
-
*
|
|
8082
|
-
* @param args - {@link JoinArgs}
|
|
8083
|
-
*/
|
|
8084
|
-
rightJoin(arg, ...args) {
|
|
8085
|
-
return _join(this.clone(), true, "RIGHT JOIN", arg, args);
|
|
8086
|
-
}
|
|
8087
|
-
/**
|
|
8088
|
-
* `fullJoin` is a method for SQL `FULL JOIN`, which is equivalent to `FULL OUTER JOIN`.
|
|
8089
|
-
*
|
|
8090
|
-
* Takes the same arguments as `json`.
|
|
8428
|
+
* ## where sub query
|
|
8091
8429
|
*
|
|
8092
|
-
*
|
|
8430
|
+
* `where` handles a special callback where you can query a relation to get some value and filter by that value.
|
|
8093
8431
|
*
|
|
8094
|
-
*
|
|
8432
|
+
* It is useful for a faceted search. For instance, posts have tags, and we want to find all posts that have all the given tags.
|
|
8095
8433
|
*
|
|
8096
8434
|
* ```ts
|
|
8097
|
-
* const
|
|
8098
|
-
* .rightJoin('messages')
|
|
8099
|
-
* .select('name', 'messages.text');
|
|
8435
|
+
* const givenTags = ['typescript', 'node.js'];
|
|
8100
8436
|
*
|
|
8101
|
-
*
|
|
8102
|
-
*
|
|
8437
|
+
* const posts = await db.post.where(
|
|
8438
|
+
* (post) =>
|
|
8439
|
+
* post.tags // query tags of the post
|
|
8440
|
+
* .whereIn('tagName', givenTags) // where name of the tag is inside array
|
|
8441
|
+
* .count() // count how many such tags were found
|
|
8442
|
+
* .equals(wantedTags.length), // the count must be exactly the length of array
|
|
8443
|
+
* // if the post has ony `typescript` tag but not the `node.js` it will be omitted
|
|
8444
|
+
* );
|
|
8103
8445
|
* ```
|
|
8104
8446
|
*
|
|
8105
|
-
*
|
|
8106
|
-
* @param args - {@link JoinArgs}
|
|
8107
|
-
*/
|
|
8108
|
-
fullJoin(arg, ...args) {
|
|
8109
|
-
return _join(this.clone(), false, "FULL JOIN", arg, args);
|
|
8110
|
-
}
|
|
8111
|
-
/**
|
|
8112
|
-
* `joinLateral` allows joining a table with a sub-query that can reference the main table of current query and the other joined tables.
|
|
8447
|
+
* This will produce an efficient SQL query:
|
|
8113
8448
|
*
|
|
8114
|
-
*
|
|
8449
|
+
* ```sql
|
|
8450
|
+
* SELECT * FROM "post"
|
|
8451
|
+
* WHERE (
|
|
8452
|
+
* SELECT count(*) = 3
|
|
8453
|
+
* FROM "tag" AS "tags"
|
|
8454
|
+
* WHERE "tag"."tagName" IN ('typescript', 'node.js')
|
|
8455
|
+
* -- join tags to the post via "postTag" table
|
|
8456
|
+
* AND EXISTS (
|
|
8457
|
+
* SELECT 1 FROM "postTag"
|
|
8458
|
+
* WHERE "postTag"."postId" = "post"."id"
|
|
8459
|
+
* AND "postTag"."tagId" = "tag"."id"
|
|
8460
|
+
* )
|
|
8461
|
+
* )
|
|
8462
|
+
* ```
|
|
8115
8463
|
*
|
|
8116
|
-
*
|
|
8464
|
+
* In the example above we use `count()`, you can also use any other aggregate method instead, such as `min`, `max`, `avg`.
|
|
8117
8465
|
*
|
|
8118
|
-
*
|
|
8466
|
+
* The `count()` is chained with `equals` to check for a strict equality, any other operation is also allowed, such as `not`, `lt`, `gt`.
|
|
8119
8467
|
*
|
|
8120
|
-
*
|
|
8468
|
+
* ## where special keys
|
|
8121
8469
|
*
|
|
8122
|
-
*
|
|
8123
|
-
* // joinLateral a Message table, alias it as `m`
|
|
8124
|
-
* // without aliasing you can refer to the message by a table name
|
|
8125
|
-
* User.joinLateral(Message.as('m'), (q) =>
|
|
8126
|
-
* q
|
|
8127
|
-
* // select message columns
|
|
8128
|
-
* .select('text')
|
|
8129
|
-
* // join the message to the user, column names can be prefixed with table names
|
|
8130
|
-
* .on('authorId', 'id')
|
|
8131
|
-
* // message columns are available without prefixing,
|
|
8132
|
-
* // outer table columns are available with a table name
|
|
8133
|
-
* .where({ text: 'some text', 'user.name': 'name' })
|
|
8134
|
-
* .order({ createdAt: 'DESC' }),
|
|
8135
|
-
* )
|
|
8136
|
-
* // only selected message columns are available in select and where
|
|
8137
|
-
* .select('id', 'name', 'm.text')
|
|
8138
|
-
* .where({ 'm.text': messageData.text });
|
|
8139
|
-
* ```
|
|
8470
|
+
* The object passed to `where` can contain special keys, each of the keys corresponds to its own method and takes the same value as the type of argument of the method.
|
|
8140
8471
|
*
|
|
8141
|
-
*
|
|
8472
|
+
* For example:
|
|
8142
8473
|
*
|
|
8143
8474
|
* ```ts
|
|
8144
|
-
*
|
|
8145
|
-
*
|
|
8146
|
-
* '
|
|
8147
|
-
*
|
|
8148
|
-
*
|
|
8149
|
-
*
|
|
8150
|
-
*
|
|
8151
|
-
*
|
|
8152
|
-
*
|
|
8153
|
-
*
|
|
8154
|
-
*
|
|
8155
|
-
* }[] = result;
|
|
8475
|
+
* db.table.where({
|
|
8476
|
+
* NOT: { key: 'value' },
|
|
8477
|
+
* OR: [{ name: 'a' }, { name: 'b' }],
|
|
8478
|
+
* IN: {
|
|
8479
|
+
* columns: ['id', 'name'],
|
|
8480
|
+
* values: [
|
|
8481
|
+
* [1, 'a'],
|
|
8482
|
+
* [2, 'b'],
|
|
8483
|
+
* ],
|
|
8484
|
+
* },
|
|
8485
|
+
* });
|
|
8156
8486
|
* ```
|
|
8157
8487
|
*
|
|
8158
|
-
* `
|
|
8488
|
+
* Using methods `whereNot`, `orWhere`, `whereIn` instead of this is a shorter and cleaner way, but in some cases, such object keys way may be more convenient.
|
|
8159
8489
|
*
|
|
8160
8490
|
* ```ts
|
|
8161
|
-
*
|
|
8162
|
-
*
|
|
8163
|
-
*
|
|
8164
|
-
*
|
|
8165
|
-
*
|
|
8491
|
+
* db.table.where({
|
|
8492
|
+
* // see .whereNot
|
|
8493
|
+
* NOT: { id: 1 },
|
|
8494
|
+
* // can be an array:
|
|
8495
|
+
* NOT: [{ id: 1 }, { id: 2 }],
|
|
8166
8496
|
*
|
|
8167
|
-
*
|
|
8168
|
-
*
|
|
8169
|
-
*
|
|
8170
|
-
* //
|
|
8171
|
-
*
|
|
8172
|
-
* }
|
|
8497
|
+
* // see .orWhere
|
|
8498
|
+
* OR: [{ name: 'a' }, { name: 'b' }],
|
|
8499
|
+
* // can be an array:
|
|
8500
|
+
* // this will give id = 1 AND id = 2 OR id = 3 AND id = 4
|
|
8501
|
+
* OR: [
|
|
8502
|
+
* [{ id: 1 }, { id: 2 }],
|
|
8503
|
+
* [{ id: 3 }, { id: 4 }],
|
|
8504
|
+
* ],
|
|
8505
|
+
*
|
|
8506
|
+
* // see .in, the key syntax requires an object with columns and values
|
|
8507
|
+
* IN: {
|
|
8508
|
+
* columns: ['id', 'name'],
|
|
8509
|
+
* values: [
|
|
8510
|
+
* [1, 'a'],
|
|
8511
|
+
* [2, 'b'],
|
|
8512
|
+
* ],
|
|
8513
|
+
* },
|
|
8514
|
+
* // can be an array:
|
|
8515
|
+
* IN: [
|
|
8516
|
+
* {
|
|
8517
|
+
* columns: ['id', 'name'],
|
|
8518
|
+
* values: [
|
|
8519
|
+
* [1, 'a'],
|
|
8520
|
+
* [2, 'b'],
|
|
8521
|
+
* ],
|
|
8522
|
+
* },
|
|
8523
|
+
* { columns: ['someColumn'], values: [['foo', 'bar']] },
|
|
8524
|
+
* ],
|
|
8525
|
+
* });
|
|
8173
8526
|
* ```
|
|
8174
8527
|
*
|
|
8175
|
-
*
|
|
8176
|
-
* @param cb - {@link JoinLateralCallback}
|
|
8177
|
-
*/
|
|
8178
|
-
joinLateral(arg, cb) {
|
|
8179
|
-
return _joinLateral(this.clone(), "JOIN", arg, cb);
|
|
8180
|
-
}
|
|
8181
|
-
/**
|
|
8182
|
-
* The same as {@link joinLateral}, but when no records found for the join it will result in `null`:
|
|
8528
|
+
* ## column operators
|
|
8183
8529
|
*
|
|
8184
|
-
*
|
|
8185
|
-
* const result = await db.user
|
|
8186
|
-
* .leftJoinLateral('messages', (q) => q.as('message'))
|
|
8187
|
-
* .select('name', 'message.text');
|
|
8530
|
+
* `where` argument can take an object where the key is the name of the operator and the value is its argument.
|
|
8188
8531
|
*
|
|
8189
|
-
*
|
|
8190
|
-
* const ok: { name: string; text: string | null }[] = result;
|
|
8191
|
-
* ```
|
|
8532
|
+
* Different types of columns support different sets of operators.
|
|
8192
8533
|
*
|
|
8193
|
-
*
|
|
8194
|
-
* @param cb - {@link JoinLateralCallback}
|
|
8195
|
-
*/
|
|
8196
|
-
leftJoinLateral(arg, cb) {
|
|
8197
|
-
return _joinLateral(this.clone(), "LEFT JOIN", arg, cb);
|
|
8198
|
-
}
|
|
8199
|
-
}
|
|
8200
|
-
const makeOnItem = (joinTo, joinFrom, args) => {
|
|
8201
|
-
return {
|
|
8202
|
-
ON: {
|
|
8203
|
-
joinTo,
|
|
8204
|
-
joinFrom,
|
|
8205
|
-
on: args
|
|
8206
|
-
}
|
|
8207
|
-
};
|
|
8208
|
-
};
|
|
8209
|
-
const pushQueryOn = (q, joinFrom, joinTo, ...on) => {
|
|
8210
|
-
return pushQueryValue(q, "and", makeOnItem(joinFrom, joinTo, on));
|
|
8211
|
-
};
|
|
8212
|
-
const pushQueryOrOn = (q, joinFrom, joinTo, ...on) => {
|
|
8213
|
-
return pushQueryValue(q, "or", [makeOnItem(joinFrom, joinTo, on)]);
|
|
8214
|
-
};
|
|
8215
|
-
const addQueryOn = (q, joinFrom, joinTo, ...args) => {
|
|
8216
|
-
const cloned = q.clone();
|
|
8217
|
-
setQueryObjectValue(
|
|
8218
|
-
cloned,
|
|
8219
|
-
"joinedShapes",
|
|
8220
|
-
joinFrom.q.as || joinFrom.table,
|
|
8221
|
-
joinFrom.q.shape
|
|
8222
|
-
);
|
|
8223
|
-
return pushQueryOn(cloned, joinFrom, joinTo, ...args);
|
|
8224
|
-
};
|
|
8225
|
-
const _queryJoinOn = (q, args) => {
|
|
8226
|
-
return pushQueryOn(q, q.q.joinTo, q, ...args);
|
|
8227
|
-
};
|
|
8228
|
-
const _queryJoinOrOn = (q, args) => {
|
|
8229
|
-
return pushQueryOrOn(q, q.q.joinTo, q, ...args);
|
|
8230
|
-
};
|
|
8231
|
-
const _queryJoinOnJsonPathEquals = (q, args) => {
|
|
8232
|
-
return pushQueryValue(q, "and", { ON: args });
|
|
8233
|
-
};
|
|
8234
|
-
class OnQueryBuilder extends WhereQueryBase {
|
|
8235
|
-
constructor(q, { shape, joinedShapes }, joinTo) {
|
|
8236
|
-
super();
|
|
8237
|
-
this.withData = orchidCore.emptyObject;
|
|
8238
|
-
this.internal = q.internal;
|
|
8239
|
-
this.table = typeof q === "object" ? q.table : q;
|
|
8240
|
-
this.shape = shape;
|
|
8241
|
-
this.q = {
|
|
8242
|
-
shape,
|
|
8243
|
-
joinedShapes
|
|
8244
|
-
};
|
|
8245
|
-
this.baseQuery = this;
|
|
8246
|
-
if (typeof q === "object" && q.q.as) {
|
|
8247
|
-
this.q.as = q.q.as;
|
|
8248
|
-
}
|
|
8249
|
-
this.q.joinTo = joinTo;
|
|
8250
|
-
}
|
|
8251
|
-
/**
|
|
8252
|
-
* Use `on` to specify columns to join records.
|
|
8534
|
+
* All column operators can take a value of the same type as the column, a sub-query, or a raw SQL expression:
|
|
8253
8535
|
*
|
|
8254
8536
|
* ```ts
|
|
8255
|
-
*
|
|
8256
|
-
* // left column is the db.message column, right column is the db.user column
|
|
8257
|
-
* .on('userId', 'id')
|
|
8258
|
-
* // table names can be provided:
|
|
8259
|
-
* .on('message.userId', 'user.id')
|
|
8260
|
-
* // operator can be specified:
|
|
8261
|
-
* .on('userId', '!=', 'id')
|
|
8262
|
-
* // operator can be specified with table names as well:
|
|
8263
|
-
* .on('message.userId', '!=', 'user.id')
|
|
8264
|
-
* // `.orOn` takes the same arguments as `.on` and acts like `.or`:
|
|
8265
|
-
* .on('userId', 'id') // where message.userId = user.id
|
|
8266
|
-
* ```
|
|
8267
|
-
*
|
|
8268
|
-
* @param args - columns to join with
|
|
8269
|
-
*/
|
|
8270
|
-
on(...args) {
|
|
8271
|
-
return _queryJoinOn(this.clone(), args);
|
|
8272
|
-
}
|
|
8273
|
-
/**
|
|
8274
|
-
* Works as {@link on}, but the added conditions will be separated from previous with `OR`.
|
|
8537
|
+
* import { sql } from 'orchid-orm';
|
|
8275
8538
|
*
|
|
8276
|
-
*
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
}
|
|
8281
|
-
/**
|
|
8282
|
-
* Use `onJsonPathEquals` to join record based on a field of their JSON column:
|
|
8539
|
+
* db.table.where({
|
|
8540
|
+
* numericColumn: {
|
|
8541
|
+
* // lower than 5
|
|
8542
|
+
* lt: 5,
|
|
8283
8543
|
*
|
|
8284
|
-
*
|
|
8285
|
-
*
|
|
8286
|
-
*
|
|
8287
|
-
*
|
|
8288
|
-
*
|
|
8544
|
+
* // lower than the value returned by sub-query
|
|
8545
|
+
* lt: OtherTable.select('someNumber').take(),
|
|
8546
|
+
*
|
|
8547
|
+
* // raw SQL expression produces WHERE "numericColumn" < "otherColumn" + 10
|
|
8548
|
+
* lt: sql`"otherColumn" + 10`,
|
|
8549
|
+
* },
|
|
8550
|
+
* });
|
|
8289
8551
|
* ```
|
|
8290
8552
|
*
|
|
8291
|
-
*
|
|
8292
|
-
*/
|
|
8293
|
-
onJsonPathEquals(...args) {
|
|
8294
|
-
return _queryJoinOnJsonPathEquals(this.clone(), args);
|
|
8295
|
-
}
|
|
8296
|
-
}
|
|
8297
|
-
|
|
8298
|
-
class JsonModifiers {
|
|
8299
|
-
/**
|
|
8300
|
-
* Return a JSON value/object/array where a given value is set at the given path.
|
|
8301
|
-
* The path is an array of keys to access the value.
|
|
8553
|
+
* ### Any type of column operators
|
|
8302
8554
|
*
|
|
8303
|
-
*
|
|
8555
|
+
* `equals` is a simple `=` operator, it may be useful for comparing column value with JSON object:
|
|
8304
8556
|
*
|
|
8305
8557
|
* ```ts
|
|
8306
|
-
*
|
|
8558
|
+
* db.table.where({
|
|
8559
|
+
* // when searching for an exact same JSON value, this won't work:
|
|
8560
|
+
* jsonColumn: someObject,
|
|
8307
8561
|
*
|
|
8308
|
-
*
|
|
8562
|
+
* // use `{ equals: ... }` instead:
|
|
8563
|
+
* jsonColumn: { equals: someObject },
|
|
8564
|
+
* });
|
|
8309
8565
|
* ```
|
|
8310
8566
|
*
|
|
8311
|
-
*
|
|
8567
|
+
* `not` is `!=` (aka `<>`) not equal operator:
|
|
8312
8568
|
*
|
|
8313
8569
|
* ```ts
|
|
8314
|
-
*
|
|
8315
|
-
*
|
|
8316
|
-
* createIfMissing: true, // ignored if missing by default
|
|
8570
|
+
* db.table.where({
|
|
8571
|
+
* anyColumn: { not: value },
|
|
8317
8572
|
* });
|
|
8318
8573
|
* ```
|
|
8319
8574
|
*
|
|
8320
|
-
*
|
|
8321
|
-
* @param path - path to value inside the json
|
|
8322
|
-
* @param value - value to set into the json
|
|
8323
|
-
* @param options - `as` to alias the json value when selecting, `createIfMissing: true` will create a new JSON property if it didn't exist before
|
|
8324
|
-
*/
|
|
8325
|
-
jsonSet(column, path, value, options) {
|
|
8326
|
-
var _a;
|
|
8327
|
-
const q = this.clone();
|
|
8328
|
-
const json = {
|
|
8329
|
-
__json: [
|
|
8330
|
-
"set",
|
|
8331
|
-
(_a = options == null ? void 0 : options.as) != null ? _a : typeof column === "string" ? column : column.__json[1],
|
|
8332
|
-
typeof column === "string" ? q.q.shape[column] : column.__json[2],
|
|
8333
|
-
column,
|
|
8334
|
-
path,
|
|
8335
|
-
value,
|
|
8336
|
-
options
|
|
8337
|
-
]
|
|
8338
|
-
};
|
|
8339
|
-
return Object.assign(
|
|
8340
|
-
pushQueryValue(q, "select", json),
|
|
8341
|
-
json
|
|
8342
|
-
);
|
|
8343
|
-
}
|
|
8344
|
-
/**
|
|
8345
|
-
* Return a JSON value/object/array where a given value is inserted at the given JSON path. Value can be a single value or JSON object. If a value exists at the given path, the value is not replaced.
|
|
8575
|
+
* `in` is for the `IN` operator to check if the column value is included in a list of values.
|
|
8346
8576
|
*
|
|
8347
|
-
*
|
|
8577
|
+
* Takes an array of the same type as a column, a sub-query that returns a list of values, or a raw SQL expression that returns a list.
|
|
8348
8578
|
*
|
|
8349
8579
|
* ```ts
|
|
8350
|
-
*
|
|
8351
|
-
*
|
|
8580
|
+
* db.table.where({
|
|
8581
|
+
* column: {
|
|
8582
|
+
* in: ['a', 'b', 'c'],
|
|
8352
8583
|
*
|
|
8353
|
-
*
|
|
8354
|
-
*
|
|
8584
|
+
* // WHERE "column" IN (SELECT "column" FROM "otherTable")
|
|
8585
|
+
* in: OtherTable.select('column'),
|
|
8586
|
+
*
|
|
8587
|
+
* in: db.table.sql`('a', 'b')`,
|
|
8588
|
+
* },
|
|
8589
|
+
* });
|
|
8355
8590
|
* ```
|
|
8356
8591
|
*
|
|
8357
|
-
*
|
|
8592
|
+
* `notIn` is for the `NOT IN` operator, and takes the same arguments as `in`
|
|
8593
|
+
*
|
|
8594
|
+
* ### Numeric, Date, and Time column operators
|
|
8595
|
+
*
|
|
8596
|
+
* To compare numbers, dates, and times.
|
|
8597
|
+
*
|
|
8598
|
+
* `lt` is for `<` (lower than)
|
|
8599
|
+
*
|
|
8600
|
+
* `lte` is for `<=` (lower than or equal)
|
|
8601
|
+
*
|
|
8602
|
+
* `gt` is for `>` (greater than)
|
|
8603
|
+
*
|
|
8604
|
+
* `gte` is for `>=` (greater than or equal)
|
|
8358
8605
|
*
|
|
8359
8606
|
* ```ts
|
|
8360
|
-
*
|
|
8361
|
-
*
|
|
8362
|
-
*
|
|
8363
|
-
*
|
|
8364
|
-
*
|
|
8365
|
-
* })
|
|
8366
|
-
* .take();
|
|
8607
|
+
* db.table.where({
|
|
8608
|
+
* numericColumn: {
|
|
8609
|
+
* gt: 5,
|
|
8610
|
+
* lt: 10,
|
|
8611
|
+
* },
|
|
8367
8612
|
*
|
|
8368
|
-
*
|
|
8369
|
-
*
|
|
8613
|
+
* date: {
|
|
8614
|
+
* lte: new Date(),
|
|
8615
|
+
* },
|
|
8616
|
+
*
|
|
8617
|
+
* time: {
|
|
8618
|
+
* gte: new Date(),
|
|
8619
|
+
* },
|
|
8620
|
+
* });
|
|
8370
8621
|
* ```
|
|
8371
|
-
* @param column - name of JSON column, or a result of a nested json method
|
|
8372
|
-
* @param path - path to the array inside the json, last path element is index to insert into
|
|
8373
|
-
* @param value - value to insert into the json array
|
|
8374
|
-
* @param options - `as` to alias the json value when selecting, `insertAfter: true` to insert after the specified position
|
|
8375
|
-
*/
|
|
8376
|
-
jsonInsert(column, path, value, options) {
|
|
8377
|
-
var _a;
|
|
8378
|
-
const q = this.clone();
|
|
8379
|
-
const json = {
|
|
8380
|
-
__json: [
|
|
8381
|
-
"insert",
|
|
8382
|
-
(_a = options == null ? void 0 : options.as) != null ? _a : typeof column === "string" ? column : column.__json[1],
|
|
8383
|
-
typeof column === "string" ? q.q.shape[column] : column.__json[2],
|
|
8384
|
-
column,
|
|
8385
|
-
path,
|
|
8386
|
-
value,
|
|
8387
|
-
options
|
|
8388
|
-
]
|
|
8389
|
-
};
|
|
8390
|
-
return Object.assign(
|
|
8391
|
-
pushQueryValue(q, "select", json),
|
|
8392
|
-
json
|
|
8393
|
-
);
|
|
8394
|
-
}
|
|
8395
|
-
/**
|
|
8396
|
-
* Return a JSON value/object/array where a given value is removed at the given JSON path.
|
|
8397
8622
|
*
|
|
8398
|
-
*
|
|
8623
|
+
* `between` also works with numeric, dates, and time columns, it takes an array of two elements.
|
|
8624
|
+
*
|
|
8625
|
+
* Both elements can be of the same type as a column, a sub-query, or a raw SQL expression.
|
|
8399
8626
|
*
|
|
8400
8627
|
* ```ts
|
|
8401
|
-
*
|
|
8402
|
-
*
|
|
8403
|
-
*
|
|
8404
|
-
*
|
|
8405
|
-
* ['tags', 0],
|
|
8406
|
-
* // optional parameters:
|
|
8407
|
-
* {
|
|
8408
|
-
* as: 'alias', // select as an alias
|
|
8409
|
-
* },
|
|
8410
|
-
* )
|
|
8411
|
-
* .take();
|
|
8628
|
+
* db.table.where({
|
|
8629
|
+
* column: {
|
|
8630
|
+
* // simple values
|
|
8631
|
+
* between: [1, 10],
|
|
8412
8632
|
*
|
|
8413
|
-
*
|
|
8633
|
+
* // sub-query and raw SQL expression
|
|
8634
|
+
* between: [OtherTable.select('column').take(), db.table.sql`2 + 2`],
|
|
8635
|
+
* },
|
|
8636
|
+
* });
|
|
8414
8637
|
* ```
|
|
8415
8638
|
*
|
|
8416
|
-
*
|
|
8417
|
-
*
|
|
8418
|
-
*
|
|
8419
|
-
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
|
|
8423
|
-
const json = {
|
|
8424
|
-
__json: [
|
|
8425
|
-
"remove",
|
|
8426
|
-
(_a = options == null ? void 0 : options.as) != null ? _a : typeof column === "string" ? column : column.__json[1],
|
|
8427
|
-
typeof column === "string" ? q.q.shape[column] : column.__json[2],
|
|
8428
|
-
column,
|
|
8429
|
-
path
|
|
8430
|
-
]
|
|
8431
|
-
};
|
|
8432
|
-
return Object.assign(
|
|
8433
|
-
pushQueryValue(q, "select", json),
|
|
8434
|
-
json
|
|
8435
|
-
);
|
|
8436
|
-
}
|
|
8437
|
-
/**
|
|
8438
|
-
* Selects a value from JSON data using a JSON path.
|
|
8639
|
+
* ### Text column operators
|
|
8640
|
+
*
|
|
8641
|
+
* For `text`, `char`, `varchar`, and `json` columns.
|
|
8642
|
+
*
|
|
8643
|
+
* `json` is stored as text, so it has text operators. Use the `jsonb` type for JSON operators.
|
|
8644
|
+
*
|
|
8645
|
+
* Takes a string, or sub-query returning string, or raw SQL expression as well as other operators.
|
|
8439
8646
|
*
|
|
8440
8647
|
* ```ts
|
|
8441
|
-
*
|
|
8648
|
+
* db.table.where({
|
|
8649
|
+
* textColumn: {
|
|
8650
|
+
* // WHERE "textColumn" LIKE '%string%'
|
|
8651
|
+
* contains: 'string',
|
|
8652
|
+
* // WHERE "textColumn" ILIKE '%string%'
|
|
8653
|
+
* containsInsensitive: 'string',
|
|
8654
|
+
* // WHERE "textColumn" LIKE 'string%'
|
|
8655
|
+
* startsWith: 'string',
|
|
8656
|
+
* // WHERE "textColumn" ILIKE 'string%'
|
|
8657
|
+
* startsWithInsensitive: 'string',
|
|
8658
|
+
* // WHERE "textColumn" LIKE '%string'
|
|
8659
|
+
* endsWith: 'string',
|
|
8660
|
+
* // WHERE "textColumn" ILIKE '%string'
|
|
8661
|
+
* endsWithInsensitive: 'string',
|
|
8662
|
+
* },
|
|
8663
|
+
* });
|
|
8664
|
+
* ```
|
|
8442
8665
|
*
|
|
8443
|
-
*
|
|
8444
|
-
* columnTypes.text(3, 100), // type of the value
|
|
8445
|
-
* 'data', // name of the JSON column
|
|
8446
|
-
* '$.name', // JSON path
|
|
8447
|
-
* 'name', // select value as name
|
|
8666
|
+
* ### JSONB column operators
|
|
8448
8667
|
*
|
|
8449
|
-
*
|
|
8450
|
-
*
|
|
8451
|
-
*
|
|
8452
|
-
*
|
|
8453
|
-
*
|
|
8668
|
+
* For the `jsonb` column, note that the `json` type has text operators instead.
|
|
8669
|
+
*
|
|
8670
|
+
* `jsonPath` operator: compare a column value under a given JSON path with the provided value.
|
|
8671
|
+
*
|
|
8672
|
+
* Value can be of any type to compare with JSON value, or it can be a sub-query or a raw SQL expression.
|
|
8673
|
+
*
|
|
8674
|
+
* ```ts
|
|
8675
|
+
* db.table.where({
|
|
8676
|
+
* jsonbColumn: {
|
|
8677
|
+
* jsonPath: [
|
|
8678
|
+
* '$.name', // first element is JSON path
|
|
8679
|
+
* '=', // second argument is comparison operator
|
|
8680
|
+
* 'value', // third argument is a value to compare with
|
|
8681
|
+
* ],
|
|
8682
|
+
* },
|
|
8683
|
+
* });
|
|
8684
|
+
* ```
|
|
8685
|
+
*
|
|
8686
|
+
* `jsonSupersetOf`: check if the column value is a superset of provided value.
|
|
8687
|
+
*
|
|
8688
|
+
* For instance, it is true if the column has JSON `{ "a": 1, "b": 2 }` and provided value is `{ "a": 1 }`.
|
|
8689
|
+
*
|
|
8690
|
+
* Takes the value of any type, or sub query which returns a single value, or a raw SQL expression.
|
|
8691
|
+
*
|
|
8692
|
+
* ```ts
|
|
8693
|
+
* db.table.where({
|
|
8694
|
+
* jsonbColumn: {
|
|
8695
|
+
* jsonSupersetOf: { a: 1 },
|
|
8454
8696
|
* },
|
|
8455
|
-
* );
|
|
8697
|
+
* });
|
|
8456
8698
|
* ```
|
|
8457
8699
|
*
|
|
8458
|
-
*
|
|
8700
|
+
* `jsonSubsetOf`: check if the column value is a subset of provided value.
|
|
8701
|
+
*
|
|
8702
|
+
* For instance, it is true if the column has JSON `{ "a": 1 }` and provided value is `{ "a": 1, "b": 2 }`.
|
|
8703
|
+
*
|
|
8704
|
+
* Takes the value of any type, or sub query which returns a single value, or a raw SQL expression.
|
|
8459
8705
|
*
|
|
8460
8706
|
* ```ts
|
|
8461
|
-
* db.table.
|
|
8462
|
-
*
|
|
8463
|
-
*
|
|
8464
|
-
*
|
|
8465
|
-
*
|
|
8466
|
-
* 'name',
|
|
8467
|
-
* );
|
|
8707
|
+
* db.table.where({
|
|
8708
|
+
* jsonbColumn: {
|
|
8709
|
+
* jsonSupersetOf: { a: 1 },
|
|
8710
|
+
* },
|
|
8711
|
+
* });
|
|
8468
8712
|
* ```
|
|
8469
8713
|
*
|
|
8470
|
-
* @param
|
|
8471
|
-
* @param column - name of JSON column, or a result of a nested json method
|
|
8472
|
-
* @param path - special JSON path string to reference a JSON value
|
|
8473
|
-
* @param as - optional alias for the selected value
|
|
8474
|
-
* @param options - supports `vars` and `silent`, check Postgres docs of `json_path_query` for these
|
|
8714
|
+
* @param args - {@link WhereArgs}
|
|
8475
8715
|
*/
|
|
8476
|
-
|
|
8477
|
-
|
|
8478
|
-
|
|
8479
|
-
|
|
8480
|
-
};
|
|
8481
|
-
return Object.assign(
|
|
8482
|
-
pushQueryValue(q, "select", json),
|
|
8483
|
-
json
|
|
8716
|
+
where(...args) {
|
|
8717
|
+
return _queryWhere(
|
|
8718
|
+
this.clone(),
|
|
8719
|
+
args
|
|
8484
8720
|
);
|
|
8485
8721
|
}
|
|
8486
|
-
}
|
|
8487
|
-
class JsonMethods {
|
|
8488
8722
|
/**
|
|
8489
|
-
*
|
|
8490
|
-
* So that JSON encoding is done on a database side, and the application doesn't have to turn a response to a JSON.
|
|
8491
|
-
* It may be better for performance in some cases.
|
|
8723
|
+
* Use a custom SQL expression in `WHERE` statement:
|
|
8492
8724
|
*
|
|
8493
8725
|
* ```ts
|
|
8494
|
-
*
|
|
8495
|
-
*
|
|
8726
|
+
* db.table.where`a = b`;
|
|
8727
|
+
*
|
|
8728
|
+
* // or
|
|
8729
|
+
* db.table.where(db.table.sql`a = b`);
|
|
8730
|
+
*
|
|
8731
|
+
* // or
|
|
8732
|
+
* import { raw } from 'orchid-orm';
|
|
8733
|
+
*
|
|
8734
|
+
* db.table.where(raw`a = b`);
|
|
8496
8735
|
* ```
|
|
8497
8736
|
*
|
|
8498
|
-
* @param
|
|
8737
|
+
* @param args - SQL expression
|
|
8499
8738
|
*/
|
|
8500
|
-
|
|
8501
|
-
return
|
|
8739
|
+
whereSql(...args) {
|
|
8740
|
+
return _queryWhereSql(
|
|
8502
8741
|
this.clone(),
|
|
8503
|
-
|
|
8742
|
+
args
|
|
8504
8743
|
);
|
|
8505
8744
|
}
|
|
8506
|
-
}
|
|
8507
|
-
|
|
8508
|
-
const logColors = {
|
|
8509
|
-
boldCyanBright: (message) => `\x1B[1m\x1B[96m${message}\x1B[39m\x1B[22m`,
|
|
8510
|
-
boldBlue: (message) => `\x1B[1m\x1B[34m${message}\x1B[39m\x1B[22m`,
|
|
8511
|
-
boldYellow: (message) => `\x1B[1m\x1B[33m${message}\x1B[39m\x1B[22m`,
|
|
8512
|
-
boldMagenta: (message) => `\x1B[1m\x1B[33m${message}\x1B[39m\x1B[22m`,
|
|
8513
|
-
boldRed: (message) => `\x1B[1m\x1B[31m${message}\x1B[39m\x1B[22m`
|
|
8514
|
-
};
|
|
8515
|
-
const makeMessage = (colors, timeColor, time, sqlColor, sql, valuesColor, values) => {
|
|
8516
|
-
const elapsed = process.hrtime(time);
|
|
8517
|
-
const formattedTime = `(${elapsed[0] ? `${elapsed[0]}s ` : ""}${(elapsed[1] / 1e6).toFixed(1)}ms)`;
|
|
8518
|
-
const result = `${colors ? timeColor(formattedTime) : formattedTime} ${colors ? sqlColor(sql) : sql}`;
|
|
8519
|
-
if (!values.length) {
|
|
8520
|
-
return result;
|
|
8521
|
-
}
|
|
8522
|
-
const formattedValues = `[${values.map(quote).join(", ")}]`;
|
|
8523
|
-
return `${result} ${colors ? valuesColor(formattedValues) : formattedValues}`;
|
|
8524
|
-
};
|
|
8525
|
-
const logParamToLogObject = (logger, log) => {
|
|
8526
|
-
if (!log)
|
|
8527
|
-
return;
|
|
8528
|
-
const logObject = Object.assign(
|
|
8529
|
-
{
|
|
8530
|
-
colors: true,
|
|
8531
|
-
beforeQuery() {
|
|
8532
|
-
return process.hrtime();
|
|
8533
|
-
},
|
|
8534
|
-
afterQuery(sql, time) {
|
|
8535
|
-
logger.log(
|
|
8536
|
-
makeMessage(
|
|
8537
|
-
colors,
|
|
8538
|
-
logColors.boldCyanBright,
|
|
8539
|
-
time,
|
|
8540
|
-
logColors.boldBlue,
|
|
8541
|
-
sql.text,
|
|
8542
|
-
logColors.boldYellow,
|
|
8543
|
-
sql.values
|
|
8544
|
-
)
|
|
8545
|
-
);
|
|
8546
|
-
},
|
|
8547
|
-
onError(error, sql, time) {
|
|
8548
|
-
const message = `Error: ${error.message}`;
|
|
8549
|
-
logger.error(
|
|
8550
|
-
`${makeMessage(
|
|
8551
|
-
colors,
|
|
8552
|
-
logColors.boldMagenta,
|
|
8553
|
-
time,
|
|
8554
|
-
logColors.boldRed,
|
|
8555
|
-
sql.text,
|
|
8556
|
-
logColors.boldYellow,
|
|
8557
|
-
sql.values
|
|
8558
|
-
)} ${colors ? logColors.boldRed(message) : message}`
|
|
8559
|
-
);
|
|
8560
|
-
}
|
|
8561
|
-
},
|
|
8562
|
-
log === true ? {} : log
|
|
8563
|
-
);
|
|
8564
|
-
const colors = logObject.colors;
|
|
8565
|
-
return logObject;
|
|
8566
|
-
};
|
|
8567
|
-
class QueryLog {
|
|
8568
|
-
log(log = true) {
|
|
8569
|
-
const q = this.clone();
|
|
8570
|
-
q.q.log = logParamToLogObject(q.q.logger, log);
|
|
8571
|
-
return q;
|
|
8572
|
-
}
|
|
8573
|
-
}
|
|
8574
|
-
|
|
8575
|
-
var __defProp$5 = Object.defineProperty;
|
|
8576
|
-
var __getOwnPropSymbols$5 = Object.getOwnPropertySymbols;
|
|
8577
|
-
var __hasOwnProp$5 = Object.prototype.hasOwnProperty;
|
|
8578
|
-
var __propIsEnum$5 = Object.prototype.propertyIsEnumerable;
|
|
8579
|
-
var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8580
|
-
var __spreadValues$5 = (a, b) => {
|
|
8581
|
-
for (var prop in b || (b = {}))
|
|
8582
|
-
if (__hasOwnProp$5.call(b, prop))
|
|
8583
|
-
__defNormalProp$5(a, prop, b[prop]);
|
|
8584
|
-
if (__getOwnPropSymbols$5)
|
|
8585
|
-
for (var prop of __getOwnPropSymbols$5(b)) {
|
|
8586
|
-
if (__propIsEnum$5.call(b, prop))
|
|
8587
|
-
__defNormalProp$5(a, prop, b[prop]);
|
|
8588
|
-
}
|
|
8589
|
-
return a;
|
|
8590
|
-
};
|
|
8591
|
-
const mergableObjects = {
|
|
8592
|
-
shape: true,
|
|
8593
|
-
withShapes: true,
|
|
8594
|
-
parsers: true,
|
|
8595
|
-
defaults: true,
|
|
8596
|
-
joinedShapes: true,
|
|
8597
|
-
joinedParsers: true
|
|
8598
|
-
};
|
|
8599
|
-
class MergeQueryMethods {
|
|
8600
|
-
merge(q) {
|
|
8601
|
-
const query = this.clone();
|
|
8602
|
-
const a = query.q;
|
|
8603
|
-
const b = q.q;
|
|
8604
|
-
for (const key in b) {
|
|
8605
|
-
const value = b[key];
|
|
8606
|
-
switch (typeof value) {
|
|
8607
|
-
case "boolean":
|
|
8608
|
-
case "string":
|
|
8609
|
-
case "number":
|
|
8610
|
-
a[key] = value;
|
|
8611
|
-
break;
|
|
8612
|
-
case "object":
|
|
8613
|
-
if (Array.isArray(value)) {
|
|
8614
|
-
a[key] = a[key] ? [...a[key], ...value] : value;
|
|
8615
|
-
} else if (mergableObjects[key]) {
|
|
8616
|
-
a[key] = a[key] ? __spreadValues$5(__spreadValues$5({}, a[key]), value) : value;
|
|
8617
|
-
} else {
|
|
8618
|
-
a[key] = value;
|
|
8619
|
-
}
|
|
8620
|
-
break;
|
|
8621
|
-
}
|
|
8622
|
-
}
|
|
8623
|
-
a[orchidCore.getValueKey] = b[orchidCore.getValueKey];
|
|
8624
|
-
if (b.returnType)
|
|
8625
|
-
a.returnType = b.returnType;
|
|
8626
|
-
return query;
|
|
8627
|
-
}
|
|
8628
|
-
}
|
|
8629
|
-
|
|
8630
|
-
var __defProp$4 = Object.defineProperty;
|
|
8631
|
-
var __defProps$2 = Object.defineProperties;
|
|
8632
|
-
var __getOwnPropDescs$2 = Object.getOwnPropertyDescriptors;
|
|
8633
|
-
var __getOwnPropSymbols$4 = Object.getOwnPropertySymbols;
|
|
8634
|
-
var __hasOwnProp$4 = Object.prototype.hasOwnProperty;
|
|
8635
|
-
var __propIsEnum$4 = Object.prototype.propertyIsEnumerable;
|
|
8636
|
-
var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8637
|
-
var __spreadValues$4 = (a, b) => {
|
|
8638
|
-
for (var prop in b || (b = {}))
|
|
8639
|
-
if (__hasOwnProp$4.call(b, prop))
|
|
8640
|
-
__defNormalProp$4(a, prop, b[prop]);
|
|
8641
|
-
if (__getOwnPropSymbols$4)
|
|
8642
|
-
for (var prop of __getOwnPropSymbols$4(b)) {
|
|
8643
|
-
if (__propIsEnum$4.call(b, prop))
|
|
8644
|
-
__defNormalProp$4(a, prop, b[prop]);
|
|
8645
|
-
}
|
|
8646
|
-
return a;
|
|
8647
|
-
};
|
|
8648
|
-
var __spreadProps$2 = (a, b) => __defProps$2(a, __getOwnPropDescs$2(b));
|
|
8649
|
-
class With {
|
|
8650
8745
|
/**
|
|
8651
|
-
*
|
|
8746
|
+
* `whereNot` takes the same argument as `where`,
|
|
8747
|
+
* multiple conditions are combined with `AND`,
|
|
8748
|
+
* the whole group of conditions is negated with `NOT`.
|
|
8652
8749
|
*
|
|
8653
8750
|
* ```ts
|
|
8654
|
-
*
|
|
8655
|
-
*
|
|
8656
|
-
*
|
|
8657
|
-
*
|
|
8658
|
-
*
|
|
8659
|
-
*
|
|
8660
|
-
* // by default all columns from provided column shape will be included
|
|
8661
|
-
* // true is for default behavior
|
|
8662
|
-
* columns?: string[] | boolean;
|
|
8663
|
-
*
|
|
8664
|
-
* // Adds RECURSIVE keyword:
|
|
8665
|
-
* recursive?: true;
|
|
8666
|
-
*
|
|
8667
|
-
* // Adds MATERIALIZED keyword:
|
|
8668
|
-
* materialized?: true;
|
|
8669
|
-
*
|
|
8670
|
-
* // Adds NOT MATERIALIZED keyword:
|
|
8671
|
-
* notMaterialized?: true;
|
|
8672
|
-
* };
|
|
8751
|
+
* // find records of different colors than red
|
|
8752
|
+
* db.table.whereNot({ color: 'red' });
|
|
8753
|
+
* // WHERE NOT color = 'red'
|
|
8754
|
+
* db.table.whereNot({ one: 1, two: 2 });
|
|
8755
|
+
* // WHERE NOT (one = 1 AND two = 2)
|
|
8756
|
+
* ```
|
|
8673
8757
|
*
|
|
8674
|
-
*
|
|
8675
|
-
|
|
8676
|
-
|
|
8677
|
-
|
|
8678
|
-
|
|
8679
|
-
|
|
8680
|
-
|
|
8681
|
-
|
|
8682
|
-
|
|
8758
|
+
* @param args - {@link WhereArgs}
|
|
8759
|
+
*/
|
|
8760
|
+
whereNot(...args) {
|
|
8761
|
+
return _queryWhereNot(
|
|
8762
|
+
this.clone(),
|
|
8763
|
+
args
|
|
8764
|
+
);
|
|
8765
|
+
}
|
|
8766
|
+
/**
|
|
8767
|
+
* `whereNot` version accepting SQL expression:
|
|
8683
8768
|
*
|
|
8684
|
-
*
|
|
8685
|
-
* db.table.
|
|
8769
|
+
* ```ts
|
|
8770
|
+
* db.table.whereNot`sql expression`
|
|
8771
|
+
* ```
|
|
8686
8772
|
*
|
|
8687
|
-
*
|
|
8688
|
-
|
|
8689
|
-
|
|
8690
|
-
|
|
8773
|
+
* @param args - SQL expression
|
|
8774
|
+
*/
|
|
8775
|
+
whereNotSql(...args) {
|
|
8776
|
+
return _queryWhereNotSql(this.clone(), args);
|
|
8777
|
+
}
|
|
8778
|
+
/**
|
|
8779
|
+
* `orWhere` is accepting the same arguments as {@link where}, joining arguments with `OR`.
|
|
8691
8780
|
*
|
|
8692
|
-
*
|
|
8693
|
-
* db.table.with(
|
|
8694
|
-
* 'alias',
|
|
8695
|
-
* {
|
|
8696
|
-
* recursive: true,
|
|
8697
|
-
* materialized: true,
|
|
8698
|
-
* },
|
|
8699
|
-
* rawOrQueryOrCallback,
|
|
8700
|
-
* );
|
|
8701
|
-
* ```
|
|
8781
|
+
* Columns in single arguments are still joined with `AND`.
|
|
8702
8782
|
*
|
|
8703
|
-
*
|
|
8783
|
+
* The database is processing `AND` before `OR`, so this should be intuitively clear.
|
|
8704
8784
|
*
|
|
8705
8785
|
* ```ts
|
|
8706
|
-
* db.table.
|
|
8786
|
+
* db.table.where({ id: 1, color: 'red' }).orWhere({ id: 2, color: 'blue' });
|
|
8787
|
+
* // equivalent:
|
|
8788
|
+
* db.table.orWhere({ id: 1, color: 'red' }, { id: 2, color: 'blue' });
|
|
8789
|
+
* ```
|
|
8707
8790
|
*
|
|
8708
|
-
*
|
|
8709
|
-
*
|
|
8710
|
-
*
|
|
8711
|
-
*
|
|
8791
|
+
* This query will produce such SQL (simplified):
|
|
8792
|
+
*
|
|
8793
|
+
* ```sql
|
|
8794
|
+
* SELECT * FROM "table"
|
|
8795
|
+
* WHERE id = 1 AND color = 'red'
|
|
8796
|
+
* OR id = 2 AND color = 'blue'
|
|
8712
8797
|
* ```
|
|
8713
8798
|
*
|
|
8714
|
-
* @param args -
|
|
8799
|
+
* @param args - {@link WhereArgs} will be joined with `OR`
|
|
8715
8800
|
*/
|
|
8716
|
-
|
|
8717
|
-
|
|
8718
|
-
let options = args.length === 3 && !orchidCore.isExpression(args[2]) || args.length === 4 ? args[1] : void 0;
|
|
8719
|
-
const last = args[args.length - 1];
|
|
8720
|
-
const query = typeof last === "function" ? last(q.queryBuilder) : last;
|
|
8721
|
-
const shape = args.length === 4 ? args[2] : orchidCore.isExpression(query) ? args[1] : query.q.shape;
|
|
8722
|
-
if ((options == null ? void 0 : options.columns) === true) {
|
|
8723
|
-
options = __spreadProps$2(__spreadValues$4({}, options), {
|
|
8724
|
-
columns: Object.keys(shape)
|
|
8725
|
-
});
|
|
8726
|
-
}
|
|
8727
|
-
pushQueryValue(q, "with", [args[0], options || orchidCore.emptyObject, query]);
|
|
8728
|
-
return setQueryObjectValue(q, "withShapes", args[0], shape);
|
|
8801
|
+
orWhere(...args) {
|
|
8802
|
+
return _queryOr(this.clone(), args);
|
|
8729
8803
|
}
|
|
8730
|
-
}
|
|
8731
|
-
|
|
8732
|
-
class Union {
|
|
8733
8804
|
/**
|
|
8734
|
-
*
|
|
8735
|
-
*
|
|
8805
|
+
* `orWhereNot` takes the same arguments as {@link orWhere}, and prepends each condition with `NOT` just as {@link whereNot} does.
|
|
8806
|
+
*
|
|
8807
|
+
* @param args - {@link WhereArgs} will be prefixed with `NOT` and joined with `OR`
|
|
8808
|
+
*/
|
|
8809
|
+
orWhereNot(...args) {
|
|
8810
|
+
return _queryOrNot(
|
|
8811
|
+
this.clone(),
|
|
8812
|
+
args
|
|
8813
|
+
);
|
|
8814
|
+
}
|
|
8815
|
+
/**
|
|
8816
|
+
* `whereIn` and related methods are for the `IN` operator to check for inclusion in a list of values.
|
|
8817
|
+
*
|
|
8818
|
+
* When used with a single column it works equivalent to the `in` column operator:
|
|
8736
8819
|
*
|
|
8737
8820
|
* ```ts
|
|
8738
|
-
*
|
|
8821
|
+
* db.table.whereIn('column', [1, 2, 3]);
|
|
8822
|
+
* // the same as:
|
|
8823
|
+
* db.table.where({ column: [1, 2, 3] });
|
|
8824
|
+
* ```
|
|
8825
|
+
*
|
|
8826
|
+
* `whereIn` can support a tuple of columns, that's what the `in` operator cannot support:
|
|
8827
|
+
*
|
|
8828
|
+
* ```ts
|
|
8829
|
+
* db.table.whereIn(
|
|
8830
|
+
* ['id', 'name'],
|
|
8739
8831
|
* [
|
|
8740
|
-
*
|
|
8741
|
-
*
|
|
8832
|
+
* [1, 'Alice'],
|
|
8833
|
+
* [2, 'Bob'],
|
|
8742
8834
|
* ],
|
|
8743
|
-
* true, // optional wrap parameter
|
|
8744
8835
|
* );
|
|
8745
8836
|
* ```
|
|
8746
8837
|
*
|
|
8747
|
-
*
|
|
8748
|
-
*
|
|
8838
|
+
* It supports sub query which should return records with columns of the same type:
|
|
8839
|
+
*
|
|
8840
|
+
* ```ts
|
|
8841
|
+
* db.table.whereIn(['id', 'name'], OtherTable.select('id', 'name'));
|
|
8842
|
+
* ```
|
|
8843
|
+
*
|
|
8844
|
+
* It supports raw SQL expression:
|
|
8845
|
+
*
|
|
8846
|
+
* ```ts
|
|
8847
|
+
* db.table.whereIn(['id', 'name'], db.table.sql`((1, 'one'), (2, 'two'))`);
|
|
8848
|
+
* ```
|
|
8749
8849
|
*/
|
|
8750
|
-
|
|
8751
|
-
return
|
|
8850
|
+
whereIn(...args) {
|
|
8851
|
+
return _queryWhereIn(
|
|
8752
8852
|
this.clone(),
|
|
8753
|
-
|
|
8754
|
-
args
|
|
8853
|
+
true,
|
|
8854
|
+
args[0],
|
|
8855
|
+
args[1]
|
|
8755
8856
|
);
|
|
8756
8857
|
}
|
|
8757
8858
|
/**
|
|
8758
|
-
*
|
|
8859
|
+
* Takes the same arguments as {@link whereIn}.
|
|
8860
|
+
* Add a `WHERE IN` condition prefixed with `OR` to the query:
|
|
8759
8861
|
*
|
|
8760
|
-
*
|
|
8761
|
-
*
|
|
8862
|
+
* ```ts
|
|
8863
|
+
* db.table.whereIn('a', [1, 2, 3]).orWhereIn('b', ['one', 'two']);
|
|
8864
|
+
* ```
|
|
8762
8865
|
*/
|
|
8763
|
-
|
|
8764
|
-
return
|
|
8866
|
+
orWhereIn(...args) {
|
|
8867
|
+
return _queryWhereIn(
|
|
8765
8868
|
this.clone(),
|
|
8766
|
-
|
|
8767
|
-
args
|
|
8869
|
+
false,
|
|
8870
|
+
args[0],
|
|
8871
|
+
args[1]
|
|
8768
8872
|
);
|
|
8769
8873
|
}
|
|
8770
8874
|
/**
|
|
8771
|
-
*
|
|
8875
|
+
* Acts as `whereIn`, but negates the condition with `NOT`:
|
|
8772
8876
|
*
|
|
8773
|
-
*
|
|
8774
|
-
*
|
|
8877
|
+
* ```ts
|
|
8878
|
+
* db.table.whereNotIn('color', ['red', 'green', 'blue']);
|
|
8879
|
+
* ```
|
|
8775
8880
|
*/
|
|
8776
|
-
|
|
8777
|
-
return
|
|
8881
|
+
whereNotIn(...args) {
|
|
8882
|
+
return _queryWhereIn(
|
|
8778
8883
|
this.clone(),
|
|
8779
|
-
|
|
8780
|
-
args
|
|
8884
|
+
true,
|
|
8885
|
+
args[0],
|
|
8886
|
+
args[1],
|
|
8887
|
+
true
|
|
8781
8888
|
);
|
|
8782
8889
|
}
|
|
8783
8890
|
/**
|
|
8784
|
-
*
|
|
8891
|
+
* Acts as `whereIn`, but prepends `OR` to the condition and negates it with `NOT`:
|
|
8785
8892
|
*
|
|
8786
|
-
*
|
|
8787
|
-
*
|
|
8893
|
+
* ```ts
|
|
8894
|
+
* db.table.whereNotIn('a', [1, 2, 3]).orWhereNoIn('b', ['one', 'two']);
|
|
8895
|
+
* ```
|
|
8788
8896
|
*/
|
|
8789
|
-
|
|
8790
|
-
return
|
|
8897
|
+
orWhereNotIn(...args) {
|
|
8898
|
+
return _queryWhereIn(
|
|
8791
8899
|
this.clone(),
|
|
8792
|
-
|
|
8793
|
-
args
|
|
8900
|
+
false,
|
|
8901
|
+
args[0],
|
|
8902
|
+
args[1],
|
|
8903
|
+
true
|
|
8794
8904
|
);
|
|
8795
8905
|
}
|
|
8796
8906
|
/**
|
|
8797
|
-
*
|
|
8907
|
+
* `whereExists` is for support of the `WHERE EXISTS (query)` clause.
|
|
8798
8908
|
*
|
|
8799
|
-
*
|
|
8800
|
-
*
|
|
8909
|
+
* This method is accepting the same arguments as `join`, see the {@link Join.join} section for more details.
|
|
8910
|
+
*
|
|
8911
|
+
* ```ts
|
|
8912
|
+
* // find users who have accounts
|
|
8913
|
+
* // find by a relation name if it's defined
|
|
8914
|
+
* db.user.whereExists('account');
|
|
8915
|
+
*
|
|
8916
|
+
* // find using a table and a join conditions
|
|
8917
|
+
* db.user.whereExists(db.account, 'account.id', 'user.id');
|
|
8918
|
+
*
|
|
8919
|
+
* // find using a query builder in a callback:
|
|
8920
|
+
* db.user.whereExists(db.account, (q) => q.on('account.id', '=', 'user.id'));
|
|
8921
|
+
* ```
|
|
8801
8922
|
*/
|
|
8802
|
-
|
|
8803
|
-
return
|
|
8923
|
+
whereExists(arg, ...args) {
|
|
8924
|
+
return _queryWhereExists(
|
|
8804
8925
|
this.clone(),
|
|
8805
|
-
|
|
8806
|
-
args
|
|
8926
|
+
arg,
|
|
8927
|
+
args
|
|
8807
8928
|
);
|
|
8808
8929
|
}
|
|
8809
8930
|
/**
|
|
8810
|
-
*
|
|
8931
|
+
* Acts as `whereExists`, but prepends the condition with `OR`:
|
|
8811
8932
|
*
|
|
8812
|
-
*
|
|
8813
|
-
*
|
|
8933
|
+
* ```ts
|
|
8934
|
+
* // find users who have an account or a profile,
|
|
8935
|
+
* // imagine that the user has both `account` and `profile` relations defined.
|
|
8936
|
+
* db.user.whereExist('account').orWhereExists('profile');
|
|
8937
|
+
* ```
|
|
8814
8938
|
*/
|
|
8815
|
-
|
|
8816
|
-
|
|
8817
|
-
|
|
8818
|
-
|
|
8819
|
-
|
|
8939
|
+
orWhereExists(arg, ...args) {
|
|
8940
|
+
const q = this.clone();
|
|
8941
|
+
return _queryOr(q, existsArgs(q, arg, args));
|
|
8942
|
+
}
|
|
8943
|
+
/**
|
|
8944
|
+
* Acts as `whereExists`, but negates the condition with `NOT`:
|
|
8945
|
+
*
|
|
8946
|
+
* ```ts
|
|
8947
|
+
* // find users who don't have an account,
|
|
8948
|
+
* // image that the user `belongsTo` or `hasOne` account.
|
|
8949
|
+
* db.user.whereNotExist('account');
|
|
8950
|
+
* ```
|
|
8951
|
+
*
|
|
8952
|
+
* @param arg - relation name, or a query object, or a `with` table alias, or a callback returning a query object.
|
|
8953
|
+
* @param args - no arguments needed when the first argument is a relation name, or conditions to join the table with.
|
|
8954
|
+
*/
|
|
8955
|
+
whereNotExists(arg, ...args) {
|
|
8956
|
+
const q = this.clone();
|
|
8957
|
+
return _queryWhereNot(
|
|
8958
|
+
q,
|
|
8959
|
+
existsArgs(q, arg, args)
|
|
8820
8960
|
);
|
|
8821
8961
|
}
|
|
8962
|
+
/**
|
|
8963
|
+
* Acts as `whereExists`, but prepends the condition with `OR` and negates it with `NOT`:
|
|
8964
|
+
*
|
|
8965
|
+
* ```ts
|
|
8966
|
+
* // find users who don't have an account OR who don't have a profile
|
|
8967
|
+
* // imagine that the user has both `account` and `profile` relations defined.
|
|
8968
|
+
* db.user.whereNotExists('account').orWhereNotExists('profile');
|
|
8969
|
+
* ```
|
|
8970
|
+
*/
|
|
8971
|
+
orWhereNotExists(arg, ...args) {
|
|
8972
|
+
const q = this.clone();
|
|
8973
|
+
return _queryOrNot(q, existsArgs(q, arg, args));
|
|
8974
|
+
}
|
|
8822
8975
|
}
|
|
8823
8976
|
|
|
8824
8977
|
var __defProp$3 = Object.defineProperty;
|
|
@@ -9822,6 +9975,22 @@ class RawSqlMethods {
|
|
|
9822
9975
|
}
|
|
9823
9976
|
}
|
|
9824
9977
|
|
|
9978
|
+
class QueryBase {
|
|
9979
|
+
constructor() {
|
|
9980
|
+
this.q = {};
|
|
9981
|
+
}
|
|
9982
|
+
/**
|
|
9983
|
+
* Clones the current query chain, useful for re-using partial query snippets in other queries without mutating the original.
|
|
9984
|
+
*
|
|
9985
|
+
* Used under the hood, and not really needed on the app side.
|
|
9986
|
+
*/
|
|
9987
|
+
clone() {
|
|
9988
|
+
const cloned = Object.create(this.baseQuery);
|
|
9989
|
+
cloned.q = getClonedQueryData(this.q);
|
|
9990
|
+
return cloned;
|
|
9991
|
+
}
|
|
9992
|
+
}
|
|
9993
|
+
|
|
9825
9994
|
class TransformMethods {
|
|
9826
9995
|
/**
|
|
9827
9996
|
* Transform the result of the query right after loading it.
|
|
@@ -10659,7 +10828,7 @@ orchidCore.applyMixins(QueryMethods, [
|
|
|
10659
10828
|
Select,
|
|
10660
10829
|
From,
|
|
10661
10830
|
Join,
|
|
10662
|
-
|
|
10831
|
+
OnMethods,
|
|
10663
10832
|
With,
|
|
10664
10833
|
Union,
|
|
10665
10834
|
JsonModifiers,
|
|
@@ -10743,12 +10912,14 @@ class Db {
|
|
|
10743
10912
|
const { snakeCase } = options;
|
|
10744
10913
|
for (const key in shape) {
|
|
10745
10914
|
const column = shape[key];
|
|
10915
|
+
column.data.key = key;
|
|
10746
10916
|
if (column.parseFn) {
|
|
10747
10917
|
hasParsers = true;
|
|
10748
10918
|
parsers[key] = column.parseFn;
|
|
10749
10919
|
}
|
|
10750
|
-
|
|
10751
|
-
|
|
10920
|
+
const { modifyQuery: mq } = column.data;
|
|
10921
|
+
if (mq) {
|
|
10922
|
+
modifyQuery = orchidCore.pushOrNewArray(modifyQuery, (q) => mq(q, column));
|
|
10752
10923
|
}
|
|
10753
10924
|
if (column.data.name) {
|
|
10754
10925
|
hasCustomName = true;
|
|
@@ -10829,7 +11000,11 @@ class Db {
|
|
|
10829
11000
|
return toSQL.call(q, options2);
|
|
10830
11001
|
} : toSQL;
|
|
10831
11002
|
this.relations = {};
|
|
10832
|
-
|
|
11003
|
+
if (modifyQuery) {
|
|
11004
|
+
for (const cb of modifyQuery) {
|
|
11005
|
+
cb(this);
|
|
11006
|
+
}
|
|
11007
|
+
}
|
|
10833
11008
|
this.error = class extends QueryError {
|
|
10834
11009
|
constructor(message) {
|
|
10835
11010
|
super(self, message);
|
|
@@ -10960,7 +11135,6 @@ const performQuery = async (q, args, method) => {
|
|
|
10960
11135
|
};
|
|
10961
11136
|
orchidCore.applyMixins(Db, [QueryMethods]);
|
|
10962
11137
|
Db.prototype.constructor = Db;
|
|
10963
|
-
Db.prototype.onQueryBuilder = OnQueryBuilder;
|
|
10964
11138
|
const createDb = (_a) => {
|
|
10965
11139
|
var _b = _a, {
|
|
10966
11140
|
log,
|
|
@@ -11207,7 +11381,7 @@ exports.NotFoundError = NotFoundError;
|
|
|
11207
11381
|
exports.NumberAsStringBaseColumn = NumberAsStringBaseColumn;
|
|
11208
11382
|
exports.NumberBaseColumn = NumberBaseColumn;
|
|
11209
11383
|
exports.OnConflictQueryBuilder = OnConflictQueryBuilder;
|
|
11210
|
-
exports.
|
|
11384
|
+
exports.OnMethods = OnMethods;
|
|
11211
11385
|
exports.Operators = Operators;
|
|
11212
11386
|
exports.OrchidOrmError = OrchidOrmError;
|
|
11213
11387
|
exports.OrchidOrmInternalError = OrchidOrmInternalError;
|
|
@@ -11249,7 +11423,6 @@ exports.Update = Update;
|
|
|
11249
11423
|
exports.VarCharColumn = VarCharColumn;
|
|
11250
11424
|
exports.VirtualColumn = VirtualColumn;
|
|
11251
11425
|
exports.Where = Where;
|
|
11252
|
-
exports.WhereQueryBase = WhereQueryBase;
|
|
11253
11426
|
exports.With = With;
|
|
11254
11427
|
exports.XMLColumn = XMLColumn;
|
|
11255
11428
|
exports._queryAfterSaveCommit = _queryAfterSaveCommit;
|
|
@@ -11301,6 +11474,7 @@ exports._queryUpdate = _queryUpdate;
|
|
|
11301
11474
|
exports._queryUpdateOrThrow = _queryUpdateOrThrow;
|
|
11302
11475
|
exports._queryUpdateRaw = _queryUpdateRaw;
|
|
11303
11476
|
exports._queryWhere = _queryWhere;
|
|
11477
|
+
exports._queryWhereExists = _queryWhereExists;
|
|
11304
11478
|
exports._queryWhereIn = _queryWhereIn;
|
|
11305
11479
|
exports._queryWhereNot = _queryWhereNot;
|
|
11306
11480
|
exports._queryWhereNotSql = _queryWhereNotSql;
|