datafolder 1.0.15 → 1.0.16
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 +2 -0
- package/package.json +3 -2
- package/src/QyExpire.js +1 -3
- package/static/codemirror.bundle/editor.bundle.js +93 -38
- package/test/testQyGraphQL.js +198 -0
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@ datafolder is a lightning fluid JSON database for high speed SSDs, featuring an
|
|
|
4
4
|
|
|
5
5
|
### Changelog
|
|
6
6
|
|
|
7
|
+
1.0.16 Add GraphQL sample data test (test/testQyGraphQL.js) to show how to write complex queries.
|
|
8
|
+
|
|
7
9
|
1.0.15 Use [Workers](https://nodejs.org/docs/latest/api/worker_threads.html) to implement snapshot compactor, expire service, outdated data cleaner etc.
|
|
8
10
|
|
|
9
11
|
1.0.14 Expire supports specifying milliseconds.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "datafolder",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.16",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "lysh",
|
|
6
6
|
"email": "datafolderdev@outlook.com"
|
|
@@ -52,7 +52,8 @@
|
|
|
52
52
|
"scripts": {
|
|
53
53
|
"ui": "start http://localhost:3628/datafolderui/index.html && node ./scripts/fastify.js",
|
|
54
54
|
"airbnb": "node ./scripts/airbnb2.js && start http://localhost:3628/datafolderui/index.html && node ./scripts/fastify.js",
|
|
55
|
-
"types:check": "tsc --noEmit"
|
|
55
|
+
"types:check": "tsc --noEmit",
|
|
56
|
+
"test": "node --test --experimental-test-coverage --test-coverage-exclude \"test/testQyAirBnB_Ubuntu.ts\" \"test/*\""
|
|
56
57
|
},
|
|
57
58
|
"type": "module"
|
|
58
59
|
}
|
package/src/QyExpire.js
CHANGED
|
@@ -6596,6 +6596,7 @@
|
|
|
6596
6596
|
this.cache.reused.set(oldTile, 2 /* Reused.DOM */);
|
|
6597
6597
|
let text = new TextTile(composition.text, composition.text.nodeValue);
|
|
6598
6598
|
text.flags |= 8 /* TileFlag.Composition */;
|
|
6599
|
+
this.pos = composition.range.toB;
|
|
6599
6600
|
head.append(text);
|
|
6600
6601
|
}
|
|
6601
6602
|
addInlineWidget(widget, marks, openStart) {
|
|
@@ -8120,7 +8121,7 @@
|
|
|
8120
8121
|
// (including the position after the last piece). For a text tile,
|
|
8121
8122
|
// these will be character clusters, for a composite tile, these
|
|
8122
8123
|
// will be child tiles.
|
|
8123
|
-
scan(positions, getRects) {
|
|
8124
|
+
scan(positions, getRects, recursed = false) {
|
|
8124
8125
|
let lo = 0, hi = positions.length - 1, seen = new Set();
|
|
8125
8126
|
let bidi = this.bidiIn(positions[0], positions[hi]);
|
|
8126
8127
|
let above, below;
|
|
@@ -8193,19 +8194,19 @@
|
|
|
8193
8194
|
if (!closestRect) {
|
|
8194
8195
|
let side = above && (!below || (this.y - above.bottom < below.top - this.y)) ? above : below;
|
|
8195
8196
|
this.y = (side.top + side.bottom) / 2;
|
|
8196
|
-
return this.scan(positions, getRects);
|
|
8197
|
+
return this.scan(positions, getRects, true);
|
|
8197
8198
|
}
|
|
8198
8199
|
// Handle the case where closest matched a higher element on the
|
|
8199
8200
|
// same line as an element below/above the coords
|
|
8200
|
-
if (closestDx) {
|
|
8201
|
+
if (closestDx && !recursed) {
|
|
8201
8202
|
let { top, bottom } = closestRect;
|
|
8202
8203
|
if (above && above.bottom > (top + top + bottom) / 3) {
|
|
8203
8204
|
this.y = above.bottom - 1;
|
|
8204
|
-
return this.scan(positions, getRects);
|
|
8205
|
+
return this.scan(positions, getRects, true);
|
|
8205
8206
|
}
|
|
8206
8207
|
if (below && below.top < (top + bottom + bottom) / 3) {
|
|
8207
8208
|
this.y = below.top + 1;
|
|
8208
|
-
return this.scan(positions, getRects);
|
|
8209
|
+
return this.scan(positions, getRects, true);
|
|
8209
8210
|
}
|
|
8210
8211
|
}
|
|
8211
8212
|
let ltr = (bidi ? this.dirAt(positions[closestI], 1) : this.baseDir) == Direction.LTR;
|
|
@@ -9499,7 +9500,7 @@
|
|
|
9499
9500
|
view.inputState.compositionFirstChange = null;
|
|
9500
9501
|
if (browser.chrome && browser.android) {
|
|
9501
9502
|
// Delay flushing for a bit on Android because it'll often fire a
|
|
9502
|
-
// bunch of contradictory changes in a row at end of
|
|
9503
|
+
// bunch of contradictory changes in a row at end of composition
|
|
9503
9504
|
view.observer.flushSoon();
|
|
9504
9505
|
}
|
|
9505
9506
|
else if (view.inputState.compositionPendingChange) {
|
|
@@ -9630,8 +9631,7 @@
|
|
|
9630
9631
|
}
|
|
9631
9632
|
refresh(whiteSpace, lineHeight, charWidth, textHeight, lineLength, knownHeights) {
|
|
9632
9633
|
let lineWrapping = wrappingWhiteSpace.indexOf(whiteSpace) > -1;
|
|
9633
|
-
let changed = Math.abs(lineHeight - this.lineHeight) > 0.3 || this.lineWrapping != lineWrapping
|
|
9634
|
-
Math.abs(charWidth - this.charWidth) > 0.1;
|
|
9634
|
+
let changed = Math.abs(lineHeight - this.lineHeight) > 0.3 || this.lineWrapping != lineWrapping;
|
|
9635
9635
|
this.lineWrapping = lineWrapping;
|
|
9636
9636
|
this.lineHeight = lineHeight;
|
|
9637
9637
|
this.charWidth = charWidth;
|
|
@@ -14833,11 +14833,13 @@
|
|
|
14833
14833
|
arrow: tooltips.some(t => t.arrow),
|
|
14834
14834
|
};
|
|
14835
14835
|
});
|
|
14836
|
+
const hoverPlugin = /*@__PURE__*/Facet.define();
|
|
14836
14837
|
class HoverPlugin {
|
|
14837
|
-
constructor(view, source, field, setHover, hoverTime) {
|
|
14838
|
+
constructor(view, source, field, locked, setHover, hoverTime) {
|
|
14838
14839
|
this.view = view;
|
|
14839
14840
|
this.source = source;
|
|
14840
14841
|
this.field = field;
|
|
14842
|
+
this.locked = locked;
|
|
14841
14843
|
this.setHover = setHover;
|
|
14842
14844
|
this.hoverTime = hoverTime;
|
|
14843
14845
|
this.hoverTimeout = -1;
|
|
@@ -14848,7 +14850,7 @@
|
|
|
14848
14850
|
view.dom.addEventListener("mouseleave", this.mouseleave = this.mouseleave.bind(this));
|
|
14849
14851
|
view.dom.addEventListener("mousemove", this.mousemove = this.mousemove.bind(this));
|
|
14850
14852
|
}
|
|
14851
|
-
update() {
|
|
14853
|
+
update(update) {
|
|
14852
14854
|
if (this.pending) {
|
|
14853
14855
|
this.pending = null;
|
|
14854
14856
|
clearTimeout(this.restartTimeout);
|
|
@@ -14892,19 +14894,29 @@
|
|
|
14892
14894
|
let rtl = bidi && bidi.dir == Direction.RTL ? -1 : 1;
|
|
14893
14895
|
side = (lastMove.x < posCoords.left ? -rtl : rtl);
|
|
14894
14896
|
}
|
|
14897
|
+
this.activateHover(view, pos, side);
|
|
14898
|
+
}
|
|
14899
|
+
activateHover(view, pos, side, locked) {
|
|
14895
14900
|
let open = this.source(view, pos, side);
|
|
14896
|
-
|
|
14901
|
+
let done = (value) => {
|
|
14902
|
+
if (value && !(Array.isArray(value) && !value.length)) {
|
|
14903
|
+
let tooltips = Array.isArray(value) ? value : [value];
|
|
14904
|
+
if (locked)
|
|
14905
|
+
this.locked.set(tooltips, locked);
|
|
14906
|
+
view.dispatch({ effects: this.setHover.of(tooltips) });
|
|
14907
|
+
}
|
|
14908
|
+
};
|
|
14909
|
+
if (open && "then" in open) {
|
|
14897
14910
|
let pending = this.pending = { pos };
|
|
14898
14911
|
open.then(result => {
|
|
14899
14912
|
if (this.pending == pending) {
|
|
14900
14913
|
this.pending = null;
|
|
14901
|
-
|
|
14902
|
-
view.dispatch({ effects: this.setHover.of(Array.isArray(result) ? result : [result]) });
|
|
14914
|
+
done(result);
|
|
14903
14915
|
}
|
|
14904
14916
|
}, e => logException(view.state, e, "hover tooltip"));
|
|
14905
14917
|
}
|
|
14906
|
-
else
|
|
14907
|
-
|
|
14918
|
+
else {
|
|
14919
|
+
done(open);
|
|
14908
14920
|
}
|
|
14909
14921
|
}
|
|
14910
14922
|
get tooltip() {
|
|
@@ -14918,7 +14930,7 @@
|
|
|
14918
14930
|
if (this.hoverTimeout < 0)
|
|
14919
14931
|
this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime);
|
|
14920
14932
|
let { active, tooltip } = this;
|
|
14921
|
-
if (active.length && tooltip && !isInTooltip(tooltip.dom, event) || this.pending) {
|
|
14933
|
+
if (active.length && !this.locked.has(active) && tooltip && !isInTooltip(tooltip.dom, event) || this.pending) {
|
|
14922
14934
|
let { pos } = active[0] || this.pending, end = (_b = (_a = active[0]) === null || _a === void 0 ? void 0 : _a.end) !== null && _b !== void 0 ? _b : pos;
|
|
14923
14935
|
if ((pos == end ? this.view.posAtCoords(this.lastMove) != pos
|
|
14924
14936
|
: !isOverRange(this.view, pos, end, event.clientX, event.clientY))) {
|
|
@@ -14931,7 +14943,7 @@
|
|
|
14931
14943
|
clearTimeout(this.hoverTimeout);
|
|
14932
14944
|
this.hoverTimeout = -1;
|
|
14933
14945
|
let { active } = this;
|
|
14934
|
-
if (active.length) {
|
|
14946
|
+
if (active.length && !this.locked.has(active)) {
|
|
14935
14947
|
let { tooltip } = this;
|
|
14936
14948
|
let inTooltip = tooltip && tooltip.dom.contains(event.relatedTarget);
|
|
14937
14949
|
if (!inTooltip)
|
|
@@ -14943,7 +14955,8 @@
|
|
|
14943
14955
|
watchTooltipLeave(tooltip) {
|
|
14944
14956
|
let watch = (event) => {
|
|
14945
14957
|
tooltip.removeEventListener("mouseleave", watch);
|
|
14946
|
-
|
|
14958
|
+
let { active } = this;
|
|
14959
|
+
if (active.length && !this.locked.has(active) && !this.view.dom.contains(event.relatedTarget))
|
|
14947
14960
|
this.view.dispatch({ effects: this.setHover.of([]) });
|
|
14948
14961
|
};
|
|
14949
14962
|
tooltip.addEventListener("mouseleave", watch);
|
|
@@ -14994,49 +15007,85 @@
|
|
|
14994
15007
|
*/
|
|
14995
15008
|
function hoverTooltip(source, options = {}) {
|
|
14996
15009
|
let setHover = StateEffect.define();
|
|
15010
|
+
// This would be better stored in the state field, but we've set
|
|
15011
|
+
// down the type of the field in our interface, so it's indirectly
|
|
15012
|
+
// stored by array identity.
|
|
15013
|
+
let locked = new WeakMap();
|
|
14997
15014
|
let hoverState = StateField.define({
|
|
14998
15015
|
create() { return []; },
|
|
14999
15016
|
update(value, tr) {
|
|
15017
|
+
let lock = locked.get(value);
|
|
15000
15018
|
if (value.length) {
|
|
15001
15019
|
if (options.hideOnChange && (tr.docChanged || tr.selection))
|
|
15002
15020
|
value = [];
|
|
15021
|
+
else if (lock && lock(tr))
|
|
15022
|
+
value = [];
|
|
15003
15023
|
else if (options.hideOn)
|
|
15004
15024
|
value = value.filter(v => !options.hideOn(tr, v));
|
|
15005
|
-
|
|
15006
|
-
|
|
15007
|
-
|
|
15008
|
-
|
|
15009
|
-
|
|
15010
|
-
|
|
15011
|
-
|
|
15012
|
-
|
|
15013
|
-
|
|
15014
|
-
|
|
15015
|
-
|
|
15025
|
+
}
|
|
15026
|
+
if (tr.docChanged && value.length) {
|
|
15027
|
+
let mapped = [];
|
|
15028
|
+
for (let tooltip of value) {
|
|
15029
|
+
let newPos = tr.changes.mapPos(tooltip.pos, -1, MapMode.TrackDel);
|
|
15030
|
+
if (newPos != null) {
|
|
15031
|
+
let copy = Object.assign(Object.create(null), tooltip);
|
|
15032
|
+
copy.pos = newPos;
|
|
15033
|
+
if (copy.end != null)
|
|
15034
|
+
copy.end = tr.changes.mapPos(copy.end);
|
|
15035
|
+
mapped.push(copy);
|
|
15016
15036
|
}
|
|
15017
|
-
value = mapped;
|
|
15018
15037
|
}
|
|
15038
|
+
value = mapped;
|
|
15019
15039
|
}
|
|
15020
15040
|
for (let effect of tr.effects) {
|
|
15021
|
-
if (effect.is(setHover))
|
|
15041
|
+
if (effect.is(setHover)) {
|
|
15022
15042
|
value = effect.value;
|
|
15023
|
-
|
|
15043
|
+
lock = undefined;
|
|
15044
|
+
}
|
|
15045
|
+
if (effect.is(closeHoverTooltipEffect) && !effect.value || effect.value == hoverState)
|
|
15024
15046
|
value = [];
|
|
15025
15047
|
}
|
|
15048
|
+
if (value.length && lock)
|
|
15049
|
+
locked.set(value, lock);
|
|
15026
15050
|
return value;
|
|
15027
15051
|
},
|
|
15028
15052
|
provide: f => showHoverTooltip.from(f)
|
|
15029
15053
|
});
|
|
15054
|
+
const plugin = ViewPlugin.define(view => new HoverPlugin(view, source, hoverState, locked, setHover, options.hoverTime || 300 /* Hover.Time */));
|
|
15030
15055
|
return {
|
|
15031
15056
|
active: hoverState,
|
|
15032
15057
|
extension: [
|
|
15033
15058
|
hoverState,
|
|
15034
|
-
|
|
15059
|
+
plugin,
|
|
15060
|
+
hoverPlugin.of(plugin),
|
|
15035
15061
|
showHoverTooltipHost
|
|
15036
15062
|
]
|
|
15037
15063
|
};
|
|
15038
15064
|
}
|
|
15039
15065
|
/**
|
|
15066
|
+
Activate hover tooltips for the given position and side. If you
|
|
15067
|
+
provide a specific hover tooltip (the value returned from
|
|
15068
|
+
[`hoverTooltip`](https://codemirror.net/6/docs/ref/#view.hoverTooltip)), only that one will be
|
|
15069
|
+
activated. If not given, all hover tooltips at the given position
|
|
15070
|
+
are triggered.
|
|
15071
|
+
|
|
15072
|
+
Note that tooltips opened this way don't close automatically, and
|
|
15073
|
+
you'll want to pass an `until` callback or use
|
|
15074
|
+
[`closeHoverTooltip`](https://codemirror.net/6/docs/ref/#view.closeHoverTooltip)/[`closeHoverTooltips`](https://codemirror.net/6/docs/ref/#view.closeHoverTooltips)
|
|
15075
|
+
to deactivate them.
|
|
15076
|
+
*/
|
|
15077
|
+
function activateHover(view, pos, side, options = {}) {
|
|
15078
|
+
var _a;
|
|
15079
|
+
let plugins = view.state.facet(hoverPlugin).map(p => view.plugin(p)).filter((p) => !!p);
|
|
15080
|
+
if (options.tooltip && options.tooltip.active) {
|
|
15081
|
+
let found = plugins.find(p => p.field == options.tooltip.active);
|
|
15082
|
+
if (found)
|
|
15083
|
+
plugins = [found];
|
|
15084
|
+
}
|
|
15085
|
+
for (let plugin of plugins)
|
|
15086
|
+
plugin.activateHover(view, pos, side, (_a = options.until) !== null && _a !== void 0 ? _a : (() => false));
|
|
15087
|
+
}
|
|
15088
|
+
/**
|
|
15040
15089
|
Get the active tooltip view for a given tooltip, if available.
|
|
15041
15090
|
*/
|
|
15042
15091
|
function getTooltip(view, tooltip) {
|
|
@@ -23521,8 +23570,8 @@
|
|
|
23521
23570
|
let off = Math.floor(selected / max);
|
|
23522
23571
|
return { from: off * max, to: (off + 1) * max };
|
|
23523
23572
|
}
|
|
23524
|
-
let off = Math.
|
|
23525
|
-
return { from: total -
|
|
23573
|
+
let off = Math.ceil((total - selected) / max);
|
|
23574
|
+
return { from: total - off * max, to: total - (off - 1) * max };
|
|
23526
23575
|
}
|
|
23527
23576
|
class CompletionTooltip {
|
|
23528
23577
|
constructor(view, stateField, applyCompletion) {
|
|
@@ -24381,7 +24430,8 @@
|
|
|
24381
24430
|
content: '"···"',
|
|
24382
24431
|
opacity: 0.5,
|
|
24383
24432
|
display: "block",
|
|
24384
|
-
textAlign: "center"
|
|
24433
|
+
textAlign: "center",
|
|
24434
|
+
cursor: "pointer",
|
|
24385
24435
|
},
|
|
24386
24436
|
".cm-tooltip.cm-completionInfo": {
|
|
24387
24437
|
position: "absolute",
|
|
@@ -25229,6 +25279,10 @@
|
|
|
25229
25279
|
return false;
|
|
25230
25280
|
}
|
|
25231
25281
|
view.dispatch({ selection: { anchor: next.from, head: next.to }, scrollIntoView: true });
|
|
25282
|
+
activateHover(view, next.from, 1, {
|
|
25283
|
+
tooltip: lintHover,
|
|
25284
|
+
until: tr => tr.docChanged || tr.newSelection.main.head < next.from || tr.newSelection.main.head > next.to
|
|
25285
|
+
});
|
|
25232
25286
|
return true;
|
|
25233
25287
|
};
|
|
25234
25288
|
/**
|
|
@@ -25538,7 +25592,7 @@
|
|
|
25538
25592
|
backgroundRepeat: "repeat-x",
|
|
25539
25593
|
paddingBottom: "0.7px",
|
|
25540
25594
|
},
|
|
25541
|
-
".cm-lintRange-error": { backgroundImage: /*@__PURE__*/underline("#
|
|
25595
|
+
".cm-lintRange-error": { backgroundImage: /*@__PURE__*/underline("#f11") },
|
|
25542
25596
|
".cm-lintRange-warning": { backgroundImage: /*@__PURE__*/underline("orange") },
|
|
25543
25597
|
".cm-lintRange-info": { backgroundImage: /*@__PURE__*/underline("#999") },
|
|
25544
25598
|
".cm-lintRange-hint": { backgroundImage: /*@__PURE__*/underline("#66d") },
|
|
@@ -25619,6 +25673,7 @@
|
|
|
25619
25673
|
}
|
|
25620
25674
|
return sev;
|
|
25621
25675
|
}
|
|
25676
|
+
const lintHover = /*@__PURE__*/hoverTooltip(lintTooltip, { hideOn: hideTooltip });
|
|
25622
25677
|
const lintExtensions = [
|
|
25623
25678
|
lintState,
|
|
25624
25679
|
/*@__PURE__*/EditorView.decorations.compute([lintState], state => {
|
|
@@ -25627,7 +25682,7 @@
|
|
|
25627
25682
|
activeMark.range(selected.from, selected.to)
|
|
25628
25683
|
]);
|
|
25629
25684
|
}),
|
|
25630
|
-
|
|
25685
|
+
lintHover,
|
|
25631
25686
|
baseTheme
|
|
25632
25687
|
];
|
|
25633
25688
|
|
|
@@ -0,0 +1,198 @@
|
|
|
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
|
+
const graphQLData = {
|
|
7
|
+
Characters: {
|
|
8
|
+
luke: {
|
|
9
|
+
type: "Human",
|
|
10
|
+
id: "1000",
|
|
11
|
+
name: "Luke Skywalker",
|
|
12
|
+
friends: ["1002", "1003", "2000", "2001"],
|
|
13
|
+
appearsIn: [4, 5, 6],
|
|
14
|
+
homePlanet: "Tatooine",
|
|
15
|
+
},
|
|
16
|
+
vader: {
|
|
17
|
+
type: "Human",
|
|
18
|
+
id: "1001",
|
|
19
|
+
name: "Darth Vader",
|
|
20
|
+
friends: ["1004"],
|
|
21
|
+
appearsIn: [4, 5, 6],
|
|
22
|
+
homePlanet: "Tatooine",
|
|
23
|
+
},
|
|
24
|
+
han: {
|
|
25
|
+
type: "Human",
|
|
26
|
+
id: "1002",
|
|
27
|
+
name: "Han Solo",
|
|
28
|
+
friends: ["1000", "1003", "2001"],
|
|
29
|
+
appearsIn: [4, 5, 6],
|
|
30
|
+
},
|
|
31
|
+
leia: {
|
|
32
|
+
type: "Human",
|
|
33
|
+
id: "1003",
|
|
34
|
+
name: "Leia Organa",
|
|
35
|
+
friends: ["1000", "1002", "2000", "2001"],
|
|
36
|
+
appearsIn: [4, 5, 6],
|
|
37
|
+
homePlanet: "Alderaan",
|
|
38
|
+
},
|
|
39
|
+
tarkin: {
|
|
40
|
+
type: "Human",
|
|
41
|
+
id: "1004",
|
|
42
|
+
name: "Wilhuff Tarkin",
|
|
43
|
+
friends: ["1001"],
|
|
44
|
+
appearsIn: [4],
|
|
45
|
+
},
|
|
46
|
+
threepio: {
|
|
47
|
+
type: "Droid",
|
|
48
|
+
id: "2000",
|
|
49
|
+
name: "C-3PO",
|
|
50
|
+
friends: ["1000", "1002", "1003", "2001"],
|
|
51
|
+
appearsIn: [4, 5, 6],
|
|
52
|
+
primaryFunction: "Protocol",
|
|
53
|
+
},
|
|
54
|
+
artoo: {
|
|
55
|
+
type: "Droid",
|
|
56
|
+
id: "2001",
|
|
57
|
+
name: "R2-D2",
|
|
58
|
+
friends: ["1000", "1002", "1003"],
|
|
59
|
+
appearsIn: [4, 5],
|
|
60
|
+
primaryFunction: "Astromech",
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
function insertData(qyDB) {
|
|
65
|
+
const { batch } = qyDB;
|
|
66
|
+
const { Characters } = graphQLData;
|
|
67
|
+
for (const key in Characters) {
|
|
68
|
+
const character = Characters[key];
|
|
69
|
+
const { id } = character;
|
|
70
|
+
batch.put(["Characters", id], character);
|
|
71
|
+
}
|
|
72
|
+
batch.run();
|
|
73
|
+
}
|
|
74
|
+
function query1(qyDB) {
|
|
75
|
+
deepEqual(qyDB.queryFiles("Characters", { friends: { $or: ["1000", "1004"] } }).map((x) => x.view(["id", "name"])), [
|
|
76
|
+
{ id: "1002", name: "Han Solo" },
|
|
77
|
+
{ id: "1003", name: "Leia Organa" },
|
|
78
|
+
{ id: "2000", name: "C-3PO" },
|
|
79
|
+
{ id: "2001", name: "R2-D2" },
|
|
80
|
+
{ id: "1001", name: "Darth Vader" },
|
|
81
|
+
]);
|
|
82
|
+
deepEqual(qyDB.queryFiles("Characters", { friends: { $and: ["1002", "2001"] } }).map((x) => x.view(["id", "name"])), [
|
|
83
|
+
{ id: "1000", name: "Luke Skywalker" },
|
|
84
|
+
{ id: "1003", name: "Leia Organa" },
|
|
85
|
+
{ id: "2000", name: "C-3PO" },
|
|
86
|
+
]);
|
|
87
|
+
deepEqual(qyDB.queryFiles("Characters", { friends: ["1002", "2001"] }).map((x) => x.view(["id", "name"])), [
|
|
88
|
+
{ id: "1000", name: "Luke Skywalker" },
|
|
89
|
+
{ id: "1003", name: "Leia Organa" },
|
|
90
|
+
{ id: "2000", name: "C-3PO" },
|
|
91
|
+
]);
|
|
92
|
+
}
|
|
93
|
+
function query2(qyDB) {
|
|
94
|
+
deepEqual(qyDB.queryFiles("Characters", { friends: { $or: ["1000"] } }).map((x) => x.view(["id", "name"])), [
|
|
95
|
+
{ id: "1002", name: "Han Solo" },
|
|
96
|
+
{ id: "1003", name: "Leia Organa" },
|
|
97
|
+
{ id: "2000", name: "C-3PO" },
|
|
98
|
+
{ id: "2001", name: "R2-D2" },
|
|
99
|
+
]);
|
|
100
|
+
deepEqual(qyDB.queryFiles("Characters", { friends: { $or: ["1002", "1003"] } }).map((x) => x.view(["id", "name"])), [
|
|
101
|
+
{ id: "1000", name: "Luke Skywalker" },
|
|
102
|
+
{ id: "1003", name: "Leia Organa" },
|
|
103
|
+
{ id: "2000", name: "C-3PO" },
|
|
104
|
+
{ id: "2001", name: "R2-D2" },
|
|
105
|
+
{ id: "1002", name: "Han Solo" },
|
|
106
|
+
]);
|
|
107
|
+
deepEqual(qyDB
|
|
108
|
+
.queryFiles("Characters", {
|
|
109
|
+
friends: { $link: "Characters", $spec: { friends: { $link: "Characters", $spec: { friends: "1003" } } } },
|
|
110
|
+
})
|
|
111
|
+
.map((x) => x.view(["id", "name"])), [
|
|
112
|
+
{ id: "1002", name: "Han Solo" },
|
|
113
|
+
{ id: "1003", name: "Leia Organa" },
|
|
114
|
+
{ id: "2000", name: "C-3PO" },
|
|
115
|
+
{ id: "2001", name: "R2-D2" },
|
|
116
|
+
{ id: "1000", name: "Luke Skywalker" },
|
|
117
|
+
]);
|
|
118
|
+
deepEqual(qyDB
|
|
119
|
+
.queryFiles("Characters", {
|
|
120
|
+
friends: { $link: "Characters", $spec: { friends: [{ $link: "Characters", $spec: { friends: "1003" } }] } },
|
|
121
|
+
})
|
|
122
|
+
.map((x) => x.view(["id", "name"])), [
|
|
123
|
+
{ id: "1002", name: "Han Solo" },
|
|
124
|
+
{ id: "1003", name: "Leia Organa" },
|
|
125
|
+
{ id: "2000", name: "C-3PO" },
|
|
126
|
+
{ id: "2001", name: "R2-D2" },
|
|
127
|
+
{ id: "1000", name: "Luke Skywalker" },
|
|
128
|
+
]);
|
|
129
|
+
}
|
|
130
|
+
function query3(qyDB) {
|
|
131
|
+
deepEqual(qyDB.queryFiles("Characters", { friends: { $or: ["1000"] } }).map((x) => x.view(["id", "name"])), [
|
|
132
|
+
{ id: "1002", name: "Han Solo" },
|
|
133
|
+
{ id: "1003", name: "Leia Organa" },
|
|
134
|
+
{ id: "2000", name: "C-3PO" },
|
|
135
|
+
{ id: "2001", name: "R2-D2" },
|
|
136
|
+
]);
|
|
137
|
+
deepEqual(qyDB
|
|
138
|
+
.queryFiles("Characters", { friends: (x) => x.includes("1002") || x.includes("1003") })
|
|
139
|
+
.map((x) => x.view(["id", "name"])), [
|
|
140
|
+
{ id: "1000", name: "Luke Skywalker" },
|
|
141
|
+
{ id: "1003", name: "Leia Organa" },
|
|
142
|
+
{ id: "2000", name: "C-3PO" },
|
|
143
|
+
{ id: "2001", name: "R2-D2" },
|
|
144
|
+
{ id: "1002", name: "Han Solo" },
|
|
145
|
+
]);
|
|
146
|
+
deepEqual(qyDB
|
|
147
|
+
.queryFiles("Characters", {
|
|
148
|
+
appearsIn: 6,
|
|
149
|
+
friends: {
|
|
150
|
+
$link: "Characters",
|
|
151
|
+
$spec: {
|
|
152
|
+
friends: {
|
|
153
|
+
$link: "Characters",
|
|
154
|
+
$spec: { friends: (x) => x.includes("1003"), appearsIn: (x) => x.includes(6) },
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
})
|
|
159
|
+
.map((x) => x.view(["id", "name"])), [
|
|
160
|
+
{ id: "1000", name: "Luke Skywalker" },
|
|
161
|
+
{ id: "1002", name: "Han Solo" },
|
|
162
|
+
{ id: "1003", name: "Leia Organa" },
|
|
163
|
+
{ id: "2000", name: "C-3PO" },
|
|
164
|
+
]);
|
|
165
|
+
deepEqual(qyDB
|
|
166
|
+
.queryFiles("Characters", {
|
|
167
|
+
appearsIn: 6,
|
|
168
|
+
type: "Droid",
|
|
169
|
+
friends: {
|
|
170
|
+
$link: "Characters",
|
|
171
|
+
$spec: {
|
|
172
|
+
type: "Droid",
|
|
173
|
+
friends: {
|
|
174
|
+
$link: "Characters",
|
|
175
|
+
$spec: { friends: (x) => x.includes("1003"), appearsIn: (x) => x.includes(6) },
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
})
|
|
180
|
+
.map((x) => x.view(["id", "name"])), [{ id: "2000", name: "C-3PO" }]);
|
|
181
|
+
}
|
|
182
|
+
test("test QyGraphQL", { skip: false }, async (context) => {
|
|
183
|
+
context.after(async (t) => {
|
|
184
|
+
await qyDB.stop();
|
|
185
|
+
});
|
|
186
|
+
const dataFolder = "./tmp/QyGraphQL/1";
|
|
187
|
+
const qyDB = new QyDB(dataFolder);
|
|
188
|
+
await qyDB.start();
|
|
189
|
+
insertData(qyDB);
|
|
190
|
+
query1(qyDB);
|
|
191
|
+
query2(qyDB);
|
|
192
|
+
query3(qyDB);
|
|
193
|
+
await qyDB.stop();
|
|
194
|
+
await qyDB.start();
|
|
195
|
+
query1(qyDB);
|
|
196
|
+
query2(qyDB);
|
|
197
|
+
query3(qyDB);
|
|
198
|
+
});
|