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