datafolder 1.0.20 → 1.0.22
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/README.md +1 -27
- package/package.json +1 -1
- package/static/codemirror.bundle/editor.bundle.js +109 -60
- package/test/testQyAQL.js +451 -0
package/README.md
CHANGED
|
@@ -4,33 +4,7 @@ datafolder is a lightning fluid JSON database for high speed SSDs, featuring an
|
|
|
4
4
|
|
|
5
5
|
### Changelog
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
1.0.19 Add DocumentDB test case (test/testQyDocumentDB.js)
|
|
10
|
-
|
|
11
|
-
1.0.18 Add Embedding Storage Support. Related APIs: dataFolder.insertEmbedding() and dataFolder.delEmbedding(). TODO: Add HNSW support and vector query.
|
|
12
|
-
|
|
13
|
-
1.0.17 Add Northwind sample data test (test/testQyNorthwind.js)
|
|
14
|
-
|
|
15
|
-
1.0.16 Add GraphQL sample data test (test/testQyGraphQL.js) to show how to write complex queries.
|
|
16
|
-
|
|
17
|
-
1.0.15 Use [Workers](https://nodejs.org/docs/latest/api/worker_threads.html) to implement snapshot compactor, expire service, outdated data cleaner etc.
|
|
18
|
-
|
|
19
|
-
1.0.14 Expire supports specifying milliseconds.
|
|
20
|
-
|
|
21
|
-
1.0.13 Added expire support to delete a dir in a specific future time.
|
|
22
|
-
|
|
23
|
-
1.0.12 Added expire support to expire a file or some properties of the JSON data in future.
|
|
24
|
-
|
|
25
|
-
1.0.11 Added [SKILL.md](https://github.com/datafolderdev/datafolder/blob/main/SKILL.md) for feeding to AI
|
|
26
|
-
|
|
27
|
-
1.0.10 Added document [datafolder for high speed SSDs](https://github.com/datafolderdev/datafolder/wiki/datafolder-for-high-speed-SSDs)
|
|
28
|
-
|
|
29
|
-
1.0.9 Added Typescript type declarations.
|
|
30
|
-
|
|
31
|
-
1.0.7 Added trigger editor and [Internals of datafolder](https://github.com/datafolderdev/datafolder/wiki/Internals-of-datafolder)
|
|
32
|
-
|
|
33
|
-
1.0.6 Added code mirror as the query editor.
|
|
7
|
+
[ChangeLog] (ChangeLog.md)
|
|
34
8
|
|
|
35
9
|
## Installation
|
|
36
10
|
|
package/package.json
CHANGED
|
@@ -1344,10 +1344,18 @@
|
|
|
1344
1344
|
/**
|
|
1345
1345
|
The upper boundary of the range.
|
|
1346
1346
|
*/
|
|
1347
|
-
to, flags
|
|
1347
|
+
to, flags,
|
|
1348
|
+
/**
|
|
1349
|
+
The goal column (stored vertical offset) associated with a
|
|
1350
|
+
cursor. This is used to preserve the vertical position when
|
|
1351
|
+
[moving](https://codemirror.net/6/docs/ref/#view.EditorView.moveVertically) across
|
|
1352
|
+
lines of different length.
|
|
1353
|
+
*/
|
|
1354
|
+
goalColumn) {
|
|
1348
1355
|
this.from = from;
|
|
1349
1356
|
this.to = to;
|
|
1350
1357
|
this.flags = flags;
|
|
1358
|
+
this.goalColumn = goalColumn;
|
|
1351
1359
|
}
|
|
1352
1360
|
/**
|
|
1353
1361
|
The anchor of the range—the side that doesn't move when you
|
|
@@ -1371,6 +1379,16 @@
|
|
|
1371
1379
|
*/
|
|
1372
1380
|
get assoc() { return this.flags & 8 /* RangeFlag.AssocBefore */ ? -1 : this.flags & 16 /* RangeFlag.AssocAfter */ ? 1 : 0; }
|
|
1373
1381
|
/**
|
|
1382
|
+
A flag that, when set, makes some selection-extending commands
|
|
1383
|
+
treat the range's head and anchor as exchangeable, so that for
|
|
1384
|
+
example Shift-ArrowUp will make the lower side of the selection
|
|
1385
|
+
the anchor, even if that was the head before. Used to implement
|
|
1386
|
+
MacOS-style undirectional selections.
|
|
1387
|
+
*/
|
|
1388
|
+
get undirectional() {
|
|
1389
|
+
return (this.flags & 64 /* RangeFlag.Undirectional */) > 0;
|
|
1390
|
+
}
|
|
1391
|
+
/**
|
|
1374
1392
|
The bidirectional text level associated with this cursor, if
|
|
1375
1393
|
any.
|
|
1376
1394
|
*/
|
|
@@ -1379,16 +1397,6 @@
|
|
|
1379
1397
|
return level == 7 ? null : level;
|
|
1380
1398
|
}
|
|
1381
1399
|
/**
|
|
1382
|
-
The goal column (stored vertical offset) associated with a
|
|
1383
|
-
cursor. This is used to preserve the vertical position when
|
|
1384
|
-
[moving](https://codemirror.net/6/docs/ref/#view.EditorView.moveVertically) across
|
|
1385
|
-
lines of different length.
|
|
1386
|
-
*/
|
|
1387
|
-
get goalColumn() {
|
|
1388
|
-
let value = this.flags >> 6 /* RangeFlag.GoalColumnOffset */;
|
|
1389
|
-
return value == 16777215 /* RangeFlag.NoGoalColumn */ ? undefined : value;
|
|
1390
|
-
}
|
|
1391
|
-
/**
|
|
1392
1400
|
Map this range through a change, producing a valid range in the
|
|
1393
1401
|
updated document.
|
|
1394
1402
|
*/
|
|
@@ -1401,7 +1409,7 @@
|
|
|
1401
1409
|
from = change.mapPos(this.from, 1);
|
|
1402
1410
|
to = change.mapPos(this.to, -1);
|
|
1403
1411
|
}
|
|
1404
|
-
return from == this.from && to == this.to ? this : new SelectionRange(from, to, this.flags);
|
|
1412
|
+
return from == this.from && to == this.to ? this : new SelectionRange(from, to, this.flags, this.goalColumn);
|
|
1405
1413
|
}
|
|
1406
1414
|
/**
|
|
1407
1415
|
Extend this range to cover at least `from` to `to`.
|
|
@@ -1435,8 +1443,8 @@
|
|
|
1435
1443
|
/**
|
|
1436
1444
|
@internal
|
|
1437
1445
|
*/
|
|
1438
|
-
static create(from, to, flags) {
|
|
1439
|
-
return new SelectionRange(from, to, flags);
|
|
1446
|
+
static create(from, to, flags, goalColumn) {
|
|
1447
|
+
return new SelectionRange(from, to, flags, goalColumn);
|
|
1440
1448
|
}
|
|
1441
1449
|
}
|
|
1442
1450
|
/**
|
|
@@ -1551,19 +1559,26 @@
|
|
|
1551
1559
|
*/
|
|
1552
1560
|
static cursor(pos, assoc = 0, bidiLevel, goalColumn) {
|
|
1553
1561
|
return SelectionRange.create(pos, pos, (assoc == 0 ? 0 : assoc < 0 ? 8 /* RangeFlag.AssocBefore */ : 16 /* RangeFlag.AssocAfter */) |
|
|
1554
|
-
(bidiLevel == null ? 7 : Math.min(6, bidiLevel))
|
|
1555
|
-
((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 16777215 /* RangeFlag.NoGoalColumn */) << 6 /* RangeFlag.GoalColumnOffset */));
|
|
1562
|
+
(bidiLevel == null ? 7 : Math.min(6, bidiLevel)), goalColumn);
|
|
1556
1563
|
}
|
|
1557
1564
|
/**
|
|
1558
1565
|
Create a selection range.
|
|
1559
1566
|
*/
|
|
1560
1567
|
static range(anchor, head, goalColumn, bidiLevel, assoc) {
|
|
1561
|
-
let flags =
|
|
1562
|
-
(bidiLevel == null ? 7 : Math.min(6, bidiLevel));
|
|
1568
|
+
let flags = bidiLevel == null ? 7 : Math.min(6, bidiLevel);
|
|
1563
1569
|
if (!assoc && anchor != head)
|
|
1564
1570
|
assoc = head < anchor ? 1 : -1;
|
|
1565
|
-
|
|
1566
|
-
|
|
1571
|
+
if (assoc)
|
|
1572
|
+
flags |= assoc < 0 ? 8 /* RangeFlag.AssocBefore */ : 16 /* RangeFlag.AssocAfter */;
|
|
1573
|
+
return head < anchor ? SelectionRange.create(head, anchor, flags | 32 /* RangeFlag.Inverted */, goalColumn)
|
|
1574
|
+
: SelectionRange.create(anchor, head, flags, goalColumn);
|
|
1575
|
+
}
|
|
1576
|
+
/**
|
|
1577
|
+
Create an [undirectional](https://codemirror.net/6/docs/ref/#state.SelectionRange.undirectional)
|
|
1578
|
+
selection range.
|
|
1579
|
+
*/
|
|
1580
|
+
static undirectionalRange(from, to) {
|
|
1581
|
+
return SelectionRange.create(from, to, 64 /* RangeFlag.Undirectional */, undefined);
|
|
1567
1582
|
}
|
|
1568
1583
|
/**
|
|
1569
1584
|
@internal
|
|
@@ -7070,8 +7085,9 @@
|
|
|
7070
7085
|
}
|
|
7071
7086
|
}
|
|
7072
7087
|
});
|
|
7073
|
-
b.addLineStartIfNotCovered(pendingLineAttrs);
|
|
7074
7088
|
this.openWidget = openEnd > markCount;
|
|
7089
|
+
if (!this.openWidget)
|
|
7090
|
+
b.addLineStartIfNotCovered(pendingLineAttrs);
|
|
7075
7091
|
this.openMarks = openEnd;
|
|
7076
7092
|
}
|
|
7077
7093
|
forward(from, to, side = 1) {
|
|
@@ -7896,7 +7912,7 @@
|
|
|
7896
7912
|
break;
|
|
7897
7913
|
to = next;
|
|
7898
7914
|
}
|
|
7899
|
-
return EditorSelection.
|
|
7915
|
+
return EditorSelection.undirectionalRange(from + line.from, to + line.from);
|
|
7900
7916
|
}
|
|
7901
7917
|
function posAtCoordsImprecise(view, contentRect, block, x, y) {
|
|
7902
7918
|
let into = Math.round((x - contentRect.left) * view.defaultCharacterWidth);
|
|
@@ -8033,8 +8049,12 @@
|
|
|
8033
8049
|
else {
|
|
8034
8050
|
let from = skipAtomicRanges(atoms, range.from, -1);
|
|
8035
8051
|
let to = skipAtomicRanges(atoms, range.to, 1);
|
|
8036
|
-
if (from != range.from || to != range.to)
|
|
8037
|
-
|
|
8052
|
+
if (from != range.from || to != range.to) {
|
|
8053
|
+
if (range.undirectional)
|
|
8054
|
+
updated = EditorSelection.undirectionalRange(range.from, range.to);
|
|
8055
|
+
else
|
|
8056
|
+
updated = EditorSelection.range(range.from == range.anchor ? from : to, range.from == range.head ? from : to);
|
|
8057
|
+
}
|
|
8038
8058
|
}
|
|
8039
8059
|
if (updated) {
|
|
8040
8060
|
if (!ranges)
|
|
@@ -8440,7 +8460,8 @@
|
|
|
8440
8460
|
// Chrome will put the selection *inside* them, confusing
|
|
8441
8461
|
// posFromDOM
|
|
8442
8462
|
let vp = view.viewport;
|
|
8443
|
-
if ((browser.ios || browser.chrome) &&
|
|
8463
|
+
if ((browser.ios || browser.chrome) && head != anchor &&
|
|
8464
|
+
Math.min(head, anchor) <= curSel.main.from && Math.max(head, anchor) >= curSel.main.to &&
|
|
8444
8465
|
(vp.from > 0 || vp.to < view.state.doc.length)) {
|
|
8445
8466
|
let from = Math.min(head, anchor), to = Math.max(head, anchor);
|
|
8446
8467
|
let offFrom = vp.from - from, offTo = vp.to - to;
|
|
@@ -8761,13 +8782,11 @@
|
|
|
8761
8782
|
// (after which we retroactively handle them and reset the DOM) to
|
|
8762
8783
|
// avoid messing up the virtual keyboard state.
|
|
8763
8784
|
this.pendingIOSKey = undefined;
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
8767
|
-
|
|
8768
|
-
|
|
8769
|
-
otherwise handled.
|
|
8770
|
-
*/
|
|
8785
|
+
// When enabled (>-1), tab presses are not given to key handlers,
|
|
8786
|
+
// leaving the browser's default behavior. If >0, the mode expires
|
|
8787
|
+
// at that timestamp, and any other keypress clears it.
|
|
8788
|
+
// Esc enables temporary tab focus mode for two seconds when not
|
|
8789
|
+
// otherwise handled.
|
|
8771
8790
|
this.tabFocusMode = -1;
|
|
8772
8791
|
this.lastSelectionOrigin = null;
|
|
8773
8792
|
this.lastSelectionTime = 0;
|
|
@@ -8875,11 +8894,16 @@
|
|
|
8875
8894
|
// state. So we let it go through, and then, in
|
|
8876
8895
|
// applyDOMChange, notify key handlers of it and reset to
|
|
8877
8896
|
// the state they produce.
|
|
8878
|
-
|
|
8879
|
-
|
|
8880
|
-
((pending = PendingKeys.find(key => key.keyCode == event.keyCode)) && !event.ctrlKey ||
|
|
8897
|
+
if (browser.ios && !event.synthetic && !event.altKey && !event.metaKey &&
|
|
8898
|
+
(PendingKeys.some(key => key.keyCode == event.keyCode) && !event.ctrlKey ||
|
|
8881
8899
|
EmacsyPendingKeys.indexOf(event.key) > -1 && event.ctrlKey)) {
|
|
8882
|
-
|
|
8900
|
+
let mods = { ctrlKey: event.ctrlKey, altKey: event.altKey, metaKey: event.metaKey, shiftKey: event.shiftKey };
|
|
8901
|
+
// On iOS with autocapitalize, drop the shift modifier for these
|
|
8902
|
+
// keys, since it will be set at the start of every sentence.
|
|
8903
|
+
if (mods.shiftKey && browser.ios && !/^(off|none)$/.test(this.view.contentDOM.autocapitalize) &&
|
|
8904
|
+
iosVirtualKeyboardOpen(this.view.win))
|
|
8905
|
+
mods.shiftKey = false;
|
|
8906
|
+
this.pendingIOSKey = { key: event.key, keyCode: event.keyCode, mods };
|
|
8883
8907
|
setTimeout(() => this.flushIOSKey(), 250);
|
|
8884
8908
|
return true;
|
|
8885
8909
|
}
|
|
@@ -8895,7 +8919,7 @@
|
|
|
8895
8919
|
if (key.key == "Enter" && change && change.from < change.to && /^\S+$/.test(change.insert.toString()))
|
|
8896
8920
|
return false;
|
|
8897
8921
|
this.pendingIOSKey = undefined;
|
|
8898
|
-
return dispatchKey(this.view.contentDOM, key.key, key.keyCode, key
|
|
8922
|
+
return dispatchKey(this.view.contentDOM, key.key, key.keyCode, key.mods);
|
|
8899
8923
|
}
|
|
8900
8924
|
ignoreDuringComposition(event) {
|
|
8901
8925
|
if (!/^key/.test(event.type) || event.synthetic)
|
|
@@ -8933,6 +8957,11 @@
|
|
|
8933
8957
|
this.mouseSelection.destroy();
|
|
8934
8958
|
}
|
|
8935
8959
|
}
|
|
8960
|
+
function iosVirtualKeyboardOpen(win) {
|
|
8961
|
+
if (!win.visualViewport)
|
|
8962
|
+
return false;
|
|
8963
|
+
return win.visualViewport.height * win.visualViewport.scale / win.document.documentElement.clientHeight < 0.85;
|
|
8964
|
+
}
|
|
8936
8965
|
function bindHandler(plugin, handler) {
|
|
8937
8966
|
return (view, event) => {
|
|
8938
8967
|
try {
|
|
@@ -9252,7 +9281,7 @@
|
|
|
9252
9281
|
let from = visual ? visual.posAtStart : line.from, to = visual ? visual.posAtEnd : line.to;
|
|
9253
9282
|
if (to < view.state.doc.length && to == line.to)
|
|
9254
9283
|
to++;
|
|
9255
|
-
return EditorSelection.
|
|
9284
|
+
return EditorSelection.undirectionalRange(from, to);
|
|
9256
9285
|
}
|
|
9257
9286
|
}
|
|
9258
9287
|
const BadMouseDetail = browser.ie && browser.ie_version <= 11;
|
|
@@ -9310,7 +9339,7 @@
|
|
|
9310
9339
|
if (tile && tile.isWidget()) {
|
|
9311
9340
|
let from = tile.posAtStart, to = from + tile.length;
|
|
9312
9341
|
if (from >= range.to || to <= range.from)
|
|
9313
|
-
range = EditorSelection.
|
|
9342
|
+
range = EditorSelection.undirectionalRange(from, to);
|
|
9314
9343
|
}
|
|
9315
9344
|
}
|
|
9316
9345
|
let { inputState } = view;
|
|
@@ -11935,7 +11964,6 @@
|
|
|
11935
11964
|
for (let event in this.handlers)
|
|
11936
11965
|
context.addEventListener(event, this.handlers[event]);
|
|
11937
11966
|
this.measureReq = { read: view => {
|
|
11938
|
-
this.editContext.updateControlBounds(view.contentDOM.getBoundingClientRect());
|
|
11939
11967
|
let sel = getSelection(view.root);
|
|
11940
11968
|
if (sel && sel.rangeCount)
|
|
11941
11969
|
this.editContext.updateSelectionBounds(sel.getRangeAt(0).getBoundingClientRect());
|
|
@@ -18739,8 +18767,7 @@
|
|
|
18739
18767
|
}
|
|
18740
18768
|
/**
|
|
18741
18769
|
A subclass of [`Language`](https://codemirror.net/6/docs/ref/#language.Language) for use with Lezer
|
|
18742
|
-
[LR parsers](https://lezer.codemirror.net/docs/ref#lr.LRParser)
|
|
18743
|
-
parsers.
|
|
18770
|
+
[LR parsers](https://lezer.codemirror.net/docs/ref#lr.LRParser).
|
|
18744
18771
|
*/
|
|
18745
18772
|
class LRLanguage extends Language {
|
|
18746
18773
|
constructor(data, parser, name) {
|
|
@@ -19732,18 +19759,25 @@
|
|
|
19732
19759
|
if (tr.isUserEvent("delete"))
|
|
19733
19760
|
tr.changes.iterChangedRanges((fromA, toA) => folded = clearTouchedFolds(folded, fromA, toA));
|
|
19734
19761
|
folded = folded.map(tr.changes);
|
|
19762
|
+
let rangesToFold = [];
|
|
19735
19763
|
for (let e of tr.effects) {
|
|
19736
19764
|
if (e.is(foldEffect) && !foldExists(folded, e.value.from, e.value.to)) {
|
|
19737
|
-
|
|
19738
|
-
let widget = !preparePlaceholder ? foldWidget :
|
|
19739
|
-
Decoration.replace({ widget: new PreparedFoldWidget(preparePlaceholder(tr.state, e.value)) });
|
|
19740
|
-
folded = folded.update({ add: [widget.range(e.value.from, e.value.to)] });
|
|
19765
|
+
rangesToFold.push(e.value);
|
|
19741
19766
|
}
|
|
19742
19767
|
else if (e.is(unfoldEffect)) {
|
|
19743
19768
|
folded = folded.update({ filter: (from, to) => e.value.from != from || e.value.to != to,
|
|
19744
19769
|
filterFrom: e.value.from, filterTo: e.value.to });
|
|
19745
19770
|
}
|
|
19746
19771
|
}
|
|
19772
|
+
if (rangesToFold.length) {
|
|
19773
|
+
let { preparePlaceholder } = tr.state.facet(foldConfig);
|
|
19774
|
+
let decorations = rangesToFold.map(value => {
|
|
19775
|
+
let widget = !preparePlaceholder ? foldWidget :
|
|
19776
|
+
Decoration.replace({ widget: new PreparedFoldWidget(preparePlaceholder(tr.state, value)) });
|
|
19777
|
+
return widget.range(value.from, value.to);
|
|
19778
|
+
});
|
|
19779
|
+
folded = folded.update({ add: decorations });
|
|
19780
|
+
}
|
|
19747
19781
|
// Clear folded ranges that cover the selection head
|
|
19748
19782
|
if (tr.selection)
|
|
19749
19783
|
folded = clearTouchedFolds(folded, tr.selection.main.head);
|
|
@@ -20059,7 +20093,7 @@
|
|
|
20059
20093
|
or array of tags in their `tag` property, and either a single
|
|
20060
20094
|
`class` property providing a static CSS class (for highlighter
|
|
20061
20095
|
that rely on external styling), or a
|
|
20062
|
-
[`style-mod`](https://
|
|
20096
|
+
[`style-mod`](https://code.haverbeke.berlin/marijn/style-mod#documentation)-style
|
|
20063
20097
|
set of CSS properties (which define the styling for those tags).
|
|
20064
20098
|
|
|
20065
20099
|
The CSS rules created for a highlighter will be emitted in the
|
|
@@ -21146,8 +21180,10 @@
|
|
|
21146
21180
|
on, if any.
|
|
21147
21181
|
*/
|
|
21148
21182
|
const cursorMatchingBracket = ({ state, dispatch }) => toMatchingBracket(state, dispatch);
|
|
21149
|
-
function extendSel(target, how) {
|
|
21183
|
+
function extendSel(target, forward, how) {
|
|
21150
21184
|
let selection = updateSel(target.state.selection, range => {
|
|
21185
|
+
if (range.undirectional && (range.head >= range.anchor) != forward)
|
|
21186
|
+
range = EditorSelection.range(range.head, range.anchor);
|
|
21151
21187
|
let head = how(range);
|
|
21152
21188
|
return EditorSelection.range(range.anchor, head.head, head.goalColumn, head.bidiLevel || undefined, head.assoc);
|
|
21153
21189
|
});
|
|
@@ -21157,7 +21193,7 @@
|
|
|
21157
21193
|
return true;
|
|
21158
21194
|
}
|
|
21159
21195
|
function selectByChar(view, forward) {
|
|
21160
|
-
return extendSel(view, range => view.moveByChar(range, forward));
|
|
21196
|
+
return extendSel(view, forward, range => view.moveByChar(range, forward));
|
|
21161
21197
|
}
|
|
21162
21198
|
/**
|
|
21163
21199
|
Move the selection head one character to the left, while leaving
|
|
@@ -21169,7 +21205,7 @@
|
|
|
21169
21205
|
*/
|
|
21170
21206
|
const selectCharRight = view => selectByChar(view, ltrAtCursor(view));
|
|
21171
21207
|
function selectByGroup(view, forward) {
|
|
21172
|
-
return extendSel(view, range => view.moveByGroup(range, forward));
|
|
21208
|
+
return extendSel(view, forward, range => view.moveByGroup(range, forward));
|
|
21173
21209
|
}
|
|
21174
21210
|
/**
|
|
21175
21211
|
Move the selection head one [group](https://codemirror.net/6/docs/ref/#commands.cursorGroupLeft) to
|
|
@@ -21183,13 +21219,19 @@
|
|
|
21183
21219
|
/**
|
|
21184
21220
|
Move the selection head over the next syntactic element to the left.
|
|
21185
21221
|
*/
|
|
21186
|
-
const selectSyntaxLeft = view =>
|
|
21222
|
+
const selectSyntaxLeft = view => {
|
|
21223
|
+
let forward = !ltrAtCursor(view);
|
|
21224
|
+
return extendSel(view, forward, range => moveBySyntax(view.state, range, forward));
|
|
21225
|
+
};
|
|
21187
21226
|
/**
|
|
21188
21227
|
Move the selection head over the next syntactic element to the right.
|
|
21189
21228
|
*/
|
|
21190
|
-
const selectSyntaxRight = view =>
|
|
21229
|
+
const selectSyntaxRight = view => {
|
|
21230
|
+
let forward = ltrAtCursor(view);
|
|
21231
|
+
return extendSel(view, forward, range => moveBySyntax(view.state, range, forward));
|
|
21232
|
+
};
|
|
21191
21233
|
function selectByLine(view, forward) {
|
|
21192
|
-
return extendSel(view, range => view.moveVertically(range, forward));
|
|
21234
|
+
return extendSel(view, forward, range => view.moveVertically(range, forward));
|
|
21193
21235
|
}
|
|
21194
21236
|
/**
|
|
21195
21237
|
Move the selection head one line up.
|
|
@@ -21200,7 +21242,7 @@
|
|
|
21200
21242
|
*/
|
|
21201
21243
|
const selectLineDown = view => selectByLine(view, true);
|
|
21202
21244
|
function selectByPage(view, forward) {
|
|
21203
|
-
return extendSel(view, range => view.moveVertically(range, forward, pageInfo(view).height));
|
|
21245
|
+
return extendSel(view, forward, range => view.moveVertically(range, forward, pageInfo(view).height));
|
|
21204
21246
|
}
|
|
21205
21247
|
/**
|
|
21206
21248
|
Move the selection head one page up.
|
|
@@ -21213,27 +21255,33 @@
|
|
|
21213
21255
|
/**
|
|
21214
21256
|
Move the selection head to the next line boundary.
|
|
21215
21257
|
*/
|
|
21216
|
-
const selectLineBoundaryForward = view => extendSel(view, range => moveByLineBoundary(view, range, true));
|
|
21258
|
+
const selectLineBoundaryForward = view => extendSel(view, true, range => moveByLineBoundary(view, range, true));
|
|
21217
21259
|
/**
|
|
21218
21260
|
Move the selection head to the previous line boundary.
|
|
21219
21261
|
*/
|
|
21220
|
-
const selectLineBoundaryBackward = view => extendSel(view, range => moveByLineBoundary(view, range, false));
|
|
21262
|
+
const selectLineBoundaryBackward = view => extendSel(view, false, range => moveByLineBoundary(view, range, false));
|
|
21221
21263
|
/**
|
|
21222
21264
|
Move the selection head one line boundary to the left.
|
|
21223
21265
|
*/
|
|
21224
|
-
const selectLineBoundaryLeft = view =>
|
|
21266
|
+
const selectLineBoundaryLeft = view => {
|
|
21267
|
+
let forward = !ltrAtCursor(view);
|
|
21268
|
+
return extendSel(view, forward, range => moveByLineBoundary(view, range, forward));
|
|
21269
|
+
};
|
|
21225
21270
|
/**
|
|
21226
21271
|
Move the selection head one line boundary to the right.
|
|
21227
21272
|
*/
|
|
21228
|
-
const selectLineBoundaryRight = view =>
|
|
21273
|
+
const selectLineBoundaryRight = view => {
|
|
21274
|
+
let forward = ltrAtCursor(view);
|
|
21275
|
+
return extendSel(view, forward, range => moveByLineBoundary(view, range, forward));
|
|
21276
|
+
};
|
|
21229
21277
|
/**
|
|
21230
21278
|
Move the selection head to the start of the line.
|
|
21231
21279
|
*/
|
|
21232
|
-
const selectLineStart = view => extendSel(view, range => EditorSelection.cursor(view.lineBlockAt(range.head).from));
|
|
21280
|
+
const selectLineStart = view => extendSel(view, false, range => EditorSelection.cursor(view.lineBlockAt(range.head).from));
|
|
21233
21281
|
/**
|
|
21234
21282
|
Move the selection head to the end of the line.
|
|
21235
21283
|
*/
|
|
21236
|
-
const selectLineEnd = view => extendSel(view, range => EditorSelection.cursor(view.lineBlockAt(range.head).to));
|
|
21284
|
+
const selectLineEnd = view => extendSel(view, true, range => EditorSelection.cursor(view.lineBlockAt(range.head).to));
|
|
21237
21285
|
/**
|
|
21238
21286
|
Move the selection to the start of the document.
|
|
21239
21287
|
*/
|
|
@@ -22811,6 +22859,7 @@
|
|
|
22811
22859
|
else if (next.from == from && next.to == to) {
|
|
22812
22860
|
replacement = state.toText(query.getReplacement(next));
|
|
22813
22861
|
changes.push({ from: next.from, to: next.to, insert: replacement });
|
|
22862
|
+
next = query.nextMatch(state, next.from, next.to);
|
|
22814
22863
|
effects.push(EditorView.announce.of(state.phrase("replaced match on line $", state.doc.lineAt(from).number) + "."));
|
|
22815
22864
|
}
|
|
22816
22865
|
let changeSet = view.state.changes(changes);
|
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import { deepEqual } from "node:assert/strict";
|
|
3
|
+
import { QyDB } from "../src/QyDB.js";
|
|
4
|
+
import { logger } from "../src/QyLogger.js";
|
|
5
|
+
logger.level = "warn";
|
|
6
|
+
// FOR doc IN employees
|
|
7
|
+
// COLLECT department = doc.department
|
|
8
|
+
// AGGREGATE count = COUNT(doc)
|
|
9
|
+
// RETURN {department: department, employeeCount: count}
|
|
10
|
+
function query1(qyDB) {
|
|
11
|
+
deepEqual(Object.fromEntries(qyDB
|
|
12
|
+
.indexDir("employees", "department")
|
|
13
|
+
.fileList.map((file) => [file.name, Object.keys(file.fileContent).length])), { sales: 4, product: 2, "R&D": 2 });
|
|
14
|
+
}
|
|
15
|
+
// FOR doc IN employees
|
|
16
|
+
// LET isOlder = doc.age > 30
|
|
17
|
+
// LET isSales = doc.department == "sales"
|
|
18
|
+
// FILTER isOlder && isSales
|
|
19
|
+
// RETURN doc
|
|
20
|
+
function query2(qyDB) {
|
|
21
|
+
deepEqual(Object.fromEntries(qyDB.queryFiles("employees", { department: "sales", age: { $gt: 30 } }).map((x) => [x.name, x.view()])), {
|
|
22
|
+
employee_1_1: { age: 31, department: "sales" },
|
|
23
|
+
employee_1_5: { age: 36, department: "sales" },
|
|
24
|
+
employee_1_6: { age: 36, department: "sales" },
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function query3(qyDB) {
|
|
28
|
+
deepEqual(Object.fromEntries(qyDB
|
|
29
|
+
.queryFiles("employees", { $and: [{ department: "sales" }, { $and: [{ age: { $gt: 30, $lt: 60 } }] }] })
|
|
30
|
+
.map((x) => [x.name, x.view()])), {
|
|
31
|
+
employee_1_1: { age: 31, department: "sales" },
|
|
32
|
+
employee_1_5: { age: 36, department: "sales" },
|
|
33
|
+
employee_1_6: { age: 36, department: "sales" },
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function query4(qyDB) {
|
|
37
|
+
const result = {};
|
|
38
|
+
qyDB.fetch
|
|
39
|
+
.cd("employees")
|
|
40
|
+
.queryFiles(".", { department: "sales", age: { $gt: 30 } })
|
|
41
|
+
.run()[0]
|
|
42
|
+
.forEach((x) => (result[x.name] = x.view()));
|
|
43
|
+
deepEqual(result, {
|
|
44
|
+
employee_1_1: { age: 31, department: "sales" },
|
|
45
|
+
employee_1_5: { age: 36, department: "sales" },
|
|
46
|
+
employee_1_6: { age: 36, department: "sales" },
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function query5(qyDB) {
|
|
50
|
+
const result = {};
|
|
51
|
+
qyDB.fetch
|
|
52
|
+
.queryFiles("employees", {
|
|
53
|
+
department: (x) => x == "sales",
|
|
54
|
+
age: (x) => x > 30,
|
|
55
|
+
})
|
|
56
|
+
.run()[0]
|
|
57
|
+
.forEach((x) => (result[x.name] = x.view()));
|
|
58
|
+
deepEqual(result, {
|
|
59
|
+
employee_1_1: { age: 31, department: "sales" },
|
|
60
|
+
employee_1_5: { age: 36, department: "sales" },
|
|
61
|
+
employee_1_6: { age: 36, department: "sales" },
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function query6(qyDB) {
|
|
65
|
+
const result = qyDB
|
|
66
|
+
.queryFiles("employees", {
|
|
67
|
+
department: (x) => x != "sales",
|
|
68
|
+
age: (x) => x > 30,
|
|
69
|
+
})
|
|
70
|
+
.reduce((map, x) => {
|
|
71
|
+
map[x.name] = x.view();
|
|
72
|
+
return map;
|
|
73
|
+
}, {});
|
|
74
|
+
deepEqual(result, {
|
|
75
|
+
employee_1_9: { age: 46, department: "R&D" },
|
|
76
|
+
employee_1_2: { age: 38, department: "product" },
|
|
77
|
+
employee_1_7: { age: 36, department: "product" },
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function query7(qyDB) {
|
|
81
|
+
const result = {};
|
|
82
|
+
const queryObj = {
|
|
83
|
+
department: (x) => x == "sales",
|
|
84
|
+
age: (x) => x > 30,
|
|
85
|
+
};
|
|
86
|
+
["employees", "employees/*/*"].forEach((folderPath) => {
|
|
87
|
+
qyDB.fetch
|
|
88
|
+
.queryFiles(folderPath, queryObj)
|
|
89
|
+
.run()[0]
|
|
90
|
+
.forEach((x) => (result[x.fullPath] = x.view()));
|
|
91
|
+
});
|
|
92
|
+
deepEqual(result, {
|
|
93
|
+
"/employees/2024/12/employee_3_1": { department: "sales", age: 31 },
|
|
94
|
+
"/employees/2025/03/employee_2_1": { department: "sales", age: 31 },
|
|
95
|
+
"/employees/employee_1_1": { department: "sales", age: 31 },
|
|
96
|
+
"/employees/2024/12/employee_3_5": { department: "sales", age: 36 },
|
|
97
|
+
"/employees/2025/03/employee_2_5": { department: "sales", age: 36 },
|
|
98
|
+
"/employees/employee_1_5": { department: "sales", age: 36 },
|
|
99
|
+
"/employees/2024/12/employee_3_6": { department: "sales", age: 36 },
|
|
100
|
+
"/employees/2025/03/employee_2_6": { department: "sales", age: 36 },
|
|
101
|
+
"/employees/employee_1_6": { department: "sales", age: 36 },
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function query8(qyDB) {
|
|
105
|
+
const result = ["employees", ["employees", "*", "*"]]
|
|
106
|
+
.map((folderPath) => qyDB.queryFiles(folderPath, {
|
|
107
|
+
department: (x) => x != "sales",
|
|
108
|
+
age: (x) => x > 30,
|
|
109
|
+
}))
|
|
110
|
+
.flat()
|
|
111
|
+
.reduce((map, x) => {
|
|
112
|
+
map[x.fullPath] = x.view();
|
|
113
|
+
return map;
|
|
114
|
+
}, {});
|
|
115
|
+
deepEqual(result, {
|
|
116
|
+
"/employees/2024/12/employee_3_2": { department: "product", age: 38 },
|
|
117
|
+
"/employees/2025/03/employee_2_2": { department: "product", age: 38 },
|
|
118
|
+
"/employees/employee_1_2": { department: "product", age: 38 },
|
|
119
|
+
"/employees/2024/12/employee_3_7": { department: "product", age: 36 },
|
|
120
|
+
"/employees/2025/03/employee_2_7": { department: "product", age: 36 },
|
|
121
|
+
"/employees/employee_1_7": { department: "product", age: 36 },
|
|
122
|
+
"/employees/2024/12/employee_3_9": { department: "R&D", age: 46 },
|
|
123
|
+
"/employees/2025/03/employee_2_9": { department: "R&D", age: 46 },
|
|
124
|
+
"/employees/employee_1_9": { department: "R&D", age: 46 },
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
function query9(qyDB) {
|
|
128
|
+
const queryObj = { $and: [{ department: (x) => x != "sales" }, { age: (x) => x > 30 }] };
|
|
129
|
+
deepEqual(qyDB.fetch
|
|
130
|
+
.cd("employees")
|
|
131
|
+
.queryFiles("2024/12", queryObj)
|
|
132
|
+
.queryFiles("2025/03", queryObj)
|
|
133
|
+
.queryFiles(".", queryObj)
|
|
134
|
+
.run()
|
|
135
|
+
.map((list) => list.reduce((map, x) => {
|
|
136
|
+
map[x.fullPath] = x.view();
|
|
137
|
+
return map;
|
|
138
|
+
}, {})), [
|
|
139
|
+
{
|
|
140
|
+
"/employees/2024/12/employee_3_2": { department: "product", age: 38 },
|
|
141
|
+
"/employees/2024/12/employee_3_7": { department: "product", age: 36 },
|
|
142
|
+
"/employees/2024/12/employee_3_9": { department: "R&D", age: 46 },
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"/employees/2025/03/employee_2_2": { department: "product", age: 38 },
|
|
146
|
+
"/employees/2025/03/employee_2_7": { department: "product", age: 36 },
|
|
147
|
+
"/employees/2025/03/employee_2_9": { department: "R&D", age: 46 },
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"/employees/employee_1_2": { department: "product", age: 38 },
|
|
151
|
+
"/employees/employee_1_7": { department: "product", age: 36 },
|
|
152
|
+
"/employees/employee_1_9": { department: "R&D", age: 46 },
|
|
153
|
+
},
|
|
154
|
+
]);
|
|
155
|
+
}
|
|
156
|
+
function query10(qyDB) {
|
|
157
|
+
const queryObj = {
|
|
158
|
+
$or: [{ department: (x) => x != "sales", age: (x) => x > 36 }, { age: (x) => x > 40 }],
|
|
159
|
+
};
|
|
160
|
+
deepEqual(qyDB.fetch
|
|
161
|
+
.cd("employees")
|
|
162
|
+
.queryFiles("*/*", queryObj)
|
|
163
|
+
.queryFiles(".", queryObj)
|
|
164
|
+
.run()
|
|
165
|
+
.map((list) => list.reduce((map, x) => {
|
|
166
|
+
map[x.fullPath] = x.view();
|
|
167
|
+
return map;
|
|
168
|
+
}, {})), [
|
|
169
|
+
{
|
|
170
|
+
"/employees/2024/12/employee_3_2": { department: "product", age: 38 },
|
|
171
|
+
"/employees/2024/12/employee_3_9": { department: "R&D", age: 46 },
|
|
172
|
+
"/employees/2025/03/employee_2_2": { department: "product", age: 38 },
|
|
173
|
+
"/employees/2025/03/employee_2_9": { department: "R&D", age: 46 },
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"/employees/employee_1_2": { department: "product", age: 38 },
|
|
177
|
+
"/employees/employee_1_9": { department: "R&D", age: 46 },
|
|
178
|
+
},
|
|
179
|
+
]);
|
|
180
|
+
}
|
|
181
|
+
function query11(qyDB) {
|
|
182
|
+
const queryObj = {
|
|
183
|
+
$and: [
|
|
184
|
+
{
|
|
185
|
+
$or: [{ department: (x) => x != "sales", age: (x) => x > 36 }],
|
|
186
|
+
},
|
|
187
|
+
{ age: (x) => x > 40 },
|
|
188
|
+
],
|
|
189
|
+
};
|
|
190
|
+
deepEqual(qyDB.fetch
|
|
191
|
+
.cd("employees")
|
|
192
|
+
.queryFiles("*/*", queryObj)
|
|
193
|
+
.queryFiles(".", queryObj)
|
|
194
|
+
.run()
|
|
195
|
+
.map((list) => list.reduce((map, x) => {
|
|
196
|
+
map[x.fullPath] = x.view();
|
|
197
|
+
return map;
|
|
198
|
+
}, {})), [
|
|
199
|
+
{
|
|
200
|
+
"/employees/2024/12/employee_3_9": { department: "R&D", age: 46 },
|
|
201
|
+
"/employees/2025/03/employee_2_9": { department: "R&D", age: 46 },
|
|
202
|
+
},
|
|
203
|
+
{ "/employees/employee_1_9": { department: "R&D", age: 46 } },
|
|
204
|
+
]);
|
|
205
|
+
}
|
|
206
|
+
function query12(qyDB) {
|
|
207
|
+
const queryObj = {
|
|
208
|
+
$and: { department: (x) => x != "sales", age: (x) => x > 36 },
|
|
209
|
+
age: (x) => x > 40,
|
|
210
|
+
};
|
|
211
|
+
deepEqual(qyDB.fetch
|
|
212
|
+
.cd("employees")
|
|
213
|
+
.queryFiles([(x) => x >= 2024 && x <= 2025, (x) => x != 10], queryObj, 2)
|
|
214
|
+
.queryFiles(".", queryObj, 2)
|
|
215
|
+
.run()
|
|
216
|
+
.map((list) => list.reduce((map, x) => {
|
|
217
|
+
map[x.fullPath] = x.view();
|
|
218
|
+
return map;
|
|
219
|
+
}, {})), [
|
|
220
|
+
{
|
|
221
|
+
"/employees/2024/12/employee_3_9": { department: "R&D", age: 46 },
|
|
222
|
+
"/employees/2025/03/employee_2_9": { age: 46, department: "R&D" },
|
|
223
|
+
},
|
|
224
|
+
{ "/employees/employee_1_9": { department: "R&D", age: 46 } },
|
|
225
|
+
]);
|
|
226
|
+
}
|
|
227
|
+
function query13(qyDB) {
|
|
228
|
+
const queryObj = { $and: { department: (x) => x != "sales", age: (x) => x > 36 }, age: (x) => x > 40 };
|
|
229
|
+
deepEqual(qyDB.fetch
|
|
230
|
+
.cd("employees")
|
|
231
|
+
.queryFiles([(x) => x >= 2024 && x <= 2025, (x) => x != 10], queryObj, 1)
|
|
232
|
+
.queryFiles(".", queryObj, 1)
|
|
233
|
+
.run()
|
|
234
|
+
.map((list) => list.reduce((map, x) => {
|
|
235
|
+
map[x.fullPath] = x.view();
|
|
236
|
+
return map;
|
|
237
|
+
}, {})), [
|
|
238
|
+
{ "/employees/2024/12/employee_3_9": { department: "R&D", age: 46 } },
|
|
239
|
+
{ "/employees/employee_1_9": { department: "R&D", age: 46 } },
|
|
240
|
+
]);
|
|
241
|
+
}
|
|
242
|
+
function query14(qyDB) {
|
|
243
|
+
const queryObj = { $and: { department: (x) => x != "sales", age: (x) => x > 36 }, age: (x) => x > 40 };
|
|
244
|
+
deepEqual(qyDB.fetch
|
|
245
|
+
.cd("employees")
|
|
246
|
+
.queryFiles([(x) => x >= 2024 && x <= 2025, (x) => x != 10], queryObj, 1, 1)
|
|
247
|
+
.queryFiles(".", queryObj, 1, 1)
|
|
248
|
+
.run()
|
|
249
|
+
.map((list) => list.reduce((map, x) => {
|
|
250
|
+
map[x.fullPath] = x.view();
|
|
251
|
+
return map;
|
|
252
|
+
}, {})), [{ "/employees/2025/03/employee_2_9": { age: 46, department: "R&D" } }, {}]);
|
|
253
|
+
deepEqual(qyDB.fetch
|
|
254
|
+
.cd("employees")
|
|
255
|
+
.queryFiles([{ $gte: 2024, $lte: 2025 }, (x) => x != 10], queryObj, 1, 1)
|
|
256
|
+
.queryFiles(".", queryObj, 1, 1)
|
|
257
|
+
.run()
|
|
258
|
+
.map((list) => list.reduce((map, x) => {
|
|
259
|
+
map[x.fullPath] = x.view();
|
|
260
|
+
return map;
|
|
261
|
+
}, {})), [{ "/employees/2025/03/employee_2_9": { age: 46, department: "R&D" } }, {}]);
|
|
262
|
+
deepEqual(qyDB.fetch
|
|
263
|
+
.cd("employees")
|
|
264
|
+
.queryFiles([{ $gt: 2023, $gte: 2024, $lte: 2025, $lt: 2026 }, (x) => x != 10], queryObj, 1, 1)
|
|
265
|
+
.queryFiles(".", queryObj, 1, 1)
|
|
266
|
+
.run()
|
|
267
|
+
.map((list) => list.reduce((map, x) => {
|
|
268
|
+
map[x.fullPath] = x.view();
|
|
269
|
+
return map;
|
|
270
|
+
}, {})), [{ "/employees/2025/03/employee_2_9": { age: 46, department: "R&D" } }, {}]);
|
|
271
|
+
deepEqual(qyDB.fetch
|
|
272
|
+
.cd("employees")
|
|
273
|
+
.queryFiles([{ $gt: 2023, $gte: 2024, $lte: 2025, $lt: 2025 }, (x) => x != 10], queryObj, 1, 1)
|
|
274
|
+
.queryFiles(".", queryObj, 1, 1)
|
|
275
|
+
.run(), [[], []]);
|
|
276
|
+
deepEqual(qyDB.fetch
|
|
277
|
+
.cd("employees")
|
|
278
|
+
.queryFiles([{ $gt: 2024, $gte: 2024, $lte: 2025, $lt: 2026 }, (x) => x != 10], queryObj, 1, 1)
|
|
279
|
+
.queryFiles(".", queryObj, 1, 1)
|
|
280
|
+
.run(), [[], []]);
|
|
281
|
+
deepEqual(qyDB.fetch
|
|
282
|
+
.cd("employees")
|
|
283
|
+
.queryFiles([{ $or: [2024, 2025] }, (x) => x != 10], queryObj, 1, 1)
|
|
284
|
+
.queryFiles(".", queryObj, 1, 1)
|
|
285
|
+
.run()
|
|
286
|
+
.map((list) => list.reduce((map, x) => {
|
|
287
|
+
map[x.fullPath] = x.view();
|
|
288
|
+
return map;
|
|
289
|
+
}, {})), [{ "/employees/2025/03/employee_2_9": { age: 46, department: "R&D" } }, {}]);
|
|
290
|
+
deepEqual(qyDB.fetch
|
|
291
|
+
.cd("employees")
|
|
292
|
+
.queryFiles([{ $in: [2023, 2024, 2025] }, (x) => x != 10], queryObj, 1, 1)
|
|
293
|
+
.queryFiles(".", queryObj, 1, 1)
|
|
294
|
+
.run()
|
|
295
|
+
.map((list) => list.reduce((map, x) => {
|
|
296
|
+
map[x.fullPath] = x.view();
|
|
297
|
+
return map;
|
|
298
|
+
}, {})), [{ "/employees/2025/03/employee_2_9": { age: 46, department: "R&D" } }, {}]);
|
|
299
|
+
}
|
|
300
|
+
function query15(qyDB) {
|
|
301
|
+
const queryObj = { $and: { department: (x) => x != "sales", age: (x) => x > 36 }, age: (x) => x > 40 };
|
|
302
|
+
deepEqual(qyDB.fetch
|
|
303
|
+
.cd("employees")
|
|
304
|
+
.queryFiles({
|
|
305
|
+
$or: [".", [(x) => x >= 2024 && x <= 2025, (x) => x != 10]],
|
|
306
|
+
}, queryObj, 10, 0)
|
|
307
|
+
.run()
|
|
308
|
+
.map((list) => list.reduce((map, x) => {
|
|
309
|
+
map[x.fullPath] = x.view();
|
|
310
|
+
return map;
|
|
311
|
+
}, {})), [
|
|
312
|
+
{
|
|
313
|
+
"/employees/2024/12/employee_3_9": { age: 46, department: "R&D" },
|
|
314
|
+
"/employees/2025/03/employee_2_9": { age: 46, department: "R&D" },
|
|
315
|
+
"/employees/employee_1_9": { age: 46, department: "R&D" },
|
|
316
|
+
},
|
|
317
|
+
]);
|
|
318
|
+
}
|
|
319
|
+
function query16(qyDB) {
|
|
320
|
+
const queryObj = {
|
|
321
|
+
$and: { department: (x) => x != "sales", age: (x) => x > 36 },
|
|
322
|
+
age: (x) => x > 40,
|
|
323
|
+
};
|
|
324
|
+
deepEqual(qyDB.fetch
|
|
325
|
+
.cd("employees")
|
|
326
|
+
.queryFilesMulti([["./**", queryObj]])
|
|
327
|
+
.run()
|
|
328
|
+
.map((list) => list.reduce((map, x) => {
|
|
329
|
+
map[x.fullPath] = x.view();
|
|
330
|
+
return map;
|
|
331
|
+
}, {})), [
|
|
332
|
+
{
|
|
333
|
+
"/employees/2025/03/employee_2_9": { age: 46, department: "R&D" },
|
|
334
|
+
"/employees/2024/12/employee_3_9": { age: 46, department: "R&D" },
|
|
335
|
+
"/employees/employee_1_9": { age: 46, department: "R&D" },
|
|
336
|
+
},
|
|
337
|
+
]);
|
|
338
|
+
deepEqual(qyDB.fetch
|
|
339
|
+
.cd("employees")
|
|
340
|
+
.queryFilesMulti([["./**", queryObj]], 1, 1)
|
|
341
|
+
.run()
|
|
342
|
+
.map((list) => list.reduce((map, x) => {
|
|
343
|
+
map[x.fullPath] = x.view();
|
|
344
|
+
return map;
|
|
345
|
+
}, {})), [{ "/employees/2024/12/employee_3_9": { age: 46, department: "R&D" } }]);
|
|
346
|
+
}
|
|
347
|
+
function query17(qyDB) {
|
|
348
|
+
const result = {};
|
|
349
|
+
qyDB.fetch
|
|
350
|
+
.queryFiles("employees", {
|
|
351
|
+
department: { $or: ["sales", "R&D", "UNEXISTED"] },
|
|
352
|
+
age: (x) => x > 30,
|
|
353
|
+
})
|
|
354
|
+
.run()[0]
|
|
355
|
+
.forEach((x) => (result[x.name] = x.view()));
|
|
356
|
+
deepEqual(result, {
|
|
357
|
+
employee_1_1: { age: 31, department: "sales" },
|
|
358
|
+
employee_1_5: { age: 36, department: "sales" },
|
|
359
|
+
employee_1_6: { age: 36, department: "sales" },
|
|
360
|
+
employee_1_9: { age: 46, department: "R&D" },
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
function query18(qyDB) {
|
|
364
|
+
const result = {};
|
|
365
|
+
qyDB.fetch
|
|
366
|
+
.queryFiles("employees", "employee_1_1")
|
|
367
|
+
.run()[0]
|
|
368
|
+
.forEach((x) => (result[x.name] = x.view()));
|
|
369
|
+
deepEqual(result, {
|
|
370
|
+
employee_1_1: { age: 31, department: "sales" },
|
|
371
|
+
});
|
|
372
|
+
const result2 = {};
|
|
373
|
+
qyDB.queryFiles("**", "employee_1_1").forEach((x) => (result2[x.name] = x.view()));
|
|
374
|
+
deepEqual(result2, {
|
|
375
|
+
employee_1_1: { age: 31, department: "sales" },
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
function query19(qyDB) {
|
|
379
|
+
const result = {};
|
|
380
|
+
qyDB.fetch
|
|
381
|
+
.cd("employees")
|
|
382
|
+
.queryFiles("**", "employee_3_2")
|
|
383
|
+
.run()[0]
|
|
384
|
+
.forEach((x) => (result[x.name] = x.view()));
|
|
385
|
+
deepEqual(result, {
|
|
386
|
+
employee_3_2: { age: 38, department: "product" },
|
|
387
|
+
});
|
|
388
|
+
const result2 = {};
|
|
389
|
+
qyDB.queryFiles("**", "employee_2_1").forEach((x) => (result2[x.name] = x.view()));
|
|
390
|
+
deepEqual(result2, {
|
|
391
|
+
employee_2_1: { age: 31, department: "sales" },
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
let count = 0;
|
|
395
|
+
function getEmployeeData() {
|
|
396
|
+
++count;
|
|
397
|
+
return {
|
|
398
|
+
[`employee_${count}_1`]: { department: "sales", age: 31 },
|
|
399
|
+
[`employee_${count}_2`]: { department: "product", age: 38 },
|
|
400
|
+
[`employee_${count}_3`]: { department: "R&D", age: 26 },
|
|
401
|
+
[`employee_${count}_4`]: { department: "sales", age: 25 },
|
|
402
|
+
[`employee_${count}_5`]: { department: "sales", age: 36 },
|
|
403
|
+
[`employee_${count}_6`]: { department: "sales", age: 36 },
|
|
404
|
+
[`employee_${count}_7`]: { department: "product", age: 36 },
|
|
405
|
+
[`employee_${count}_9`]: { department: "R&D", age: 46 },
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
function insertData(qyDB, dirPath) {
|
|
409
|
+
const { batch } = qyDB;
|
|
410
|
+
batch.cd(dirPath);
|
|
411
|
+
const employeeData = getEmployeeData();
|
|
412
|
+
for (const key in employeeData) {
|
|
413
|
+
batch.insert(key, employeeData[key]);
|
|
414
|
+
}
|
|
415
|
+
batch.run();
|
|
416
|
+
}
|
|
417
|
+
test("test QyAQL", { skip: false }, async (context) => {
|
|
418
|
+
context.after(async (t) => {
|
|
419
|
+
await qyDB.stop();
|
|
420
|
+
});
|
|
421
|
+
const dataFolder = "./tmp/QyAQL/1";
|
|
422
|
+
const qyDB = new QyDB(dataFolder, {});
|
|
423
|
+
await qyDB.start();
|
|
424
|
+
qyDB.delDir("employees");
|
|
425
|
+
qyDB.createIndex("employees", "department");
|
|
426
|
+
insertData(qyDB, "employees");
|
|
427
|
+
query1(qyDB);
|
|
428
|
+
insertData(qyDB, ["employees", "2025", "03"]);
|
|
429
|
+
insertData(qyDB, ["employees", "2024", "12"]);
|
|
430
|
+
await qyDB.stop();
|
|
431
|
+
await qyDB.start();
|
|
432
|
+
query1(qyDB);
|
|
433
|
+
query2(qyDB);
|
|
434
|
+
query3(qyDB);
|
|
435
|
+
query4(qyDB);
|
|
436
|
+
query5(qyDB);
|
|
437
|
+
query6(qyDB);
|
|
438
|
+
query7(qyDB);
|
|
439
|
+
query8(qyDB);
|
|
440
|
+
query9(qyDB);
|
|
441
|
+
query10(qyDB);
|
|
442
|
+
query11(qyDB);
|
|
443
|
+
query12(qyDB);
|
|
444
|
+
query13(qyDB);
|
|
445
|
+
query14(qyDB);
|
|
446
|
+
query15(qyDB);
|
|
447
|
+
query16(qyDB);
|
|
448
|
+
query17(qyDB);
|
|
449
|
+
query18(qyDB);
|
|
450
|
+
query19(qyDB);
|
|
451
|
+
});
|