@instantdb/core
Advanced tools
Comparing version 0.10.9 to 0.10.10
@@ -58,11 +58,7 @@ import { test, expect } from "vitest"; | ||
expect( | ||
queryWhere( | ||
store, | ||
[ | ||
["?movieId", aid("movie/title"), "The Terminator"], | ||
["?movieId", aid("movie/director"), "?directorId"], | ||
["?directorId", aid("person/name"), "?directorName"], | ||
], | ||
{}, | ||
), | ||
queryWhere(store, [ | ||
["?movieId", aid("movie/title"), "The Terminator"], | ||
["?movieId", aid("movie/director"), "?directorId"], | ||
["?directorId", aid("person/name"), "?directorName"], | ||
]), | ||
).toEqual([ | ||
@@ -69,0 +65,0 @@ { |
@@ -87,2 +87,94 @@ import { test, expect } from "vitest"; | ||
test.each([ | ||
[ | ||
"mutliple OR matches", | ||
{ | ||
or: [{ handle: "stopa" }, { handle: "joe" }], | ||
}, | ||
["joe", "stopa"], | ||
], | ||
[ | ||
"mix of matching and non-matching", | ||
{ | ||
or: [{ handle: "nobody" }, { handle: "stopa" }, { handle: "everybody" }], | ||
}, | ||
["stopa"], | ||
], | ||
[ | ||
"with and", | ||
{ | ||
"bookshelves.books.title": "The Count of Monte Cristo", | ||
or: [{ handle: "joe" }, { handle: "stopa" }], | ||
}, | ||
["stopa"], | ||
], | ||
[ | ||
"with references", | ||
{ | ||
or: [ | ||
{ handle: "joe" }, | ||
{ | ||
handle: "stopa", | ||
"bookshelves.books.title": "The Count of Monte Cristo", | ||
}, | ||
], | ||
}, | ||
["joe", "stopa"], | ||
], | ||
[ | ||
"with references in both `or` & `and` clauses, no matches", | ||
{ | ||
"bookshelves.books.title": "Unknown", | ||
or: [ | ||
{ handle: "joe" }, | ||
{ | ||
handle: "stopa", | ||
"bookshelves.books.title": "The Count of Monte Cristo", | ||
}, | ||
], | ||
}, | ||
[], | ||
], | ||
[ | ||
"with references in both `or` & `and` clauses, with matches", | ||
{ | ||
"bookshelves.books.title": "A Promised Land", | ||
or: [ | ||
{ | ||
handle: "stopa", | ||
"bookshelves.books.title": "The Count of Monte Cristo", | ||
}, | ||
{ | ||
handle: "joe", | ||
}, | ||
], | ||
}, | ||
["joe"], | ||
], | ||
[ | ||
"with nested ors", | ||
{ | ||
or: [ | ||
{ or: [{ handle: "stopa" }] }, | ||
{ | ||
handle: "joe", | ||
}, | ||
], | ||
}, | ||
["joe", "stopa"], | ||
], | ||
])("Where OR %s", (_, whereQuery, expected) => { | ||
expect( | ||
query(store, { | ||
users: { | ||
$: { | ||
where: whereQuery, | ||
}, | ||
}, | ||
}) | ||
.users.map((x) => x.handle) | ||
.sort(), | ||
).toEqual(expected); | ||
}); | ||
test("Get association", () => { | ||
@@ -155,2 +247,25 @@ expect( | ||
test("Nested wheres with OR queries", () => { | ||
expect( | ||
query(store, { | ||
users: { | ||
bookshelves: { | ||
books: {}, | ||
$: { | ||
where: { or: [{ name: "Short Stories" }] }, | ||
}, | ||
}, | ||
$: { where: { handle: "alex" } }, | ||
}, | ||
}) | ||
.users.flatMap((x) => x.bookshelves) | ||
.flatMap((x) => x.books) | ||
.map((x) => x.title), | ||
).toEqual([ | ||
"The Paper Menagerie and Other Stories", | ||
"Stories of Your Life and Others", | ||
"Aesop's Fables", | ||
]); | ||
}); | ||
test("Deep where", () => { | ||
@@ -157,0 +272,0 @@ expect( |
export function matchPattern(pattern: any, triple: any, context: any): any; | ||
export function querySingle(store: any, pattern: any, context: any): any[]; | ||
export function queryWhere(store: any, patterns: any): any; | ||
export function queryWhere(store: any, patterns: any, contexts?: {}[]): any; | ||
export function query(store: any, { find, where }: { | ||
@@ -5,0 +5,0 @@ find: any; |
@@ -57,6 +57,14 @@ "use strict"; | ||
// 3. queryWhere | ||
function queryWhere(store, patterns) { | ||
function queryPattern(store, pattern, contexts) { | ||
if (pattern.or) { | ||
return pattern.or.patterns.flatMap((patterns) => { | ||
return queryWhere(store, patterns, contexts); | ||
}); | ||
} | ||
return contexts.flatMap((context) => querySingle(store, pattern, context)); | ||
} | ||
function queryWhere(store, patterns, contexts = [{}]) { | ||
return patterns.reduce((contexts, pattern) => { | ||
return contexts.flatMap((context) => querySingle(store, pattern, context)); | ||
}, [{}]); | ||
return queryPattern(store, pattern, contexts); | ||
}, contexts); | ||
} | ||
@@ -63,0 +71,0 @@ exports.queryWhere = queryWhere; |
@@ -29,5 +29,5 @@ "use strict"; | ||
function wildcard(friendlyName) { | ||
return makeVar(`_${friendlyName}`, _seed++); | ||
return makeVarImpl(`_${friendlyName}`, _seed++); | ||
} | ||
function makeVar(x, level) { | ||
function makeVarImpl(x, level) { | ||
return `?${x}-${level}`; | ||
@@ -50,6 +50,9 @@ } | ||
} | ||
function defaultWhere(store, etype, level) { | ||
return [eidWhere(store, etype, level), attrWhere(etype, level)]; | ||
function defaultWhere(makeVar, store, etype, level) { | ||
return [ | ||
eidWhere(makeVar, store, etype, level), | ||
attrWhere(makeVar, etype, level), | ||
]; | ||
} | ||
function eidWhere(store, etype, level) { | ||
function eidWhere(makeVar, store, etype, level) { | ||
return [ | ||
@@ -62,3 +65,3 @@ makeVar(etype, level), | ||
} | ||
function attrWhere(etype, level) { | ||
function attrWhere(makeVar, etype, level) { | ||
return [ | ||
@@ -74,3 +77,3 @@ makeVar(etype, level), | ||
} | ||
function refAttrPat(store, etype, level, label) { | ||
function refAttrPat(makeVar, store, etype, level, label) { | ||
const fwdAttr = getAttrByFwdIdentName(store.attrs, etype, label); | ||
@@ -104,3 +107,3 @@ const revAttr = getAttrByReverseIdentName(store.attrs, etype, label); | ||
} | ||
function valueAttrPat(store, valueEtype, valueLevel, valueLabel, v) { | ||
function valueAttrPat(makeVar, store, valueEtype, valueLevel, valueLabel, v) { | ||
const attr = getAttrByFwdIdentName(store.attrs, valueEtype, valueLabel); | ||
@@ -112,6 +115,6 @@ if (!attr) { | ||
} | ||
function refAttrPats(store, etype, level, refsPath) { | ||
function refAttrPats(makeVar, store, etype, level, refsPath) { | ||
const [lastEtype, lastLevel, attrPats] = refsPath.reduce((acc, label) => { | ||
const [etype, level, attrPats] = acc; | ||
const [nextEtype, nextLevel, attrPat] = refAttrPat(store, etype, level, label); | ||
const [nextEtype, nextLevel, attrPat] = refAttrPat(makeVar, store, etype, level, label); | ||
return [nextEtype, nextLevel, [...attrPats, attrPat]]; | ||
@@ -121,7 +124,7 @@ }, [etype, level, []]); | ||
} | ||
function whereCondAttrPats(store, etype, level, path, v) { | ||
function whereCondAttrPats(makeVar, store, etype, level, path, v) { | ||
const refsPath = path.slice(0, path.length - 1); | ||
const valueLabel = path[path.length - 1]; | ||
const [lastEtype, lastLevel, refPats] = refAttrPats(store, etype, level, refsPath); | ||
const valuePat = valueAttrPat(store, lastEtype, lastLevel, valueLabel, v); | ||
const [lastEtype, lastLevel, refPats] = refAttrPats(makeVar, store, etype, level, refsPath); | ||
const valuePat = valueAttrPat(makeVar, store, lastEtype, lastLevel, valueLabel, v); | ||
return refPats.concat([valuePat]); | ||
@@ -132,15 +135,43 @@ } | ||
} | ||
function isOrClauses([k, v]) { | ||
return k === "or" && Array.isArray(v); | ||
} | ||
// Creates a makeVar that will namespace symbols for or clauses | ||
// to prevent conflicts, except for the base etype | ||
function genMakeVar(baseMakeVar, etype, orIdx) { | ||
return (x, lvl) => { | ||
if (x == etype) { | ||
return baseMakeVar(x, lvl); | ||
} | ||
return `${baseMakeVar(x, lvl)}-${orIdx}`; | ||
}; | ||
} | ||
function parseWhereOrClauses(makeVar, store, etype, level, whereValue) { | ||
const patterns = whereValue.map((w, i) => { | ||
const makeOrVar = genMakeVar(makeVar, etype, i); | ||
return parseWhere(makeOrVar, store, etype, level, w); | ||
}); | ||
const joinSym = makeVar(etype, level); | ||
return { or: { patterns, joinSym } }; | ||
} | ||
function parseWhere(makeVar, store, etype, level, where) { | ||
return Object.entries(where).flatMap(([k, v]) => { | ||
if (isOrClauses([k, v])) { | ||
return parseWhereOrClauses(makeVar, store, etype, level, v); | ||
} | ||
const path = k.split("."); | ||
return whereCondAttrPats(makeVar, store, etype, level, path, v); | ||
}); | ||
} | ||
function makeWhere(store, etype, level, where) { | ||
const makeVar = makeVarImpl; | ||
if (!where) { | ||
return defaultWhere(store, etype, level); | ||
return defaultWhere(makeVar, store, etype, level); | ||
} | ||
const parsedWhere = Object.entries(where).flatMap(([k, v]) => { | ||
const path = k.split("."); | ||
return whereCondAttrPats(store, etype, level, path, v); | ||
}); | ||
return parsedWhere.concat(defaultWhere(store, etype, level)); | ||
const parsedWhere = parseWhere(makeVar, store, etype, level, where); | ||
return parsedWhere.concat(defaultWhere(makeVar, store, etype, level)); | ||
} | ||
// Find | ||
// ----------------- | ||
function makeFind(etype, level) { | ||
function makeFind(makeVar, etype, level) { | ||
return [ | ||
@@ -155,8 +186,8 @@ makeVar(etype, level), | ||
// ----------------- | ||
function makeJoin(store, etype, level, label, eid) { | ||
const [nextEtype, nextLevel, pat] = refAttrPat(store, etype, level, label); | ||
function makeJoin(makeVar, store, etype, level, label, eid) { | ||
const [nextEtype, nextLevel, pat] = refAttrPat(makeVar, store, etype, level, label); | ||
const actualized = replaceInAttrPat(pat, makeVar(etype, level), eid); | ||
return [nextEtype, nextLevel, actualized]; | ||
} | ||
function extendObjects(store, { etype, level, form }, objects) { | ||
function extendObjects(makeVar, store, { etype, level, form }, objects) { | ||
const children = Object.keys(form).filter((c) => c !== "$"); | ||
@@ -169,3 +200,3 @@ if (!children.length) { | ||
try { | ||
const [nextEtype, nextLevel, join] = makeJoin(store, etype, level, label, eid); | ||
const [nextEtype, nextLevel, join] = makeJoin(makeVar, store, etype, level, label, eid); | ||
const child = queryOne(store, { | ||
@@ -231,3 +262,3 @@ etype: nextEtype, | ||
const where = withJoin(makeWhere(store, etype, level, (_a = form.$) === null || _a === void 0 ? void 0 : _a.where), join); | ||
const find = makeFind(etype, level); | ||
const find = makeFind(makeVarImpl, etype, level); | ||
return runDataloadAndReturnObjects(store, { where, find }); | ||
@@ -270,3 +301,3 @@ } | ||
const objects = guardedResolveObjects(store, opts); | ||
return extendObjects(store, opts, objects); | ||
return extendObjects(makeVarImpl, store, opts, objects); | ||
} | ||
@@ -273,0 +304,0 @@ function query(store, q) { |
export function matchPattern(pattern: any, triple: any, context: any): any; | ||
export function querySingle(store: any, pattern: any, context: any): any[]; | ||
export function queryWhere(store: any, patterns: any): any; | ||
export function queryWhere(store: any, patterns: any, contexts?: {}[]): any; | ||
export function query(store: any, { find, where }: { | ||
@@ -5,0 +5,0 @@ find: any; |
@@ -52,6 +52,14 @@ // 1. patternMatch | ||
// 3. queryWhere | ||
export function queryWhere(store, patterns) { | ||
function queryPattern(store, pattern, contexts) { | ||
if (pattern.or) { | ||
return pattern.or.patterns.flatMap((patterns) => { | ||
return queryWhere(store, patterns, contexts); | ||
}); | ||
} | ||
return contexts.flatMap((context) => querySingle(store, pattern, context)); | ||
} | ||
export function queryWhere(store, patterns, contexts = [{}]) { | ||
return patterns.reduce((contexts, pattern) => { | ||
return contexts.flatMap((context) => querySingle(store, pattern, context)); | ||
}, [{}]); | ||
return queryPattern(store, pattern, contexts); | ||
}, contexts); | ||
} | ||
@@ -58,0 +66,0 @@ // 4. query |
@@ -27,5 +27,5 @@ import { query as datalogQuery } from "./datalog"; | ||
function wildcard(friendlyName) { | ||
return makeVar(`_${friendlyName}`, _seed++); | ||
return makeVarImpl(`_${friendlyName}`, _seed++); | ||
} | ||
function makeVar(x, level) { | ||
function makeVarImpl(x, level) { | ||
return `?${x}-${level}`; | ||
@@ -48,6 +48,9 @@ } | ||
} | ||
function defaultWhere(store, etype, level) { | ||
return [eidWhere(store, etype, level), attrWhere(etype, level)]; | ||
function defaultWhere(makeVar, store, etype, level) { | ||
return [ | ||
eidWhere(makeVar, store, etype, level), | ||
attrWhere(makeVar, etype, level), | ||
]; | ||
} | ||
function eidWhere(store, etype, level) { | ||
function eidWhere(makeVar, store, etype, level) { | ||
return [ | ||
@@ -60,3 +63,3 @@ makeVar(etype, level), | ||
} | ||
function attrWhere(etype, level) { | ||
function attrWhere(makeVar, etype, level) { | ||
return [ | ||
@@ -72,3 +75,3 @@ makeVar(etype, level), | ||
} | ||
function refAttrPat(store, etype, level, label) { | ||
function refAttrPat(makeVar, store, etype, level, label) { | ||
const fwdAttr = getAttrByFwdIdentName(store.attrs, etype, label); | ||
@@ -102,3 +105,3 @@ const revAttr = getAttrByReverseIdentName(store.attrs, etype, label); | ||
} | ||
function valueAttrPat(store, valueEtype, valueLevel, valueLabel, v) { | ||
function valueAttrPat(makeVar, store, valueEtype, valueLevel, valueLabel, v) { | ||
const attr = getAttrByFwdIdentName(store.attrs, valueEtype, valueLabel); | ||
@@ -110,6 +113,6 @@ if (!attr) { | ||
} | ||
function refAttrPats(store, etype, level, refsPath) { | ||
function refAttrPats(makeVar, store, etype, level, refsPath) { | ||
const [lastEtype, lastLevel, attrPats] = refsPath.reduce((acc, label) => { | ||
const [etype, level, attrPats] = acc; | ||
const [nextEtype, nextLevel, attrPat] = refAttrPat(store, etype, level, label); | ||
const [nextEtype, nextLevel, attrPat] = refAttrPat(makeVar, store, etype, level, label); | ||
return [nextEtype, nextLevel, [...attrPats, attrPat]]; | ||
@@ -119,7 +122,7 @@ }, [etype, level, []]); | ||
} | ||
function whereCondAttrPats(store, etype, level, path, v) { | ||
function whereCondAttrPats(makeVar, store, etype, level, path, v) { | ||
const refsPath = path.slice(0, path.length - 1); | ||
const valueLabel = path[path.length - 1]; | ||
const [lastEtype, lastLevel, refPats] = refAttrPats(store, etype, level, refsPath); | ||
const valuePat = valueAttrPat(store, lastEtype, lastLevel, valueLabel, v); | ||
const [lastEtype, lastLevel, refPats] = refAttrPats(makeVar, store, etype, level, refsPath); | ||
const valuePat = valueAttrPat(makeVar, store, lastEtype, lastLevel, valueLabel, v); | ||
return refPats.concat([valuePat]); | ||
@@ -130,15 +133,43 @@ } | ||
} | ||
function isOrClauses([k, v]) { | ||
return k === "or" && Array.isArray(v); | ||
} | ||
// Creates a makeVar that will namespace symbols for or clauses | ||
// to prevent conflicts, except for the base etype | ||
function genMakeVar(baseMakeVar, etype, orIdx) { | ||
return (x, lvl) => { | ||
if (x == etype) { | ||
return baseMakeVar(x, lvl); | ||
} | ||
return `${baseMakeVar(x, lvl)}-${orIdx}`; | ||
}; | ||
} | ||
function parseWhereOrClauses(makeVar, store, etype, level, whereValue) { | ||
const patterns = whereValue.map((w, i) => { | ||
const makeOrVar = genMakeVar(makeVar, etype, i); | ||
return parseWhere(makeOrVar, store, etype, level, w); | ||
}); | ||
const joinSym = makeVar(etype, level); | ||
return { or: { patterns, joinSym } }; | ||
} | ||
function parseWhere(makeVar, store, etype, level, where) { | ||
return Object.entries(where).flatMap(([k, v]) => { | ||
if (isOrClauses([k, v])) { | ||
return parseWhereOrClauses(makeVar, store, etype, level, v); | ||
} | ||
const path = k.split("."); | ||
return whereCondAttrPats(makeVar, store, etype, level, path, v); | ||
}); | ||
} | ||
function makeWhere(store, etype, level, where) { | ||
const makeVar = makeVarImpl; | ||
if (!where) { | ||
return defaultWhere(store, etype, level); | ||
return defaultWhere(makeVar, store, etype, level); | ||
} | ||
const parsedWhere = Object.entries(where).flatMap(([k, v]) => { | ||
const path = k.split("."); | ||
return whereCondAttrPats(store, etype, level, path, v); | ||
}); | ||
return parsedWhere.concat(defaultWhere(store, etype, level)); | ||
const parsedWhere = parseWhere(makeVar, store, etype, level, where); | ||
return parsedWhere.concat(defaultWhere(makeVar, store, etype, level)); | ||
} | ||
// Find | ||
// ----------------- | ||
function makeFind(etype, level) { | ||
function makeFind(makeVar, etype, level) { | ||
return [ | ||
@@ -153,8 +184,8 @@ makeVar(etype, level), | ||
// ----------------- | ||
function makeJoin(store, etype, level, label, eid) { | ||
const [nextEtype, nextLevel, pat] = refAttrPat(store, etype, level, label); | ||
function makeJoin(makeVar, store, etype, level, label, eid) { | ||
const [nextEtype, nextLevel, pat] = refAttrPat(makeVar, store, etype, level, label); | ||
const actualized = replaceInAttrPat(pat, makeVar(etype, level), eid); | ||
return [nextEtype, nextLevel, actualized]; | ||
} | ||
function extendObjects(store, { etype, level, form }, objects) { | ||
function extendObjects(makeVar, store, { etype, level, form }, objects) { | ||
const children = Object.keys(form).filter((c) => c !== "$"); | ||
@@ -167,3 +198,3 @@ if (!children.length) { | ||
try { | ||
const [nextEtype, nextLevel, join] = makeJoin(store, etype, level, label, eid); | ||
const [nextEtype, nextLevel, join] = makeJoin(makeVar, store, etype, level, label, eid); | ||
const child = queryOne(store, { | ||
@@ -229,3 +260,3 @@ etype: nextEtype, | ||
const where = withJoin(makeWhere(store, etype, level, (_a = form.$) === null || _a === void 0 ? void 0 : _a.where), join); | ||
const find = makeFind(etype, level); | ||
const find = makeFind(makeVarImpl, etype, level); | ||
return runDataloadAndReturnObjects(store, { where, find }); | ||
@@ -268,3 +299,3 @@ } | ||
const objects = guardedResolveObjects(store, opts); | ||
return extendObjects(store, opts, objects); | ||
return extendObjects(makeVarImpl, store, opts, objects); | ||
} | ||
@@ -271,0 +302,0 @@ export default function query(store, q) { |
@@ -7,5 +7,10 @@ declare type NonEmpty<T> = { | ||
}; | ||
declare type WhereClause = { | ||
[key: string]: string | number | boolean | NonEmpty<WhereArgs>; | ||
declare type WhereClauseValue = string | number | boolean | NonEmpty<WhereArgs>; | ||
declare type WhereClauseWithOr = { | ||
or?: BaseWhereClause[] | WhereClauseValue; | ||
}; | ||
declare type BaseWhereClause = { | ||
[key: string]: WhereClauseValue; | ||
}; | ||
declare type WhereClause = WhereClauseWithOr | (WhereClauseWithOr & BaseWhereClause); | ||
declare type $Option = { | ||
@@ -12,0 +17,0 @@ $?: { |
@@ -39,2 +39,9 @@ // Query | ||
}); | ||
const t1 = dummyQuery({ | ||
users: { $: { where: { or: [{ foo: 1 }] } } }, | ||
}); | ||
// You can have a field named or | ||
const t2 = dummyQuery({ | ||
users: { $: { where: { or: "fieldNamedOr" } } }, | ||
}); | ||
// ------------------ | ||
@@ -52,2 +59,6 @@ // Bad $ clauses fail | ||
}); | ||
const s3 = dummyQuery({ | ||
// @ts-expect-error | ||
users: { $: { where: { foo: [] } } }, | ||
}); | ||
// ---------------- | ||
@@ -54,0 +65,0 @@ // Good Nested queries succeed |
@@ -7,5 +7,10 @@ declare type NonEmpty<T> = { | ||
}; | ||
declare type WhereClause = { | ||
[key: string]: string | number | boolean | NonEmpty<WhereArgs>; | ||
declare type WhereClauseValue = string | number | boolean | NonEmpty<WhereArgs>; | ||
declare type WhereClauseWithOr = { | ||
or?: BaseWhereClause[] | WhereClauseValue; | ||
}; | ||
declare type BaseWhereClause = { | ||
[key: string]: WhereClauseValue; | ||
}; | ||
declare type WhereClause = WhereClauseWithOr | (WhereClauseWithOr & BaseWhereClause); | ||
declare type $Option = { | ||
@@ -12,0 +17,0 @@ $?: { |
@@ -41,2 +41,9 @@ "use strict"; | ||
}); | ||
const t1 = dummyQuery({ | ||
users: { $: { where: { or: [{ foo: 1 }] } } }, | ||
}); | ||
// You can have a field named or | ||
const t2 = dummyQuery({ | ||
users: { $: { where: { or: "fieldNamedOr" } } }, | ||
}); | ||
// ------------------ | ||
@@ -54,2 +61,6 @@ // Bad $ clauses fail | ||
}); | ||
const s3 = dummyQuery({ | ||
// @ts-expect-error | ||
users: { $: { where: { foo: [] } } }, | ||
}); | ||
// ---------------- | ||
@@ -56,0 +67,0 @@ // Good Nested queries succeed |
{ | ||
"name": "@instantdb/core", | ||
"version": "0.10.9", | ||
"version": "0.10.10", | ||
"description": "Instant's core local abstraction", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -62,13 +62,17 @@ // 1. patternMatch | ||
export function queryWhere(store, patterns) { | ||
return patterns.reduce( | ||
(contexts, pattern) => { | ||
return contexts.flatMap((context) => | ||
querySingle(store, pattern, context), | ||
); | ||
}, | ||
[{}], | ||
); | ||
function queryPattern(store, pattern, contexts) { | ||
if (pattern.or) { | ||
return pattern.or.patterns.flatMap((patterns) => { | ||
return queryWhere(store, patterns, contexts); | ||
}); | ||
} | ||
return contexts.flatMap((context) => querySingle(store, pattern, context)); | ||
} | ||
export function queryWhere(store, patterns, contexts = [{}]) { | ||
return patterns.reduce((contexts, pattern) => { | ||
return queryPattern(store, pattern, contexts); | ||
}, contexts); | ||
} | ||
// 4. query | ||
@@ -75,0 +79,0 @@ |
@@ -31,6 +31,6 @@ import { query as datalogQuery } from "./datalog"; | ||
function wildcard(friendlyName) { | ||
return makeVar(`_${friendlyName}`, _seed++); | ||
return makeVarImpl(`_${friendlyName}`, _seed++); | ||
} | ||
function makeVar(x, level) { | ||
function makeVarImpl(x, level) { | ||
return `?${x}-${level}`; | ||
@@ -57,7 +57,10 @@ } | ||
function defaultWhere(store, etype, level) { | ||
return [eidWhere(store, etype, level), attrWhere(etype, level)]; | ||
function defaultWhere(makeVar, store, etype, level) { | ||
return [ | ||
eidWhere(makeVar, store, etype, level), | ||
attrWhere(makeVar, etype, level), | ||
]; | ||
} | ||
function eidWhere(store, etype, level) { | ||
function eidWhere(makeVar, store, etype, level) { | ||
return [ | ||
@@ -71,3 +74,3 @@ makeVar(etype, level), | ||
function attrWhere(etype, level) { | ||
function attrWhere(makeVar, etype, level) { | ||
return [ | ||
@@ -85,3 +88,3 @@ makeVar(etype, level), | ||
function refAttrPat(store, etype, level, label) { | ||
function refAttrPat(makeVar, store, etype, level, label) { | ||
const fwdAttr = getAttrByFwdIdentName(store.attrs, etype, label); | ||
@@ -121,3 +124,3 @@ const revAttr = getAttrByReverseIdentName(store.attrs, etype, label); | ||
function valueAttrPat(store, valueEtype, valueLevel, valueLabel, v) { | ||
function valueAttrPat(makeVar, store, valueEtype, valueLevel, valueLabel, v) { | ||
const attr = getAttrByFwdIdentName(store.attrs, valueEtype, valueLabel); | ||
@@ -134,3 +137,3 @@ | ||
function refAttrPats(store, etype, level, refsPath) { | ||
function refAttrPats(makeVar, store, etype, level, refsPath) { | ||
const [lastEtype, lastLevel, attrPats] = refsPath.reduce( | ||
@@ -140,2 +143,3 @@ (acc, label) => { | ||
const [nextEtype, nextLevel, attrPat] = refAttrPat( | ||
makeVar, | ||
store, | ||
@@ -154,6 +158,7 @@ etype, | ||
function whereCondAttrPats(store, etype, level, path, v) { | ||
function whereCondAttrPats(makeVar, store, etype, level, path, v) { | ||
const refsPath = path.slice(0, path.length - 1); | ||
const valueLabel = path[path.length - 1]; | ||
const [lastEtype, lastLevel, refPats] = refAttrPats( | ||
makeVar, | ||
store, | ||
@@ -164,3 +169,10 @@ etype, | ||
); | ||
const valuePat = valueAttrPat(store, lastEtype, lastLevel, valueLabel, v); | ||
const valuePat = valueAttrPat( | ||
makeVar, | ||
store, | ||
lastEtype, | ||
lastLevel, | ||
valueLabel, | ||
v, | ||
); | ||
@@ -174,11 +186,43 @@ return refPats.concat([valuePat]); | ||
function isOrClauses([k, v]) { | ||
return k === "or" && Array.isArray(v); | ||
} | ||
// Creates a makeVar that will namespace symbols for or clauses | ||
// to prevent conflicts, except for the base etype | ||
function genMakeVar(baseMakeVar, etype, orIdx) { | ||
return (x, lvl) => { | ||
if (x == etype) { | ||
return baseMakeVar(x, lvl); | ||
} | ||
return `${baseMakeVar(x, lvl)}-${orIdx}`; | ||
}; | ||
} | ||
function parseWhereOrClauses(makeVar, store, etype, level, whereValue) { | ||
const patterns = whereValue.map((w, i) => { | ||
const makeOrVar = genMakeVar(makeVar, etype, i); | ||
return parseWhere(makeOrVar, store, etype, level, w); | ||
}); | ||
const joinSym = makeVar(etype, level); | ||
return { or: { patterns, joinSym } }; | ||
} | ||
function parseWhere(makeVar, store, etype, level, where) { | ||
return Object.entries(where).flatMap(([k, v]) => { | ||
if (isOrClauses([k, v])) { | ||
return parseWhereOrClauses(makeVar, store, etype, level, v); | ||
} | ||
const path = k.split("."); | ||
return whereCondAttrPats(makeVar, store, etype, level, path, v); | ||
}); | ||
} | ||
function makeWhere(store, etype, level, where) { | ||
const makeVar = makeVarImpl; | ||
if (!where) { | ||
return defaultWhere(store, etype, level); | ||
return defaultWhere(makeVar, store, etype, level); | ||
} | ||
const parsedWhere = Object.entries(where).flatMap(([k, v]) => { | ||
const path = k.split("."); | ||
return whereCondAttrPats(store, etype, level, path, v); | ||
}); | ||
return parsedWhere.concat(defaultWhere(store, etype, level)); | ||
const parsedWhere = parseWhere(makeVar, store, etype, level, where); | ||
return parsedWhere.concat(defaultWhere(makeVar, store, etype, level)); | ||
} | ||
@@ -189,3 +233,3 @@ | ||
function makeFind(etype, level) { | ||
function makeFind(makeVar, etype, level) { | ||
return [ | ||
@@ -202,4 +246,10 @@ makeVar(etype, level), | ||
function makeJoin(store, etype, level, label, eid) { | ||
const [nextEtype, nextLevel, pat] = refAttrPat(store, etype, level, label); | ||
function makeJoin(makeVar, store, etype, level, label, eid) { | ||
const [nextEtype, nextLevel, pat] = refAttrPat( | ||
makeVar, | ||
store, | ||
etype, | ||
level, | ||
label, | ||
); | ||
const actualized = replaceInAttrPat(pat, makeVar(etype, level), eid); | ||
@@ -209,3 +259,3 @@ return [nextEtype, nextLevel, actualized]; | ||
function extendObjects(store, { etype, level, form }, objects) { | ||
function extendObjects(makeVar, store, { etype, level, form }, objects) { | ||
const children = Object.keys(form).filter((c) => c !== "$"); | ||
@@ -219,2 +269,3 @@ if (!children.length) { | ||
const [nextEtype, nextLevel, join] = makeJoin( | ||
makeVar, | ||
store, | ||
@@ -288,3 +339,3 @@ etype, | ||
const where = withJoin(makeWhere(store, etype, level, form.$?.where), join); | ||
const find = makeFind(etype, level); | ||
const find = makeFind(makeVarImpl, etype, level); | ||
return runDataloadAndReturnObjects(store, { where, find }); | ||
@@ -327,3 +378,3 @@ } | ||
const objects = guardedResolveObjects(store, opts); | ||
return extendObjects(store, opts, objects); | ||
return extendObjects(makeVarImpl, store, opts, objects); | ||
} | ||
@@ -330,0 +381,0 @@ |
@@ -13,6 +13,11 @@ // Query | ||
type WhereClause = { | ||
[key: string]: string | number | boolean | NonEmpty<WhereArgs>; | ||
type WhereClauseValue = string | number | boolean | NonEmpty<WhereArgs>; | ||
type WhereClauseWithOr = { or?: BaseWhereClause[] | WhereClauseValue }; | ||
type BaseWhereClause = { | ||
[key: string]: WhereClauseValue; | ||
}; | ||
type WhereClause = WhereClauseWithOr | (WhereClauseWithOr & BaseWhereClause); | ||
type $Option = { $?: { where: WhereClause } }; | ||
@@ -143,2 +148,9 @@ | ||
}); | ||
const t1 = dummyQuery({ | ||
users: { $: { where: { or: [{ foo: 1 }] } } }, | ||
}); | ||
// You can have a field named or | ||
const t2 = dummyQuery({ | ||
users: { $: { where: { or: "fieldNamedOr" } } }, | ||
}); | ||
@@ -158,2 +170,6 @@ // ------------------ | ||
}); | ||
const s3 = dummyQuery({ | ||
// @ts-expect-error | ||
users: { $: { where: { foo: [] } } }, | ||
}); | ||
@@ -160,0 +176,0 @@ // ---------------- |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
1579668
35491