pqb 0.27.0 → 0.27.2
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 +196 -176
- package/dist/index.js +1913 -1733
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1912 -1733
- 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) {
|
|
@@ -2572,11 +2666,17 @@ class From {
|
|
|
2572
2666
|
function queryWrap(self, query, as = "t") {
|
|
2573
2667
|
return _queryAs(queryFrom(query, self), as);
|
|
2574
2668
|
}
|
|
2669
|
+
function cloneQueryBaseUnscoped(query) {
|
|
2670
|
+
const q = query.baseQuery.clone();
|
|
2671
|
+
q.q.or = q.q.and = void 0;
|
|
2672
|
+
q.q.scopes = orchidCore.emptyObject;
|
|
2673
|
+
return q;
|
|
2674
|
+
}
|
|
2575
2675
|
|
|
2576
2676
|
function queryJson(self, coalesce) {
|
|
2577
2677
|
const q = queryWrap(
|
|
2578
2678
|
self,
|
|
2579
|
-
self
|
|
2679
|
+
cloneQueryBaseUnscoped(self)
|
|
2580
2680
|
);
|
|
2581
2681
|
_queryGetOptional(
|
|
2582
2682
|
q,
|
|
@@ -3280,7 +3380,7 @@ const processValue = (ctx, table, QueryClass, key, value, quotedAs) => {
|
|
|
3280
3380
|
};
|
|
3281
3381
|
|
|
3282
3382
|
const pushDeleteSql = (ctx, table, query, quotedAs) => {
|
|
3283
|
-
var _a, _b, _c;
|
|
3383
|
+
var _a, _b, _c, _d;
|
|
3284
3384
|
const from = `"${table.table}"`;
|
|
3285
3385
|
ctx.sql.push(`DELETE FROM ${from}`);
|
|
3286
3386
|
if (from !== quotedAs) {
|
|
@@ -3288,28 +3388,40 @@ const pushDeleteSql = (ctx, table, query, quotedAs) => {
|
|
|
3288
3388
|
}
|
|
3289
3389
|
let conditions;
|
|
3290
3390
|
if ((_a = query.join) == null ? void 0 : _a.length) {
|
|
3291
|
-
const
|
|
3391
|
+
const targets = [];
|
|
3392
|
+
const ons = [];
|
|
3292
3393
|
const joinSet = query.join.length > 1 ? /* @__PURE__ */ new Set() : null;
|
|
3293
3394
|
for (const item of query.join) {
|
|
3294
|
-
if (
|
|
3295
|
-
const
|
|
3296
|
-
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}`;
|
|
3297
3407
|
if (joinSet) {
|
|
3298
3408
|
if (joinSet.has(key))
|
|
3299
3409
|
continue;
|
|
3300
3410
|
joinSet.add(key);
|
|
3301
3411
|
}
|
|
3302
|
-
|
|
3412
|
+
targets.push(join.target);
|
|
3413
|
+
if (join.on)
|
|
3414
|
+
ons.push(join.on);
|
|
3303
3415
|
}
|
|
3304
3416
|
}
|
|
3305
|
-
if (
|
|
3306
|
-
ctx.sql.push(`USING ${
|
|
3307
|
-
conditions = items.map((item) => item.conditions).filter(Boolean).join(" AND ");
|
|
3417
|
+
if (targets.length) {
|
|
3418
|
+
ctx.sql.push(`USING ${targets.join(", ")}`);
|
|
3308
3419
|
}
|
|
3420
|
+
conditions = ons.join(" AND ");
|
|
3309
3421
|
}
|
|
3310
3422
|
pushWhereStatementSql(ctx, table, query, quotedAs);
|
|
3311
|
-
if (conditions
|
|
3312
|
-
if (((
|
|
3423
|
+
if (conditions) {
|
|
3424
|
+
if (((_c = query.and) == null ? void 0 : _c.length) || ((_d = query.or) == null ? void 0 : _d.length)) {
|
|
3313
3425
|
ctx.sql.push("AND", conditions);
|
|
3314
3426
|
} else {
|
|
3315
3427
|
ctx.sql.push("WHERE", conditions);
|
|
@@ -6875,1944 +6987,1996 @@ class QueryHooks {
|
|
|
6875
6987
|
}
|
|
6876
6988
|
}
|
|
6877
6989
|
|
|
6878
|
-
class
|
|
6879
|
-
constructor() {
|
|
6880
|
-
this.q = {};
|
|
6881
|
-
}
|
|
6990
|
+
class Join {
|
|
6882
6991
|
/**
|
|
6883
|
-
*
|
|
6992
|
+
* ## Select relation
|
|
6884
6993
|
*
|
|
6885
|
-
*
|
|
6886
|
-
*/
|
|
6887
|
-
clone() {
|
|
6888
|
-
const cloned = Object.create(this.baseQuery);
|
|
6889
|
-
cloned.q = getClonedQueryData(this.q);
|
|
6890
|
-
return cloned;
|
|
6891
|
-
}
|
|
6892
|
-
}
|
|
6893
|
-
|
|
6894
|
-
const _queryWhere = (q, args) => {
|
|
6895
|
-
return pushQueryArray(
|
|
6896
|
-
q,
|
|
6897
|
-
"and",
|
|
6898
|
-
args
|
|
6899
|
-
);
|
|
6900
|
-
};
|
|
6901
|
-
const _queryWhereSql = (q, args) => {
|
|
6902
|
-
return pushQueryValue(
|
|
6903
|
-
q,
|
|
6904
|
-
"and",
|
|
6905
|
-
sqlQueryArgsToExpression(args)
|
|
6906
|
-
);
|
|
6907
|
-
};
|
|
6908
|
-
const _queryWhereNot = (q, args) => {
|
|
6909
|
-
return pushQueryValue(q, "and", {
|
|
6910
|
-
NOT: args
|
|
6911
|
-
});
|
|
6912
|
-
};
|
|
6913
|
-
const _queryWhereNotSql = (q, args) => {
|
|
6914
|
-
return pushQueryValue(q, "and", {
|
|
6915
|
-
NOT: sqlQueryArgsToExpression(args)
|
|
6916
|
-
});
|
|
6917
|
-
};
|
|
6918
|
-
const _queryOr = (q, args) => {
|
|
6919
|
-
return pushQueryArray(
|
|
6920
|
-
q,
|
|
6921
|
-
"or",
|
|
6922
|
-
args.map((item) => [item])
|
|
6923
|
-
);
|
|
6924
|
-
};
|
|
6925
|
-
const _queryOrNot = (q, args) => {
|
|
6926
|
-
return pushQueryArray(
|
|
6927
|
-
q,
|
|
6928
|
-
"or",
|
|
6929
|
-
args.map((item) => [{ NOT: item }])
|
|
6930
|
-
);
|
|
6931
|
-
};
|
|
6932
|
-
const _queryWhereIn = (q, and, arg, values, not) => {
|
|
6933
|
-
let item;
|
|
6934
|
-
if (values) {
|
|
6935
|
-
if (Array.isArray(arg)) {
|
|
6936
|
-
item = {
|
|
6937
|
-
IN: {
|
|
6938
|
-
columns: arg,
|
|
6939
|
-
values
|
|
6940
|
-
}
|
|
6941
|
-
};
|
|
6942
|
-
} else {
|
|
6943
|
-
item = { [arg]: { in: values } };
|
|
6944
|
-
}
|
|
6945
|
-
} else {
|
|
6946
|
-
item = {};
|
|
6947
|
-
for (const key in arg) {
|
|
6948
|
-
item[key] = { in: arg[key] };
|
|
6949
|
-
}
|
|
6950
|
-
}
|
|
6951
|
-
if (not)
|
|
6952
|
-
item = { NOT: item };
|
|
6953
|
-
if (and) {
|
|
6954
|
-
pushQueryValue(q, "and", item);
|
|
6955
|
-
} else {
|
|
6956
|
-
pushQueryValue(q, "or", [item]);
|
|
6957
|
-
}
|
|
6958
|
-
return q;
|
|
6959
|
-
};
|
|
6960
|
-
const existsArgs = (q, args) => {
|
|
6961
|
-
let isSubQuery;
|
|
6962
|
-
if (typeof q === "object") {
|
|
6963
|
-
isSubQuery = getIsJoinSubQuery(q.q, q.baseQuery.q);
|
|
6964
|
-
if (isSubQuery) {
|
|
6965
|
-
q = q.clone();
|
|
6966
|
-
q.shape = getShapeFromSelect(q, true);
|
|
6967
|
-
}
|
|
6968
|
-
} else {
|
|
6969
|
-
isSubQuery = false;
|
|
6970
|
-
}
|
|
6971
|
-
return [
|
|
6972
|
-
{
|
|
6973
|
-
EXISTS: {
|
|
6974
|
-
first: q,
|
|
6975
|
-
args,
|
|
6976
|
-
isSubQuery
|
|
6977
|
-
}
|
|
6978
|
-
}
|
|
6979
|
-
];
|
|
6980
|
-
};
|
|
6981
|
-
class Where {
|
|
6982
|
-
/**
|
|
6983
|
-
* Constructing `WHERE` conditions:
|
|
6994
|
+
* Before joining a table, consider if selecting a relation is enough for your case:
|
|
6984
6995
|
*
|
|
6985
6996
|
* ```ts
|
|
6986
|
-
*
|
|
6987
|
-
*
|
|
6988
|
-
*
|
|
6989
|
-
*
|
|
6990
|
-
*
|
|
6991
|
-
* 'table.lastName': 'Johnsonuk',
|
|
6997
|
+
* // select users with profiles
|
|
6998
|
+
* // result type is Array<{ name: string, profile: Profile }>
|
|
6999
|
+
* await db.user.select('name', {
|
|
7000
|
+
* profile: (q) => q.profile,
|
|
7001
|
+
* });
|
|
6992
7002
|
*
|
|
6993
|
-
*
|
|
6994
|
-
*
|
|
6995
|
-
*
|
|
6996
|
-
*
|
|
6997
|
-
*
|
|
7003
|
+
* // select posts with counts of comments, order by comments count
|
|
7004
|
+
* // result type is Array<Post & { commentsCount: number }>
|
|
7005
|
+
* await db.post
|
|
7006
|
+
* .select('*', {
|
|
7007
|
+
* commentsCount: (q) => q.comments.count(),
|
|
7008
|
+
* })
|
|
7009
|
+
* .order({
|
|
7010
|
+
* commentsCount: 'DESC',
|
|
7011
|
+
* });
|
|
6998
7012
|
*
|
|
6999
|
-
*
|
|
7000
|
-
*
|
|
7013
|
+
* // select authors with array of their book titles
|
|
7014
|
+
* // result type is Array<Author & { books: string[] }>
|
|
7015
|
+
* await db.author.select('*', {
|
|
7016
|
+
* books: (q) => q.books.pluck('title'),
|
|
7001
7017
|
* });
|
|
7002
7018
|
* ```
|
|
7003
7019
|
*
|
|
7004
|
-
*
|
|
7020
|
+
* Internally, such selects will use `LEFT JOIN LATERAL` to join a relation.
|
|
7021
|
+
* 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.
|
|
7022
|
+
* If you want to load only users that have profiles, and filter out the rest, add `.join()` method to the relation without arguments:
|
|
7005
7023
|
*
|
|
7006
7024
|
* ```ts
|
|
7007
|
-
*
|
|
7008
|
-
*
|
|
7025
|
+
* // load only users who have a profile
|
|
7026
|
+
* await db.user.select('*', {
|
|
7027
|
+
* profile: (q) => q.profile.join(),
|
|
7028
|
+
* });
|
|
7009
7029
|
*
|
|
7010
|
-
*
|
|
7011
|
-
*
|
|
7030
|
+
* // load only users who have a specific profile
|
|
7031
|
+
* await db.user.select('*', {
|
|
7032
|
+
* profile: (q) => q.profile.join().where({ age: { gt: 20 } }),
|
|
7033
|
+
* });
|
|
7012
7034
|
* ```
|
|
7013
7035
|
*
|
|
7014
|
-
* `
|
|
7036
|
+
* You can also use this `.join()` method on the one-to-many relations, and records with empty array will be filtered out:
|
|
7015
7037
|
*
|
|
7016
7038
|
* ```ts
|
|
7017
|
-
*
|
|
7018
|
-
*
|
|
7019
|
-
*
|
|
7020
|
-
*
|
|
7021
|
-
*
|
|
7022
|
-
* const loadRecords = async (params: Params) => {
|
|
7023
|
-
* // this will load all records if params is an empty object
|
|
7024
|
-
* const records = await db.table.where(params);
|
|
7025
|
-
* };
|
|
7039
|
+
* // posts that have no tags won't be loaded
|
|
7040
|
+
* // result type is Array<Post & { tags: Tag[] }>
|
|
7041
|
+
* db.post.select('*', {
|
|
7042
|
+
* tags: (q) => q.tags.join(),
|
|
7043
|
+
* });
|
|
7026
7044
|
* ```
|
|
7027
7045
|
*
|
|
7028
|
-
*
|
|
7046
|
+
* # Joins
|
|
7029
7047
|
*
|
|
7030
|
-
*
|
|
7031
|
-
* db.table.where({
|
|
7032
|
-
* // compare `someColumn` in one table with the `column` value returned from another query.
|
|
7033
|
-
* someColumn: db.otherTable.where(...conditions).get('column'),
|
|
7034
|
-
* });
|
|
7035
|
-
* ```
|
|
7048
|
+
* `join` methods allows to join other tables, relations by name, [with](/guide/advanced-queries#with) statements, sub queries.
|
|
7036
7049
|
*
|
|
7037
|
-
* `
|
|
7050
|
+
* 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.
|
|
7051
|
+
*
|
|
7052
|
+
* 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:
|
|
7038
7053
|
*
|
|
7039
7054
|
* ```ts
|
|
7040
|
-
*
|
|
7055
|
+
* export class UserTable extends BaseTable {
|
|
7056
|
+
* readonly table = 'user';
|
|
7057
|
+
* columns = this.setColumns((t) => ({
|
|
7058
|
+
* id: t.identity().primaryKey(),
|
|
7059
|
+
* name: t.text(),
|
|
7060
|
+
* }));
|
|
7041
7061
|
*
|
|
7042
|
-
*
|
|
7043
|
-
*
|
|
7062
|
+
* relations = {
|
|
7063
|
+
* messages: this.hasMany(() => MessageTable, {
|
|
7064
|
+
* primaryKey: 'id',
|
|
7065
|
+
* foreignKey: 'userId',
|
|
7066
|
+
* }),
|
|
7067
|
+
* };
|
|
7068
|
+
* }
|
|
7069
|
+
*
|
|
7070
|
+
* export class MessageTable extends BaseTable {
|
|
7071
|
+
* readonly table = 'message';
|
|
7072
|
+
* columns = this.setColumns((t) => ({
|
|
7073
|
+
* id: t.identity().primaryKey(),
|
|
7074
|
+
* text: t.text(),
|
|
7075
|
+
* ...t.timestamps(),
|
|
7076
|
+
* }));
|
|
7077
|
+
*
|
|
7078
|
+
* relations = {
|
|
7079
|
+
* user: this.belongsTo(() => UserTable, {
|
|
7080
|
+
* primaryKey: 'id',
|
|
7081
|
+
* foreignKey: 'userId',
|
|
7082
|
+
* }),
|
|
7083
|
+
* };
|
|
7084
|
+
* }
|
|
7044
7085
|
* ```
|
|
7045
7086
|
*
|
|
7046
|
-
*
|
|
7087
|
+
* ## join
|
|
7047
7088
|
*
|
|
7048
|
-
*
|
|
7049
|
-
* db.table.where(db.table.sql`a = b`);
|
|
7089
|
+
* `join` is a method for SQL `JOIN`, which is equivalent to `INNER JOIN`, `LEFT INNERT JOIN`.
|
|
7050
7090
|
*
|
|
7051
|
-
*
|
|
7052
|
-
* import { raw } from 'orchid-orm';
|
|
7091
|
+
* When no matching record is found, it will skip records of the main table.
|
|
7053
7092
|
*
|
|
7054
|
-
*
|
|
7093
|
+
* When joining the same table with the same condition more than once, duplicated joins will be ignored:
|
|
7094
|
+
*
|
|
7095
|
+
* ```ts
|
|
7096
|
+
* // joining a relation
|
|
7097
|
+
* db.post.join('comments').join('comments');
|
|
7098
|
+
*
|
|
7099
|
+
* // joining a table with a condition
|
|
7100
|
+
* db.post
|
|
7101
|
+
* .join('comments', 'comments.postId', 'post.id')
|
|
7102
|
+
* .join('comments', 'comments.postId', 'post.id');
|
|
7055
7103
|
* ```
|
|
7056
7104
|
*
|
|
7057
|
-
*
|
|
7105
|
+
* Both queries will produce SQL with only 1 join
|
|
7058
7106
|
*
|
|
7059
|
-
* ```
|
|
7060
|
-
*
|
|
7061
|
-
* q
|
|
7062
|
-
* .where({ name: 'Name' })
|
|
7063
|
-
* .orWhere({ id: 1 }, { id: 2 })
|
|
7064
|
-
* .whereIn('letter', ['a', 'b', 'c'])
|
|
7065
|
-
* .whereExists(Message, 'authorId', 'id'),
|
|
7066
|
-
* );
|
|
7107
|
+
* ```sql
|
|
7108
|
+
* SELECT * FROM post JOIN comments ON comments.postId = post.id
|
|
7067
7109
|
* ```
|
|
7068
7110
|
*
|
|
7069
|
-
*
|
|
7111
|
+
* However, this is only possible if the join has no dynamic values:
|
|
7070
7112
|
*
|
|
7071
7113
|
* ```ts
|
|
7072
|
-
* db.
|
|
7073
|
-
* {
|
|
7074
|
-
*
|
|
7075
|
-
* db.table.sql`a = b`,
|
|
7076
|
-
* );
|
|
7114
|
+
* db.post
|
|
7115
|
+
* .join('comments', (q) => q.where({ rating: { gt: 5 } }))
|
|
7116
|
+
* .join('comments', (q) => q.where({ rating: { gt: 5 } }));
|
|
7077
7117
|
* ```
|
|
7078
7118
|
*
|
|
7079
|
-
*
|
|
7119
|
+
* Both joins above have the same `{ gt: 5 }`, but still, the `5` is a dynamic value and in this case joins will be duplicated,
|
|
7120
|
+
* resulting in a database error.
|
|
7080
7121
|
*
|
|
7081
|
-
*
|
|
7122
|
+
* ### join relation
|
|
7082
7123
|
*
|
|
7083
|
-
*
|
|
7124
|
+
* When relations are defined between the tables, you can join them by a relation name.
|
|
7125
|
+
* Joined table can be references from `where` and `select` by a relation name.
|
|
7084
7126
|
*
|
|
7085
7127
|
* ```ts
|
|
7086
|
-
* const
|
|
7128
|
+
* const result = await db.user
|
|
7129
|
+
* .join('messages')
|
|
7130
|
+
* // after joining a table, we can use it in `where` conditions:
|
|
7131
|
+
* .where({ 'messages.text': { startsWith: 'Hi' } })
|
|
7132
|
+
* .select(
|
|
7133
|
+
* 'name', // name is User column, table name may be omitted
|
|
7134
|
+
* 'messages.text', // text is the Message column, and the table name is required
|
|
7135
|
+
* );
|
|
7087
7136
|
*
|
|
7088
|
-
*
|
|
7089
|
-
*
|
|
7090
|
-
* post.tags // query tags of the post
|
|
7091
|
-
* .whereIn('tagName', givenTags) // where name of the tag is inside array
|
|
7092
|
-
* .count() // count how many such tags were found
|
|
7093
|
-
* .equals(wantedTags.length), // the count must be exactly the length of array
|
|
7094
|
-
* // if the post has ony `typescript` tag but not the `node.js` it will be omitted
|
|
7095
|
-
* );
|
|
7137
|
+
* // result has the following type:
|
|
7138
|
+
* const ok: { name: string; text: string }[] = result;
|
|
7096
7139
|
* ```
|
|
7097
7140
|
*
|
|
7098
|
-
*
|
|
7141
|
+
* 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`.
|
|
7142
|
+
* In such a way, we can alias the relation with `as`, add `where` conditions, use other query methods.
|
|
7099
7143
|
*
|
|
7100
|
-
* ```
|
|
7101
|
-
*
|
|
7102
|
-
*
|
|
7103
|
-
*
|
|
7104
|
-
* FROM "tag" AS "tags"
|
|
7105
|
-
* WHERE "tag"."tagName" IN ('typescript', 'node.js')
|
|
7106
|
-
* -- join tags to the post via "postTag" table
|
|
7107
|
-
* AND EXISTS (
|
|
7108
|
-
* SELECT 1 FROM "postTag"
|
|
7109
|
-
* WHERE "postTag"."postId" = "post"."id"
|
|
7110
|
-
* AND "postTag"."tagId" = "tag"."id"
|
|
7111
|
-
* )
|
|
7112
|
-
* )
|
|
7144
|
+
* ```ts
|
|
7145
|
+
* const result = await db.user.join((q) =>
|
|
7146
|
+
* q.messages.as('m').where({ text: 'some text' }),
|
|
7147
|
+
* );
|
|
7113
7148
|
* ```
|
|
7114
7149
|
*
|
|
7115
|
-
*
|
|
7150
|
+
* Optionally, you can pass a second callback argument, it makes `on` and `orOn` methods available.
|
|
7116
7151
|
*
|
|
7117
|
-
*
|
|
7152
|
+
* But remember that when joining a relation, the relevant `ON` conditions are already handled automatically.
|
|
7118
7153
|
*
|
|
7119
|
-
*
|
|
7154
|
+
* ```ts
|
|
7155
|
+
* const result = await db.user.join(
|
|
7156
|
+
* (q) => q.messages.as('m'),
|
|
7157
|
+
* (q) =>
|
|
7158
|
+
* q
|
|
7159
|
+
* .on('text', 'name') // additionally, match message with user name
|
|
7160
|
+
* .where({ text: 'some text' }), // you can add `where` in a second callback as well.
|
|
7161
|
+
* );
|
|
7162
|
+
* ```
|
|
7120
7163
|
*
|
|
7121
|
-
*
|
|
7164
|
+
* ### Selecting full joined records
|
|
7122
7165
|
*
|
|
7123
|
-
*
|
|
7166
|
+
* `select` supports selecting a full record of a previously joined table by passing a table name with `.*` at the end:
|
|
7124
7167
|
*
|
|
7125
7168
|
* ```ts
|
|
7126
|
-
* db.
|
|
7127
|
-
*
|
|
7128
|
-
* OR: [{ name: 'a' }, { name: 'b' }],
|
|
7129
|
-
* IN: {
|
|
7130
|
-
* columns: ['id', 'name'],
|
|
7131
|
-
* values: [
|
|
7132
|
-
* [1, 'a'],
|
|
7133
|
-
* [2, 'b'],
|
|
7134
|
-
* ],
|
|
7135
|
-
* },
|
|
7169
|
+
* const result = await db.book.join('author').select('title', {
|
|
7170
|
+
* author: 'author.*',
|
|
7136
7171
|
* });
|
|
7172
|
+
*
|
|
7173
|
+
* // result has the following type:
|
|
7174
|
+
* const ok: {
|
|
7175
|
+
* // title of the book
|
|
7176
|
+
* title: string;
|
|
7177
|
+
* // a full author record is included:
|
|
7178
|
+
* author: { id: number; name: string; updatedAt: Date; createdAt: Date };
|
|
7179
|
+
* }[] = result;
|
|
7137
7180
|
* ```
|
|
7138
7181
|
*
|
|
7139
|
-
*
|
|
7182
|
+
* 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.
|
|
7183
|
+
* 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.
|
|
7140
7184
|
*
|
|
7141
7185
|
* ```ts
|
|
7142
|
-
* db.
|
|
7143
|
-
*
|
|
7144
|
-
*
|
|
7145
|
-
*
|
|
7146
|
-
* NOT: [{ id: 1 }, { id: 2 }],
|
|
7147
|
-
*
|
|
7148
|
-
* // see .orWhere
|
|
7149
|
-
* OR: [{ name: 'a' }, { name: 'b' }],
|
|
7150
|
-
* // can be an array:
|
|
7151
|
-
* // this will give id = 1 AND id = 2 OR id = 3 AND id = 4
|
|
7152
|
-
* OR: [
|
|
7153
|
-
* [{ id: 1 }, { id: 2 }],
|
|
7154
|
-
* [{ id: 3 }, { id: 4 }],
|
|
7155
|
-
* ],
|
|
7186
|
+
* const result = await db.user
|
|
7187
|
+
* .join('messages')
|
|
7188
|
+
* .where({ 'messages.text': { startsWith: 'Hi' } })
|
|
7189
|
+
* .select('name', { messages: 'messages.*' });
|
|
7156
7190
|
*
|
|
7157
|
-
*
|
|
7158
|
-
*
|
|
7159
|
-
*
|
|
7160
|
-
*
|
|
7161
|
-
*
|
|
7162
|
-
*
|
|
7163
|
-
* ],
|
|
7164
|
-
* },
|
|
7165
|
-
* // can be an array:
|
|
7166
|
-
* IN: [
|
|
7167
|
-
* {
|
|
7168
|
-
* columns: ['id', 'name'],
|
|
7169
|
-
* values: [
|
|
7170
|
-
* [1, 'a'],
|
|
7171
|
-
* [2, 'b'],
|
|
7172
|
-
* ],
|
|
7173
|
-
* },
|
|
7174
|
-
* { columns: ['someColumn'], values: [['foo', 'bar']] },
|
|
7175
|
-
* ],
|
|
7176
|
-
* });
|
|
7191
|
+
* // result has the following type:
|
|
7192
|
+
* const ok: {
|
|
7193
|
+
* name: string;
|
|
7194
|
+
* // full message is included:
|
|
7195
|
+
* messages: { id: number; text: string; updatedAt: Date; createdAt: Date };
|
|
7196
|
+
* }[] = result;
|
|
7177
7197
|
* ```
|
|
7178
7198
|
*
|
|
7179
|
-
*
|
|
7199
|
+
* Because it's a one-to-many relation, one user has many messages, the user data will be duplicated for different messages data:
|
|
7180
7200
|
*
|
|
7181
|
-
*
|
|
7201
|
+
* | name | msg |
|
|
7202
|
+
* | ------ | ------------------------------ |
|
|
7203
|
+
* | user 1 | `{ id: 1, text: 'message 1' }` |
|
|
7204
|
+
* | user 1 | `{ id: 2, text: 'message 2' }` |
|
|
7205
|
+
* | user 1 | `{ id: 3, text: 'message 3' }` |
|
|
7182
7206
|
*
|
|
7183
|
-
*
|
|
7207
|
+
* ### join table
|
|
7184
7208
|
*
|
|
7185
|
-
*
|
|
7209
|
+
* If relation wasn't defined, provide a `db.table` instance and specify columns for the join.
|
|
7210
|
+
* Joined table can be references from `where` and `select` by a table name.
|
|
7186
7211
|
*
|
|
7187
7212
|
* ```ts
|
|
7188
|
-
*
|
|
7189
|
-
*
|
|
7190
|
-
*
|
|
7191
|
-
*
|
|
7192
|
-
*
|
|
7193
|
-
* lt: 5,
|
|
7194
|
-
*
|
|
7195
|
-
* // lower than the value returned by sub-query
|
|
7196
|
-
* lt: OtherTable.select('someNumber').take(),
|
|
7197
|
-
*
|
|
7198
|
-
* // raw SQL expression produces WHERE "numericColumn" < "otherColumn" + 10
|
|
7199
|
-
* lt: sql`"otherColumn" + 10`,
|
|
7200
|
-
* },
|
|
7201
|
-
* });
|
|
7213
|
+
* // Join message where userId = id:
|
|
7214
|
+
* db.user
|
|
7215
|
+
* .join(db.message, 'userId', 'id')
|
|
7216
|
+
* .where({ 'message.text': { startsWith: 'Hi' } })
|
|
7217
|
+
* .select('name', 'message.text');
|
|
7202
7218
|
* ```
|
|
7203
7219
|
*
|
|
7204
|
-
*
|
|
7205
|
-
*
|
|
7206
|
-
* `equals` is a simple `=` operator, it may be useful for comparing column value with JSON object:
|
|
7220
|
+
* Columns in the join list may be prefixed with table names for clarity:
|
|
7207
7221
|
*
|
|
7208
7222
|
* ```ts
|
|
7209
|
-
* db.
|
|
7210
|
-
* // when searching for an exact same JSON value, this won't work:
|
|
7211
|
-
* jsonColumn: someObject,
|
|
7212
|
-
*
|
|
7213
|
-
* // use `{ equals: ... }` instead:
|
|
7214
|
-
* jsonColumn: { equals: someObject },
|
|
7215
|
-
* });
|
|
7223
|
+
* db.user.join(db.message, 'message.userId', 'user.id');
|
|
7216
7224
|
* ```
|
|
7217
7225
|
*
|
|
7218
|
-
*
|
|
7226
|
+
* Joined table can have an alias for referencing it further:
|
|
7219
7227
|
*
|
|
7220
7228
|
* ```ts
|
|
7221
|
-
* db.
|
|
7222
|
-
*
|
|
7223
|
-
* })
|
|
7229
|
+
* db.user
|
|
7230
|
+
* .join(db.message.as('m'), 'message.userId', 'user.id')
|
|
7231
|
+
* .where({ 'm.text': { startsWith: 'Hi' } })
|
|
7232
|
+
* .select('name', 'm.text');
|
|
7224
7233
|
* ```
|
|
7225
7234
|
*
|
|
7226
|
-
*
|
|
7227
|
-
*
|
|
7228
|
-
* 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.
|
|
7235
|
+
* Joined table can be selected as an object as well as the relation join above:
|
|
7229
7236
|
*
|
|
7230
7237
|
* ```ts
|
|
7231
|
-
* db.
|
|
7232
|
-
*
|
|
7233
|
-
*
|
|
7234
|
-
*
|
|
7235
|
-
* // WHERE "column" IN (SELECT "column" FROM "otherTable")
|
|
7236
|
-
* in: OtherTable.select('column'),
|
|
7238
|
+
* const result = await db.user
|
|
7239
|
+
* .join(db.message.as('m'), 'message.userId', 'user.id')
|
|
7240
|
+
* .where({ 'm.text': { startsWith: 'Hi' } })
|
|
7241
|
+
* .select('name', { msg: 'm.*' });
|
|
7237
7242
|
*
|
|
7238
|
-
*
|
|
7239
|
-
*
|
|
7240
|
-
*
|
|
7243
|
+
* // result has the following type:
|
|
7244
|
+
* const ok: {
|
|
7245
|
+
* name: string;
|
|
7246
|
+
* // full message is included as msg:
|
|
7247
|
+
* msg: { id: number; text: string; updatedAt: Date; createdAt: Date };
|
|
7248
|
+
* }[] = result;
|
|
7241
7249
|
* ```
|
|
7242
7250
|
*
|
|
7243
|
-
*
|
|
7244
|
-
*
|
|
7245
|
-
* ### Numeric, Date, and Time column operators
|
|
7246
|
-
*
|
|
7247
|
-
* To compare numbers, dates, and times.
|
|
7248
|
-
*
|
|
7249
|
-
* `lt` is for `<` (lower than)
|
|
7250
|
-
*
|
|
7251
|
-
* `lte` is for `<=` (lower than or equal)
|
|
7252
|
-
*
|
|
7253
|
-
* `gt` is for `>` (greater than)
|
|
7254
|
-
*
|
|
7255
|
-
* `gte` is for `>=` (greater than or equal)
|
|
7251
|
+
* You can provide a custom comparison operator
|
|
7256
7252
|
*
|
|
7257
7253
|
* ```ts
|
|
7258
|
-
* db.
|
|
7259
|
-
*
|
|
7260
|
-
* gt: 5,
|
|
7261
|
-
* lt: 10,
|
|
7262
|
-
* },
|
|
7254
|
+
* db.user.join(db.message, 'userId', '!=', 'id');
|
|
7255
|
+
* ```
|
|
7263
7256
|
*
|
|
7264
|
-
*
|
|
7265
|
-
* lte: new Date(),
|
|
7266
|
-
* },
|
|
7257
|
+
* Join can accept raw SQL for the `ON` part of join:
|
|
7267
7258
|
*
|
|
7268
|
-
*
|
|
7269
|
-
*
|
|
7270
|
-
*
|
|
7271
|
-
*
|
|
7259
|
+
* ```ts
|
|
7260
|
+
* db.user.join(
|
|
7261
|
+
* db.message,
|
|
7262
|
+
* db.user.sql`lower("message"."text") = lower("user"."name")`,
|
|
7263
|
+
* );
|
|
7272
7264
|
* ```
|
|
7273
7265
|
*
|
|
7274
|
-
*
|
|
7275
|
-
*
|
|
7276
|
-
* Both elements can be of the same type as a column, a sub-query, or a raw SQL expression.
|
|
7266
|
+
* Join can accept raw SQL instead of columns:
|
|
7277
7267
|
*
|
|
7278
7268
|
* ```ts
|
|
7279
|
-
* db.
|
|
7280
|
-
*
|
|
7281
|
-
*
|
|
7282
|
-
*
|
|
7269
|
+
* db.user.join(
|
|
7270
|
+
* db.message,
|
|
7271
|
+
* db.user.sql`lower("message"."text")`,
|
|
7272
|
+
* db.user.sql`lower("user"."name")`,
|
|
7273
|
+
* );
|
|
7283
7274
|
*
|
|
7284
|
-
*
|
|
7285
|
-
*
|
|
7286
|
-
*
|
|
7287
|
-
*
|
|
7275
|
+
* // with operator:
|
|
7276
|
+
* db.user.join(
|
|
7277
|
+
* db.message,
|
|
7278
|
+
* db.user.sql`lower("message"."text")`,
|
|
7279
|
+
* '!=',
|
|
7280
|
+
* db.user.sql`lower("user"."name")`,
|
|
7281
|
+
* );
|
|
7288
7282
|
* ```
|
|
7289
7283
|
*
|
|
7290
|
-
*
|
|
7291
|
-
*
|
|
7292
|
-
* For `text`, `char`, `varchar`, and `json` columns.
|
|
7284
|
+
* 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:
|
|
7293
7285
|
*
|
|
7294
|
-
*
|
|
7286
|
+
* ```ts
|
|
7287
|
+
* db.user.join(db.message, {
|
|
7288
|
+
* userId: 'id',
|
|
7295
7289
|
*
|
|
7296
|
-
*
|
|
7290
|
+
* // with table names:
|
|
7291
|
+
* 'message.userId': 'user.id',
|
|
7297
7292
|
*
|
|
7298
|
-
*
|
|
7299
|
-
* db.
|
|
7300
|
-
* textColumn: {
|
|
7301
|
-
* // WHERE "textColumn" LIKE '%string%'
|
|
7302
|
-
* contains: 'string',
|
|
7303
|
-
* // WHERE "textColumn" ILIKE '%string%'
|
|
7304
|
-
* containsInsensitive: 'string',
|
|
7305
|
-
* // WHERE "textColumn" LIKE 'string%'
|
|
7306
|
-
* startsWith: 'string',
|
|
7307
|
-
* // WHERE "textColumn" ILIKE 'string%'
|
|
7308
|
-
* startsWithInsensitive: 'string',
|
|
7309
|
-
* // WHERE "textColumn" LIKE '%string'
|
|
7310
|
-
* endsWith: 'string',
|
|
7311
|
-
* // WHERE "textColumn" ILIKE '%string'
|
|
7312
|
-
* endsWithInsensitive: 'string',
|
|
7313
|
-
* },
|
|
7293
|
+
* // value can be a raw SQL expression:
|
|
7294
|
+
* text: db.user.sql`lower("user"."name")`,
|
|
7314
7295
|
* });
|
|
7315
7296
|
* ```
|
|
7316
7297
|
*
|
|
7317
|
-
*
|
|
7318
|
-
*
|
|
7319
|
-
* For the `jsonb` column, note that the `json` type has text operators instead.
|
|
7320
|
-
*
|
|
7321
|
-
* `jsonPath` operator: compare a column value under a given JSON path with the provided value.
|
|
7322
|
-
*
|
|
7323
|
-
* Value can be of any type to compare with JSON value, or it can be a sub-query or a raw SQL expression.
|
|
7298
|
+
* Join all records without conditions by providing `true`:
|
|
7324
7299
|
*
|
|
7325
7300
|
* ```ts
|
|
7326
|
-
* db.
|
|
7327
|
-
* jsonbColumn: {
|
|
7328
|
-
* jsonPath: [
|
|
7329
|
-
* '$.name', // first element is JSON path
|
|
7330
|
-
* '=', // second argument is comparison operator
|
|
7331
|
-
* 'value', // third argument is a value to compare with
|
|
7332
|
-
* ],
|
|
7333
|
-
* },
|
|
7334
|
-
* });
|
|
7301
|
+
* db.user.join(db.message, true);
|
|
7335
7302
|
* ```
|
|
7336
7303
|
*
|
|
7337
|
-
*
|
|
7338
|
-
*
|
|
7339
|
-
* For instance, it is true if the column has JSON `{ "a": 1, "b": 2 }` and provided value is `{ "a": 1 }`.
|
|
7340
|
-
*
|
|
7341
|
-
* Takes the value of any type, or sub query which returns a single value, or a raw SQL expression.
|
|
7304
|
+
* Join methods can accept a callback with a special query builder that has `on` and `orOn` methods for handling advanced cases:
|
|
7342
7305
|
*
|
|
7343
7306
|
* ```ts
|
|
7344
|
-
* db.
|
|
7345
|
-
*
|
|
7346
|
-
*
|
|
7347
|
-
*
|
|
7348
|
-
*
|
|
7307
|
+
* db.user.join(
|
|
7308
|
+
* db.message,
|
|
7309
|
+
* (q) =>
|
|
7310
|
+
* q
|
|
7311
|
+
* // left column is the db.message column, right column is the db.user column
|
|
7312
|
+
* .on('userId', 'id')
|
|
7313
|
+
* // table names can be provided:
|
|
7314
|
+
* .on('message.userId', 'user.id')
|
|
7315
|
+
* // operator can be specified:
|
|
7316
|
+
* .on('userId', '!=', 'id')
|
|
7317
|
+
* // operator can be specified with table names as well:
|
|
7318
|
+
* .on('message.userId', '!=', 'user.id')
|
|
7319
|
+
* // `.orOn` takes the same arguments as `.on` and acts like `.or`:
|
|
7320
|
+
* .on('userId', 'id') // where message.userId = user.id
|
|
7321
|
+
* .orOn('text', 'name'), // or message.text = user.name
|
|
7322
|
+
* );
|
|
7349
7323
|
* ```
|
|
7350
7324
|
*
|
|
7351
|
-
*
|
|
7325
|
+
* 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.
|
|
7352
7326
|
*
|
|
7353
|
-
*
|
|
7327
|
+
* ```ts
|
|
7328
|
+
* db.user.join(db.message, (q) =>
|
|
7329
|
+
* q
|
|
7330
|
+
* .on('userId', 'id')
|
|
7331
|
+
* .where({
|
|
7332
|
+
* // not prefixed column name is for joined table:
|
|
7333
|
+
* text: { startsWith: 'hello' },
|
|
7334
|
+
* // specify a table name to set condition on the main table:
|
|
7335
|
+
* 'user.name': 'Bob',
|
|
7336
|
+
* })
|
|
7337
|
+
* // id is a column of a joined table Message
|
|
7338
|
+
* .whereIn('id', [1, 2, 3])
|
|
7339
|
+
* // condition for id of a user
|
|
7340
|
+
* .whereIn('user.id', [4, 5, 6]),
|
|
7341
|
+
* );
|
|
7342
|
+
* ```
|
|
7354
7343
|
*
|
|
7355
|
-
*
|
|
7344
|
+
* The query above will generate the following SQL (simplified):
|
|
7356
7345
|
*
|
|
7357
|
-
* ```
|
|
7358
|
-
*
|
|
7359
|
-
*
|
|
7360
|
-
*
|
|
7361
|
-
*
|
|
7362
|
-
*
|
|
7346
|
+
* ```sql
|
|
7347
|
+
* SELECT * FROM "user"
|
|
7348
|
+
* JOIN "message"
|
|
7349
|
+
* ON "message"."userId" = "user"."id"
|
|
7350
|
+
* AND "message"."text" ILIKE 'hello%'
|
|
7351
|
+
* AND "user"."name" = 'Bob'
|
|
7352
|
+
* AND "message"."id" IN (1, 2, 3)
|
|
7353
|
+
* AND "user"."id" IN (4, 5, 6)
|
|
7363
7354
|
* ```
|
|
7364
7355
|
*
|
|
7365
|
-
*
|
|
7366
|
-
*/
|
|
7367
|
-
where(...args) {
|
|
7368
|
-
return _queryWhere(
|
|
7369
|
-
this.clone(),
|
|
7370
|
-
args
|
|
7371
|
-
);
|
|
7372
|
-
}
|
|
7373
|
-
/**
|
|
7374
|
-
* Use a custom SQL expression in `WHERE` statement:
|
|
7356
|
+
* The join argument can be a query with `select`, `where`, and other methods. In such case, it will be handled as a sub query:
|
|
7375
7357
|
*
|
|
7376
7358
|
* ```ts
|
|
7377
|
-
* db.
|
|
7359
|
+
* db.user.join(
|
|
7360
|
+
* db.message
|
|
7361
|
+
* .select('id', 'userId', 'text')
|
|
7362
|
+
* .where({ text: { startsWith: 'Hi' } })
|
|
7363
|
+
* .as('t'),
|
|
7364
|
+
* 'userId',
|
|
7365
|
+
* 'id',
|
|
7366
|
+
* );
|
|
7367
|
+
* ```
|
|
7378
7368
|
*
|
|
7379
|
-
*
|
|
7380
|
-
* db.table.where(db.table.sql`a = b`);
|
|
7369
|
+
* It will produce such SQL:
|
|
7381
7370
|
*
|
|
7382
|
-
*
|
|
7383
|
-
*
|
|
7371
|
+
* ```sql
|
|
7372
|
+
* SELECT * FROM "user"
|
|
7373
|
+
* JOIN (
|
|
7374
|
+
* SELECT "t"."id", "t"."userId", "t"."text"
|
|
7375
|
+
* FROM "message" AS "t"
|
|
7376
|
+
* ) "t" ON "t"."userId" = "user"."id"
|
|
7377
|
+
* ```
|
|
7384
7378
|
*
|
|
7385
|
-
*
|
|
7379
|
+
* ## implicit join lateral
|
|
7380
|
+
*
|
|
7381
|
+
* `JOIN`'s source expression that comes before `ON` cannot access other tables, but in some cases this may be needed.
|
|
7382
|
+
*
|
|
7383
|
+
* For example, let's consider joining last 10 messages of a user:
|
|
7384
|
+
*
|
|
7385
|
+
* ```ts
|
|
7386
|
+
* await db.user.join('messages', (q) => q.order({ createdAt: 'DESC' }).limit(10));
|
|
7386
7387
|
* ```
|
|
7387
7388
|
*
|
|
7388
|
-
*
|
|
7389
|
+
* When the `join`'s callback returns a more complex query than the one that simply applies certain conditions,
|
|
7390
|
+
* it will implicitly generate a `JOIN LATERAL` SQL query, as the following:
|
|
7391
|
+
*
|
|
7392
|
+
* ```sql
|
|
7393
|
+
* SELECT *
|
|
7394
|
+
* FROM "user"
|
|
7395
|
+
* JOIN LATERAL (
|
|
7396
|
+
* SELECT *
|
|
7397
|
+
* FROM "message" AS "messages"
|
|
7398
|
+
* WHERE "message"."userId" = "user"."id"
|
|
7399
|
+
* ORDER BY "message"."createdAt" DESC
|
|
7400
|
+
* LIMIT 10
|
|
7401
|
+
* ) "messages" ON true
|
|
7402
|
+
* ```
|
|
7403
|
+
*
|
|
7404
|
+
* @param arg - {@link JoinFirstArg}
|
|
7405
|
+
* @param args - {@link JoinArgs}
|
|
7389
7406
|
*/
|
|
7390
|
-
|
|
7391
|
-
return
|
|
7407
|
+
join(arg, ...args) {
|
|
7408
|
+
return _join(
|
|
7392
7409
|
this.clone(),
|
|
7410
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7411
|
+
true,
|
|
7412
|
+
"JOIN",
|
|
7413
|
+
arg,
|
|
7393
7414
|
args
|
|
7415
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7394
7416
|
);
|
|
7395
7417
|
}
|
|
7396
7418
|
/**
|
|
7397
|
-
* `
|
|
7398
|
-
*
|
|
7399
|
-
*
|
|
7419
|
+
* `leftJoin` is a method for SQL `LEFT JOIN`, which is equivalent to `OUTER JOIN`, `LEFT OUTER JOIN`.
|
|
7420
|
+
*
|
|
7421
|
+
* When no matching record is found, it will fill joined table columns with `NULL` values in the result rows.
|
|
7422
|
+
*
|
|
7423
|
+
* Works just like `join`, except for result type that may have `null`:
|
|
7400
7424
|
*
|
|
7401
7425
|
* ```ts
|
|
7402
|
-
*
|
|
7403
|
-
*
|
|
7404
|
-
*
|
|
7405
|
-
*
|
|
7406
|
-
* //
|
|
7426
|
+
* const result = await db.user
|
|
7427
|
+
* .leftJoin('messages')
|
|
7428
|
+
* .select('name', 'messages.text');
|
|
7429
|
+
*
|
|
7430
|
+
* // the same query, but joining table explicitly
|
|
7431
|
+
* const result2: typeof result = await db.user
|
|
7432
|
+
* .leftJoin(db.message, 'userId', 'id')
|
|
7433
|
+
* .select('name', 'message.text');
|
|
7434
|
+
*
|
|
7435
|
+
* // result has the following type:
|
|
7436
|
+
* const ok: { name: string; text: string | null }[] = result;
|
|
7407
7437
|
* ```
|
|
7408
7438
|
*
|
|
7409
|
-
* @param
|
|
7439
|
+
* @param arg - {@link JoinFirstArg}
|
|
7440
|
+
* @param args - {@link JoinArgs}
|
|
7410
7441
|
*/
|
|
7411
|
-
|
|
7412
|
-
return
|
|
7442
|
+
leftJoin(arg, ...args) {
|
|
7443
|
+
return _join(
|
|
7413
7444
|
this.clone(),
|
|
7445
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7446
|
+
false,
|
|
7447
|
+
"LEFT JOIN",
|
|
7448
|
+
arg,
|
|
7414
7449
|
args
|
|
7450
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7415
7451
|
);
|
|
7416
7452
|
}
|
|
7417
7453
|
/**
|
|
7418
|
-
* `
|
|
7454
|
+
* `rightJoin` is a method for SQL `RIGHT JOIN`, which is equivalent to `RIGHT OUTER JOIN`.
|
|
7455
|
+
*
|
|
7456
|
+
* Takes the same arguments as `json`.
|
|
7457
|
+
*
|
|
7458
|
+
* It will load all records from the joining table, and fill the main table columns with `null` when no match is found.
|
|
7459
|
+
*
|
|
7460
|
+
* The columns of the table you're joining to are becoming nullable when using `rightJoin`.
|
|
7419
7461
|
*
|
|
7420
7462
|
* ```ts
|
|
7421
|
-
* db.
|
|
7463
|
+
* const result = await db.user
|
|
7464
|
+
* .rightJoin('messages')
|
|
7465
|
+
* .select('name', 'messages.text');
|
|
7466
|
+
*
|
|
7467
|
+
* // even though name is not a nullable column, it becomes nullable after using rightJoin
|
|
7468
|
+
* const ok: { name: string | null; text: string }[] = result;
|
|
7422
7469
|
* ```
|
|
7423
7470
|
*
|
|
7424
|
-
* @param
|
|
7471
|
+
* @param arg - {@link JoinFirstArg}
|
|
7472
|
+
* @param args - {@link JoinArgs}
|
|
7425
7473
|
*/
|
|
7426
|
-
|
|
7427
|
-
return
|
|
7474
|
+
rightJoin(arg, ...args) {
|
|
7475
|
+
return _join(
|
|
7476
|
+
this.clone(),
|
|
7477
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7478
|
+
true,
|
|
7479
|
+
"RIGHT JOIN",
|
|
7480
|
+
arg,
|
|
7481
|
+
args
|
|
7482
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7483
|
+
);
|
|
7428
7484
|
}
|
|
7429
7485
|
/**
|
|
7430
|
-
* `
|
|
7486
|
+
* `fullJoin` is a method for SQL `FULL JOIN`, which is equivalent to `FULL OUTER JOIN`.
|
|
7431
7487
|
*
|
|
7432
|
-
*
|
|
7488
|
+
* Takes the same arguments as `json`.
|
|
7433
7489
|
*
|
|
7434
|
-
*
|
|
7490
|
+
* It will load all records from the joining table, both sides of the join may result in `null` values when there is no match.
|
|
7435
7491
|
*
|
|
7436
|
-
*
|
|
7437
|
-
* db.table.where({ id: 1, color: 'red' }).orWhere({ id: 2, color: 'blue' });
|
|
7438
|
-
* // equivalent:
|
|
7439
|
-
* db.table.orWhere({ id: 1, color: 'red' }, { id: 2, color: 'blue' });
|
|
7440
|
-
* ```
|
|
7492
|
+
* All columns become nullable after using `fullJoin`.
|
|
7441
7493
|
*
|
|
7442
|
-
*
|
|
7494
|
+
* ```ts
|
|
7495
|
+
* const result = await db.user
|
|
7496
|
+
* .rightJoin('messages')
|
|
7497
|
+
* .select('name', 'messages.text');
|
|
7443
7498
|
*
|
|
7444
|
-
*
|
|
7445
|
-
*
|
|
7446
|
-
* WHERE id = 1 AND color = 'red'
|
|
7447
|
-
* OR id = 2 AND color = 'blue'
|
|
7499
|
+
* // all columns can be null
|
|
7500
|
+
* const ok: { name: string | null; text: string | null }[] = result;
|
|
7448
7501
|
* ```
|
|
7449
7502
|
*
|
|
7450
|
-
* @param
|
|
7451
|
-
|
|
7452
|
-
orWhere(...args) {
|
|
7453
|
-
return _queryOr(this.clone(), args);
|
|
7454
|
-
}
|
|
7455
|
-
/**
|
|
7456
|
-
* `orWhereNot` takes the same arguments as {@link orWhere}, and prepends each condition with `NOT` just as {@link whereNot} does.
|
|
7457
|
-
*
|
|
7458
|
-
* @param args - {@link WhereArgs} will be prefixed with `NOT` and joined with `OR`
|
|
7503
|
+
* @param arg - {@link JoinFirstArg}
|
|
7504
|
+
* @param args - {@link JoinArgs}
|
|
7459
7505
|
*/
|
|
7460
|
-
|
|
7461
|
-
return
|
|
7506
|
+
fullJoin(arg, ...args) {
|
|
7507
|
+
return _join(
|
|
7462
7508
|
this.clone(),
|
|
7509
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7510
|
+
false,
|
|
7511
|
+
"FULL JOIN",
|
|
7512
|
+
arg,
|
|
7463
7513
|
args
|
|
7514
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7464
7515
|
);
|
|
7465
7516
|
}
|
|
7466
7517
|
/**
|
|
7467
|
-
* `
|
|
7518
|
+
* `joinLateral` allows joining a table with a sub-query that can reference the main table of current query and the other joined tables.
|
|
7468
7519
|
*
|
|
7469
|
-
*
|
|
7520
|
+
* First argument is the other table you want to join, or a name of relation, or a name of `with` defined table.
|
|
7470
7521
|
*
|
|
7471
|
-
*
|
|
7472
|
-
* db.table.whereIn('column', [1, 2, 3]);
|
|
7473
|
-
* // the same as:
|
|
7474
|
-
* db.table.where({ column: [1, 2, 3] });
|
|
7475
|
-
* ```
|
|
7522
|
+
* 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.
|
|
7476
7523
|
*
|
|
7477
|
-
* `
|
|
7524
|
+
* 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).
|
|
7478
7525
|
*
|
|
7479
7526
|
* ```ts
|
|
7480
|
-
*
|
|
7481
|
-
*
|
|
7482
|
-
*
|
|
7483
|
-
*
|
|
7484
|
-
*
|
|
7485
|
-
*
|
|
7486
|
-
*
|
|
7527
|
+
* // joinLateral a Message table, alias it as `m`
|
|
7528
|
+
* // without aliasing you can refer to the message by a table name
|
|
7529
|
+
* User.joinLateral(Message.as('m'), (q) =>
|
|
7530
|
+
* q
|
|
7531
|
+
* // select message columns
|
|
7532
|
+
* .select('text')
|
|
7533
|
+
* // join the message to the user, column names can be prefixed with table names
|
|
7534
|
+
* .on('authorId', 'id')
|
|
7535
|
+
* // message columns are available without prefixing,
|
|
7536
|
+
* // outer table columns are available with a table name
|
|
7537
|
+
* .where({ text: 'some text', 'user.name': 'name' })
|
|
7538
|
+
* .order({ createdAt: 'DESC' }),
|
|
7539
|
+
* )
|
|
7540
|
+
* // only selected message columns are available in select and where
|
|
7541
|
+
* .select('id', 'name', 'm.text')
|
|
7542
|
+
* .where({ 'm.text': messageData.text });
|
|
7487
7543
|
* ```
|
|
7488
7544
|
*
|
|
7489
|
-
*
|
|
7545
|
+
* As well as simple `join`, `joinLateral` can select an object of full joined record:
|
|
7490
7546
|
*
|
|
7491
7547
|
* ```ts
|
|
7492
|
-
*
|
|
7548
|
+
* // join by relation name
|
|
7549
|
+
* const result = await User.joinLateral(
|
|
7550
|
+
* 'messages',
|
|
7551
|
+
* (q) => q.as('message'), // alias to 'message'
|
|
7552
|
+
* ).select('name', { message: 'message.*' });
|
|
7553
|
+
*
|
|
7554
|
+
* // result has the following type:
|
|
7555
|
+
* const ok: {
|
|
7556
|
+
* name: string;
|
|
7557
|
+
* // full message is included:
|
|
7558
|
+
* message: { id: number; text: string; updatedAt: Date; createdAt: Date };
|
|
7559
|
+
* }[] = result;
|
|
7493
7560
|
* ```
|
|
7494
7561
|
*
|
|
7495
|
-
*
|
|
7562
|
+
* `message` can be aliased withing the `select` as well as in case of a simple `join`:
|
|
7496
7563
|
*
|
|
7497
7564
|
* ```ts
|
|
7498
|
-
*
|
|
7499
|
-
*
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
this.clone(),
|
|
7504
|
-
true,
|
|
7505
|
-
args[0],
|
|
7506
|
-
args[1]
|
|
7507
|
-
);
|
|
7508
|
-
}
|
|
7509
|
-
/**
|
|
7510
|
-
* Takes the same arguments as {@link whereIn}.
|
|
7511
|
-
* Add a `WHERE IN` condition prefixed with `OR` to the query:
|
|
7565
|
+
* // join by relation name
|
|
7566
|
+
* const result = await User.joinLateral(
|
|
7567
|
+
* 'messages',
|
|
7568
|
+
* (q) => q.as('message'), // alias to 'message'
|
|
7569
|
+
* ).select('name', { msg: 'message.*' });
|
|
7512
7570
|
*
|
|
7513
|
-
*
|
|
7514
|
-
*
|
|
7571
|
+
* // result has the following type:
|
|
7572
|
+
* const ok: {
|
|
7573
|
+
* name: string;
|
|
7574
|
+
* // full message is included as msg:
|
|
7575
|
+
* msg: { id: number; text: string; updatedAt: Date; createdAt: Date };
|
|
7576
|
+
* }[] = result;
|
|
7515
7577
|
* ```
|
|
7578
|
+
*
|
|
7579
|
+
* @param arg - {@link JoinFirstArg}
|
|
7580
|
+
* @param cb - {@link JoinLateralCallback}
|
|
7516
7581
|
*/
|
|
7517
|
-
|
|
7518
|
-
return
|
|
7582
|
+
joinLateral(arg, cb) {
|
|
7583
|
+
return _joinLateral(
|
|
7519
7584
|
this.clone(),
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7585
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7586
|
+
"JOIN",
|
|
7587
|
+
arg,
|
|
7588
|
+
cb
|
|
7523
7589
|
);
|
|
7524
7590
|
}
|
|
7525
7591
|
/**
|
|
7526
|
-
*
|
|
7592
|
+
* The same as {@link joinLateral}, but when no records found for the join it will result in `null`:
|
|
7527
7593
|
*
|
|
7528
7594
|
* ```ts
|
|
7529
|
-
*
|
|
7595
|
+
* const result = await db.user
|
|
7596
|
+
* .leftJoinLateral('messages', (q) => q.as('message'))
|
|
7597
|
+
* .select('name', 'message.text');
|
|
7598
|
+
*
|
|
7599
|
+
* // result has the following type:
|
|
7600
|
+
* const ok: { name: string; text: string | null }[] = result;
|
|
7530
7601
|
* ```
|
|
7602
|
+
*
|
|
7603
|
+
* @param arg - {@link JoinFirstArg}
|
|
7604
|
+
* @param cb - {@link JoinLateralCallback}
|
|
7531
7605
|
*/
|
|
7532
|
-
|
|
7533
|
-
return
|
|
7606
|
+
leftJoinLateral(arg, cb) {
|
|
7607
|
+
return _joinLateral(
|
|
7534
7608
|
this.clone(),
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7609
|
+
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
7610
|
+
"LEFT JOIN",
|
|
7611
|
+
arg,
|
|
7612
|
+
cb
|
|
7539
7613
|
);
|
|
7540
7614
|
}
|
|
7615
|
+
}
|
|
7616
|
+
const makeOnItem = (joinTo, joinFrom, args) => {
|
|
7617
|
+
return {
|
|
7618
|
+
ON: {
|
|
7619
|
+
joinTo,
|
|
7620
|
+
joinFrom,
|
|
7621
|
+
on: args
|
|
7622
|
+
}
|
|
7623
|
+
};
|
|
7624
|
+
};
|
|
7625
|
+
const pushQueryOn = (q, joinFrom, joinTo, ...on) => {
|
|
7626
|
+
return pushQueryValue(
|
|
7627
|
+
q,
|
|
7628
|
+
"and",
|
|
7629
|
+
makeOnItem(joinFrom, joinTo, on)
|
|
7630
|
+
);
|
|
7631
|
+
};
|
|
7632
|
+
const pushQueryOrOn = (q, joinFrom, joinTo, ...on) => {
|
|
7633
|
+
return pushQueryValue(q, "or", [
|
|
7634
|
+
makeOnItem(joinFrom, joinTo, on)
|
|
7635
|
+
]);
|
|
7636
|
+
};
|
|
7637
|
+
const addQueryOn = (q, joinFrom, joinTo, ...args) => {
|
|
7638
|
+
const cloned = q.clone();
|
|
7639
|
+
setQueryObjectValue(
|
|
7640
|
+
cloned,
|
|
7641
|
+
"joinedShapes",
|
|
7642
|
+
joinFrom.q.as || joinFrom.table,
|
|
7643
|
+
joinFrom.q.shape
|
|
7644
|
+
);
|
|
7645
|
+
return pushQueryOn(cloned, joinFrom, joinTo, ...args);
|
|
7646
|
+
};
|
|
7647
|
+
const _queryJoinOn = (q, args) => {
|
|
7648
|
+
return pushQueryOn(
|
|
7649
|
+
q,
|
|
7650
|
+
q.q.joinTo,
|
|
7651
|
+
q,
|
|
7652
|
+
...args
|
|
7653
|
+
);
|
|
7654
|
+
};
|
|
7655
|
+
const _queryJoinOrOn = (q, args) => {
|
|
7656
|
+
return pushQueryOrOn(
|
|
7657
|
+
q,
|
|
7658
|
+
q.q.joinTo,
|
|
7659
|
+
q,
|
|
7660
|
+
...args
|
|
7661
|
+
);
|
|
7662
|
+
};
|
|
7663
|
+
const _queryJoinOnJsonPathEquals = (q, args) => {
|
|
7664
|
+
return pushQueryValue(q, "and", {
|
|
7665
|
+
ON: args
|
|
7666
|
+
});
|
|
7667
|
+
};
|
|
7668
|
+
class OnMethods {
|
|
7541
7669
|
/**
|
|
7542
|
-
*
|
|
7670
|
+
* Use `on` to specify columns to join records.
|
|
7543
7671
|
*
|
|
7544
7672
|
* ```ts
|
|
7545
|
-
*
|
|
7673
|
+
* q
|
|
7674
|
+
* // left column is the db.message column, right column is the db.user column
|
|
7675
|
+
* .on('userId', 'id')
|
|
7676
|
+
* // table names can be provided:
|
|
7677
|
+
* .on('message.userId', 'user.id')
|
|
7678
|
+
* // operator can be specified:
|
|
7679
|
+
* .on('userId', '!=', 'id')
|
|
7680
|
+
* // operator can be specified with table names as well:
|
|
7681
|
+
* .on('message.userId', '!=', 'user.id')
|
|
7682
|
+
* // `.orOn` takes the same arguments as `.on` and acts like `.or`:
|
|
7683
|
+
* .on('userId', 'id') // where message.userId = user.id
|
|
7546
7684
|
* ```
|
|
7685
|
+
*
|
|
7686
|
+
* @param args - columns to join with
|
|
7547
7687
|
*/
|
|
7548
|
-
|
|
7549
|
-
return
|
|
7550
|
-
this.clone(),
|
|
7551
|
-
false,
|
|
7552
|
-
args[0],
|
|
7553
|
-
args[1],
|
|
7554
|
-
true
|
|
7555
|
-
);
|
|
7688
|
+
on(...args) {
|
|
7689
|
+
return _queryJoinOn(this.clone(), args);
|
|
7556
7690
|
}
|
|
7557
7691
|
/**
|
|
7558
|
-
*
|
|
7559
|
-
*
|
|
7560
|
-
* This method is accepting the same arguments as `join`, see the {@link Join.join} section for more details.
|
|
7561
|
-
*
|
|
7562
|
-
* ```ts
|
|
7563
|
-
* // find users who have accounts
|
|
7564
|
-
* // find by a relation name if it's defined
|
|
7565
|
-
* db.user.whereExists('account');
|
|
7566
|
-
*
|
|
7567
|
-
* // find using a table and a join conditions
|
|
7568
|
-
* db.user.whereExists(db.account, 'account.id', 'user.id');
|
|
7692
|
+
* Works as {@link on}, but the added conditions will be separated from previous with `OR`.
|
|
7569
7693
|
*
|
|
7570
|
-
*
|
|
7571
|
-
* db.user.whereExists(db.account, (q) => q.on('account.id', '=', 'user.id'));
|
|
7572
|
-
* ```
|
|
7694
|
+
* @param args - columns to join with
|
|
7573
7695
|
*/
|
|
7574
|
-
|
|
7575
|
-
return
|
|
7576
|
-
this.clone(),
|
|
7577
|
-
existsArgs(arg, args)
|
|
7578
|
-
);
|
|
7696
|
+
orOn(...args) {
|
|
7697
|
+
return _queryJoinOrOn(this.clone(), args);
|
|
7579
7698
|
}
|
|
7580
7699
|
/**
|
|
7581
|
-
*
|
|
7700
|
+
* Use `onJsonPathEquals` to join record based on a field of their JSON column:
|
|
7582
7701
|
*
|
|
7583
7702
|
* ```ts
|
|
7584
|
-
*
|
|
7585
|
-
*
|
|
7586
|
-
*
|
|
7703
|
+
* db.table.join(db.otherTable, (q) =>
|
|
7704
|
+
* // '$.key' is a JSON path
|
|
7705
|
+
* q.onJsonPathEquals('otherTable.data', '$.key', 'table.data', '$.key'),
|
|
7706
|
+
* );
|
|
7587
7707
|
* ```
|
|
7708
|
+
*
|
|
7709
|
+
* @param args - columns and JSON paths to join with.
|
|
7588
7710
|
*/
|
|
7589
|
-
|
|
7590
|
-
return
|
|
7591
|
-
this.clone(),
|
|
7592
|
-
existsArgs(arg, args)
|
|
7593
|
-
);
|
|
7711
|
+
onJsonPathEquals(...args) {
|
|
7712
|
+
return _queryJoinOnJsonPathEquals(this.clone(), args);
|
|
7594
7713
|
}
|
|
7714
|
+
}
|
|
7715
|
+
|
|
7716
|
+
class JsonModifiers {
|
|
7595
7717
|
/**
|
|
7596
|
-
*
|
|
7718
|
+
* Return a JSON value/object/array where a given value is set at the given path.
|
|
7719
|
+
* The path is an array of keys to access the value.
|
|
7720
|
+
*
|
|
7721
|
+
* Can be used in `update` callback.
|
|
7597
7722
|
*
|
|
7598
7723
|
* ```ts
|
|
7599
|
-
*
|
|
7600
|
-
*
|
|
7601
|
-
*
|
|
7724
|
+
* const result = await db.table.jsonSet('data', ['name'], 'new value').take();
|
|
7725
|
+
*
|
|
7726
|
+
* expect(result.data).toEqual({ name: 'new value' });
|
|
7602
7727
|
* ```
|
|
7603
7728
|
*
|
|
7604
|
-
*
|
|
7605
|
-
* @param args - no arguments needed when the first argument is a relation name, or conditions to join the table with.
|
|
7606
|
-
*/
|
|
7607
|
-
whereNotExists(arg, ...args) {
|
|
7608
|
-
return _queryWhereNot(
|
|
7609
|
-
this.clone(),
|
|
7610
|
-
existsArgs(arg, args)
|
|
7611
|
-
);
|
|
7612
|
-
}
|
|
7613
|
-
/**
|
|
7614
|
-
* Acts as `whereExists`, but prepends the condition with `OR` and negates it with `NOT`:
|
|
7729
|
+
* Optionally takes parameters of type `{ as?: string, createIfMissing?: boolean }`
|
|
7615
7730
|
*
|
|
7616
7731
|
* ```ts
|
|
7617
|
-
*
|
|
7618
|
-
* //
|
|
7619
|
-
*
|
|
7732
|
+
* await db.table.jsonSet('data', ['name'], 'new value', {
|
|
7733
|
+
* as: 'alias', // select data as `alias`
|
|
7734
|
+
* createIfMissing: true, // ignored if missing by default
|
|
7735
|
+
* });
|
|
7620
7736
|
* ```
|
|
7737
|
+
*
|
|
7738
|
+
* @param column - name of JSON column, or a result of a nested json method
|
|
7739
|
+
* @param path - path to value inside the json
|
|
7740
|
+
* @param value - value to set into the json
|
|
7741
|
+
* @param options - `as` to alias the json value when selecting, `createIfMissing: true` will create a new JSON property if it didn't exist before
|
|
7621
7742
|
*/
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
7743
|
+
jsonSet(column, path, value, options) {
|
|
7744
|
+
var _a;
|
|
7745
|
+
const q = this.clone();
|
|
7746
|
+
const json = {
|
|
7747
|
+
__json: [
|
|
7748
|
+
"set",
|
|
7749
|
+
(_a = options == null ? void 0 : options.as) != null ? _a : typeof column === "string" ? column : column.__json[1],
|
|
7750
|
+
typeof column === "string" ? q.q.shape[column] : column.__json[2],
|
|
7751
|
+
column,
|
|
7752
|
+
path,
|
|
7753
|
+
value,
|
|
7754
|
+
options
|
|
7755
|
+
]
|
|
7756
|
+
};
|
|
7757
|
+
return Object.assign(
|
|
7758
|
+
pushQueryValue(q, "select", json),
|
|
7759
|
+
json
|
|
7626
7760
|
);
|
|
7627
7761
|
}
|
|
7628
|
-
}
|
|
7629
|
-
class WhereQueryBase extends QueryBase {
|
|
7630
|
-
}
|
|
7631
|
-
orchidCore.applyMixins(WhereQueryBase, [Where]);
|
|
7632
|
-
|
|
7633
|
-
class Join {
|
|
7634
7762
|
/**
|
|
7635
|
-
*
|
|
7763
|
+
* 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.
|
|
7636
7764
|
*
|
|
7637
|
-
*
|
|
7765
|
+
* Can be used in `update` callback.
|
|
7638
7766
|
*
|
|
7639
7767
|
* ```ts
|
|
7640
|
-
* //
|
|
7641
|
-
*
|
|
7642
|
-
* await db.user.select('name', {
|
|
7643
|
-
* profile: (q) => q.profile,
|
|
7644
|
-
* });
|
|
7645
|
-
*
|
|
7646
|
-
* // select posts with counts of comments, order by comments count
|
|
7647
|
-
* // result type is Array<Post & { commentsCount: number }>
|
|
7648
|
-
* await db.post
|
|
7649
|
-
* .select('*', {
|
|
7650
|
-
* commentsCount: (q) => q.comments.count(),
|
|
7651
|
-
* })
|
|
7652
|
-
* .order({
|
|
7653
|
-
* commentsCount: 'DESC',
|
|
7654
|
-
* });
|
|
7768
|
+
* // imagine user has data = { tags: ['two'] }
|
|
7769
|
+
* const result = await db.table.jsonInsert('data', ['tags', 0], 'one').take();
|
|
7655
7770
|
*
|
|
7656
|
-
* //
|
|
7657
|
-
*
|
|
7658
|
-
* await db.author.select('*', {
|
|
7659
|
-
* books: (q) => q.books.pluck('title'),
|
|
7660
|
-
* });
|
|
7771
|
+
* // 'one' is inserted to 0 position
|
|
7772
|
+
* expect(result.data).toEqual({ tags: ['one', 'two'] });
|
|
7661
7773
|
* ```
|
|
7662
7774
|
*
|
|
7663
|
-
*
|
|
7664
|
-
* 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.
|
|
7665
|
-
* If you want to load only users that have profiles, and filter out the rest, add `.join()` method to the relation without arguments:
|
|
7775
|
+
* Optionally takes parameters of type `{ as?: string, insertAfter?: boolean }`
|
|
7666
7776
|
*
|
|
7667
7777
|
* ```ts
|
|
7668
|
-
* //
|
|
7669
|
-
* await db.
|
|
7670
|
-
*
|
|
7671
|
-
*
|
|
7672
|
-
*
|
|
7673
|
-
*
|
|
7674
|
-
*
|
|
7675
|
-
* profile: (q) => q.profile.join().where({ age: { gt: 20 } }),
|
|
7676
|
-
* });
|
|
7677
|
-
* ```
|
|
7678
|
-
*
|
|
7679
|
-
* You can also use this `.join()` method on the one-to-many relations, and records with empty array will be filtered out:
|
|
7680
|
-
*
|
|
7681
|
-
* ```ts
|
|
7682
|
-
* // posts that have no tags won't be loaded
|
|
7683
|
-
* // result type is Array<Post & { tags: Tag[] }>
|
|
7684
|
-
* db.post.select('*', {
|
|
7685
|
-
* tags: (q) => q.tags.join(),
|
|
7686
|
-
* });
|
|
7687
|
-
* ```
|
|
7688
|
-
*
|
|
7689
|
-
* # Joins
|
|
7690
|
-
*
|
|
7691
|
-
* `join` methods allows to join other tables, relations by name, [with](/guide/advanced-queries#with) statements, sub queries.
|
|
7692
|
-
*
|
|
7693
|
-
* 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.
|
|
7694
|
-
*
|
|
7695
|
-
* 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:
|
|
7696
|
-
*
|
|
7697
|
-
* ```ts
|
|
7698
|
-
* export class UserTable extends BaseTable {
|
|
7699
|
-
* readonly table = 'user';
|
|
7700
|
-
* columns = this.setColumns((t) => ({
|
|
7701
|
-
* id: t.identity().primaryKey(),
|
|
7702
|
-
* name: t.text(),
|
|
7703
|
-
* }));
|
|
7704
|
-
*
|
|
7705
|
-
* relations = {
|
|
7706
|
-
* messages: this.hasMany(() => MessageTable, {
|
|
7707
|
-
* primaryKey: 'id',
|
|
7708
|
-
* foreignKey: 'userId',
|
|
7709
|
-
* }),
|
|
7710
|
-
* };
|
|
7711
|
-
* }
|
|
7712
|
-
*
|
|
7713
|
-
* export class MessageTable extends BaseTable {
|
|
7714
|
-
* readonly table = 'message';
|
|
7715
|
-
* columns = this.setColumns((t) => ({
|
|
7716
|
-
* id: t.identity().primaryKey(),
|
|
7717
|
-
* text: t.text(),
|
|
7718
|
-
* ...t.timestamps(),
|
|
7719
|
-
* }));
|
|
7778
|
+
* // imagine user has data = { tags: ['one'] }
|
|
7779
|
+
* const result = await db.table
|
|
7780
|
+
* .jsonInsert('data', ['tags', 0], 'two', {
|
|
7781
|
+
* as: 'alias', // select as an alias
|
|
7782
|
+
* insertAfter: true, // insert after the specified position
|
|
7783
|
+
* })
|
|
7784
|
+
* .take();
|
|
7720
7785
|
*
|
|
7721
|
-
*
|
|
7722
|
-
*
|
|
7723
|
-
* primaryKey: 'id',
|
|
7724
|
-
* foreignKey: 'userId',
|
|
7725
|
-
* }),
|
|
7726
|
-
* };
|
|
7727
|
-
* }
|
|
7786
|
+
* // 'one' is inserted to 0 position
|
|
7787
|
+
* expect(result.alias).toEqual({ tags: ['one', 'two'] });
|
|
7728
7788
|
* ```
|
|
7789
|
+
* @param column - name of JSON column, or a result of a nested json method
|
|
7790
|
+
* @param path - path to the array inside the json, last path element is index to insert into
|
|
7791
|
+
* @param value - value to insert into the json array
|
|
7792
|
+
* @param options - `as` to alias the json value when selecting, `insertAfter: true` to insert after the specified position
|
|
7793
|
+
*/
|
|
7794
|
+
jsonInsert(column, path, value, options) {
|
|
7795
|
+
var _a;
|
|
7796
|
+
const q = this.clone();
|
|
7797
|
+
const json = {
|
|
7798
|
+
__json: [
|
|
7799
|
+
"insert",
|
|
7800
|
+
(_a = options == null ? void 0 : options.as) != null ? _a : typeof column === "string" ? column : column.__json[1],
|
|
7801
|
+
typeof column === "string" ? q.q.shape[column] : column.__json[2],
|
|
7802
|
+
column,
|
|
7803
|
+
path,
|
|
7804
|
+
value,
|
|
7805
|
+
options
|
|
7806
|
+
]
|
|
7807
|
+
};
|
|
7808
|
+
return Object.assign(
|
|
7809
|
+
pushQueryValue(q, "select", json),
|
|
7810
|
+
json
|
|
7811
|
+
);
|
|
7812
|
+
}
|
|
7813
|
+
/**
|
|
7814
|
+
* Return a JSON value/object/array where a given value is removed at the given JSON path.
|
|
7729
7815
|
*
|
|
7730
|
-
*
|
|
7731
|
-
*
|
|
7732
|
-
* `join` is a method for SQL `JOIN`, which is equivalent to `INNER JOIN`, `LEFT INNERT JOIN`.
|
|
7733
|
-
*
|
|
7734
|
-
* When no matching record is found, it will skip records of the main table.
|
|
7735
|
-
*
|
|
7736
|
-
* When joining the same table with the same condition more than once, duplicated joins will be ignored:
|
|
7816
|
+
* Can be used in `update` callback.
|
|
7737
7817
|
*
|
|
7738
7818
|
* ```ts
|
|
7739
|
-
* //
|
|
7740
|
-
* db.
|
|
7741
|
-
*
|
|
7742
|
-
*
|
|
7743
|
-
*
|
|
7744
|
-
*
|
|
7745
|
-
*
|
|
7746
|
-
*
|
|
7747
|
-
*
|
|
7748
|
-
*
|
|
7749
|
-
*
|
|
7750
|
-
* ```sql
|
|
7751
|
-
* SELECT * FROM post JOIN comments ON comments.postId = post.id
|
|
7752
|
-
* ```
|
|
7753
|
-
*
|
|
7754
|
-
* However, this is only possible if the join has no dynamic values:
|
|
7819
|
+
* // imagine a user has data = { tags: ['one', 'two'] }
|
|
7820
|
+
* const result = await db.table
|
|
7821
|
+
* .jsonRemove(
|
|
7822
|
+
* 'data',
|
|
7823
|
+
* ['tags', 0],
|
|
7824
|
+
* // optional parameters:
|
|
7825
|
+
* {
|
|
7826
|
+
* as: 'alias', // select as an alias
|
|
7827
|
+
* },
|
|
7828
|
+
* )
|
|
7829
|
+
* .take();
|
|
7755
7830
|
*
|
|
7756
|
-
*
|
|
7757
|
-
* db.post
|
|
7758
|
-
* .join('comments', (q) => q.where({ rating: { gt: 5 } }))
|
|
7759
|
-
* .join('comments', (q) => q.where({ rating: { gt: 5 } }));
|
|
7831
|
+
* expect(result.alias).toEqual({ tags: ['two'] });
|
|
7760
7832
|
* ```
|
|
7761
7833
|
*
|
|
7762
|
-
*
|
|
7763
|
-
*
|
|
7764
|
-
*
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
|
|
7834
|
+
* @param column - name of JSON column, or a result of a nested json method
|
|
7835
|
+
* @param path - path to the array inside the json, last path element is index to remove this element
|
|
7836
|
+
* @param options - `as` to alias the json value when selecting
|
|
7837
|
+
*/
|
|
7838
|
+
jsonRemove(column, path, options) {
|
|
7839
|
+
var _a;
|
|
7840
|
+
const q = this.clone();
|
|
7841
|
+
const json = {
|
|
7842
|
+
__json: [
|
|
7843
|
+
"remove",
|
|
7844
|
+
(_a = options == null ? void 0 : options.as) != null ? _a : typeof column === "string" ? column : column.__json[1],
|
|
7845
|
+
typeof column === "string" ? q.q.shape[column] : column.__json[2],
|
|
7846
|
+
column,
|
|
7847
|
+
path
|
|
7848
|
+
]
|
|
7849
|
+
};
|
|
7850
|
+
return Object.assign(
|
|
7851
|
+
pushQueryValue(q, "select", json),
|
|
7852
|
+
json
|
|
7853
|
+
);
|
|
7854
|
+
}
|
|
7855
|
+
/**
|
|
7856
|
+
* Selects a value from JSON data using a JSON path.
|
|
7769
7857
|
*
|
|
7770
7858
|
* ```ts
|
|
7771
|
-
*
|
|
7772
|
-
* .join('messages')
|
|
7773
|
-
* // after joining a table, we can use it in `where` conditions:
|
|
7774
|
-
* .where({ 'messages.text': { startsWith: 'Hi' } })
|
|
7775
|
-
* .select(
|
|
7776
|
-
* 'name', // name is User column, table name may be omitted
|
|
7777
|
-
* 'messages.text', // text is the Message column, and the table name is required
|
|
7778
|
-
* );
|
|
7779
|
-
*
|
|
7780
|
-
* // result has the following type:
|
|
7781
|
-
* const ok: { name: string; text: string }[] = result;
|
|
7782
|
-
* ```
|
|
7859
|
+
* import { columnTypes } from 'orchid-orm';
|
|
7783
7860
|
*
|
|
7784
|
-
*
|
|
7785
|
-
*
|
|
7861
|
+
* db.table.jsonPathQuery(
|
|
7862
|
+
* columnTypes.text(3, 100), // type of the value
|
|
7863
|
+
* 'data', // name of the JSON column
|
|
7864
|
+
* '$.name', // JSON path
|
|
7865
|
+
* 'name', // select value as name
|
|
7786
7866
|
*
|
|
7787
|
-
*
|
|
7788
|
-
*
|
|
7789
|
-
*
|
|
7867
|
+
* // Optionally supports `vars` and `silent` options
|
|
7868
|
+
* // check Postgres docs for jsonb_path_query for details
|
|
7869
|
+
* {
|
|
7870
|
+
* vars: 'vars',
|
|
7871
|
+
* silent: true,
|
|
7872
|
+
* },
|
|
7790
7873
|
* );
|
|
7791
7874
|
* ```
|
|
7792
7875
|
*
|
|
7793
|
-
*
|
|
7794
|
-
*
|
|
7795
|
-
* But remember that when joining a relation, the needed `ON` conditions are already handled automatically.
|
|
7876
|
+
* Nested JSON operations can be used in place of JSON column name:
|
|
7796
7877
|
*
|
|
7797
7878
|
* ```ts
|
|
7798
|
-
*
|
|
7799
|
-
* (
|
|
7800
|
-
*
|
|
7801
|
-
*
|
|
7802
|
-
*
|
|
7803
|
-
*
|
|
7879
|
+
* db.table.jsonPathQuery(
|
|
7880
|
+
* columnTypes.text(3, 100),
|
|
7881
|
+
* // Available: .jsonSet, .jsonInsert, .jsonRemove
|
|
7882
|
+
* db.table.jsonSet('data', ['key'], 'value'),
|
|
7883
|
+
* '$.name',
|
|
7884
|
+
* 'name',
|
|
7804
7885
|
* );
|
|
7805
7886
|
* ```
|
|
7806
7887
|
*
|
|
7807
|
-
*
|
|
7808
|
-
*
|
|
7809
|
-
*
|
|
7888
|
+
* @param type - provide a column type to have a correct result type
|
|
7889
|
+
* @param column - name of JSON column, or a result of a nested json method
|
|
7890
|
+
* @param path - special JSON path string to reference a JSON value
|
|
7891
|
+
* @param as - optional alias for the selected value
|
|
7892
|
+
* @param options - supports `vars` and `silent`, check Postgres docs of `json_path_query` for these
|
|
7893
|
+
*/
|
|
7894
|
+
jsonPathQuery(type, column, path, as, options) {
|
|
7895
|
+
const q = this.clone();
|
|
7896
|
+
const json = {
|
|
7897
|
+
__json: ["pathQuery", as, type, column, path, options]
|
|
7898
|
+
};
|
|
7899
|
+
return Object.assign(
|
|
7900
|
+
pushQueryValue(q, "select", json),
|
|
7901
|
+
json
|
|
7902
|
+
);
|
|
7903
|
+
}
|
|
7904
|
+
}
|
|
7905
|
+
class JsonMethods {
|
|
7906
|
+
/**
|
|
7907
|
+
* Wraps the query in a way to select a single JSON string.
|
|
7908
|
+
* So that JSON encoding is done on a database side, and the application doesn't have to turn a response to a JSON.
|
|
7909
|
+
* It may be better for performance in some cases.
|
|
7810
7910
|
*
|
|
7811
7911
|
* ```ts
|
|
7812
|
-
*
|
|
7813
|
-
*
|
|
7814
|
-
* });
|
|
7815
|
-
*
|
|
7816
|
-
* // result has the following type:
|
|
7817
|
-
* const ok: {
|
|
7818
|
-
* // title of the book
|
|
7819
|
-
* title: string;
|
|
7820
|
-
* // a full author record is included:
|
|
7821
|
-
* author: { id: number; name: string; updatedAt: Date; createdAt: Date };
|
|
7822
|
-
* }[] = result;
|
|
7912
|
+
* // json is a JSON string that you can directly send as a response.
|
|
7913
|
+
* const json = await db.table.select('id', 'name').json();
|
|
7823
7914
|
* ```
|
|
7824
7915
|
*
|
|
7825
|
-
*
|
|
7826
|
-
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7916
|
+
* @param coalesce
|
|
7917
|
+
*/
|
|
7918
|
+
json(coalesce) {
|
|
7919
|
+
return queryJson(
|
|
7920
|
+
this.clone(),
|
|
7921
|
+
coalesce
|
|
7922
|
+
);
|
|
7923
|
+
}
|
|
7924
|
+
}
|
|
7925
|
+
|
|
7926
|
+
const logColors = {
|
|
7927
|
+
boldCyanBright: (message) => `\x1B[1m\x1B[96m${message}\x1B[39m\x1B[22m`,
|
|
7928
|
+
boldBlue: (message) => `\x1B[1m\x1B[34m${message}\x1B[39m\x1B[22m`,
|
|
7929
|
+
boldYellow: (message) => `\x1B[1m\x1B[33m${message}\x1B[39m\x1B[22m`,
|
|
7930
|
+
boldMagenta: (message) => `\x1B[1m\x1B[33m${message}\x1B[39m\x1B[22m`,
|
|
7931
|
+
boldRed: (message) => `\x1B[1m\x1B[31m${message}\x1B[39m\x1B[22m`
|
|
7932
|
+
};
|
|
7933
|
+
const makeMessage = (colors, timeColor, time, sqlColor, sql, valuesColor, values) => {
|
|
7934
|
+
const elapsed = process.hrtime(time);
|
|
7935
|
+
const formattedTime = `(${elapsed[0] ? `${elapsed[0]}s ` : ""}${(elapsed[1] / 1e6).toFixed(1)}ms)`;
|
|
7936
|
+
const result = `${colors ? timeColor(formattedTime) : formattedTime} ${colors ? sqlColor(sql) : sql}`;
|
|
7937
|
+
if (!values.length) {
|
|
7938
|
+
return result;
|
|
7939
|
+
}
|
|
7940
|
+
const formattedValues = `[${values.map(quote).join(", ")}]`;
|
|
7941
|
+
return `${result} ${colors ? valuesColor(formattedValues) : formattedValues}`;
|
|
7942
|
+
};
|
|
7943
|
+
const logParamToLogObject = (logger, log) => {
|
|
7944
|
+
if (!log)
|
|
7945
|
+
return;
|
|
7946
|
+
const logObject = Object.assign(
|
|
7947
|
+
{
|
|
7948
|
+
colors: true,
|
|
7949
|
+
beforeQuery() {
|
|
7950
|
+
return process.hrtime();
|
|
7951
|
+
},
|
|
7952
|
+
afterQuery(sql, time) {
|
|
7953
|
+
logger.log(
|
|
7954
|
+
makeMessage(
|
|
7955
|
+
colors,
|
|
7956
|
+
logColors.boldCyanBright,
|
|
7957
|
+
time,
|
|
7958
|
+
logColors.boldBlue,
|
|
7959
|
+
sql.text,
|
|
7960
|
+
logColors.boldYellow,
|
|
7961
|
+
sql.values
|
|
7962
|
+
)
|
|
7963
|
+
);
|
|
7964
|
+
},
|
|
7965
|
+
onError(error, sql, time) {
|
|
7966
|
+
const message = `Error: ${error.message}`;
|
|
7967
|
+
logger.error(
|
|
7968
|
+
`${makeMessage(
|
|
7969
|
+
colors,
|
|
7970
|
+
logColors.boldMagenta,
|
|
7971
|
+
time,
|
|
7972
|
+
logColors.boldRed,
|
|
7973
|
+
sql.text,
|
|
7974
|
+
logColors.boldYellow,
|
|
7975
|
+
sql.values
|
|
7976
|
+
)} ${colors ? logColors.boldRed(message) : message}`
|
|
7977
|
+
);
|
|
7978
|
+
}
|
|
7979
|
+
},
|
|
7980
|
+
log === true ? {} : log
|
|
7981
|
+
);
|
|
7982
|
+
const colors = logObject.colors;
|
|
7983
|
+
return logObject;
|
|
7984
|
+
};
|
|
7985
|
+
class QueryLog {
|
|
7986
|
+
log(log = true) {
|
|
7987
|
+
const q = this.clone();
|
|
7988
|
+
q.q.log = logParamToLogObject(q.q.logger, log);
|
|
7989
|
+
return q;
|
|
7990
|
+
}
|
|
7991
|
+
}
|
|
7992
|
+
|
|
7993
|
+
var __defProp$5 = Object.defineProperty;
|
|
7994
|
+
var __getOwnPropSymbols$5 = Object.getOwnPropertySymbols;
|
|
7995
|
+
var __hasOwnProp$5 = Object.prototype.hasOwnProperty;
|
|
7996
|
+
var __propIsEnum$5 = Object.prototype.propertyIsEnumerable;
|
|
7997
|
+
var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
7998
|
+
var __spreadValues$5 = (a, b) => {
|
|
7999
|
+
for (var prop in b || (b = {}))
|
|
8000
|
+
if (__hasOwnProp$5.call(b, prop))
|
|
8001
|
+
__defNormalProp$5(a, prop, b[prop]);
|
|
8002
|
+
if (__getOwnPropSymbols$5)
|
|
8003
|
+
for (var prop of __getOwnPropSymbols$5(b)) {
|
|
8004
|
+
if (__propIsEnum$5.call(b, prop))
|
|
8005
|
+
__defNormalProp$5(a, prop, b[prop]);
|
|
8006
|
+
}
|
|
8007
|
+
return a;
|
|
8008
|
+
};
|
|
8009
|
+
const mergableObjects = {
|
|
8010
|
+
shape: true,
|
|
8011
|
+
withShapes: true,
|
|
8012
|
+
parsers: true,
|
|
8013
|
+
defaults: true,
|
|
8014
|
+
joinedShapes: true,
|
|
8015
|
+
joinedParsers: true
|
|
8016
|
+
};
|
|
8017
|
+
class MergeQueryMethods {
|
|
8018
|
+
merge(q) {
|
|
8019
|
+
const query = this.clone();
|
|
8020
|
+
const a = query.q;
|
|
8021
|
+
const b = q.q;
|
|
8022
|
+
for (const key in b) {
|
|
8023
|
+
const value = b[key];
|
|
8024
|
+
switch (typeof value) {
|
|
8025
|
+
case "boolean":
|
|
8026
|
+
case "string":
|
|
8027
|
+
case "number":
|
|
8028
|
+
a[key] = value;
|
|
8029
|
+
break;
|
|
8030
|
+
case "object":
|
|
8031
|
+
if (Array.isArray(value)) {
|
|
8032
|
+
a[key] = a[key] ? [...a[key], ...value] : value;
|
|
8033
|
+
} else if (mergableObjects[key]) {
|
|
8034
|
+
a[key] = a[key] ? __spreadValues$5(__spreadValues$5({}, a[key]), value) : value;
|
|
8035
|
+
} else {
|
|
8036
|
+
a[key] = value;
|
|
8037
|
+
}
|
|
8038
|
+
break;
|
|
8039
|
+
}
|
|
8040
|
+
}
|
|
8041
|
+
a[orchidCore.getValueKey] = b[orchidCore.getValueKey];
|
|
8042
|
+
if (b.returnType)
|
|
8043
|
+
a.returnType = b.returnType;
|
|
8044
|
+
return query;
|
|
8045
|
+
}
|
|
8046
|
+
}
|
|
8047
|
+
|
|
8048
|
+
var __defProp$4 = Object.defineProperty;
|
|
8049
|
+
var __defProps$2 = Object.defineProperties;
|
|
8050
|
+
var __getOwnPropDescs$2 = Object.getOwnPropertyDescriptors;
|
|
8051
|
+
var __getOwnPropSymbols$4 = Object.getOwnPropertySymbols;
|
|
8052
|
+
var __hasOwnProp$4 = Object.prototype.hasOwnProperty;
|
|
8053
|
+
var __propIsEnum$4 = Object.prototype.propertyIsEnumerable;
|
|
8054
|
+
var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8055
|
+
var __spreadValues$4 = (a, b) => {
|
|
8056
|
+
for (var prop in b || (b = {}))
|
|
8057
|
+
if (__hasOwnProp$4.call(b, prop))
|
|
8058
|
+
__defNormalProp$4(a, prop, b[prop]);
|
|
8059
|
+
if (__getOwnPropSymbols$4)
|
|
8060
|
+
for (var prop of __getOwnPropSymbols$4(b)) {
|
|
8061
|
+
if (__propIsEnum$4.call(b, prop))
|
|
8062
|
+
__defNormalProp$4(a, prop, b[prop]);
|
|
8063
|
+
}
|
|
8064
|
+
return a;
|
|
8065
|
+
};
|
|
8066
|
+
var __spreadProps$2 = (a, b) => __defProps$2(a, __getOwnPropDescs$2(b));
|
|
8067
|
+
class With {
|
|
8068
|
+
/**
|
|
8069
|
+
* Add Common Table Expression (CTE) to the query.
|
|
7841
8070
|
*
|
|
7842
|
-
*
|
|
8071
|
+
* ```ts
|
|
8072
|
+
* import { columnTypes } from 'orchid-orm';
|
|
8073
|
+
* import { NumberColumn } from './number';
|
|
7843
8074
|
*
|
|
7844
|
-
*
|
|
7845
|
-
*
|
|
7846
|
-
*
|
|
7847
|
-
*
|
|
7848
|
-
*
|
|
8075
|
+
* // .with optionally accepts such options:
|
|
8076
|
+
* type WithOptions = {
|
|
8077
|
+
* // list of columns returned by this WITH statement
|
|
8078
|
+
* // by default all columns from provided column shape will be included
|
|
8079
|
+
* // true is for default behavior
|
|
8080
|
+
* columns?: string[] | boolean;
|
|
7849
8081
|
*
|
|
7850
|
-
*
|
|
8082
|
+
* // Adds RECURSIVE keyword:
|
|
8083
|
+
* recursive?: true;
|
|
7851
8084
|
*
|
|
7852
|
-
*
|
|
7853
|
-
*
|
|
8085
|
+
* // Adds MATERIALIZED keyword:
|
|
8086
|
+
* materialized?: true;
|
|
8087
|
+
*
|
|
8088
|
+
* // Adds NOT MATERIALIZED keyword:
|
|
8089
|
+
* notMaterialized?: true;
|
|
8090
|
+
* };
|
|
8091
|
+
*
|
|
8092
|
+
* // accepts columns shape and a raw expression:
|
|
8093
|
+
* db.table.with(
|
|
8094
|
+
* 'alias',
|
|
8095
|
+
* {
|
|
8096
|
+
* id: columnTypes.integer(),
|
|
8097
|
+
* name: columnTypes.text(3, 100),
|
|
8098
|
+
* },
|
|
8099
|
+
* db.table.sql`SELECT id, name FROM "someTable"`,
|
|
8100
|
+
* );
|
|
8101
|
+
*
|
|
8102
|
+
* // accepts query:
|
|
8103
|
+
* db.table.with('alias', db.table.all());
|
|
8104
|
+
*
|
|
8105
|
+
* // accepts a callback for a query builder:
|
|
8106
|
+
* db.table.with('alias', (qb) =>
|
|
8107
|
+
* qb.select({ one: db.table.sql((t) => t.integer())`1` }),
|
|
8108
|
+
* );
|
|
8109
|
+
*
|
|
8110
|
+
* // All mentioned forms can accept options as a second argument:
|
|
8111
|
+
* db.table.with(
|
|
8112
|
+
* 'alias',
|
|
8113
|
+
* {
|
|
8114
|
+
* recursive: true,
|
|
8115
|
+
* materialized: true,
|
|
8116
|
+
* },
|
|
8117
|
+
* rawOrQueryOrCallback,
|
|
8118
|
+
* );
|
|
8119
|
+
* ```
|
|
8120
|
+
*
|
|
8121
|
+
* Defined `WITH` table can be used in `.from` or `.join` with all the type safeness:
|
|
7854
8122
|
*
|
|
7855
8123
|
* ```ts
|
|
7856
|
-
*
|
|
7857
|
-
*
|
|
7858
|
-
*
|
|
7859
|
-
* .
|
|
7860
|
-
* .
|
|
8124
|
+
* db.table.with('alias', db.table.all()).from('alias').select('alias.id');
|
|
8125
|
+
*
|
|
8126
|
+
* db.table
|
|
8127
|
+
* .with('alias', db.table.all())
|
|
8128
|
+
* .join('alias', 'alias.id', 'user.id')
|
|
8129
|
+
* .select('alias.id');
|
|
7861
8130
|
* ```
|
|
7862
8131
|
*
|
|
7863
|
-
*
|
|
8132
|
+
* @param args - first argument is an alias for this CTE, other arguments can be column shape, query object, or raw SQL.
|
|
8133
|
+
*/
|
|
8134
|
+
with(...args) {
|
|
8135
|
+
const q = this.clone();
|
|
8136
|
+
let options = args.length === 3 && !orchidCore.isExpression(args[2]) || args.length === 4 ? args[1] : void 0;
|
|
8137
|
+
const last = args[args.length - 1];
|
|
8138
|
+
const query = typeof last === "function" ? last(q.queryBuilder) : last;
|
|
8139
|
+
const shape = args.length === 4 ? args[2] : orchidCore.isExpression(query) ? args[1] : query.q.shape;
|
|
8140
|
+
if ((options == null ? void 0 : options.columns) === true) {
|
|
8141
|
+
options = __spreadProps$2(__spreadValues$4({}, options), {
|
|
8142
|
+
columns: Object.keys(shape)
|
|
8143
|
+
});
|
|
8144
|
+
}
|
|
8145
|
+
pushQueryValue(q, "with", [args[0], options || orchidCore.emptyObject, query]);
|
|
8146
|
+
return setQueryObjectValue(q, "withShapes", args[0], shape);
|
|
8147
|
+
}
|
|
8148
|
+
}
|
|
8149
|
+
|
|
8150
|
+
class Union {
|
|
8151
|
+
/**
|
|
8152
|
+
* 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`.
|
|
8153
|
+
* If the `wrap` parameter is true, the queries will be individually wrapped in parentheses.
|
|
7864
8154
|
*
|
|
7865
8155
|
* ```ts
|
|
7866
|
-
*
|
|
8156
|
+
* SomeTable.select('id', 'name').union(
|
|
8157
|
+
* [
|
|
8158
|
+
* OtherTable.select('id', 'name'),
|
|
8159
|
+
* SomeTable.sql`SELECT id, name FROM "thirdTable"`,
|
|
8160
|
+
* ],
|
|
8161
|
+
* true, // optional wrap parameter
|
|
8162
|
+
* );
|
|
7867
8163
|
* ```
|
|
7868
8164
|
*
|
|
7869
|
-
*
|
|
8165
|
+
* @param args - array of queries or raw SQLs
|
|
8166
|
+
* @param wrap - provide `true` if you want the queries to be wrapped into parentheses
|
|
8167
|
+
*/
|
|
8168
|
+
union(args, wrap) {
|
|
8169
|
+
return pushQueryArray(
|
|
8170
|
+
this.clone(),
|
|
8171
|
+
"union",
|
|
8172
|
+
args.map((arg) => ({ arg, kind: "UNION", wrap }))
|
|
8173
|
+
);
|
|
8174
|
+
}
|
|
8175
|
+
/**
|
|
8176
|
+
* Same as `union`, but allows duplicated rows.
|
|
8177
|
+
*
|
|
8178
|
+
* @param args - array of queries or raw SQLs
|
|
8179
|
+
* @param wrap - provide `true` if you want the queries to be wrapped into parentheses
|
|
8180
|
+
*/
|
|
8181
|
+
unionAll(args, wrap) {
|
|
8182
|
+
return pushQueryArray(
|
|
8183
|
+
this.clone(),
|
|
8184
|
+
"union",
|
|
8185
|
+
args.map((arg) => ({ arg, kind: "UNION ALL", wrap }))
|
|
8186
|
+
);
|
|
8187
|
+
}
|
|
8188
|
+
/**
|
|
8189
|
+
* Same as `union`, but uses a `INTERSECT` SQL keyword instead
|
|
8190
|
+
*
|
|
8191
|
+
* @param args - array of queries or raw SQLs
|
|
8192
|
+
* @param wrap - provide `true` if you want the queries to be wrapped into parentheses
|
|
8193
|
+
*/
|
|
8194
|
+
intersect(args, wrap) {
|
|
8195
|
+
return pushQueryArray(
|
|
8196
|
+
this.clone(),
|
|
8197
|
+
"union",
|
|
8198
|
+
args.map((arg) => ({ arg, kind: "INTERSECT", wrap }))
|
|
8199
|
+
);
|
|
8200
|
+
}
|
|
8201
|
+
/**
|
|
8202
|
+
* Same as `intersect`, but allows duplicated rows.
|
|
8203
|
+
*
|
|
8204
|
+
* @param args - array of queries or raw SQLs
|
|
8205
|
+
* @param wrap - provide `true` if you want the queries to be wrapped into parentheses
|
|
8206
|
+
*/
|
|
8207
|
+
intersectAll(args, wrap) {
|
|
8208
|
+
return pushQueryArray(
|
|
8209
|
+
this.clone(),
|
|
8210
|
+
"union",
|
|
8211
|
+
args.map((arg) => ({ arg, kind: "INTERSECT ALL", wrap }))
|
|
8212
|
+
);
|
|
8213
|
+
}
|
|
8214
|
+
/**
|
|
8215
|
+
* Same as `union`, but uses an `EXCEPT` SQL keyword instead
|
|
8216
|
+
*
|
|
8217
|
+
* @param args - array of queries or raw SQLs
|
|
8218
|
+
* @param wrap - provide `true` if you want the queries to be wrapped into parentheses
|
|
8219
|
+
*/
|
|
8220
|
+
except(args, wrap) {
|
|
8221
|
+
return pushQueryArray(
|
|
8222
|
+
this.clone(),
|
|
8223
|
+
"union",
|
|
8224
|
+
args.map((arg) => ({ arg, kind: "EXCEPT", wrap }))
|
|
8225
|
+
);
|
|
8226
|
+
}
|
|
8227
|
+
/**
|
|
8228
|
+
* Same as `except`, but allows duplicated rows.
|
|
8229
|
+
*
|
|
8230
|
+
* @param args - array of queries or raw SQLs
|
|
8231
|
+
* @param wrap - provide `true` if you want the queries to be wrapped into parentheses
|
|
8232
|
+
*/
|
|
8233
|
+
exceptAll(args, wrap) {
|
|
8234
|
+
return pushQueryArray(
|
|
8235
|
+
this.clone(),
|
|
8236
|
+
"union",
|
|
8237
|
+
args.map((arg) => ({ arg, kind: "EXCEPT ALL", wrap }))
|
|
8238
|
+
);
|
|
8239
|
+
}
|
|
8240
|
+
}
|
|
8241
|
+
|
|
8242
|
+
const _queryWhere = (q, args) => {
|
|
8243
|
+
return pushQueryArray(
|
|
8244
|
+
q,
|
|
8245
|
+
"and",
|
|
8246
|
+
args
|
|
8247
|
+
);
|
|
8248
|
+
};
|
|
8249
|
+
const _queryWhereSql = (q, args) => {
|
|
8250
|
+
return pushQueryValue(
|
|
8251
|
+
q,
|
|
8252
|
+
"and",
|
|
8253
|
+
sqlQueryArgsToExpression(args)
|
|
8254
|
+
);
|
|
8255
|
+
};
|
|
8256
|
+
const _queryWhereNot = (q, args) => {
|
|
8257
|
+
return pushQueryValue(q, "and", {
|
|
8258
|
+
NOT: args
|
|
8259
|
+
});
|
|
8260
|
+
};
|
|
8261
|
+
const _queryWhereNotSql = (q, args) => {
|
|
8262
|
+
return pushQueryValue(q, "and", {
|
|
8263
|
+
NOT: sqlQueryArgsToExpression(args)
|
|
8264
|
+
});
|
|
8265
|
+
};
|
|
8266
|
+
const _queryOr = (q, args) => {
|
|
8267
|
+
return pushQueryArray(
|
|
8268
|
+
q,
|
|
8269
|
+
"or",
|
|
8270
|
+
args.map((item) => [item])
|
|
8271
|
+
);
|
|
8272
|
+
};
|
|
8273
|
+
const _queryOrNot = (q, args) => {
|
|
8274
|
+
return pushQueryArray(
|
|
8275
|
+
q,
|
|
8276
|
+
"or",
|
|
8277
|
+
args.map((item) => [{ NOT: item }])
|
|
8278
|
+
);
|
|
8279
|
+
};
|
|
8280
|
+
const _queryWhereIn = (q, and, arg, values, not) => {
|
|
8281
|
+
let item;
|
|
8282
|
+
if (values) {
|
|
8283
|
+
if (Array.isArray(arg)) {
|
|
8284
|
+
item = {
|
|
8285
|
+
IN: {
|
|
8286
|
+
columns: arg,
|
|
8287
|
+
values
|
|
8288
|
+
}
|
|
8289
|
+
};
|
|
8290
|
+
} else {
|
|
8291
|
+
item = { [arg]: { in: values } };
|
|
8292
|
+
}
|
|
8293
|
+
} else {
|
|
8294
|
+
item = {};
|
|
8295
|
+
for (const key in arg) {
|
|
8296
|
+
item[key] = { in: arg[key] };
|
|
8297
|
+
}
|
|
8298
|
+
}
|
|
8299
|
+
if (not)
|
|
8300
|
+
item = { NOT: item };
|
|
8301
|
+
if (and) {
|
|
8302
|
+
pushQueryValue(q, "and", item);
|
|
8303
|
+
} else {
|
|
8304
|
+
pushQueryValue(q, "or", [item]);
|
|
8305
|
+
}
|
|
8306
|
+
return q;
|
|
8307
|
+
};
|
|
8308
|
+
const existsArgs = (self, q, args) => {
|
|
8309
|
+
let joinSubQuery;
|
|
8310
|
+
if (typeof q === "object") {
|
|
8311
|
+
joinSubQuery = getIsJoinSubQuery(q);
|
|
8312
|
+
if (joinSubQuery) {
|
|
8313
|
+
q = q.clone();
|
|
8314
|
+
q.shape = getShapeFromSelect(
|
|
8315
|
+
q,
|
|
8316
|
+
true
|
|
8317
|
+
);
|
|
8318
|
+
}
|
|
8319
|
+
} else {
|
|
8320
|
+
joinSubQuery = false;
|
|
8321
|
+
}
|
|
8322
|
+
const joinArgs = processJoinArgs(self, q, args, joinSubQuery);
|
|
8323
|
+
return [
|
|
8324
|
+
{
|
|
8325
|
+
EXISTS: joinArgs
|
|
8326
|
+
}
|
|
8327
|
+
];
|
|
8328
|
+
};
|
|
8329
|
+
const _queryWhereExists = (q, arg, args) => {
|
|
8330
|
+
return _queryWhere(
|
|
8331
|
+
q,
|
|
8332
|
+
existsArgs(q, arg, args)
|
|
8333
|
+
);
|
|
8334
|
+
};
|
|
8335
|
+
class Where {
|
|
8336
|
+
/**
|
|
8337
|
+
* Constructing `WHERE` conditions:
|
|
7870
8338
|
*
|
|
7871
8339
|
* ```ts
|
|
7872
|
-
* db.
|
|
7873
|
-
*
|
|
7874
|
-
*
|
|
7875
|
-
* .select('name', 'm.text');
|
|
7876
|
-
* ```
|
|
8340
|
+
* db.table.where({
|
|
8341
|
+
* // column of the current table
|
|
8342
|
+
* name: 'John',
|
|
7877
8343
|
*
|
|
7878
|
-
*
|
|
8344
|
+
* // table name may be specified, it can be the name of a joined table
|
|
8345
|
+
* 'table.lastName': 'Johnsonuk',
|
|
7879
8346
|
*
|
|
7880
|
-
*
|
|
7881
|
-
*
|
|
7882
|
-
*
|
|
7883
|
-
*
|
|
7884
|
-
*
|
|
8347
|
+
* // object with operators, see the "column operators" section to see a full list of them:
|
|
8348
|
+
* age: {
|
|
8349
|
+
* gt: 30,
|
|
8350
|
+
* lt: 70,
|
|
8351
|
+
* },
|
|
7885
8352
|
*
|
|
7886
|
-
*
|
|
7887
|
-
*
|
|
7888
|
-
*
|
|
7889
|
-
* // full message is included as msg:
|
|
7890
|
-
* msg: { id: number; text: string; updatedAt: Date; createdAt: Date };
|
|
7891
|
-
* }[] = result;
|
|
8353
|
+
* // where column equals to raw SQL
|
|
8354
|
+
* column: db.table.sql`sql expression`,
|
|
8355
|
+
* });
|
|
7892
8356
|
* ```
|
|
7893
8357
|
*
|
|
7894
|
-
*
|
|
8358
|
+
* Multiple `where`s are joined with `AND`:
|
|
7895
8359
|
*
|
|
7896
8360
|
* ```ts
|
|
7897
|
-
* db.
|
|
8361
|
+
* db.table.where({ foo: 'foo' }).where({ bar: 'bar' });
|
|
7898
8362
|
* ```
|
|
7899
8363
|
*
|
|
7900
|
-
*
|
|
7901
|
-
*
|
|
7902
|
-
* ```ts
|
|
7903
|
-
* db.user.join(
|
|
7904
|
-
* db.message,
|
|
7905
|
-
* db.user.sql`lower("message"."text") = lower("user"."name")`,
|
|
7906
|
-
* );
|
|
8364
|
+
* ```sql
|
|
8365
|
+
* SELECT * FROM table WHERE foo = 'foo' AND bar = 'bar'
|
|
7907
8366
|
* ```
|
|
7908
8367
|
*
|
|
7909
|
-
*
|
|
8368
|
+
* `undefined` values are ignored, so you can supply a partial object with conditions:
|
|
7910
8369
|
*
|
|
7911
8370
|
* ```ts
|
|
7912
|
-
*
|
|
7913
|
-
*
|
|
7914
|
-
*
|
|
7915
|
-
*
|
|
7916
|
-
* );
|
|
8371
|
+
* type Params = {
|
|
8372
|
+
* // allow providing exact age, or lower or greater than
|
|
8373
|
+
* age?: number | { lt?: number; gt?: number };
|
|
8374
|
+
* };
|
|
7917
8375
|
*
|
|
7918
|
-
*
|
|
7919
|
-
*
|
|
7920
|
-
* db.
|
|
7921
|
-
*
|
|
7922
|
-
* '!=',
|
|
7923
|
-
* db.user.sql`lower("user"."name")`,
|
|
7924
|
-
* );
|
|
8376
|
+
* const loadRecords = async (params: Params) => {
|
|
8377
|
+
* // this will load all records if params is an empty object
|
|
8378
|
+
* const records = await db.table.where(params);
|
|
8379
|
+
* };
|
|
7925
8380
|
* ```
|
|
7926
8381
|
*
|
|
7927
|
-
*
|
|
8382
|
+
* It supports a sub-query that is selecting a single value to compare it with a column:
|
|
7928
8383
|
*
|
|
7929
8384
|
* ```ts
|
|
7930
|
-
* db.
|
|
7931
|
-
*
|
|
7932
|
-
*
|
|
7933
|
-
* // with table names:
|
|
7934
|
-
* 'message.userId': 'user.id',
|
|
7935
|
-
*
|
|
7936
|
-
* // value can be a raw SQL expression:
|
|
7937
|
-
* text: db.user.sql`lower("user"."name")`,
|
|
8385
|
+
* db.table.where({
|
|
8386
|
+
* // compare `someColumn` in one table with the `column` value returned from another query.
|
|
8387
|
+
* someColumn: db.otherTable.where(...conditions).get('column'),
|
|
7938
8388
|
* });
|
|
7939
8389
|
* ```
|
|
7940
8390
|
*
|
|
7941
|
-
*
|
|
7942
|
-
*
|
|
7943
|
-
* ```ts
|
|
7944
|
-
* db.user.join(db.message, true);
|
|
7945
|
-
* ```
|
|
7946
|
-
*
|
|
7947
|
-
* Join methods can accept a callback with a special query builder that has `on` and `orOn` methods for handling advanced cases:
|
|
7948
|
-
*
|
|
7949
|
-
* ```ts
|
|
7950
|
-
* db.user.join(
|
|
7951
|
-
* db.message,
|
|
7952
|
-
* (q) =>
|
|
7953
|
-
* q
|
|
7954
|
-
* // left column is the db.message column, right column is the db.user column
|
|
7955
|
-
* .on('userId', 'id')
|
|
7956
|
-
* // table names can be provided:
|
|
7957
|
-
* .on('message.userId', 'user.id')
|
|
7958
|
-
* // operator can be specified:
|
|
7959
|
-
* .on('userId', '!=', 'id')
|
|
7960
|
-
* // operator can be specified with table names as well:
|
|
7961
|
-
* .on('message.userId', '!=', 'user.id')
|
|
7962
|
-
* // `.orOn` takes the same arguments as `.on` and acts like `.or`:
|
|
7963
|
-
* .on('userId', 'id') // where message.userId = user.id
|
|
7964
|
-
* .orOn('text', 'name'), // or message.text = user.name
|
|
7965
|
-
* );
|
|
7966
|
-
* ```
|
|
7967
|
-
*
|
|
7968
|
-
* Join query builder supports all `where` methods: `.where`, `.whereIn`, `.whereExists`, and all `.or`, `.not`, and `.orNot` forms.
|
|
7969
|
-
*
|
|
7970
|
-
* 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.
|
|
7971
|
-
*
|
|
7972
|
-
* ```ts
|
|
7973
|
-
* db.user.join(db.message, (q) =>
|
|
7974
|
-
* q
|
|
7975
|
-
* .on('userId', 'id')
|
|
7976
|
-
* .where({
|
|
7977
|
-
* // not prefixed column name is for joined table:
|
|
7978
|
-
* text: { startsWith: 'hello' },
|
|
7979
|
-
* // specify a table name to set condition on the main table:
|
|
7980
|
-
* 'user.name': 'Bob',
|
|
7981
|
-
* })
|
|
7982
|
-
* // id is a column of a joined table Message
|
|
7983
|
-
* .whereIn('id', [1, 2, 3])
|
|
7984
|
-
* // condition for id of a user
|
|
7985
|
-
* .whereIn('user.id', [4, 5, 6]),
|
|
7986
|
-
* );
|
|
7987
|
-
* ```
|
|
7988
|
-
*
|
|
7989
|
-
* The query above will generate the following SQL (simplified):
|
|
7990
|
-
*
|
|
7991
|
-
* ```sql
|
|
7992
|
-
* SELECT * FROM "user"
|
|
7993
|
-
* JOIN "message"
|
|
7994
|
-
* ON "message"."userId" = "user"."id"
|
|
7995
|
-
* AND "message"."text" ILIKE 'hello%'
|
|
7996
|
-
* AND "user"."name" = 'Bob'
|
|
7997
|
-
* AND "message"."id" IN (1, 2, 3)
|
|
7998
|
-
* AND "user"."id" IN (4, 5, 6)
|
|
7999
|
-
* ```
|
|
8000
|
-
*
|
|
8001
|
-
* The join argument can be a query with `select`, `where`, and other methods. In such case, it will be handled as a sub query:
|
|
8391
|
+
* `where` can accept other queries and merge their conditions:
|
|
8002
8392
|
*
|
|
8003
8393
|
* ```ts
|
|
8004
|
-
* db.
|
|
8005
|
-
* db.message
|
|
8006
|
-
* .select('id', 'userId', 'text')
|
|
8007
|
-
* .where({ text: { startsWith: 'Hi' } })
|
|
8008
|
-
* .as('t'),
|
|
8009
|
-
* 'userId',
|
|
8010
|
-
* 'id',
|
|
8011
|
-
* );
|
|
8012
|
-
* ```
|
|
8013
|
-
*
|
|
8014
|
-
* It will produce such SQL:
|
|
8394
|
+
* const otherQuery = db.table.where({ name: 'John' });
|
|
8015
8395
|
*
|
|
8016
|
-
*
|
|
8017
|
-
*
|
|
8018
|
-
* JOIN (
|
|
8019
|
-
* SELECT "t"."id", "t"."userId", "t"."text"
|
|
8020
|
-
* FROM "message" AS "t"
|
|
8021
|
-
* ) "t" ON "t"."userId" = "user"."id"
|
|
8396
|
+
* db.table.where({ id: 1 }, otherQuery);
|
|
8397
|
+
* // this will produce WHERE "table"."id" = 1 AND "table"."name' = 'John'
|
|
8022
8398
|
* ```
|
|
8023
8399
|
*
|
|
8024
|
-
*
|
|
8025
|
-
* @param args - {@link JoinArgs}
|
|
8026
|
-
*/
|
|
8027
|
-
join(arg, ...args) {
|
|
8028
|
-
return _join(this.clone(), true, "JOIN", arg, args);
|
|
8029
|
-
}
|
|
8030
|
-
/**
|
|
8031
|
-
* `leftJoin` is a method for SQL `LEFT JOIN`, which is equivalent to `OUTER JOIN`, `LEFT OUTER JOIN`.
|
|
8032
|
-
*
|
|
8033
|
-
* When no matching record is found, it will fill joined table columns with `NULL` values in the result rows.
|
|
8034
|
-
*
|
|
8035
|
-
* Works just like `join`, except for result type that may have `null`:
|
|
8400
|
+
* `where` supports raw SQL:
|
|
8036
8401
|
*
|
|
8037
8402
|
* ```ts
|
|
8038
|
-
*
|
|
8039
|
-
* .leftJoin('messages')
|
|
8040
|
-
* .select('name', 'messages.text');
|
|
8041
|
-
*
|
|
8042
|
-
* // the same query, but joining table explicitly
|
|
8043
|
-
* const result2: typeof result = await db.user
|
|
8044
|
-
* .leftJoin(db.message, 'userId', 'id')
|
|
8045
|
-
* .select('name', 'message.text');
|
|
8046
|
-
*
|
|
8047
|
-
* // result has the following type:
|
|
8048
|
-
* const ok: { name: string; text: string | null }[] = result;
|
|
8049
|
-
* ```
|
|
8050
|
-
*
|
|
8051
|
-
* @param arg - {@link JoinFirstArg}
|
|
8052
|
-
* @param args - {@link JoinArgs}
|
|
8053
|
-
*/
|
|
8054
|
-
leftJoin(arg, ...args) {
|
|
8055
|
-
return _join(this.clone(), false, "LEFT JOIN", arg, args);
|
|
8056
|
-
}
|
|
8057
|
-
/**
|
|
8058
|
-
* `rightJoin` is a method for SQL `RIGHT JOIN`, which is equivalent to `RIGHT OUTER JOIN`.
|
|
8403
|
+
* db.table.where(db.table.sql`a = b`);
|
|
8059
8404
|
*
|
|
8060
|
-
*
|
|
8405
|
+
* // or
|
|
8406
|
+
* import { raw } from 'orchid-orm';
|
|
8061
8407
|
*
|
|
8062
|
-
*
|
|
8408
|
+
* db.table.where(raw`a = b`);
|
|
8409
|
+
* ```
|
|
8063
8410
|
*
|
|
8064
|
-
*
|
|
8411
|
+
* `where` can accept a callback with a specific query builder containing all "where" methods such as `where`, `orWhere`, `whereNot`, `whereIn`, `whereExists`:
|
|
8065
8412
|
*
|
|
8066
8413
|
* ```ts
|
|
8067
|
-
*
|
|
8068
|
-
*
|
|
8069
|
-
*
|
|
8070
|
-
*
|
|
8071
|
-
*
|
|
8072
|
-
*
|
|
8414
|
+
* db.table.where((q) =>
|
|
8415
|
+
* q
|
|
8416
|
+
* .where({ name: 'Name' })
|
|
8417
|
+
* .orWhere({ id: 1 }, { id: 2 })
|
|
8418
|
+
* .whereIn('letter', ['a', 'b', 'c'])
|
|
8419
|
+
* .whereExists(Message, 'authorId', 'id'),
|
|
8420
|
+
* );
|
|
8073
8421
|
* ```
|
|
8074
8422
|
*
|
|
8075
|
-
*
|
|
8076
|
-
* @param args - {@link JoinArgs}
|
|
8077
|
-
*/
|
|
8078
|
-
rightJoin(arg, ...args) {
|
|
8079
|
-
return _join(this.clone(), true, "RIGHT JOIN", arg, args);
|
|
8080
|
-
}
|
|
8081
|
-
/**
|
|
8082
|
-
* `fullJoin` is a method for SQL `FULL JOIN`, which is equivalent to `FULL OUTER JOIN`.
|
|
8423
|
+
* `where` can accept multiple arguments, conditions are joined with `AND`:
|
|
8083
8424
|
*
|
|
8084
|
-
*
|
|
8425
|
+
* ```ts
|
|
8426
|
+
* db.table.where(
|
|
8427
|
+
* { id: 1 },
|
|
8428
|
+
* db.table.where({ name: 'John' }),
|
|
8429
|
+
* db.table.sql`a = b`,
|
|
8430
|
+
* );
|
|
8431
|
+
* ```
|
|
8085
8432
|
*
|
|
8086
|
-
*
|
|
8433
|
+
* ## where sub query
|
|
8087
8434
|
*
|
|
8088
|
-
*
|
|
8435
|
+
* `where` handles a special callback where you can query a relation to get some value and filter by that value.
|
|
8436
|
+
*
|
|
8437
|
+
* 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.
|
|
8089
8438
|
*
|
|
8090
8439
|
* ```ts
|
|
8091
|
-
* const
|
|
8092
|
-
* .rightJoin('messages')
|
|
8093
|
-
* .select('name', 'messages.text');
|
|
8440
|
+
* const givenTags = ['typescript', 'node.js'];
|
|
8094
8441
|
*
|
|
8095
|
-
*
|
|
8096
|
-
*
|
|
8442
|
+
* const posts = await db.post.where(
|
|
8443
|
+
* (post) =>
|
|
8444
|
+
* post.tags // query tags of the post
|
|
8445
|
+
* .whereIn('tagName', givenTags) // where name of the tag is inside array
|
|
8446
|
+
* .count() // count how many such tags were found
|
|
8447
|
+
* .equals(wantedTags.length), // the count must be exactly the length of array
|
|
8448
|
+
* // if the post has ony `typescript` tag but not the `node.js` it will be omitted
|
|
8449
|
+
* );
|
|
8097
8450
|
* ```
|
|
8098
8451
|
*
|
|
8099
|
-
*
|
|
8100
|
-
*
|
|
8101
|
-
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
|
|
8105
|
-
|
|
8106
|
-
*
|
|
8452
|
+
* This will produce an efficient SQL query:
|
|
8453
|
+
*
|
|
8454
|
+
* ```sql
|
|
8455
|
+
* SELECT * FROM "post"
|
|
8456
|
+
* WHERE (
|
|
8457
|
+
* SELECT count(*) = 3
|
|
8458
|
+
* FROM "tag" AS "tags"
|
|
8459
|
+
* WHERE "tag"."tagName" IN ('typescript', 'node.js')
|
|
8460
|
+
* -- join tags to the post via "postTag" table
|
|
8461
|
+
* AND EXISTS (
|
|
8462
|
+
* SELECT 1 FROM "postTag"
|
|
8463
|
+
* WHERE "postTag"."postId" = "post"."id"
|
|
8464
|
+
* AND "postTag"."tagId" = "tag"."id"
|
|
8465
|
+
* )
|
|
8466
|
+
* )
|
|
8467
|
+
* ```
|
|
8107
8468
|
*
|
|
8108
|
-
*
|
|
8469
|
+
* In the example above we use `count()`, you can also use any other aggregate method instead, such as `min`, `max`, `avg`.
|
|
8109
8470
|
*
|
|
8110
|
-
*
|
|
8471
|
+
* The `count()` is chained with `equals` to check for a strict equality, any other operation is also allowed, such as `not`, `lt`, `gt`.
|
|
8111
8472
|
*
|
|
8112
|
-
*
|
|
8473
|
+
* ## where special keys
|
|
8113
8474
|
*
|
|
8114
|
-
*
|
|
8475
|
+
* 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.
|
|
8476
|
+
*
|
|
8477
|
+
* For example:
|
|
8115
8478
|
*
|
|
8116
8479
|
* ```ts
|
|
8117
|
-
*
|
|
8118
|
-
*
|
|
8119
|
-
*
|
|
8120
|
-
*
|
|
8121
|
-
*
|
|
8122
|
-
*
|
|
8123
|
-
*
|
|
8124
|
-
*
|
|
8125
|
-
*
|
|
8126
|
-
*
|
|
8127
|
-
*
|
|
8128
|
-
* .order({ createdAt: 'DESC' }),
|
|
8129
|
-
* )
|
|
8130
|
-
* // only selected message columns are available in select and where
|
|
8131
|
-
* .select('id', 'name', 'm.text')
|
|
8132
|
-
* .where({ 'm.text': messageData.text });
|
|
8480
|
+
* db.table.where({
|
|
8481
|
+
* NOT: { key: 'value' },
|
|
8482
|
+
* OR: [{ name: 'a' }, { name: 'b' }],
|
|
8483
|
+
* IN: {
|
|
8484
|
+
* columns: ['id', 'name'],
|
|
8485
|
+
* values: [
|
|
8486
|
+
* [1, 'a'],
|
|
8487
|
+
* [2, 'b'],
|
|
8488
|
+
* ],
|
|
8489
|
+
* },
|
|
8490
|
+
* });
|
|
8133
8491
|
* ```
|
|
8134
8492
|
*
|
|
8135
|
-
*
|
|
8493
|
+
* 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.
|
|
8136
8494
|
*
|
|
8137
8495
|
* ```ts
|
|
8138
|
-
*
|
|
8139
|
-
*
|
|
8140
|
-
*
|
|
8141
|
-
*
|
|
8142
|
-
*
|
|
8496
|
+
* db.table.where({
|
|
8497
|
+
* // see .whereNot
|
|
8498
|
+
* NOT: { id: 1 },
|
|
8499
|
+
* // can be an array:
|
|
8500
|
+
* NOT: [{ id: 1 }, { id: 2 }],
|
|
8143
8501
|
*
|
|
8144
|
-
*
|
|
8145
|
-
*
|
|
8146
|
-
*
|
|
8147
|
-
* //
|
|
8148
|
-
*
|
|
8149
|
-
* }
|
|
8502
|
+
* // see .orWhere
|
|
8503
|
+
* OR: [{ name: 'a' }, { name: 'b' }],
|
|
8504
|
+
* // can be an array:
|
|
8505
|
+
* // this will give id = 1 AND id = 2 OR id = 3 AND id = 4
|
|
8506
|
+
* OR: [
|
|
8507
|
+
* [{ id: 1 }, { id: 2 }],
|
|
8508
|
+
* [{ id: 3 }, { id: 4 }],
|
|
8509
|
+
* ],
|
|
8510
|
+
*
|
|
8511
|
+
* // see .in, the key syntax requires an object with columns and values
|
|
8512
|
+
* IN: {
|
|
8513
|
+
* columns: ['id', 'name'],
|
|
8514
|
+
* values: [
|
|
8515
|
+
* [1, 'a'],
|
|
8516
|
+
* [2, 'b'],
|
|
8517
|
+
* ],
|
|
8518
|
+
* },
|
|
8519
|
+
* // can be an array:
|
|
8520
|
+
* IN: [
|
|
8521
|
+
* {
|
|
8522
|
+
* columns: ['id', 'name'],
|
|
8523
|
+
* values: [
|
|
8524
|
+
* [1, 'a'],
|
|
8525
|
+
* [2, 'b'],
|
|
8526
|
+
* ],
|
|
8527
|
+
* },
|
|
8528
|
+
* { columns: ['someColumn'], values: [['foo', 'bar']] },
|
|
8529
|
+
* ],
|
|
8530
|
+
* });
|
|
8150
8531
|
* ```
|
|
8151
8532
|
*
|
|
8152
|
-
*
|
|
8533
|
+
* ## column operators
|
|
8534
|
+
*
|
|
8535
|
+
* `where` argument can take an object where the key is the name of the operator and the value is its argument.
|
|
8536
|
+
*
|
|
8537
|
+
* Different types of columns support different sets of operators.
|
|
8538
|
+
*
|
|
8539
|
+
* All column operators can take a value of the same type as the column, a sub-query, or a raw SQL expression:
|
|
8153
8540
|
*
|
|
8154
8541
|
* ```ts
|
|
8155
|
-
*
|
|
8156
|
-
* const result = await User.joinLateral(
|
|
8157
|
-
* 'messages',
|
|
8158
|
-
* (q) => q.as('message'), // alias to 'message'
|
|
8159
|
-
* ).select('name', { msg: 'message.*' });
|
|
8542
|
+
* import { sql } from 'orchid-orm';
|
|
8160
8543
|
*
|
|
8161
|
-
*
|
|
8162
|
-
*
|
|
8163
|
-
*
|
|
8164
|
-
*
|
|
8165
|
-
*
|
|
8166
|
-
*
|
|
8544
|
+
* db.table.where({
|
|
8545
|
+
* numericColumn: {
|
|
8546
|
+
* // lower than 5
|
|
8547
|
+
* lt: 5,
|
|
8548
|
+
*
|
|
8549
|
+
* // lower than the value returned by sub-query
|
|
8550
|
+
* lt: OtherTable.select('someNumber').take(),
|
|
8551
|
+
*
|
|
8552
|
+
* // raw SQL expression produces WHERE "numericColumn" < "otherColumn" + 10
|
|
8553
|
+
* lt: sql`"otherColumn" + 10`,
|
|
8554
|
+
* },
|
|
8555
|
+
* });
|
|
8167
8556
|
* ```
|
|
8168
8557
|
*
|
|
8169
|
-
*
|
|
8170
|
-
*
|
|
8171
|
-
|
|
8172
|
-
joinLateral(arg, cb) {
|
|
8173
|
-
return _joinLateral(this.clone(), "JOIN", arg, cb);
|
|
8174
|
-
}
|
|
8175
|
-
/**
|
|
8176
|
-
* The same as {@link joinLateral}, but when no records found for the join it will result in `null`:
|
|
8558
|
+
* ### Any type of column operators
|
|
8559
|
+
*
|
|
8560
|
+
* `equals` is a simple `=` operator, it may be useful for comparing column value with JSON object:
|
|
8177
8561
|
*
|
|
8178
8562
|
* ```ts
|
|
8179
|
-
*
|
|
8180
|
-
*
|
|
8181
|
-
*
|
|
8563
|
+
* db.table.where({
|
|
8564
|
+
* // when searching for an exact same JSON value, this won't work:
|
|
8565
|
+
* jsonColumn: someObject,
|
|
8182
8566
|
*
|
|
8183
|
-
*
|
|
8184
|
-
*
|
|
8567
|
+
* // use `{ equals: ... }` instead:
|
|
8568
|
+
* jsonColumn: { equals: someObject },
|
|
8569
|
+
* });
|
|
8185
8570
|
* ```
|
|
8186
8571
|
*
|
|
8187
|
-
*
|
|
8188
|
-
* @param cb - {@link JoinLateralCallback}
|
|
8189
|
-
*/
|
|
8190
|
-
leftJoinLateral(arg, cb) {
|
|
8191
|
-
return _joinLateral(this.clone(), "LEFT JOIN", arg, cb);
|
|
8192
|
-
}
|
|
8193
|
-
}
|
|
8194
|
-
const makeOnItem = (joinTo, joinFrom, args) => {
|
|
8195
|
-
return {
|
|
8196
|
-
ON: {
|
|
8197
|
-
joinTo,
|
|
8198
|
-
joinFrom,
|
|
8199
|
-
on: args
|
|
8200
|
-
}
|
|
8201
|
-
};
|
|
8202
|
-
};
|
|
8203
|
-
const pushQueryOn = (q, joinFrom, joinTo, ...on) => {
|
|
8204
|
-
return pushQueryValue(q, "and", makeOnItem(joinFrom, joinTo, on));
|
|
8205
|
-
};
|
|
8206
|
-
const pushQueryOrOn = (q, joinFrom, joinTo, ...on) => {
|
|
8207
|
-
return pushQueryValue(q, "or", [makeOnItem(joinFrom, joinTo, on)]);
|
|
8208
|
-
};
|
|
8209
|
-
const addQueryOn = (q, joinFrom, joinTo, ...args) => {
|
|
8210
|
-
const cloned = q.clone();
|
|
8211
|
-
setQueryObjectValue(
|
|
8212
|
-
cloned,
|
|
8213
|
-
"joinedShapes",
|
|
8214
|
-
joinFrom.q.as || joinFrom.table,
|
|
8215
|
-
joinFrom.q.shape
|
|
8216
|
-
);
|
|
8217
|
-
return pushQueryOn(cloned, joinFrom, joinTo, ...args);
|
|
8218
|
-
};
|
|
8219
|
-
const _queryJoinOn = (q, args) => {
|
|
8220
|
-
return pushQueryOn(q, q.q.joinTo, q, ...args);
|
|
8221
|
-
};
|
|
8222
|
-
const _queryJoinOrOn = (q, args) => {
|
|
8223
|
-
return pushQueryOrOn(q, q.q.joinTo, q, ...args);
|
|
8224
|
-
};
|
|
8225
|
-
const _queryJoinOnJsonPathEquals = (q, args) => {
|
|
8226
|
-
return pushQueryValue(q, "and", { ON: args });
|
|
8227
|
-
};
|
|
8228
|
-
class OnQueryBuilder extends WhereQueryBase {
|
|
8229
|
-
constructor(q, { shape, joinedShapes }, joinTo) {
|
|
8230
|
-
super();
|
|
8231
|
-
this.withData = orchidCore.emptyObject;
|
|
8232
|
-
this.internal = q.internal;
|
|
8233
|
-
this.table = typeof q === "object" ? q.table : q;
|
|
8234
|
-
this.shape = shape;
|
|
8235
|
-
this.q = {
|
|
8236
|
-
shape,
|
|
8237
|
-
joinedShapes
|
|
8238
|
-
};
|
|
8239
|
-
this.baseQuery = this;
|
|
8240
|
-
if (typeof q === "object" && q.q.as) {
|
|
8241
|
-
this.q.as = q.q.as;
|
|
8242
|
-
}
|
|
8243
|
-
this.q.joinTo = joinTo;
|
|
8244
|
-
}
|
|
8245
|
-
/**
|
|
8246
|
-
* Use `on` to specify columns to join records.
|
|
8572
|
+
* `not` is `!=` (aka `<>`) not equal operator:
|
|
8247
8573
|
*
|
|
8248
8574
|
* ```ts
|
|
8249
|
-
*
|
|
8250
|
-
*
|
|
8251
|
-
*
|
|
8252
|
-
* // table names can be provided:
|
|
8253
|
-
* .on('message.userId', 'user.id')
|
|
8254
|
-
* // operator can be specified:
|
|
8255
|
-
* .on('userId', '!=', 'id')
|
|
8256
|
-
* // operator can be specified with table names as well:
|
|
8257
|
-
* .on('message.userId', '!=', 'user.id')
|
|
8258
|
-
* // `.orOn` takes the same arguments as `.on` and acts like `.or`:
|
|
8259
|
-
* .on('userId', 'id') // where message.userId = user.id
|
|
8575
|
+
* db.table.where({
|
|
8576
|
+
* anyColumn: { not: value },
|
|
8577
|
+
* });
|
|
8260
8578
|
* ```
|
|
8261
8579
|
*
|
|
8262
|
-
*
|
|
8263
|
-
*/
|
|
8264
|
-
on(...args) {
|
|
8265
|
-
return _queryJoinOn(this.clone(), args);
|
|
8266
|
-
}
|
|
8267
|
-
/**
|
|
8268
|
-
* Works as {@link on}, but the added conditions will be separated from previous with `OR`.
|
|
8580
|
+
* `in` is for the `IN` operator to check if the column value is included in a list of values.
|
|
8269
8581
|
*
|
|
8270
|
-
*
|
|
8271
|
-
*/
|
|
8272
|
-
orOn(...args) {
|
|
8273
|
-
return _queryJoinOrOn(this.clone(), args);
|
|
8274
|
-
}
|
|
8275
|
-
/**
|
|
8276
|
-
* Use `onJsonPathEquals` to join record based on a field of their JSON column:
|
|
8582
|
+
* 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.
|
|
8277
8583
|
*
|
|
8278
8584
|
* ```ts
|
|
8279
|
-
* db.table.
|
|
8280
|
-
*
|
|
8281
|
-
*
|
|
8282
|
-
*
|
|
8585
|
+
* db.table.where({
|
|
8586
|
+
* column: {
|
|
8587
|
+
* in: ['a', 'b', 'c'],
|
|
8588
|
+
*
|
|
8589
|
+
* // WHERE "column" IN (SELECT "column" FROM "otherTable")
|
|
8590
|
+
* in: OtherTable.select('column'),
|
|
8591
|
+
*
|
|
8592
|
+
* in: db.table.sql`('a', 'b')`,
|
|
8593
|
+
* },
|
|
8594
|
+
* });
|
|
8283
8595
|
* ```
|
|
8284
8596
|
*
|
|
8285
|
-
*
|
|
8286
|
-
*/
|
|
8287
|
-
onJsonPathEquals(...args) {
|
|
8288
|
-
return _queryJoinOnJsonPathEquals(this.clone(), args);
|
|
8289
|
-
}
|
|
8290
|
-
}
|
|
8291
|
-
|
|
8292
|
-
class JsonModifiers {
|
|
8293
|
-
/**
|
|
8294
|
-
* Return a JSON value/object/array where a given value is set at the given path.
|
|
8295
|
-
* The path is an array of keys to access the value.
|
|
8597
|
+
* `notIn` is for the `NOT IN` operator, and takes the same arguments as `in`
|
|
8296
8598
|
*
|
|
8297
|
-
*
|
|
8599
|
+
* ### Numeric, Date, and Time column operators
|
|
8298
8600
|
*
|
|
8299
|
-
*
|
|
8300
|
-
* const result = await db.table.jsonSet('data', ['name'], 'new value').take();
|
|
8601
|
+
* To compare numbers, dates, and times.
|
|
8301
8602
|
*
|
|
8302
|
-
*
|
|
8303
|
-
* ```
|
|
8603
|
+
* `lt` is for `<` (lower than)
|
|
8304
8604
|
*
|
|
8305
|
-
*
|
|
8605
|
+
* `lte` is for `<=` (lower than or equal)
|
|
8606
|
+
*
|
|
8607
|
+
* `gt` is for `>` (greater than)
|
|
8608
|
+
*
|
|
8609
|
+
* `gte` is for `>=` (greater than or equal)
|
|
8306
8610
|
*
|
|
8307
8611
|
* ```ts
|
|
8308
|
-
*
|
|
8309
|
-
*
|
|
8310
|
-
*
|
|
8612
|
+
* db.table.where({
|
|
8613
|
+
* numericColumn: {
|
|
8614
|
+
* gt: 5,
|
|
8615
|
+
* lt: 10,
|
|
8616
|
+
* },
|
|
8617
|
+
*
|
|
8618
|
+
* date: {
|
|
8619
|
+
* lte: new Date(),
|
|
8620
|
+
* },
|
|
8621
|
+
*
|
|
8622
|
+
* time: {
|
|
8623
|
+
* gte: new Date(),
|
|
8624
|
+
* },
|
|
8311
8625
|
* });
|
|
8312
8626
|
* ```
|
|
8313
8627
|
*
|
|
8314
|
-
*
|
|
8315
|
-
* @param path - path to value inside the json
|
|
8316
|
-
* @param value - value to set into the json
|
|
8317
|
-
* @param options - `as` to alias the json value when selecting, `createIfMissing: true` will create a new JSON property if it didn't exist before
|
|
8318
|
-
*/
|
|
8319
|
-
jsonSet(column, path, value, options) {
|
|
8320
|
-
var _a;
|
|
8321
|
-
const q = this.clone();
|
|
8322
|
-
const json = {
|
|
8323
|
-
__json: [
|
|
8324
|
-
"set",
|
|
8325
|
-
(_a = options == null ? void 0 : options.as) != null ? _a : typeof column === "string" ? column : column.__json[1],
|
|
8326
|
-
typeof column === "string" ? q.q.shape[column] : column.__json[2],
|
|
8327
|
-
column,
|
|
8328
|
-
path,
|
|
8329
|
-
value,
|
|
8330
|
-
options
|
|
8331
|
-
]
|
|
8332
|
-
};
|
|
8333
|
-
return Object.assign(
|
|
8334
|
-
pushQueryValue(q, "select", json),
|
|
8335
|
-
json
|
|
8336
|
-
);
|
|
8337
|
-
}
|
|
8338
|
-
/**
|
|
8339
|
-
* 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.
|
|
8628
|
+
* `between` also works with numeric, dates, and time columns, it takes an array of two elements.
|
|
8340
8629
|
*
|
|
8341
|
-
*
|
|
8630
|
+
* Both elements can be of the same type as a column, a sub-query, or a raw SQL expression.
|
|
8342
8631
|
*
|
|
8343
8632
|
* ```ts
|
|
8344
|
-
*
|
|
8345
|
-
*
|
|
8633
|
+
* db.table.where({
|
|
8634
|
+
* column: {
|
|
8635
|
+
* // simple values
|
|
8636
|
+
* between: [1, 10],
|
|
8346
8637
|
*
|
|
8347
|
-
*
|
|
8348
|
-
*
|
|
8638
|
+
* // sub-query and raw SQL expression
|
|
8639
|
+
* between: [OtherTable.select('column').take(), db.table.sql`2 + 2`],
|
|
8640
|
+
* },
|
|
8641
|
+
* });
|
|
8349
8642
|
* ```
|
|
8350
8643
|
*
|
|
8351
|
-
*
|
|
8644
|
+
* ### Text column operators
|
|
8352
8645
|
*
|
|
8353
|
-
*
|
|
8354
|
-
* // imagine user has data = { tags: ['one'] }
|
|
8355
|
-
* const result = await db.table
|
|
8356
|
-
* .jsonInsert('data', ['tags', 0], 'two', {
|
|
8357
|
-
* as: 'alias', // select as an alias
|
|
8358
|
-
* insertAfter: true, // insert after the specified position
|
|
8359
|
-
* })
|
|
8360
|
-
* .take();
|
|
8646
|
+
* For `text`, `char`, `varchar`, and `json` columns.
|
|
8361
8647
|
*
|
|
8362
|
-
*
|
|
8363
|
-
* expect(result.alias).toEqual({ tags: ['one', 'two'] });
|
|
8364
|
-
* ```
|
|
8365
|
-
* @param column - name of JSON column, or a result of a nested json method
|
|
8366
|
-
* @param path - path to the array inside the json, last path element is index to insert into
|
|
8367
|
-
* @param value - value to insert into the json array
|
|
8368
|
-
* @param options - `as` to alias the json value when selecting, `insertAfter: true` to insert after the specified position
|
|
8369
|
-
*/
|
|
8370
|
-
jsonInsert(column, path, value, options) {
|
|
8371
|
-
var _a;
|
|
8372
|
-
const q = this.clone();
|
|
8373
|
-
const json = {
|
|
8374
|
-
__json: [
|
|
8375
|
-
"insert",
|
|
8376
|
-
(_a = options == null ? void 0 : options.as) != null ? _a : typeof column === "string" ? column : column.__json[1],
|
|
8377
|
-
typeof column === "string" ? q.q.shape[column] : column.__json[2],
|
|
8378
|
-
column,
|
|
8379
|
-
path,
|
|
8380
|
-
value,
|
|
8381
|
-
options
|
|
8382
|
-
]
|
|
8383
|
-
};
|
|
8384
|
-
return Object.assign(
|
|
8385
|
-
pushQueryValue(q, "select", json),
|
|
8386
|
-
json
|
|
8387
|
-
);
|
|
8388
|
-
}
|
|
8389
|
-
/**
|
|
8390
|
-
* Return a JSON value/object/array where a given value is removed at the given JSON path.
|
|
8648
|
+
* `json` is stored as text, so it has text operators. Use the `jsonb` type for JSON operators.
|
|
8391
8649
|
*
|
|
8392
|
-
*
|
|
8650
|
+
* Takes a string, or sub-query returning string, or raw SQL expression as well as other operators.
|
|
8393
8651
|
*
|
|
8394
8652
|
* ```ts
|
|
8395
|
-
*
|
|
8396
|
-
*
|
|
8397
|
-
*
|
|
8398
|
-
* '
|
|
8399
|
-
*
|
|
8400
|
-
*
|
|
8401
|
-
*
|
|
8402
|
-
*
|
|
8403
|
-
*
|
|
8404
|
-
*
|
|
8405
|
-
*
|
|
8406
|
-
*
|
|
8407
|
-
*
|
|
8653
|
+
* db.table.where({
|
|
8654
|
+
* textColumn: {
|
|
8655
|
+
* // WHERE "textColumn" LIKE '%string%'
|
|
8656
|
+
* contains: 'string',
|
|
8657
|
+
* // WHERE "textColumn" ILIKE '%string%'
|
|
8658
|
+
* containsInsensitive: 'string',
|
|
8659
|
+
* // WHERE "textColumn" LIKE 'string%'
|
|
8660
|
+
* startsWith: 'string',
|
|
8661
|
+
* // WHERE "textColumn" ILIKE 'string%'
|
|
8662
|
+
* startsWithInsensitive: 'string',
|
|
8663
|
+
* // WHERE "textColumn" LIKE '%string'
|
|
8664
|
+
* endsWith: 'string',
|
|
8665
|
+
* // WHERE "textColumn" ILIKE '%string'
|
|
8666
|
+
* endsWithInsensitive: 'string',
|
|
8667
|
+
* },
|
|
8668
|
+
* });
|
|
8408
8669
|
* ```
|
|
8409
8670
|
*
|
|
8410
|
-
*
|
|
8411
|
-
*
|
|
8412
|
-
*
|
|
8413
|
-
|
|
8414
|
-
|
|
8415
|
-
|
|
8416
|
-
|
|
8417
|
-
const json = {
|
|
8418
|
-
__json: [
|
|
8419
|
-
"remove",
|
|
8420
|
-
(_a = options == null ? void 0 : options.as) != null ? _a : typeof column === "string" ? column : column.__json[1],
|
|
8421
|
-
typeof column === "string" ? q.q.shape[column] : column.__json[2],
|
|
8422
|
-
column,
|
|
8423
|
-
path
|
|
8424
|
-
]
|
|
8425
|
-
};
|
|
8426
|
-
return Object.assign(
|
|
8427
|
-
pushQueryValue(q, "select", json),
|
|
8428
|
-
json
|
|
8429
|
-
);
|
|
8430
|
-
}
|
|
8431
|
-
/**
|
|
8432
|
-
* Selects a value from JSON data using a JSON path.
|
|
8671
|
+
* ### JSONB column operators
|
|
8672
|
+
*
|
|
8673
|
+
* For the `jsonb` column, note that the `json` type has text operators instead.
|
|
8674
|
+
*
|
|
8675
|
+
* `jsonPath` operator: compare a column value under a given JSON path with the provided value.
|
|
8676
|
+
*
|
|
8677
|
+
* Value can be of any type to compare with JSON value, or it can be a sub-query or a raw SQL expression.
|
|
8433
8678
|
*
|
|
8434
8679
|
* ```ts
|
|
8435
|
-
*
|
|
8680
|
+
* db.table.where({
|
|
8681
|
+
* jsonbColumn: {
|
|
8682
|
+
* jsonPath: [
|
|
8683
|
+
* '$.name', // first element is JSON path
|
|
8684
|
+
* '=', // second argument is comparison operator
|
|
8685
|
+
* 'value', // third argument is a value to compare with
|
|
8686
|
+
* ],
|
|
8687
|
+
* },
|
|
8688
|
+
* });
|
|
8689
|
+
* ```
|
|
8436
8690
|
*
|
|
8437
|
-
*
|
|
8438
|
-
* columnTypes.text(3, 100), // type of the value
|
|
8439
|
-
* 'data', // name of the JSON column
|
|
8440
|
-
* '$.name', // JSON path
|
|
8441
|
-
* 'name', // select value as name
|
|
8691
|
+
* `jsonSupersetOf`: check if the column value is a superset of provided value.
|
|
8442
8692
|
*
|
|
8443
|
-
*
|
|
8444
|
-
*
|
|
8445
|
-
*
|
|
8446
|
-
*
|
|
8447
|
-
*
|
|
8693
|
+
* For instance, it is true if the column has JSON `{ "a": 1, "b": 2 }` and provided value is `{ "a": 1 }`.
|
|
8694
|
+
*
|
|
8695
|
+
* Takes the value of any type, or sub query which returns a single value, or a raw SQL expression.
|
|
8696
|
+
*
|
|
8697
|
+
* ```ts
|
|
8698
|
+
* db.table.where({
|
|
8699
|
+
* jsonbColumn: {
|
|
8700
|
+
* jsonSupersetOf: { a: 1 },
|
|
8448
8701
|
* },
|
|
8449
|
-
* );
|
|
8702
|
+
* });
|
|
8450
8703
|
* ```
|
|
8451
8704
|
*
|
|
8452
|
-
*
|
|
8705
|
+
* `jsonSubsetOf`: check if the column value is a subset of provided value.
|
|
8706
|
+
*
|
|
8707
|
+
* For instance, it is true if the column has JSON `{ "a": 1 }` and provided value is `{ "a": 1, "b": 2 }`.
|
|
8708
|
+
*
|
|
8709
|
+
* Takes the value of any type, or sub query which returns a single value, or a raw SQL expression.
|
|
8453
8710
|
*
|
|
8454
8711
|
* ```ts
|
|
8455
|
-
* db.table.
|
|
8456
|
-
*
|
|
8457
|
-
*
|
|
8458
|
-
*
|
|
8459
|
-
*
|
|
8460
|
-
* 'name',
|
|
8461
|
-
* );
|
|
8712
|
+
* db.table.where({
|
|
8713
|
+
* jsonbColumn: {
|
|
8714
|
+
* jsonSupersetOf: { a: 1 },
|
|
8715
|
+
* },
|
|
8716
|
+
* });
|
|
8462
8717
|
* ```
|
|
8463
8718
|
*
|
|
8464
|
-
* @param
|
|
8465
|
-
* @param column - name of JSON column, or a result of a nested json method
|
|
8466
|
-
* @param path - special JSON path string to reference a JSON value
|
|
8467
|
-
* @param as - optional alias for the selected value
|
|
8468
|
-
* @param options - supports `vars` and `silent`, check Postgres docs of `json_path_query` for these
|
|
8719
|
+
* @param args - {@link WhereArgs}
|
|
8469
8720
|
*/
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
};
|
|
8475
|
-
return Object.assign(
|
|
8476
|
-
pushQueryValue(q, "select", json),
|
|
8477
|
-
json
|
|
8721
|
+
where(...args) {
|
|
8722
|
+
return _queryWhere(
|
|
8723
|
+
this.clone(),
|
|
8724
|
+
args
|
|
8478
8725
|
);
|
|
8479
8726
|
}
|
|
8480
|
-
}
|
|
8481
|
-
class JsonMethods {
|
|
8482
8727
|
/**
|
|
8483
|
-
*
|
|
8484
|
-
* So that JSON encoding is done on a database side, and the application doesn't have to turn a response to a JSON.
|
|
8485
|
-
* It may be better for performance in some cases.
|
|
8728
|
+
* Use a custom SQL expression in `WHERE` statement:
|
|
8486
8729
|
*
|
|
8487
8730
|
* ```ts
|
|
8488
|
-
*
|
|
8489
|
-
*
|
|
8731
|
+
* db.table.where`a = b`;
|
|
8732
|
+
*
|
|
8733
|
+
* // or
|
|
8734
|
+
* db.table.where(db.table.sql`a = b`);
|
|
8735
|
+
*
|
|
8736
|
+
* // or
|
|
8737
|
+
* import { raw } from 'orchid-orm';
|
|
8738
|
+
*
|
|
8739
|
+
* db.table.where(raw`a = b`);
|
|
8490
8740
|
* ```
|
|
8491
8741
|
*
|
|
8492
|
-
* @param
|
|
8742
|
+
* @param args - SQL expression
|
|
8493
8743
|
*/
|
|
8494
|
-
|
|
8495
|
-
return
|
|
8744
|
+
whereSql(...args) {
|
|
8745
|
+
return _queryWhereSql(
|
|
8496
8746
|
this.clone(),
|
|
8497
|
-
|
|
8747
|
+
args
|
|
8498
8748
|
);
|
|
8499
8749
|
}
|
|
8500
|
-
}
|
|
8501
|
-
|
|
8502
|
-
const logColors = {
|
|
8503
|
-
boldCyanBright: (message) => `\x1B[1m\x1B[96m${message}\x1B[39m\x1B[22m`,
|
|
8504
|
-
boldBlue: (message) => `\x1B[1m\x1B[34m${message}\x1B[39m\x1B[22m`,
|
|
8505
|
-
boldYellow: (message) => `\x1B[1m\x1B[33m${message}\x1B[39m\x1B[22m`,
|
|
8506
|
-
boldMagenta: (message) => `\x1B[1m\x1B[33m${message}\x1B[39m\x1B[22m`,
|
|
8507
|
-
boldRed: (message) => `\x1B[1m\x1B[31m${message}\x1B[39m\x1B[22m`
|
|
8508
|
-
};
|
|
8509
|
-
const makeMessage = (colors, timeColor, time, sqlColor, sql, valuesColor, values) => {
|
|
8510
|
-
const elapsed = process.hrtime(time);
|
|
8511
|
-
const formattedTime = `(${elapsed[0] ? `${elapsed[0]}s ` : ""}${(elapsed[1] / 1e6).toFixed(1)}ms)`;
|
|
8512
|
-
const result = `${colors ? timeColor(formattedTime) : formattedTime} ${colors ? sqlColor(sql) : sql}`;
|
|
8513
|
-
if (!values.length) {
|
|
8514
|
-
return result;
|
|
8515
|
-
}
|
|
8516
|
-
const formattedValues = `[${values.map(quote).join(", ")}]`;
|
|
8517
|
-
return `${result} ${colors ? valuesColor(formattedValues) : formattedValues}`;
|
|
8518
|
-
};
|
|
8519
|
-
const logParamToLogObject = (logger, log) => {
|
|
8520
|
-
if (!log)
|
|
8521
|
-
return;
|
|
8522
|
-
const logObject = Object.assign(
|
|
8523
|
-
{
|
|
8524
|
-
colors: true,
|
|
8525
|
-
beforeQuery() {
|
|
8526
|
-
return process.hrtime();
|
|
8527
|
-
},
|
|
8528
|
-
afterQuery(sql, time) {
|
|
8529
|
-
logger.log(
|
|
8530
|
-
makeMessage(
|
|
8531
|
-
colors,
|
|
8532
|
-
logColors.boldCyanBright,
|
|
8533
|
-
time,
|
|
8534
|
-
logColors.boldBlue,
|
|
8535
|
-
sql.text,
|
|
8536
|
-
logColors.boldYellow,
|
|
8537
|
-
sql.values
|
|
8538
|
-
)
|
|
8539
|
-
);
|
|
8540
|
-
},
|
|
8541
|
-
onError(error, sql, time) {
|
|
8542
|
-
const message = `Error: ${error.message}`;
|
|
8543
|
-
logger.error(
|
|
8544
|
-
`${makeMessage(
|
|
8545
|
-
colors,
|
|
8546
|
-
logColors.boldMagenta,
|
|
8547
|
-
time,
|
|
8548
|
-
logColors.boldRed,
|
|
8549
|
-
sql.text,
|
|
8550
|
-
logColors.boldYellow,
|
|
8551
|
-
sql.values
|
|
8552
|
-
)} ${colors ? logColors.boldRed(message) : message}`
|
|
8553
|
-
);
|
|
8554
|
-
}
|
|
8555
|
-
},
|
|
8556
|
-
log === true ? {} : log
|
|
8557
|
-
);
|
|
8558
|
-
const colors = logObject.colors;
|
|
8559
|
-
return logObject;
|
|
8560
|
-
};
|
|
8561
|
-
class QueryLog {
|
|
8562
|
-
log(log = true) {
|
|
8563
|
-
const q = this.clone();
|
|
8564
|
-
q.q.log = logParamToLogObject(q.q.logger, log);
|
|
8565
|
-
return q;
|
|
8566
|
-
}
|
|
8567
|
-
}
|
|
8568
|
-
|
|
8569
|
-
var __defProp$5 = Object.defineProperty;
|
|
8570
|
-
var __getOwnPropSymbols$5 = Object.getOwnPropertySymbols;
|
|
8571
|
-
var __hasOwnProp$5 = Object.prototype.hasOwnProperty;
|
|
8572
|
-
var __propIsEnum$5 = Object.prototype.propertyIsEnumerable;
|
|
8573
|
-
var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8574
|
-
var __spreadValues$5 = (a, b) => {
|
|
8575
|
-
for (var prop in b || (b = {}))
|
|
8576
|
-
if (__hasOwnProp$5.call(b, prop))
|
|
8577
|
-
__defNormalProp$5(a, prop, b[prop]);
|
|
8578
|
-
if (__getOwnPropSymbols$5)
|
|
8579
|
-
for (var prop of __getOwnPropSymbols$5(b)) {
|
|
8580
|
-
if (__propIsEnum$5.call(b, prop))
|
|
8581
|
-
__defNormalProp$5(a, prop, b[prop]);
|
|
8582
|
-
}
|
|
8583
|
-
return a;
|
|
8584
|
-
};
|
|
8585
|
-
const mergableObjects = {
|
|
8586
|
-
shape: true,
|
|
8587
|
-
withShapes: true,
|
|
8588
|
-
parsers: true,
|
|
8589
|
-
defaults: true,
|
|
8590
|
-
joinedShapes: true,
|
|
8591
|
-
joinedParsers: true
|
|
8592
|
-
};
|
|
8593
|
-
class MergeQueryMethods {
|
|
8594
|
-
merge(q) {
|
|
8595
|
-
const query = this.clone();
|
|
8596
|
-
const a = query.q;
|
|
8597
|
-
const b = q.q;
|
|
8598
|
-
for (const key in b) {
|
|
8599
|
-
const value = b[key];
|
|
8600
|
-
switch (typeof value) {
|
|
8601
|
-
case "boolean":
|
|
8602
|
-
case "string":
|
|
8603
|
-
case "number":
|
|
8604
|
-
a[key] = value;
|
|
8605
|
-
break;
|
|
8606
|
-
case "object":
|
|
8607
|
-
if (Array.isArray(value)) {
|
|
8608
|
-
a[key] = a[key] ? [...a[key], ...value] : value;
|
|
8609
|
-
} else if (mergableObjects[key]) {
|
|
8610
|
-
a[key] = a[key] ? __spreadValues$5(__spreadValues$5({}, a[key]), value) : value;
|
|
8611
|
-
} else {
|
|
8612
|
-
a[key] = value;
|
|
8613
|
-
}
|
|
8614
|
-
break;
|
|
8615
|
-
}
|
|
8616
|
-
}
|
|
8617
|
-
a[orchidCore.getValueKey] = b[orchidCore.getValueKey];
|
|
8618
|
-
if (b.returnType)
|
|
8619
|
-
a.returnType = b.returnType;
|
|
8620
|
-
return query;
|
|
8621
|
-
}
|
|
8622
|
-
}
|
|
8623
|
-
|
|
8624
|
-
var __defProp$4 = Object.defineProperty;
|
|
8625
|
-
var __defProps$2 = Object.defineProperties;
|
|
8626
|
-
var __getOwnPropDescs$2 = Object.getOwnPropertyDescriptors;
|
|
8627
|
-
var __getOwnPropSymbols$4 = Object.getOwnPropertySymbols;
|
|
8628
|
-
var __hasOwnProp$4 = Object.prototype.hasOwnProperty;
|
|
8629
|
-
var __propIsEnum$4 = Object.prototype.propertyIsEnumerable;
|
|
8630
|
-
var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8631
|
-
var __spreadValues$4 = (a, b) => {
|
|
8632
|
-
for (var prop in b || (b = {}))
|
|
8633
|
-
if (__hasOwnProp$4.call(b, prop))
|
|
8634
|
-
__defNormalProp$4(a, prop, b[prop]);
|
|
8635
|
-
if (__getOwnPropSymbols$4)
|
|
8636
|
-
for (var prop of __getOwnPropSymbols$4(b)) {
|
|
8637
|
-
if (__propIsEnum$4.call(b, prop))
|
|
8638
|
-
__defNormalProp$4(a, prop, b[prop]);
|
|
8639
|
-
}
|
|
8640
|
-
return a;
|
|
8641
|
-
};
|
|
8642
|
-
var __spreadProps$2 = (a, b) => __defProps$2(a, __getOwnPropDescs$2(b));
|
|
8643
|
-
class With {
|
|
8644
8750
|
/**
|
|
8645
|
-
*
|
|
8751
|
+
* `whereNot` takes the same argument as `where`,
|
|
8752
|
+
* multiple conditions are combined with `AND`,
|
|
8753
|
+
* the whole group of conditions is negated with `NOT`.
|
|
8646
8754
|
*
|
|
8647
8755
|
* ```ts
|
|
8648
|
-
*
|
|
8649
|
-
*
|
|
8650
|
-
*
|
|
8651
|
-
*
|
|
8652
|
-
*
|
|
8653
|
-
*
|
|
8654
|
-
* // by default all columns from provided column shape will be included
|
|
8655
|
-
* // true is for default behavior
|
|
8656
|
-
* columns?: string[] | boolean;
|
|
8657
|
-
*
|
|
8658
|
-
* // Adds RECURSIVE keyword:
|
|
8659
|
-
* recursive?: true;
|
|
8660
|
-
*
|
|
8661
|
-
* // Adds MATERIALIZED keyword:
|
|
8662
|
-
* materialized?: true;
|
|
8663
|
-
*
|
|
8664
|
-
* // Adds NOT MATERIALIZED keyword:
|
|
8665
|
-
* notMaterialized?: true;
|
|
8666
|
-
* };
|
|
8756
|
+
* // find records of different colors than red
|
|
8757
|
+
* db.table.whereNot({ color: 'red' });
|
|
8758
|
+
* // WHERE NOT color = 'red'
|
|
8759
|
+
* db.table.whereNot({ one: 1, two: 2 });
|
|
8760
|
+
* // WHERE NOT (one = 1 AND two = 2)
|
|
8761
|
+
* ```
|
|
8667
8762
|
*
|
|
8668
|
-
*
|
|
8669
|
-
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
|
|
8763
|
+
* @param args - {@link WhereArgs}
|
|
8764
|
+
*/
|
|
8765
|
+
whereNot(...args) {
|
|
8766
|
+
return _queryWhereNot(
|
|
8767
|
+
this.clone(),
|
|
8768
|
+
args
|
|
8769
|
+
);
|
|
8770
|
+
}
|
|
8771
|
+
/**
|
|
8772
|
+
* `whereNot` version accepting SQL expression:
|
|
8677
8773
|
*
|
|
8678
|
-
*
|
|
8679
|
-
* db.table.
|
|
8774
|
+
* ```ts
|
|
8775
|
+
* db.table.whereNot`sql expression`
|
|
8776
|
+
* ```
|
|
8680
8777
|
*
|
|
8681
|
-
*
|
|
8682
|
-
|
|
8683
|
-
|
|
8684
|
-
|
|
8778
|
+
* @param args - SQL expression
|
|
8779
|
+
*/
|
|
8780
|
+
whereNotSql(...args) {
|
|
8781
|
+
return _queryWhereNotSql(this.clone(), args);
|
|
8782
|
+
}
|
|
8783
|
+
/**
|
|
8784
|
+
* `orWhere` is accepting the same arguments as {@link where}, joining arguments with `OR`.
|
|
8685
8785
|
*
|
|
8686
|
-
*
|
|
8687
|
-
* db.table.with(
|
|
8688
|
-
* 'alias',
|
|
8689
|
-
* {
|
|
8690
|
-
* recursive: true,
|
|
8691
|
-
* materialized: true,
|
|
8692
|
-
* },
|
|
8693
|
-
* rawOrQueryOrCallback,
|
|
8694
|
-
* );
|
|
8695
|
-
* ```
|
|
8786
|
+
* Columns in single arguments are still joined with `AND`.
|
|
8696
8787
|
*
|
|
8697
|
-
*
|
|
8788
|
+
* The database is processing `AND` before `OR`, so this should be intuitively clear.
|
|
8698
8789
|
*
|
|
8699
8790
|
* ```ts
|
|
8700
|
-
* db.table.
|
|
8791
|
+
* db.table.where({ id: 1, color: 'red' }).orWhere({ id: 2, color: 'blue' });
|
|
8792
|
+
* // equivalent:
|
|
8793
|
+
* db.table.orWhere({ id: 1, color: 'red' }, { id: 2, color: 'blue' });
|
|
8794
|
+
* ```
|
|
8701
8795
|
*
|
|
8702
|
-
*
|
|
8703
|
-
*
|
|
8704
|
-
*
|
|
8705
|
-
*
|
|
8796
|
+
* This query will produce such SQL (simplified):
|
|
8797
|
+
*
|
|
8798
|
+
* ```sql
|
|
8799
|
+
* SELECT * FROM "table"
|
|
8800
|
+
* WHERE id = 1 AND color = 'red'
|
|
8801
|
+
* OR id = 2 AND color = 'blue'
|
|
8706
8802
|
* ```
|
|
8707
8803
|
*
|
|
8708
|
-
* @param args -
|
|
8804
|
+
* @param args - {@link WhereArgs} will be joined with `OR`
|
|
8709
8805
|
*/
|
|
8710
|
-
|
|
8711
|
-
|
|
8712
|
-
let options = args.length === 3 && !orchidCore.isExpression(args[2]) || args.length === 4 ? args[1] : void 0;
|
|
8713
|
-
const last = args[args.length - 1];
|
|
8714
|
-
const query = typeof last === "function" ? last(q.queryBuilder) : last;
|
|
8715
|
-
const shape = args.length === 4 ? args[2] : orchidCore.isExpression(query) ? args[1] : query.q.shape;
|
|
8716
|
-
if ((options == null ? void 0 : options.columns) === true) {
|
|
8717
|
-
options = __spreadProps$2(__spreadValues$4({}, options), {
|
|
8718
|
-
columns: Object.keys(shape)
|
|
8719
|
-
});
|
|
8720
|
-
}
|
|
8721
|
-
pushQueryValue(q, "with", [args[0], options || orchidCore.emptyObject, query]);
|
|
8722
|
-
return setQueryObjectValue(q, "withShapes", args[0], shape);
|
|
8806
|
+
orWhere(...args) {
|
|
8807
|
+
return _queryOr(this.clone(), args);
|
|
8723
8808
|
}
|
|
8724
|
-
}
|
|
8725
|
-
|
|
8726
|
-
class Union {
|
|
8727
8809
|
/**
|
|
8728
|
-
*
|
|
8729
|
-
*
|
|
8810
|
+
* `orWhereNot` takes the same arguments as {@link orWhere}, and prepends each condition with `NOT` just as {@link whereNot} does.
|
|
8811
|
+
*
|
|
8812
|
+
* @param args - {@link WhereArgs} will be prefixed with `NOT` and joined with `OR`
|
|
8813
|
+
*/
|
|
8814
|
+
orWhereNot(...args) {
|
|
8815
|
+
return _queryOrNot(
|
|
8816
|
+
this.clone(),
|
|
8817
|
+
args
|
|
8818
|
+
);
|
|
8819
|
+
}
|
|
8820
|
+
/**
|
|
8821
|
+
* `whereIn` and related methods are for the `IN` operator to check for inclusion in a list of values.
|
|
8822
|
+
*
|
|
8823
|
+
* When used with a single column it works equivalent to the `in` column operator:
|
|
8730
8824
|
*
|
|
8731
8825
|
* ```ts
|
|
8732
|
-
*
|
|
8826
|
+
* db.table.whereIn('column', [1, 2, 3]);
|
|
8827
|
+
* // the same as:
|
|
8828
|
+
* db.table.where({ column: [1, 2, 3] });
|
|
8829
|
+
* ```
|
|
8830
|
+
*
|
|
8831
|
+
* `whereIn` can support a tuple of columns, that's what the `in` operator cannot support:
|
|
8832
|
+
*
|
|
8833
|
+
* ```ts
|
|
8834
|
+
* db.table.whereIn(
|
|
8835
|
+
* ['id', 'name'],
|
|
8733
8836
|
* [
|
|
8734
|
-
*
|
|
8735
|
-
*
|
|
8837
|
+
* [1, 'Alice'],
|
|
8838
|
+
* [2, 'Bob'],
|
|
8736
8839
|
* ],
|
|
8737
|
-
* true, // optional wrap parameter
|
|
8738
8840
|
* );
|
|
8739
8841
|
* ```
|
|
8740
8842
|
*
|
|
8741
|
-
*
|
|
8742
|
-
*
|
|
8843
|
+
* It supports sub query which should return records with columns of the same type:
|
|
8844
|
+
*
|
|
8845
|
+
* ```ts
|
|
8846
|
+
* db.table.whereIn(['id', 'name'], OtherTable.select('id', 'name'));
|
|
8847
|
+
* ```
|
|
8848
|
+
*
|
|
8849
|
+
* It supports raw SQL expression:
|
|
8850
|
+
*
|
|
8851
|
+
* ```ts
|
|
8852
|
+
* db.table.whereIn(['id', 'name'], db.table.sql`((1, 'one'), (2, 'two'))`);
|
|
8853
|
+
* ```
|
|
8743
8854
|
*/
|
|
8744
|
-
|
|
8745
|
-
return
|
|
8855
|
+
whereIn(...args) {
|
|
8856
|
+
return _queryWhereIn(
|
|
8746
8857
|
this.clone(),
|
|
8747
|
-
|
|
8748
|
-
args
|
|
8858
|
+
true,
|
|
8859
|
+
args[0],
|
|
8860
|
+
args[1]
|
|
8749
8861
|
);
|
|
8750
8862
|
}
|
|
8751
8863
|
/**
|
|
8752
|
-
*
|
|
8864
|
+
* Takes the same arguments as {@link whereIn}.
|
|
8865
|
+
* Add a `WHERE IN` condition prefixed with `OR` to the query:
|
|
8753
8866
|
*
|
|
8754
|
-
*
|
|
8755
|
-
*
|
|
8867
|
+
* ```ts
|
|
8868
|
+
* db.table.whereIn('a', [1, 2, 3]).orWhereIn('b', ['one', 'two']);
|
|
8869
|
+
* ```
|
|
8756
8870
|
*/
|
|
8757
|
-
|
|
8758
|
-
return
|
|
8871
|
+
orWhereIn(...args) {
|
|
8872
|
+
return _queryWhereIn(
|
|
8759
8873
|
this.clone(),
|
|
8760
|
-
|
|
8761
|
-
args
|
|
8874
|
+
false,
|
|
8875
|
+
args[0],
|
|
8876
|
+
args[1]
|
|
8762
8877
|
);
|
|
8763
8878
|
}
|
|
8764
8879
|
/**
|
|
8765
|
-
*
|
|
8880
|
+
* Acts as `whereIn`, but negates the condition with `NOT`:
|
|
8766
8881
|
*
|
|
8767
|
-
*
|
|
8768
|
-
*
|
|
8882
|
+
* ```ts
|
|
8883
|
+
* db.table.whereNotIn('color', ['red', 'green', 'blue']);
|
|
8884
|
+
* ```
|
|
8769
8885
|
*/
|
|
8770
|
-
|
|
8771
|
-
return
|
|
8886
|
+
whereNotIn(...args) {
|
|
8887
|
+
return _queryWhereIn(
|
|
8772
8888
|
this.clone(),
|
|
8773
|
-
|
|
8774
|
-
args
|
|
8889
|
+
true,
|
|
8890
|
+
args[0],
|
|
8891
|
+
args[1],
|
|
8892
|
+
true
|
|
8775
8893
|
);
|
|
8776
8894
|
}
|
|
8777
8895
|
/**
|
|
8778
|
-
*
|
|
8896
|
+
* Acts as `whereIn`, but prepends `OR` to the condition and negates it with `NOT`:
|
|
8779
8897
|
*
|
|
8780
|
-
*
|
|
8781
|
-
*
|
|
8898
|
+
* ```ts
|
|
8899
|
+
* db.table.whereNotIn('a', [1, 2, 3]).orWhereNoIn('b', ['one', 'two']);
|
|
8900
|
+
* ```
|
|
8782
8901
|
*/
|
|
8783
|
-
|
|
8784
|
-
return
|
|
8902
|
+
orWhereNotIn(...args) {
|
|
8903
|
+
return _queryWhereIn(
|
|
8785
8904
|
this.clone(),
|
|
8786
|
-
|
|
8787
|
-
args
|
|
8905
|
+
false,
|
|
8906
|
+
args[0],
|
|
8907
|
+
args[1],
|
|
8908
|
+
true
|
|
8788
8909
|
);
|
|
8789
8910
|
}
|
|
8790
8911
|
/**
|
|
8791
|
-
*
|
|
8912
|
+
* `whereExists` is for support of the `WHERE EXISTS (query)` clause.
|
|
8792
8913
|
*
|
|
8793
|
-
*
|
|
8794
|
-
*
|
|
8914
|
+
* This method is accepting the same arguments as `join`, see the {@link Join.join} section for more details.
|
|
8915
|
+
*
|
|
8916
|
+
* ```ts
|
|
8917
|
+
* // find users who have accounts
|
|
8918
|
+
* // find by a relation name if it's defined
|
|
8919
|
+
* db.user.whereExists('account');
|
|
8920
|
+
*
|
|
8921
|
+
* // find using a table and a join conditions
|
|
8922
|
+
* db.user.whereExists(db.account, 'account.id', 'user.id');
|
|
8923
|
+
*
|
|
8924
|
+
* // find using a query builder in a callback:
|
|
8925
|
+
* db.user.whereExists(db.account, (q) => q.on('account.id', '=', 'user.id'));
|
|
8926
|
+
* ```
|
|
8795
8927
|
*/
|
|
8796
|
-
|
|
8797
|
-
return
|
|
8928
|
+
whereExists(arg, ...args) {
|
|
8929
|
+
return _queryWhereExists(
|
|
8798
8930
|
this.clone(),
|
|
8799
|
-
|
|
8800
|
-
args
|
|
8931
|
+
arg,
|
|
8932
|
+
args
|
|
8801
8933
|
);
|
|
8802
8934
|
}
|
|
8803
8935
|
/**
|
|
8804
|
-
*
|
|
8936
|
+
* Acts as `whereExists`, but prepends the condition with `OR`:
|
|
8805
8937
|
*
|
|
8806
|
-
*
|
|
8807
|
-
*
|
|
8938
|
+
* ```ts
|
|
8939
|
+
* // find users who have an account or a profile,
|
|
8940
|
+
* // imagine that the user has both `account` and `profile` relations defined.
|
|
8941
|
+
* db.user.whereExist('account').orWhereExists('profile');
|
|
8942
|
+
* ```
|
|
8808
8943
|
*/
|
|
8809
|
-
|
|
8810
|
-
|
|
8811
|
-
|
|
8812
|
-
|
|
8813
|
-
|
|
8944
|
+
orWhereExists(arg, ...args) {
|
|
8945
|
+
const q = this.clone();
|
|
8946
|
+
return _queryOr(q, existsArgs(q, arg, args));
|
|
8947
|
+
}
|
|
8948
|
+
/**
|
|
8949
|
+
* Acts as `whereExists`, but negates the condition with `NOT`:
|
|
8950
|
+
*
|
|
8951
|
+
* ```ts
|
|
8952
|
+
* // find users who don't have an account,
|
|
8953
|
+
* // image that the user `belongsTo` or `hasOne` account.
|
|
8954
|
+
* db.user.whereNotExist('account');
|
|
8955
|
+
* ```
|
|
8956
|
+
*
|
|
8957
|
+
* @param arg - relation name, or a query object, or a `with` table alias, or a callback returning a query object.
|
|
8958
|
+
* @param args - no arguments needed when the first argument is a relation name, or conditions to join the table with.
|
|
8959
|
+
*/
|
|
8960
|
+
whereNotExists(arg, ...args) {
|
|
8961
|
+
const q = this.clone();
|
|
8962
|
+
return _queryWhereNot(
|
|
8963
|
+
q,
|
|
8964
|
+
existsArgs(q, arg, args)
|
|
8814
8965
|
);
|
|
8815
8966
|
}
|
|
8967
|
+
/**
|
|
8968
|
+
* Acts as `whereExists`, but prepends the condition with `OR` and negates it with `NOT`:
|
|
8969
|
+
*
|
|
8970
|
+
* ```ts
|
|
8971
|
+
* // find users who don't have an account OR who don't have a profile
|
|
8972
|
+
* // imagine that the user has both `account` and `profile` relations defined.
|
|
8973
|
+
* db.user.whereNotExists('account').orWhereNotExists('profile');
|
|
8974
|
+
* ```
|
|
8975
|
+
*/
|
|
8976
|
+
orWhereNotExists(arg, ...args) {
|
|
8977
|
+
const q = this.clone();
|
|
8978
|
+
return _queryOrNot(q, existsArgs(q, arg, args));
|
|
8979
|
+
}
|
|
8816
8980
|
}
|
|
8817
8981
|
|
|
8818
8982
|
var __defProp$3 = Object.defineProperty;
|
|
@@ -9816,6 +9980,22 @@ class RawSqlMethods {
|
|
|
9816
9980
|
}
|
|
9817
9981
|
}
|
|
9818
9982
|
|
|
9983
|
+
class QueryBase {
|
|
9984
|
+
constructor() {
|
|
9985
|
+
this.q = {};
|
|
9986
|
+
}
|
|
9987
|
+
/**
|
|
9988
|
+
* Clones the current query chain, useful for re-using partial query snippets in other queries without mutating the original.
|
|
9989
|
+
*
|
|
9990
|
+
* Used under the hood, and not really needed on the app side.
|
|
9991
|
+
*/
|
|
9992
|
+
clone() {
|
|
9993
|
+
const cloned = Object.create(this.baseQuery);
|
|
9994
|
+
cloned.q = getClonedQueryData(this.q);
|
|
9995
|
+
return cloned;
|
|
9996
|
+
}
|
|
9997
|
+
}
|
|
9998
|
+
|
|
9819
9999
|
class TransformMethods {
|
|
9820
10000
|
/**
|
|
9821
10001
|
* Transform the result of the query right after loading it.
|
|
@@ -10653,7 +10833,7 @@ orchidCore.applyMixins(QueryMethods, [
|
|
|
10653
10833
|
Select,
|
|
10654
10834
|
From,
|
|
10655
10835
|
Join,
|
|
10656
|
-
|
|
10836
|
+
OnMethods,
|
|
10657
10837
|
With,
|
|
10658
10838
|
Union,
|
|
10659
10839
|
JsonModifiers,
|
|
@@ -10954,7 +11134,6 @@ const performQuery = async (q, args, method) => {
|
|
|
10954
11134
|
};
|
|
10955
11135
|
orchidCore.applyMixins(Db, [QueryMethods]);
|
|
10956
11136
|
Db.prototype.constructor = Db;
|
|
10957
|
-
Db.prototype.onQueryBuilder = OnQueryBuilder;
|
|
10958
11137
|
const createDb = (_a) => {
|
|
10959
11138
|
var _b = _a, {
|
|
10960
11139
|
log,
|
|
@@ -11201,7 +11380,7 @@ exports.NotFoundError = NotFoundError;
|
|
|
11201
11380
|
exports.NumberAsStringBaseColumn = NumberAsStringBaseColumn;
|
|
11202
11381
|
exports.NumberBaseColumn = NumberBaseColumn;
|
|
11203
11382
|
exports.OnConflictQueryBuilder = OnConflictQueryBuilder;
|
|
11204
|
-
exports.
|
|
11383
|
+
exports.OnMethods = OnMethods;
|
|
11205
11384
|
exports.Operators = Operators;
|
|
11206
11385
|
exports.OrchidOrmError = OrchidOrmError;
|
|
11207
11386
|
exports.OrchidOrmInternalError = OrchidOrmInternalError;
|
|
@@ -11243,7 +11422,6 @@ exports.Update = Update;
|
|
|
11243
11422
|
exports.VarCharColumn = VarCharColumn;
|
|
11244
11423
|
exports.VirtualColumn = VirtualColumn;
|
|
11245
11424
|
exports.Where = Where;
|
|
11246
|
-
exports.WhereQueryBase = WhereQueryBase;
|
|
11247
11425
|
exports.With = With;
|
|
11248
11426
|
exports.XMLColumn = XMLColumn;
|
|
11249
11427
|
exports._queryAfterSaveCommit = _queryAfterSaveCommit;
|
|
@@ -11295,6 +11473,7 @@ exports._queryUpdate = _queryUpdate;
|
|
|
11295
11473
|
exports._queryUpdateOrThrow = _queryUpdateOrThrow;
|
|
11296
11474
|
exports._queryUpdateRaw = _queryUpdateRaw;
|
|
11297
11475
|
exports._queryWhere = _queryWhere;
|
|
11476
|
+
exports._queryWhereExists = _queryWhereExists;
|
|
11298
11477
|
exports._queryWhereIn = _queryWhereIn;
|
|
11299
11478
|
exports._queryWhereNot = _queryWhereNot;
|
|
11300
11479
|
exports._queryWhereNotSql = _queryWhereNotSql;
|
|
@@ -11306,6 +11485,7 @@ exports.addQueryOn = addQueryOn;
|
|
|
11306
11485
|
exports.anyShape = anyShape;
|
|
11307
11486
|
exports.checkIfASimpleQuery = checkIfASimpleQuery;
|
|
11308
11487
|
exports.cloneQuery = cloneQuery;
|
|
11488
|
+
exports.cloneQueryBaseUnscoped = cloneQueryBaseUnscoped;
|
|
11309
11489
|
exports.columnCheckToCode = columnCheckToCode;
|
|
11310
11490
|
exports.columnCode = columnCode;
|
|
11311
11491
|
exports.columnForeignKeysToCode = columnForeignKeysToCode;
|