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