@omegup/msync 0.1.44 → 0.1.46
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/index.d.ts +35 -11
- package/index.esm.js +362 -229
- package/index.js +363 -228
- package/package.json +1 -1
package/index.esm.js
CHANGED
|
@@ -217,7 +217,7 @@ const { root, ctx } = Field;
|
|
|
217
217
|
const asAccumulator = (x) => x;
|
|
218
218
|
const $sum_ = (expr) => ({ raw: f => asAccumulator({ $sum: expr.raw(f).get() }) });
|
|
219
219
|
const $sum = (expr) => ({
|
|
220
|
-
group: $sum_(ite(root().of('old').expr(), subtract(val(0), $ifNull(sub(expr, root().of('v')), val(0))), sub(expr, root().of('v')))),
|
|
220
|
+
group: $sum_(ite(root().of('deleted').expr(), val(0), ite(root().of('old').expr(), subtract(val(0), $ifNull(sub(expr, root().of('v')), val(0))), sub(expr, root().of('v'))))),
|
|
221
221
|
merge: (x, y) => add($ifNull(x, val(0)), y),
|
|
222
222
|
});
|
|
223
223
|
const $accumulator_ = (init, accumulateArgs, accumulate, merge) => {
|
|
@@ -240,7 +240,13 @@ const $accumulator = (init, accumulateArgs, accumulate, merge) => ({
|
|
|
240
240
|
});
|
|
241
241
|
const $countDict = (expr) => $accumulator(function () {
|
|
242
242
|
return {};
|
|
243
|
-
}, [
|
|
243
|
+
}, [
|
|
244
|
+
sub(expr, root().of('v')),
|
|
245
|
+
root().of('old').expr(),
|
|
246
|
+
root().of('deleted').expr(),
|
|
247
|
+
], function (a, k, old, deleted) {
|
|
248
|
+
if (deleted)
|
|
249
|
+
return a;
|
|
244
250
|
let y = (a[k] || 0) + (old ? -1 : 1);
|
|
245
251
|
return y ? (a[k] = y) : delete a[k], a;
|
|
246
252
|
}, function (a, b) {
|
|
@@ -251,7 +257,13 @@ const $countDict = (expr) => $accumulator(function () {
|
|
|
251
257
|
});
|
|
252
258
|
const $countDictArray = (expr) => $accumulator(function () {
|
|
253
259
|
return {};
|
|
254
|
-
}, [
|
|
260
|
+
}, [
|
|
261
|
+
sub(expr, root().of('v')),
|
|
262
|
+
root().of('old').expr(),
|
|
263
|
+
root().of('deleted').expr(),
|
|
264
|
+
], function (a, keys, old, deleted) {
|
|
265
|
+
if (deleted)
|
|
266
|
+
return a;
|
|
255
267
|
keys.forEach(k => {
|
|
256
268
|
const y = (a[k] || 0) + (old ? -1 : 1);
|
|
257
269
|
if (y)
|
|
@@ -272,7 +284,10 @@ const $pushDict = (key, value) => $accumulator(function () {
|
|
|
272
284
|
sub(key, root().of('v')),
|
|
273
285
|
sub(value, root().of('v')),
|
|
274
286
|
root().of('old').expr(),
|
|
275
|
-
|
|
287
|
+
root().of('deleted').expr(),
|
|
288
|
+
], function (ra, k, v, old, deleted) {
|
|
289
|
+
if (deleted)
|
|
290
|
+
return ra;
|
|
276
291
|
let a = { ...ra };
|
|
277
292
|
const equal = (a, b) => {
|
|
278
293
|
if (!a || !b)
|
|
@@ -400,6 +415,7 @@ const assertEqual = () => ({
|
|
|
400
415
|
forward1: id,
|
|
401
416
|
backward1: id,
|
|
402
417
|
});
|
|
418
|
+
const literalsEqaul = (_) => assertEqual();
|
|
403
419
|
const omitRORec = () => assertEqual();
|
|
404
420
|
const omitPick = () => assertEqual();
|
|
405
421
|
const translateOmit = () => assertEqual();
|
|
@@ -418,7 +434,7 @@ const filter = ({ as, cond, expr, limit, }) => asExpr({
|
|
|
418
434
|
input: expr.raw(f).get(),
|
|
419
435
|
as,
|
|
420
436
|
cond: cond.raw(f).get(),
|
|
421
|
-
limit: limit
|
|
437
|
+
...(limit ? { limit: limit.raw(f).get() } : {}),
|
|
422
438
|
},
|
|
423
439
|
}),
|
|
424
440
|
});
|
|
@@ -562,9 +578,23 @@ const subGroup = (id, args, addGrp) => {
|
|
|
562
578
|
const part = filterDefined(array(ite(eqTyped(root().of('after').expr(), nil), nil, field({
|
|
563
579
|
v: ['v', root().of('after').expr()],
|
|
564
580
|
old: ['old', val(false)],
|
|
581
|
+
deleted: [
|
|
582
|
+
'deleted',
|
|
583
|
+
root()
|
|
584
|
+
.of('deleted')
|
|
585
|
+
.of('after')
|
|
586
|
+
.expr(),
|
|
587
|
+
],
|
|
565
588
|
})), ite(eqTyped(root().of('before').expr(), nil), nil, field({
|
|
566
589
|
v: ['v', root().of('before').expr()],
|
|
567
590
|
old: ['old', val(true)],
|
|
591
|
+
deleted: [
|
|
592
|
+
'deleted',
|
|
593
|
+
root()
|
|
594
|
+
.of('deleted')
|
|
595
|
+
.of('before')
|
|
596
|
+
.expr(),
|
|
597
|
+
],
|
|
568
598
|
}))));
|
|
569
599
|
const replaceWith = (expr) => $replaceWith_(expr);
|
|
570
600
|
return link()
|
|
@@ -1050,9 +1080,10 @@ const $setDelta = (updater) => {
|
|
|
1050
1080
|
after1: ['after1', 'after'],
|
|
1051
1081
|
before1: ['before1', 'before'],
|
|
1052
1082
|
};
|
|
1053
|
-
const
|
|
1083
|
+
const side = (k) => root().of(k);
|
|
1084
|
+
const update = (k) => subUpdater(weaken(updater), side(k));
|
|
1054
1085
|
return link()
|
|
1055
|
-
.with($setEach(k => to(
|
|
1086
|
+
.with($setEach(k => to(side(k).expr()), dict))
|
|
1056
1087
|
.with($setEach(update, dict))
|
|
1057
1088
|
.with($replaceWithEach(field => root().of(`${field}1`).expr()))
|
|
1058
1089
|
.with($setEach(k => to(nil), dict)).stages;
|
|
@@ -1086,147 +1117,229 @@ const dualIn = operator();
|
|
|
1086
1117
|
const $in = dualIn('$in');
|
|
1087
1118
|
const $nin = dualIn('$nin');
|
|
1088
1119
|
|
|
1089
|
-
const $
|
|
1090
|
-
|
|
1091
|
-
const
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1120
|
+
const $matchDelta = (query) => concatStages($replaceWithDelta(ite(query, root().expr(), nil)), matchDelta());
|
|
1121
|
+
const matchDelta = () => {
|
|
1122
|
+
const part = (side) => root().of(side).expr();
|
|
1123
|
+
return $match_($expr(ne(part('before'))(part('after'))));
|
|
1124
|
+
};
|
|
1125
|
+
|
|
1126
|
+
const $match = (query) => ({
|
|
1127
|
+
raw: $match1($expr(query)),
|
|
1128
|
+
delta: $matchDelta(query),
|
|
1129
|
+
});
|
|
1130
|
+
|
|
1131
|
+
const joinIdOf = (k, k1, k1Id, k2Id) => {
|
|
1132
|
+
if (k === k1)
|
|
1133
|
+
return k1Id;
|
|
1134
|
+
if (typeof k === 'string')
|
|
1135
|
+
return k2Id;
|
|
1136
|
+
const idOf = (key) => (key === k1 ? k1Id : k2Id);
|
|
1137
|
+
return concat(idOf(k[0]), val(k[1]), idOf(k[2]));
|
|
1138
|
+
};
|
|
1139
|
+
const andAlso = (probe, keep) => ite(eqTyped(probe, nil), nil, keep);
|
|
1140
|
+
const notObjAndAlso = (probe, keep) => ite(eqTyped(probe, field({})), nil, keep);
|
|
1141
|
+
const $unwindDelta = (k1, k2, k, includeNull1, includeNull2) => {
|
|
1142
|
+
const emptyArr = array();
|
|
1143
|
+
const src = root();
|
|
1144
|
+
const k2Arr = (side) => src.of(side).of(k2).expr();
|
|
1145
|
+
const k2OrEmpty = (side) => $ifNull(k2Arr(side), emptyArr);
|
|
1146
|
+
const k2Empty = (side) => eq(k2Arr(side))(emptyArr);
|
|
1147
|
+
const beforeItems = k2OrEmpty('before');
|
|
1148
|
+
const afterItems = k2OrEmpty('after');
|
|
1149
|
+
const beforeIds = $map1(beforeItems, (item) => item.of('_id').expr());
|
|
1150
|
+
const newByK2Id = filter({
|
|
1151
|
+
expr: afterItems,
|
|
1152
|
+
as: 'a',
|
|
1153
|
+
cond: not(inArray(ctx()('a').of('_id').expr(), beforeIds)),
|
|
1154
|
+
});
|
|
1155
|
+
const oldByK2Id = $map0({
|
|
1156
|
+
input: beforeItems,
|
|
1157
|
+
as: 'b',
|
|
1158
|
+
expr: field({
|
|
1159
|
+
before: ['before', ctx()('b').expr()],
|
|
1160
|
+
after: [
|
|
1161
|
+
'after',
|
|
1162
|
+
first$1(filter({
|
|
1163
|
+
expr: afterItems,
|
|
1164
|
+
as: 'a',
|
|
1165
|
+
cond: eq(ctx()('a').of('_id').expr())(ctx()('b').of('_id').expr()),
|
|
1166
|
+
})),
|
|
1167
|
+
],
|
|
1168
|
+
}),
|
|
1169
|
+
});
|
|
1170
|
+
const k1Slot = (fill) => array(field({
|
|
1171
|
+
before: [
|
|
1172
|
+
'before',
|
|
1173
|
+
$ifNull(first$1(beforeItems), fill),
|
|
1174
|
+
],
|
|
1175
|
+
after: [
|
|
1176
|
+
'after',
|
|
1177
|
+
$ifNull(first$1(afterItems), fill),
|
|
1178
|
+
],
|
|
1179
|
+
}));
|
|
1180
|
+
const kept = k === k1 ? k1Slot(nil) : oldByK2Id;
|
|
1181
|
+
const added = k === k1 ? emptyArr : newByK2Id;
|
|
1182
|
+
const k1Delta = field({
|
|
1183
|
+
before: [
|
|
1184
|
+
'before',
|
|
1185
|
+
$ifNull(src.of('before').of(k1).expr(), nil),
|
|
1186
|
+
],
|
|
1187
|
+
after: [
|
|
1188
|
+
'after',
|
|
1189
|
+
$ifNull(src.of('after').of(k1).expr(), nil),
|
|
1190
|
+
],
|
|
1191
|
+
});
|
|
1192
|
+
const unwindJoin = ({ kept, added, padToNull, isPad, join, }) => {
|
|
1193
|
+
const newDeltas = $map0({
|
|
1194
|
+
input: added,
|
|
1095
1195
|
as: 'a',
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
in: {
|
|
1117
|
-
before: '$$b',
|
|
1118
|
-
after: {
|
|
1119
|
-
$ifNull: [
|
|
1120
|
-
{
|
|
1121
|
-
$first: {
|
|
1122
|
-
$filter: {
|
|
1123
|
-
input: `$after.${k2}`,
|
|
1124
|
-
as: 'a',
|
|
1125
|
-
cond: { $eq: ['$$a._id', '$$b._id'] },
|
|
1126
|
-
},
|
|
1127
|
-
},
|
|
1128
|
-
},
|
|
1129
|
-
null,
|
|
1130
|
-
],
|
|
1131
|
-
},
|
|
1132
|
-
},
|
|
1133
|
-
},
|
|
1134
|
-
};
|
|
1135
|
-
const ifNull = (k, part, str = `$${k}.${part}._id`) => outer2 && k == k2 ? { $ifNull: [str, 'null'] } : str;
|
|
1136
|
-
const interDot = ([a, b]) => [a, middle ?? '.', b];
|
|
1137
|
-
const partReplace = (part) => {
|
|
1138
|
-
const def = {
|
|
1139
|
-
_id: k
|
|
1140
|
-
? ifNull(k, part)
|
|
1141
|
-
: {
|
|
1142
|
-
$concat: interDot([k1, k2].sort().map(k => ifNull(k, part))),
|
|
1143
|
-
},
|
|
1144
|
-
[k1]: `$${k1}.${part}`,
|
|
1145
|
-
[k2]: outer2
|
|
1146
|
-
? {
|
|
1147
|
-
$cond: {
|
|
1148
|
-
if: `$${k2}.${part}._id`,
|
|
1149
|
-
then: `$${k2}.${part}`,
|
|
1150
|
-
else: null,
|
|
1151
|
-
},
|
|
1152
|
-
}
|
|
1153
|
-
: `$${k2}.${part}`,
|
|
1196
|
+
expr: field({
|
|
1197
|
+
before: ['before', nil],
|
|
1198
|
+
after: ['after', ctx()('a').expr()],
|
|
1199
|
+
}),
|
|
1200
|
+
});
|
|
1201
|
+
const deltas = mergeObjects(field(map1(k1, k1Delta)), field(map1(k2, concatArray(kept, newDeltas))));
|
|
1202
|
+
const doc = root();
|
|
1203
|
+
const parentId = doc.of('_id').expr();
|
|
1204
|
+
const k1At = (side) => doc.of(k1).of(side).expr();
|
|
1205
|
+
const k2Raw = (side) => doc.of(k2).of(side).expr();
|
|
1206
|
+
const docK2Null = root();
|
|
1207
|
+
const k2Keep = (side) => docK2Null.of(k2).of(side).expr();
|
|
1208
|
+
const k2At = (side) => padToNull(k2Raw(side), k2Keep(side));
|
|
1209
|
+
const rowOf = (id, a, b) => mergeObjects(mergeObjects(field({ _id: ['_id', id] }), field(map1(k1, a))), field(map1(k2, b)));
|
|
1210
|
+
const docK1 = root();
|
|
1211
|
+
const k2RawK1 = (side) => docK1.of(k2).of(side).expr();
|
|
1212
|
+
const k2KeepK1K2 = (side) => root().of(k2).of(side).expr();
|
|
1213
|
+
const rowOnK1 = (side) => {
|
|
1214
|
+
const t = docK1.of(k1).of(side);
|
|
1215
|
+
return rowOf(t.of('_id').expr(), t.expr(), padToNull(k2RawK1(side), k2KeepK1K2(side)));
|
|
1154
1216
|
};
|
|
1155
|
-
const
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
if: skipNulls.length === 1 ? skipNulls[0] : { $or: skipNulls },
|
|
1161
|
-
then: null,
|
|
1162
|
-
else: def,
|
|
1163
|
-
},
|
|
1217
|
+
const docK2 = root();
|
|
1218
|
+
const k1AtK2 = (side) => docK2.of(k1).of(side).expr();
|
|
1219
|
+
const rowOnK2 = (side) => {
|
|
1220
|
+
const u = docK2.of(k2).of(side);
|
|
1221
|
+
return rowOf(u.of('_id').expr(), k1AtK2(side), u.expr());
|
|
1164
1222
|
};
|
|
1223
|
+
const docK1K2 = root();
|
|
1224
|
+
const k1AtK1K2 = (side) => docK1K2.of(k1).of(side).expr();
|
|
1225
|
+
const k2AtK1K2 = (side) => docK1K2.of(k2).of(side).expr();
|
|
1226
|
+
const k1IdK1K2 = (side) => docK1K2.of(k1).of(side).of('_id').expr();
|
|
1227
|
+
const k2IdK1K2 = (side) => docK1K2.of(k2).of(side).of('_id').expr();
|
|
1228
|
+
const completeId = (side) => joinIdOf(k, k1, k1IdK1K2(side), k2IdK1K2(side));
|
|
1229
|
+
const rowK1K2 = (side) => rowOf(completeId(side), k1AtK1K2(side), k2AtK1K2(side));
|
|
1230
|
+
const liftRowId = $replaceWith_(mergeObjects(root().expr(), field({
|
|
1231
|
+
_id: [
|
|
1232
|
+
'_id',
|
|
1233
|
+
$ifNull(root()
|
|
1234
|
+
.of('before')
|
|
1235
|
+
.of('_id')
|
|
1236
|
+
.expr(), root()
|
|
1237
|
+
.of('after')
|
|
1238
|
+
.of('_id')
|
|
1239
|
+
.expr(), root().of('_id').expr()),
|
|
1240
|
+
],
|
|
1241
|
+
})));
|
|
1242
|
+
const padded = (side) => isPad(k2Raw(side));
|
|
1243
|
+
const emptyObj = field({});
|
|
1244
|
+
const deletedFlags = mergeObjects(ite(padded('before'), field({ before: ['before', val(true)] }), emptyObj), ite(padded('after'), field({ after: ['after', val(true)] }), emptyObj));
|
|
1245
|
+
const joinAt = (side) => join({
|
|
1246
|
+
k1: k1At(side),
|
|
1247
|
+
k2: k2At(side),
|
|
1248
|
+
rowK1K2: rowK1K2(side),
|
|
1249
|
+
rowOnK1: rowOnK1(side),
|
|
1250
|
+
rowOnK2: rowOnK2(side),
|
|
1251
|
+
k2Keep: k2Keep(side),
|
|
1252
|
+
k2Raw: k2Raw(side),
|
|
1253
|
+
k2RawK1: k2RawK1(side),
|
|
1254
|
+
k2KeepK1K2: k2KeepK1K2(side),
|
|
1255
|
+
});
|
|
1256
|
+
const joinDelta = mergeObjects(field({
|
|
1257
|
+
_id: ['_id', parentId],
|
|
1258
|
+
before: ['before', joinAt('before')],
|
|
1259
|
+
after: ['after', joinAt('after')],
|
|
1260
|
+
}), ite(or(padded('before'), padded('after')), field({ deleted: ['deleted', deletedFlags] }), emptyObj));
|
|
1261
|
+
return link()
|
|
1262
|
+
.with($replaceWith_(mergeObjects(src.expr(), deltas)))
|
|
1263
|
+
.with($unwind_(k2))
|
|
1264
|
+
.with($replaceWith_(joinDelta))
|
|
1265
|
+
.with(matchDelta())
|
|
1266
|
+
.with(liftRowId).stages;
|
|
1165
1267
|
};
|
|
1166
|
-
const
|
|
1167
|
-
const
|
|
1168
|
-
|
|
1169
|
-
{
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1268
|
+
const noPadToNull = (k2) => k2;
|
|
1269
|
+
const neverPad = (_k2) => val(false);
|
|
1270
|
+
if (includeNull2 === null) {
|
|
1271
|
+
const padObj = field({});
|
|
1272
|
+
const padDelta = (after) => field({
|
|
1273
|
+
before: ['before', padObj],
|
|
1274
|
+
after: ['after', after],
|
|
1275
|
+
});
|
|
1276
|
+
const addedPadded = k === k1
|
|
1277
|
+
? array()
|
|
1278
|
+
: ite(and(not(k2Empty('before')), k2Empty('after')), array(padObj), added);
|
|
1279
|
+
const padToNull = (k2, keep) => ite(eqTyped(k2, field({})), nil, keep);
|
|
1280
|
+
const isPad = (k2) => ite(eqTyped(k2, field({})), val(true), val(false));
|
|
1281
|
+
const keptMatched = ite(k2Empty('before'), ite(k2Empty('after'), array(padDelta(padObj)), array(padDelta(nil))), kept);
|
|
1282
|
+
const padded = { added: addedPadded, padToNull, isPad };
|
|
1283
|
+
if (includeNull1 === null) {
|
|
1284
|
+
if (k === k1) {
|
|
1285
|
+
return unwindJoin({
|
|
1286
|
+
...padded,
|
|
1287
|
+
kept: k1Slot(padObj),
|
|
1288
|
+
join: ({ k1: k1Val, rowOnK1 }) => andAlso(k1Val, rowOnK1),
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
if (k === k2) {
|
|
1292
|
+
return unwindJoin({
|
|
1293
|
+
...padded,
|
|
1294
|
+
kept: keptMatched,
|
|
1295
|
+
join: ({ k2Raw, k2Keep, rowOnK2 }) => notObjAndAlso(k2Raw, andAlso(k2Keep, rowOnK2)),
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
return unwindJoin({
|
|
1299
|
+
...padded,
|
|
1300
|
+
kept: keptMatched,
|
|
1301
|
+
join: ({ k1: k1Val, k2RawK1, k2KeepK1K2, rowK1K2 }) => andAlso(k1Val, notObjAndAlso(k2RawK1, andAlso(k2KeepK1K2, rowK1K2))),
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
const eq2 = literalsEqaul();
|
|
1305
|
+
if (k === k1) {
|
|
1306
|
+
return unwindJoin({
|
|
1307
|
+
...padded,
|
|
1308
|
+
kept: k1Slot(padObj),
|
|
1309
|
+
join: ({ k1: k1Val, rowOnK1 }) => andAlso(k1Val, rowOnK1),
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
return unwindJoin({
|
|
1313
|
+
...padded,
|
|
1314
|
+
kept: keptMatched,
|
|
1315
|
+
join: ({ k1: k1Val, k2RawK1, k2KeepK1K2, rowK1K2 }) => andAlso(k1Val, notObjAndAlso(k2RawK1, andAlso(k2KeepK1K2, eq2.forward(rowK1K2)))),
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
if (includeNull1 === null) {
|
|
1319
|
+
const eq1 = literalsEqaul();
|
|
1320
|
+
return unwindJoin({
|
|
1321
|
+
kept,
|
|
1322
|
+
added,
|
|
1323
|
+
padToNull: noPadToNull,
|
|
1324
|
+
isPad: neverPad,
|
|
1325
|
+
join: ({ k1, k2: k2Val, k2KeepK1K2, rowK1K2, rowOnK2 }) => {
|
|
1326
|
+
return k === k2
|
|
1327
|
+
? andAlso(k2Val, rowOnK2)
|
|
1328
|
+
: andAlso(k1, andAlso(k2KeepK1K2, eq1.forward(rowK1K2)));
|
|
1221
1329
|
},
|
|
1222
|
-
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1332
|
+
return unwindJoin({
|
|
1333
|
+
kept,
|
|
1334
|
+
added,
|
|
1335
|
+
padToNull: noPadToNull,
|
|
1336
|
+
isPad: neverPad,
|
|
1337
|
+
join: ({ k1, k2KeepK1K2, rowK1K2 }) => andAlso(k1, andAlso(k2KeepK1K2, rowK1K2)),
|
|
1338
|
+
});
|
|
1227
1339
|
};
|
|
1228
1340
|
|
|
1229
|
-
const $unwind = (k, dict, middle) => {
|
|
1341
|
+
const $unwind = (k, dict, middle = '.') => {
|
|
1342
|
+
const joinK = ['left', middle, 'right'];
|
|
1230
1343
|
return {
|
|
1231
1344
|
delta: link()
|
|
1232
1345
|
.with($replaceWithDelta(field({
|
|
@@ -1234,22 +1347,32 @@ const $unwind = (k, dict, middle) => {
|
|
|
1234
1347
|
left: ['left', root().expr()],
|
|
1235
1348
|
right: ['right', root().of(k).expr()],
|
|
1236
1349
|
})))
|
|
1237
|
-
.with($unwindDelta('left', 'right',
|
|
1350
|
+
.with($unwindDelta('left', 'right', joinK))
|
|
1238
1351
|
.with($replaceWithDelta(mergeObjects(root().of('left').expr(), fieldM({
|
|
1239
1352
|
key: root().of('right').expr(),
|
|
1240
1353
|
id: root().of('_id').expr(),
|
|
1241
1354
|
}, dict)))).stages,
|
|
1242
|
-
raw:
|
|
1355
|
+
raw: (f) => link()
|
|
1356
|
+
.with($unwind1(k)(f))
|
|
1357
|
+
.with($set1(set()({
|
|
1358
|
+
_id: [
|
|
1359
|
+
'_id',
|
|
1360
|
+
to(joinIdOf(joinK, 'left', root().of('_id').expr(), root().of(k).of('_id').expr())),
|
|
1361
|
+
],
|
|
1362
|
+
}))(f)).stages,
|
|
1243
1363
|
};
|
|
1244
1364
|
};
|
|
1245
1365
|
|
|
1246
1366
|
const $lookupDelta = ({ field1, field2 }, { coll, exec, input }, k1, k2, k, includeNull1, includeNull2) => {
|
|
1247
1367
|
const omit = omitRORec();
|
|
1248
|
-
const hash = crypto$1
|
|
1249
|
-
|
|
1368
|
+
const hash = crypto$1
|
|
1369
|
+
.createHash('md5')
|
|
1370
|
+
.update(coll.collectionName + canonicalize(input) + canonicalize(exec))
|
|
1371
|
+
.digest('base64url');
|
|
1372
|
+
const normForeignKey = (f) => to($ifNull(root().of(f).of(k1).with(field1).expr(), val(hash)));
|
|
1250
1373
|
return link()
|
|
1251
1374
|
.with($replaceWithDelta(field(map1(k1, root().expr()))))
|
|
1252
|
-
.with($set_(set()({ bId: ['bId',
|
|
1375
|
+
.with($set_(set()({ bId: ['bId', normForeignKey('before')], aId: ['aId', normForeignKey('after')] })))
|
|
1253
1376
|
.with($simpleLookup_({
|
|
1254
1377
|
coll,
|
|
1255
1378
|
k: 'a',
|
|
@@ -1282,16 +1405,22 @@ const $lookupDelta = ({ field1, field2 }, { coll, exec, input }, k1, k2, k, incl
|
|
|
1282
1405
|
return $ifNull(root().of(f).expr(), field(omit.backward(mergeExpr(omit.forward(map1(k2, otherField.of(k2).expr())), map1(k1, nil)))));
|
|
1283
1406
|
})
|
|
1284
1407
|
: link().stages)
|
|
1285
|
-
.with($unwindDelta(k1, k2, k,
|
|
1408
|
+
.with($unwindDelta(k1, k2, k, includeNull1, includeNull2)).stages;
|
|
1286
1409
|
};
|
|
1287
1410
|
|
|
1288
1411
|
const $lookupRaw = ({ field1, field2 }, { coll, exec, input }, k2, k, includeNull) => (f) => {
|
|
1289
|
-
root().of('_id').expr();
|
|
1412
|
+
const left = root().of('_id').expr();
|
|
1413
|
+
const right = root().of(k2).of('_id').expr();
|
|
1414
|
+
const keepLeft = k === 'left';
|
|
1415
|
+
const composedId = typeof k === 'string' ? right : concat(left, val(k[1]), right);
|
|
1290
1416
|
return link()
|
|
1291
1417
|
.with($simpleLookup1({
|
|
1292
1418
|
coll,
|
|
1293
1419
|
k: k2,
|
|
1294
|
-
fields: {
|
|
1420
|
+
fields: {
|
|
1421
|
+
local: field1,
|
|
1422
|
+
foreign: root().of('before').with(field2),
|
|
1423
|
+
},
|
|
1295
1424
|
vars: {},
|
|
1296
1425
|
pipeline: link()
|
|
1297
1426
|
.with(input)
|
|
@@ -1299,8 +1428,11 @@ const $lookupRaw = ({ field1, field2 }, { coll, exec, input }, k2, k, includeNul
|
|
|
1299
1428
|
.with($replaceWith_(root().of('before').expr())).stages,
|
|
1300
1429
|
})(f))
|
|
1301
1430
|
.with($unwind1(k2, includeNull)(f))
|
|
1302
|
-
.with(
|
|
1303
|
-
).stages
|
|
1431
|
+
.with(keepLeft
|
|
1432
|
+
? link().stages
|
|
1433
|
+
: $set1(set()({
|
|
1434
|
+
_id: ['_id', to($ifNull(composedId, left))],
|
|
1435
|
+
}))(f)).stages;
|
|
1304
1436
|
};
|
|
1305
1437
|
|
|
1306
1438
|
const asBefore = (f) => f(() => root().of('before'));
|
|
@@ -1496,9 +1628,11 @@ const runCont = async (it, cb) => {
|
|
|
1496
1628
|
};
|
|
1497
1629
|
|
|
1498
1630
|
const merge = ({ lsource: L, rsource: R, }) => mergeIterators({ sources: { L, R } });
|
|
1499
|
-
const join = ({ lField, rField, left, right, as }, leftSnapshot, rightSnapshot, stagesUntilNextLookup, outerLeft) => {
|
|
1631
|
+
const join = ({ lField, rField, left, right, as, toMany }, leftSnapshot, rightSnapshot, stagesUntilNextLookup, outerLeft, middle = '.') => {
|
|
1500
1632
|
const rightJoinField = { field1: lField, field2: rField };
|
|
1501
|
-
const joinId = '
|
|
1633
|
+
const joinId = rField.str() === '_id' && toMany !== true
|
|
1634
|
+
? 'left'
|
|
1635
|
+
: ['left', middle, 'right'];
|
|
1502
1636
|
const joinR_Snapshot = asBefore($lookupRaw(rightJoinField, rightSnapshot, as, joinId, outerLeft));
|
|
1503
1637
|
const resultingSnapshot = concatTStages(leftSnapshot, joinR_Snapshot);
|
|
1504
1638
|
const dict = { [as]: 'a' };
|
|
@@ -1510,7 +1644,10 @@ const join = ({ lField, rField, left, right, as }, leftSnapshot, rightSnapshot,
|
|
|
1510
1644
|
const leftJoinField = { field1: rField, field2: lField };
|
|
1511
1645
|
const joinL_Delta = $lookupDelta(leftJoinField, leftSnapshot, 'right', 'left', joinId, outerLeft);
|
|
1512
1646
|
const joinR_Delta = $lookupDelta(rightJoinField, rightSnapshot, 'left', 'right', joinId, undefined, outerLeft);
|
|
1513
|
-
const mergeForeignIntoDoc = concatStages($replaceWithDelta(mergeObjects(root().of('left').expr(), fieldM({
|
|
1647
|
+
const mergeForeignIntoDoc = concatStages($replaceWithDelta(mergeObjects(root().of('left').expr(), fieldM({
|
|
1648
|
+
a: root().of('right').expr(),
|
|
1649
|
+
b: root().of('_id').expr(),
|
|
1650
|
+
}, dictId))), stagesUntilNextLookup.delta);
|
|
1514
1651
|
const lRunnerInput = concatStages(joinR_Delta, mergeForeignIntoDoc);
|
|
1515
1652
|
const rRunnerInput = concatStages(joinL_Delta, mergeForeignIntoDoc);
|
|
1516
1653
|
const getRunner = (f, stages, final) => f.out({
|
|
@@ -1539,6 +1676,7 @@ const $lookup = (p) => (l) => $lookup1({
|
|
|
1539
1676
|
lField: p.localField,
|
|
1540
1677
|
rField: p.foreignField,
|
|
1541
1678
|
left: l(emptyDelta()),
|
|
1679
|
+
toMany: p.toMany,
|
|
1542
1680
|
});
|
|
1543
1681
|
const $outerLookup = (p) => (l) => $lookup1({
|
|
1544
1682
|
right: p.from,
|
|
@@ -1546,28 +1684,9 @@ const $outerLookup = (p) => (l) => $lookup1({
|
|
|
1546
1684
|
lField: p.localField,
|
|
1547
1685
|
rField: p.foreignField,
|
|
1548
1686
|
left: l(emptyDelta()),
|
|
1687
|
+
toMany: p.toMany,
|
|
1549
1688
|
}, null);
|
|
1550
1689
|
|
|
1551
|
-
const filterUndefined = (applyOperator) => {
|
|
1552
|
-
return (op, args) => applyOperator(op, args.filter(defined));
|
|
1553
|
-
};
|
|
1554
|
-
const combine = (op, applyOperatorToPreItems) => (...args) => {
|
|
1555
|
-
const mapToItems = applyOperatorToPreItems(op, args);
|
|
1556
|
-
return mapToItems && { raw: field => mapToItems(x => x.raw(field)) };
|
|
1557
|
-
};
|
|
1558
|
-
const all = (op, x) => x.length === 0 ? undefined : f => ({ [op]: x.map(f) });
|
|
1559
|
-
const first = (op, x) => (x.length === 1 ? f => f(x[0]) : all(op, x));
|
|
1560
|
-
const $and = combine('$and', filterUndefined(first));
|
|
1561
|
-
const $nor = combine('$nor', filterUndefined(all));
|
|
1562
|
-
const $or = combine('$or', filterUndefined(first));
|
|
1563
|
-
|
|
1564
|
-
const $matchDelta = (query) => concatStages($replaceWithDelta(ite(query, root().expr(), nil)), $match_($or(root().of('after').has($ne(null)), root().of('before').has($ne(null)))));
|
|
1565
|
-
|
|
1566
|
-
const $match = (query) => ({
|
|
1567
|
-
raw: $match1($expr(query)),
|
|
1568
|
-
delta: $matchDelta(query),
|
|
1569
|
-
});
|
|
1570
|
-
|
|
1571
1690
|
const $setCore = (updater) => ({
|
|
1572
1691
|
delta: $setDelta(updater),
|
|
1573
1692
|
raw: $set1(updater),
|
|
@@ -1588,7 +1707,10 @@ const $insertX = (out, expr, map, ext, extExpr, update = x => x) => {
|
|
|
1588
1707
|
teardown: c => c({
|
|
1589
1708
|
collection: out,
|
|
1590
1709
|
method: 'updateMany',
|
|
1591
|
-
params: [
|
|
1710
|
+
params: [
|
|
1711
|
+
filter,
|
|
1712
|
+
[{ $set: { deletedAt: '$$NOW', touchedAt: '$$CLUSTER_TIME' } }],
|
|
1713
|
+
],
|
|
1592
1714
|
}),
|
|
1593
1715
|
raw: () => {
|
|
1594
1716
|
const replacer = map(mergeObjects(expr, field(mergeExpr(extExpr, {
|
|
@@ -1612,7 +1734,10 @@ const $insertPart = (out, ext) => {
|
|
|
1612
1734
|
const extExpr = mapExact(ext, (v) => val(v));
|
|
1613
1735
|
return $insertX(out, assertNotNull(root().of('after').expr()), x => ite(eq(root().of('after').expr())(nil), field(mergeExpr(translateOmit().forward(extExpr), {
|
|
1614
1736
|
deletedAt: ['deletedAt', current],
|
|
1615
|
-
_id: [
|
|
1737
|
+
_id: [
|
|
1738
|
+
'_id',
|
|
1739
|
+
assertNotNull(root().of('before').of('_id').expr()),
|
|
1740
|
+
],
|
|
1616
1741
|
touchedAt: ['touchedAt', current],
|
|
1617
1742
|
})), x), ext, extExpr);
|
|
1618
1743
|
};
|
|
@@ -1628,8 +1753,7 @@ const aggregate = (db, streamName, input, snapshot = true, start = Date.now()) =
|
|
|
1628
1753
|
};
|
|
1629
1754
|
log('exec', streamName, req);
|
|
1630
1755
|
const start2 = Date.now();
|
|
1631
|
-
return db
|
|
1632
|
-
.then(d => d.command(req))
|
|
1756
|
+
return db.command(req)
|
|
1633
1757
|
.then(result => {
|
|
1634
1758
|
log('prepare', streamName, Date.now() - start);
|
|
1635
1759
|
log('prepare2', streamName, start2 - start);
|
|
@@ -1860,6 +1984,19 @@ const actions = {
|
|
|
1860
1984
|
],
|
|
1861
1985
|
};
|
|
1862
1986
|
|
|
1987
|
+
const filterUndefined = (applyOperator) => {
|
|
1988
|
+
return (op, args) => applyOperator(op, args.filter(defined));
|
|
1989
|
+
};
|
|
1990
|
+
const combine = (op, applyOperatorToPreItems) => (...args) => {
|
|
1991
|
+
const mapToItems = applyOperatorToPreItems(op, args);
|
|
1992
|
+
return mapToItems && { raw: field => mapToItems(x => x.raw(field)) };
|
|
1993
|
+
};
|
|
1994
|
+
const all = (op, x) => x.length === 0 ? undefined : f => ({ [op]: x.map(f) });
|
|
1995
|
+
const first = (op, x) => (x.length === 1 ? f => f(x[0]) : all(op, x));
|
|
1996
|
+
const $and = combine('$and', filterUndefined(first));
|
|
1997
|
+
const $nor = combine('$nor', filterUndefined(all));
|
|
1998
|
+
const $or = combine('$or', filterUndefined(first));
|
|
1999
|
+
|
|
1863
2000
|
const previous = (ts) => new Timestamp({ t: ts.high - 60, i: 0 });
|
|
1864
2001
|
const getFirstStages = (view, needs) => {
|
|
1865
2002
|
const { projection, hardMatch: pre, match } = view;
|
|
@@ -1880,54 +2017,9 @@ const getFirstStages = (view, needs) => {
|
|
|
1880
2017
|
return { firstStages, hardMatch };
|
|
1881
2018
|
};
|
|
1882
2019
|
|
|
1883
|
-
require('dotenv').config();
|
|
1884
|
-
const uri = process.env['MONGO_URL'];
|
|
1885
|
-
|
|
1886
|
-
const enablePreAndPostImages = (coll) => coll.s.db.command({
|
|
1887
|
-
collMod: coll.collectionName,
|
|
1888
|
-
changeStreamPreAndPostImages: { enabled: true },
|
|
1889
|
-
});
|
|
1890
|
-
const prepare = async (testName) => {
|
|
1891
|
-
const client = new MongoClient(uri, testName ? { monitorCommands: true } : {});
|
|
1892
|
-
if (testName) {
|
|
1893
|
-
const handler = (c) => {
|
|
1894
|
-
writeFile(`./out/${testName}.log`, JSON.stringify(c.command) + ',\n', { flag: 'w' });
|
|
1895
|
-
};
|
|
1896
|
-
client.on('commandStarted', handler);
|
|
1897
|
-
client.on('commandSucceeded', handler);
|
|
1898
|
-
}
|
|
1899
|
-
await client.connect();
|
|
1900
|
-
await client.db('admin').command({
|
|
1901
|
-
setClusterParameter: {
|
|
1902
|
-
changeStreamOptions: {
|
|
1903
|
-
preAndPostImages: { expireAfterSeconds: 60 },
|
|
1904
|
-
},
|
|
1905
|
-
},
|
|
1906
|
-
});
|
|
1907
|
-
return client;
|
|
1908
|
-
};
|
|
1909
|
-
const makeCol = async (docs, database, name) => {
|
|
1910
|
-
if (!name) {
|
|
1911
|
-
(name = crypto.randomUUID());
|
|
1912
|
-
}
|
|
1913
|
-
try {
|
|
1914
|
-
const col = await database.createCollection(name, {
|
|
1915
|
-
changeStreamPreAndPostImages: { enabled: true },
|
|
1916
|
-
});
|
|
1917
|
-
if (docs.length)
|
|
1918
|
-
await col.insertMany([...docs]);
|
|
1919
|
-
return col;
|
|
1920
|
-
}
|
|
1921
|
-
catch {
|
|
1922
|
-
return database.collection(name);
|
|
1923
|
-
}
|
|
1924
|
-
};
|
|
1925
|
-
|
|
1926
2020
|
const streamNames = {};
|
|
1927
2021
|
const executes$2 = (view, input, streamName, skip = false, after, needs = {}) => {
|
|
1928
2022
|
const { collection, projection, match } = view;
|
|
1929
|
-
const client = prepare();
|
|
1930
|
-
const pdb = client.then(cl => cl.db(collection.dbName));
|
|
1931
2023
|
const { firstStages, hardMatch } = getFirstStages(view, needs);
|
|
1932
2024
|
const db = collection.s.db, coll = collection.collectionName;
|
|
1933
2025
|
const hash = crypto$1
|
|
@@ -2065,7 +2157,7 @@ const executes$2 = (view, input, streamName, skip = false, after, needs = {}) =>
|
|
|
2065
2157
|
whenNotMatched: 'insert',
|
|
2066
2158
|
})).stages;
|
|
2067
2159
|
const stream = await makeStream();
|
|
2068
|
-
const r = await aggregate(
|
|
2160
|
+
const r = await aggregate(db, streamName, c => c({ coll: collection, input: cloneIntoNew }));
|
|
2069
2161
|
const start = Date.now();
|
|
2070
2162
|
const res = await snapshotCollection.deleteMany({ updated: true, after: null, before: null });
|
|
2071
2163
|
log('deleting from cloned into new collection', Date.now() - start, res, `db['${snapshotCollection.collectionName}'].deleteMany({ updated: true, after: null, before: null })`);
|
|
@@ -2079,7 +2171,7 @@ const executes$2 = (view, input, streamName, skip = false, after, needs = {}) =>
|
|
|
2079
2171
|
const stages = finalInput.raw(first);
|
|
2080
2172
|
await last.updateOne({ _id: streamName }, { $set: { job: 1 } }, { upsert: true });
|
|
2081
2173
|
const nextRes = stream.tryNext();
|
|
2082
|
-
const aggResult = await aggregate(
|
|
2174
|
+
const aggResult = await aggregate(db, streamName, c => c({
|
|
2083
2175
|
coll: snapshotCollection,
|
|
2084
2176
|
input: link()
|
|
2085
2177
|
.with($match_(root().of('updated').has($eq(true))))
|
|
@@ -2161,8 +2253,6 @@ const executes$1 = (view, input, streamName, needs) => {
|
|
|
2161
2253
|
else if (streamNames[streamName] != hash)
|
|
2162
2254
|
throw new Error('streamName already used');
|
|
2163
2255
|
const { collection, projection, hardMatch: pre, match } = view;
|
|
2164
|
-
const client = prepare();
|
|
2165
|
-
const pdb = client.then(cl => cl.db(collection.dbName));
|
|
2166
2256
|
const removeNotYetSynchronizedFields = projection &&
|
|
2167
2257
|
Object.values(mapExactToObject(projection, (_, k) => (needs[k] ?? k.startsWith('_')) ? root().of(k).has($exists(true)) : null));
|
|
2168
2258
|
const hardMatch = removeNotYetSynchronizedFields
|
|
@@ -2256,7 +2346,7 @@ const executes$1 = (view, input, streamName, needs) => {
|
|
|
2256
2346
|
const raw = stages(lastTS).with(finalInput.raw(lastTS === null)).stages;
|
|
2257
2347
|
const stream = await makeStream();
|
|
2258
2348
|
const nextRes = stream.tryNext();
|
|
2259
|
-
const aggResult = await aggregate(
|
|
2349
|
+
const aggResult = await aggregate(db, streamName, c => c({
|
|
2260
2350
|
coll: collection,
|
|
2261
2351
|
input: raw,
|
|
2262
2352
|
}));
|
|
@@ -2299,4 +2389,47 @@ const executes = (view, input, needs) => {
|
|
|
2299
2389
|
};
|
|
2300
2390
|
const single = (view, needs = {}) => pipe(input => executes(view, input, needs), emptyDelta(), concatDelta, emptyDelta);
|
|
2301
2391
|
|
|
2302
|
-
|
|
2392
|
+
require('dotenv').config();
|
|
2393
|
+
const uri = process.env['MONGO_URL'];
|
|
2394
|
+
|
|
2395
|
+
const enablePreAndPostImages = (coll) => coll.s.db.command({
|
|
2396
|
+
collMod: coll.collectionName,
|
|
2397
|
+
changeStreamPreAndPostImages: { enabled: true },
|
|
2398
|
+
});
|
|
2399
|
+
const prepare = async (testName) => {
|
|
2400
|
+
const client = new MongoClient(uri, testName ? { monitorCommands: true } : {});
|
|
2401
|
+
if (testName) {
|
|
2402
|
+
const handler = (c) => {
|
|
2403
|
+
writeFile(`./out/${testName}.log`, JSON.stringify(c.command) + ',\n', { flag: 'w' });
|
|
2404
|
+
};
|
|
2405
|
+
client.on('commandStarted', handler);
|
|
2406
|
+
client.on('commandSucceeded', handler);
|
|
2407
|
+
}
|
|
2408
|
+
await client.connect();
|
|
2409
|
+
await client.db('admin').command({
|
|
2410
|
+
setClusterParameter: {
|
|
2411
|
+
changeStreamOptions: {
|
|
2412
|
+
preAndPostImages: { expireAfterSeconds: 60 },
|
|
2413
|
+
},
|
|
2414
|
+
},
|
|
2415
|
+
});
|
|
2416
|
+
return client;
|
|
2417
|
+
};
|
|
2418
|
+
const makeCol = async (docs, database, name) => {
|
|
2419
|
+
if (!name) {
|
|
2420
|
+
(name = crypto.randomUUID());
|
|
2421
|
+
}
|
|
2422
|
+
try {
|
|
2423
|
+
const col = await database.createCollection(name, {
|
|
2424
|
+
changeStreamPreAndPostImages: { enabled: true },
|
|
2425
|
+
});
|
|
2426
|
+
if (docs.length)
|
|
2427
|
+
await col.insertMany([...docs]);
|
|
2428
|
+
return col;
|
|
2429
|
+
}
|
|
2430
|
+
catch {
|
|
2431
|
+
return database.collection(name);
|
|
2432
|
+
}
|
|
2433
|
+
};
|
|
2434
|
+
|
|
2435
|
+
export { $accumulator, $and, $countDict, $countDictArray, $entries, $eq, $exists, $expr, $getField, $group, $groupId, $groupMerge, $group_, $gt, $gtTs, $gte, $gteTs, $ifNull, $in, $insert, $insertPart, $insertX, $keys, $let, $lookup, $lt, $lte, $map, $map0, $map1, $match, $matchDelta, $merge, $merge2, $mergeId, $mergePart, $merge_, $ne, $nin, $nor, $or, $outerLookup, $pushDict, $rand, $reduce, $replaceWith, $set, $simpleInsert, $simpleMerge, $simpleMergePart, $substr, $sum, $type, $unwind, $unwindDelta, Field, Machine, add, and, anyElementTrue, array, ceil, comp, concat, concatArray, createIndex, ctx, current, dateAdd, dateDiff, dateLt, datePart, dayAndMonthPart, divide, emptyDelta, enablePreAndPostImages, eq, eqTyped, except, exprMapVal, field, fieldF, fieldM, filter, filterDefined, first$1 as first, firstSure, floor, from, func, getWhenMatched, getWhenMatchedForMerge, gt, gte, inArray, isArray, ite, joinIdOf, last, log, lt, lte, makeCol, map1, mapFromPlainDateTime, mapToPlainDateTime, mapVal, matchDelta, max, maxDate, mergeExact, mergeExact0, mergeExpr, mergeObjects, minDate, monthPart, multiply, ne, nil, noop, not, notNull, now, or, padLeft, pair, prepare, rand, range, regex, root, set, setField, single, size, slice, sortArray, staging, startOf, str, sub, subtract, to, toInt, val, weekPart, wrap, year };
|