@instantdb/core
Advanced tools
Comparing version 0.14.23 to 0.14.24
@@ -9,2 +9,3 @@ import { test, expect } from "vitest"; | ||
import * as instaml from "../../src/instaml"; | ||
import { randomUUID } from "crypto"; | ||
@@ -646,1 +647,83 @@ const zenecaIdToAttr = zenecaAttrs.reduce((res, x) => { | ||
}); | ||
test("$isNull", () => { | ||
const q = { books: { $: { where: { title: { $isNull: true } } } } }; | ||
expect(query({ store }, q).data.books.length).toEqual(0); | ||
const chunks = [ | ||
tx.books[randomUUID()].update({ title: null }), | ||
tx.books[randomUUID()].update({ pageCount: 20 }), | ||
]; | ||
const txSteps = instaml.transform(store.attrs, chunks); | ||
const newStore = transact(store, txSteps); | ||
expect(query({ store: newStore }, q).data.books.map((x) => x.title)).toEqual([ | ||
null, | ||
undefined, | ||
]); | ||
}); | ||
test("$isNull with relations", () => { | ||
const q = { users: { $: { where: { bookshelves: { $isNull: true } } } } }; | ||
expect(query({ store }, q).data.users.length).toEqual(0); | ||
const chunks = [tx.users[randomUUID()].update({ handle: "dww" })]; | ||
const txSteps = instaml.transform(store.attrs, chunks); | ||
const newStore = transact(store, txSteps); | ||
expect(query({ store: newStore }, q).data.users.map((x) => x.handle)).toEqual( | ||
["dww"], | ||
); | ||
const bookId = query( | ||
{ store }, | ||
{ books: { $: { where: { title: "The Count of Monte Cristo" } } } }, | ||
).data.books[0].id; | ||
const usersWithBook = query( | ||
{ store }, | ||
{ | ||
users: { | ||
$: { | ||
where: { "bookshelves.books.title": "The Count of Monte Cristo" }, | ||
}, | ||
}, | ||
}, | ||
).data.users.map((x) => x.handle); | ||
const storeWithNullTitle = transact( | ||
newStore, | ||
instaml.transform(newStore.attrs, [ | ||
tx.books[bookId].update({ title: null }), | ||
]), | ||
); | ||
const usersWithNullTitle = query( | ||
{ store: storeWithNullTitle }, | ||
{ | ||
users: { | ||
$: { | ||
where: { "bookshelves.books.title": { $isNull: true } }, | ||
}, | ||
}, | ||
}, | ||
).data.users.map((x) => x.handle); | ||
expect(usersWithNullTitle).toEqual([...usersWithBook, "dww"]); | ||
}); | ||
test("$not", () => { | ||
const q = { tests: { $: { where: { val: { $not: "a" } } } } }; | ||
expect(query({ store }, q).data.tests.length).toEqual(0); | ||
const chunks = [ | ||
tx.tests[randomUUID()].update({ val: "a" }), | ||
tx.tests[randomUUID()].update({ val: "b" }), | ||
tx.tests[randomUUID()].update({ val: "c" }), | ||
tx.tests[randomUUID()].update({ val: null }), | ||
tx.tests[randomUUID()].update({ undefinedVal: "d" }), | ||
]; | ||
const txSteps = instaml.transform(store.attrs, chunks); | ||
const newStore = transact(store, txSteps); | ||
expect(query({ store: newStore }, q).data.tests.map((x) => x.val)).toEqual([ | ||
"b", | ||
"c", | ||
null, | ||
undefined, | ||
]); | ||
}); |
@@ -27,2 +27,8 @@ "use strict"; | ||
} | ||
if (patternPart.hasOwnProperty("$not") || | ||
patternPart.hasOwnProperty("$isNull")) { | ||
// If we use `$not` or `$isNull`, we've already done the filtering in | ||
// `getTriples` | ||
return context; | ||
} | ||
return null; | ||
@@ -29,0 +35,0 @@ } |
@@ -111,2 +111,14 @@ "use strict"; | ||
} | ||
if (v === null || v === void 0 ? void 0 : v.hasOwnProperty("$isNull")) { | ||
const idAttr = (0, instaml_1.getAttrByFwdIdentName)(store.attrs, valueEtype, "id"); | ||
if (!idAttr) { | ||
throw new AttrNotFoundError(`No attr for etype = ${valueEtype} label = id value-label`); | ||
} | ||
return [ | ||
makeVar(valueEtype, valueLevel), | ||
idAttr.id, | ||
{ $isNull: { attrId: attr.id, isNull: v.$isNull } }, | ||
wildcard("time"), | ||
]; | ||
} | ||
return [makeVar(valueEtype, valueLevel), attr.id, v, wildcard("time")]; | ||
@@ -156,2 +168,16 @@ } | ||
} | ||
// Given a path, returns a list of paths leading up to this path: | ||
// growPath([1, 2, 3]) -> [[1], [1, 2], [1, 2, 3]] | ||
function growPath(path) { | ||
const ret = []; | ||
for (let i = 1; i <= path.length; i++) { | ||
ret.push(path.slice(0, i)); | ||
} | ||
return ret; | ||
} | ||
// Returns array of pattern arrays that should be grouped in OR | ||
// to capture any intermediate nulls | ||
function whereCondAttrPatsForNullIsTrue(makeVar, store, etype, level, path) { | ||
return growPath(path).map((path) => whereCondAttrPats(makeVar, store, etype, level, path, { $isNull: true })); | ||
} | ||
function parseWhere(makeVar, store, etype, level, where) { | ||
@@ -166,2 +192,28 @@ return Object.entries(where).flatMap(([k, v]) => { | ||
const path = k.split("."); | ||
if (v === null || v === void 0 ? void 0 : v.hasOwnProperty("$not")) { | ||
// `$not` won't pick up entities that are missing the attr, so we | ||
// add in a `$isNull` to catch those too. | ||
const notPats = whereCondAttrPats(makeVar, store, etype, level, path, v); | ||
const nilPats = whereCondAttrPatsForNullIsTrue(makeVar, store, etype, level, path); | ||
return [ | ||
{ | ||
or: { | ||
patterns: [notPats, ...nilPats], | ||
joinSym: makeVar(etype, level), | ||
}, | ||
}, | ||
]; | ||
} | ||
if ((v === null || v === void 0 ? void 0 : v.hasOwnProperty("$isNull")) && v.$isNull === true && path.length > 1) { | ||
// Make sure we're capturing all of the intermediate paths that might be null | ||
// by checking for null at each step along the path | ||
return [ | ||
{ | ||
or: { | ||
patterns: whereCondAttrPatsForNullIsTrue(makeVar, store, etype, level, path), | ||
joinSym: makeVar(etype, level), | ||
}, | ||
}, | ||
]; | ||
} | ||
return whereCondAttrPats(makeVar, store, etype, level, path, v); | ||
@@ -254,4 +306,3 @@ }); | ||
const aid = idAttr(store, etype).id; | ||
const idVecs = (0, datalog_1.query)(store, dq) | ||
.sort(([_, tsA], [__, tsB]) => { | ||
const idVecs = (0, datalog_1.query)(store, dq).sort(([_, tsA], [__, tsB]) => { | ||
return direction === "desc" ? tsB - tsA : tsA - tsB; | ||
@@ -258,0 +309,0 @@ }); |
@@ -21,2 +21,8 @@ // 1. patternMatch | ||
} | ||
if (patternPart.hasOwnProperty("$not") || | ||
patternPart.hasOwnProperty("$isNull")) { | ||
// If we use `$not` or `$isNull`, we've already done the filtering in | ||
// `getTriples` | ||
return context; | ||
} | ||
return null; | ||
@@ -23,0 +29,0 @@ } |
@@ -85,2 +85,14 @@ import { query as datalogQuery } from "./datalog"; | ||
} | ||
if (v === null || v === void 0 ? void 0 : v.hasOwnProperty("$isNull")) { | ||
const idAttr = getAttrByFwdIdentName(store.attrs, valueEtype, "id"); | ||
if (!idAttr) { | ||
throw new AttrNotFoundError(`No attr for etype = ${valueEtype} label = id value-label`); | ||
} | ||
return [ | ||
makeVar(valueEtype, valueLevel), | ||
idAttr.id, | ||
{ $isNull: { attrId: attr.id, isNull: v.$isNull } }, | ||
wildcard("time"), | ||
]; | ||
} | ||
return [makeVar(valueEtype, valueLevel), attr.id, v, wildcard("time")]; | ||
@@ -130,2 +142,16 @@ } | ||
} | ||
// Given a path, returns a list of paths leading up to this path: | ||
// growPath([1, 2, 3]) -> [[1], [1, 2], [1, 2, 3]] | ||
function growPath(path) { | ||
const ret = []; | ||
for (let i = 1; i <= path.length; i++) { | ||
ret.push(path.slice(0, i)); | ||
} | ||
return ret; | ||
} | ||
// Returns array of pattern arrays that should be grouped in OR | ||
// to capture any intermediate nulls | ||
function whereCondAttrPatsForNullIsTrue(makeVar, store, etype, level, path) { | ||
return growPath(path).map((path) => whereCondAttrPats(makeVar, store, etype, level, path, { $isNull: true })); | ||
} | ||
function parseWhere(makeVar, store, etype, level, where) { | ||
@@ -140,2 +166,28 @@ return Object.entries(where).flatMap(([k, v]) => { | ||
const path = k.split("."); | ||
if (v === null || v === void 0 ? void 0 : v.hasOwnProperty("$not")) { | ||
// `$not` won't pick up entities that are missing the attr, so we | ||
// add in a `$isNull` to catch those too. | ||
const notPats = whereCondAttrPats(makeVar, store, etype, level, path, v); | ||
const nilPats = whereCondAttrPatsForNullIsTrue(makeVar, store, etype, level, path); | ||
return [ | ||
{ | ||
or: { | ||
patterns: [notPats, ...nilPats], | ||
joinSym: makeVar(etype, level), | ||
}, | ||
}, | ||
]; | ||
} | ||
if ((v === null || v === void 0 ? void 0 : v.hasOwnProperty("$isNull")) && v.$isNull === true && path.length > 1) { | ||
// Make sure we're capturing all of the intermediate paths that might be null | ||
// by checking for null at each step along the path | ||
return [ | ||
{ | ||
or: { | ||
patterns: whereCondAttrPatsForNullIsTrue(makeVar, store, etype, level, path), | ||
joinSym: makeVar(etype, level), | ||
}, | ||
}, | ||
]; | ||
} | ||
return whereCondAttrPats(makeVar, store, etype, level, path, v); | ||
@@ -228,4 +280,3 @@ }); | ||
const aid = idAttr(store, etype).id; | ||
const idVecs = datalogQuery(store, dq) | ||
.sort(([_, tsA], [__, tsB]) => { | ||
const idVecs = datalogQuery(store, dq).sort(([_, tsA], [__, tsB]) => { | ||
return direction === "desc" ? tsB - tsA : tsA - tsB; | ||
@@ -232,0 +283,0 @@ }); |
@@ -6,3 +6,7 @@ import type { EntitiesDef, InstantGraph, LinkAttrDef, ResolveAttrs } from "./schemaTypes"; | ||
type WhereArgs = { | ||
/** @deprecated use `$in` instead of `in` */ | ||
in?: (string | number | boolean)[]; | ||
$in?: (string | number | boolean)[]; | ||
$not?: string | number | boolean; | ||
$isNull?: boolean; | ||
}; | ||
@@ -9,0 +13,0 @@ type WhereClauseValue = string | number | boolean | NonEmpty<WhereArgs>; |
@@ -37,2 +37,5 @@ // -------- | ||
}); | ||
const s1_5 = dummyQuery({ | ||
users: { $: { where: { foo: { $in: [1, 2, 3] } } } }, | ||
}); | ||
const t1 = dummyQuery({ | ||
@@ -79,2 +82,8 @@ users: { $: { where: { or: [{ foo: 1 }] } } }, | ||
}); | ||
const t12 = dummyQuery({ | ||
users: { $: { where: { val: { $isNull: true } } } }, | ||
}); | ||
const t13 = dummyQuery({ | ||
users: { $: { where: { val: { $not: "a" } } } }, | ||
}); | ||
// ------------------ | ||
@@ -96,2 +105,10 @@ // Bad $ clauses fail | ||
}); | ||
const s4 = dummyQuery({ | ||
// @ts-expect-error | ||
users: { $: { where: { val: { $isNull: "a" } } } }, | ||
}); | ||
const s5 = dummyQuery({ | ||
// @ts-expect-error | ||
users: { $: { where: { val: { $not: { val: "a" } } } } }, | ||
}); | ||
// ---------------- | ||
@@ -98,0 +115,0 @@ // Good Nested queries succeed |
@@ -65,4 +65,3 @@ import { create } from "mutative"; | ||
export function blobAttrs({ attrs }, etype) { | ||
return Object.values(attrs) | ||
.filter((attr) => isBlob(attr) && attr['forward-identity'][1] === etype); | ||
return Object.values(attrs).filter((attr) => isBlob(attr) && attr["forward-identity"][1] === etype); | ||
} | ||
@@ -76,3 +75,3 @@ export function getAsObject(store, attrs, e) { | ||
for (const v of vs) { | ||
obj[attr['forward-identity'][2]] = v[2]; | ||
obj[attr["forward-identity"][2]] = v[2]; | ||
} | ||
@@ -392,4 +391,24 @@ } | ||
} | ||
function triplesByValue(m, v) { | ||
function triplesByValue(store, m, v) { | ||
var _a; | ||
const res = []; | ||
if (v === null || v === void 0 ? void 0 : v.hasOwnProperty('$not')) { | ||
for (const candidate of m.keys()) { | ||
if (v.$not !== candidate) { | ||
res.push(m.get(candidate)); | ||
} | ||
} | ||
return res; | ||
} | ||
if (v === null || v === void 0 ? void 0 : v.hasOwnProperty('$isNull')) { | ||
const { attrId, isNull } = v.$isNull; | ||
const aMap = store.aev.get(attrId); | ||
for (const candidate of m.keys()) { | ||
const isValNull = !aMap || ((_a = aMap.get(candidate)) === null || _a === void 0 ? void 0 : _a.get(null)) || !aMap.get(candidate); | ||
if (isNull ? isValNull : !isValNull) { | ||
res.push(m.get(candidate)); | ||
} | ||
} | ||
return res; | ||
} | ||
const values = v.in ? v.in : [v]; | ||
@@ -436,3 +455,3 @@ for (const value of values) { | ||
} | ||
return triplesByValue(aMap, v); | ||
return triplesByValue(store, aMap, v); | ||
} | ||
@@ -446,3 +465,3 @@ case "ev": { | ||
for (const aMap of eMap.values()) { | ||
res.push(...triplesByValue(aMap, v)); | ||
res.push(...triplesByValue(store, aMap, v)); | ||
} | ||
@@ -462,3 +481,3 @@ return res; | ||
for (const eMap of aMap.values()) { | ||
res.push(...triplesByValue(eMap, v)); | ||
res.push(...triplesByValue(store, eMap, v)); | ||
} | ||
@@ -471,3 +490,3 @@ return res; | ||
for (const aMap of eMap.values()) { | ||
res.push(...triplesByValue(aMap, v)); | ||
res.push(...triplesByValue(store, aMap, v)); | ||
} | ||
@@ -474,0 +493,0 @@ } |
@@ -6,3 +6,7 @@ import type { EntitiesDef, InstantGraph, LinkAttrDef, ResolveAttrs } from "./schemaTypes"; | ||
type WhereArgs = { | ||
/** @deprecated use `$in` instead of `in` */ | ||
in?: (string | number | boolean)[]; | ||
$in?: (string | number | boolean)[]; | ||
$not?: string | number | boolean; | ||
$isNull?: boolean; | ||
}; | ||
@@ -9,0 +13,0 @@ type WhereClauseValue = string | number | boolean | NonEmpty<WhereArgs>; |
@@ -41,2 +41,5 @@ "use strict"; | ||
}); | ||
const s1_5 = dummyQuery({ | ||
users: { $: { where: { foo: { $in: [1, 2, 3] } } } }, | ||
}); | ||
const t1 = dummyQuery({ | ||
@@ -83,2 +86,8 @@ users: { $: { where: { or: [{ foo: 1 }] } } }, | ||
}); | ||
const t12 = dummyQuery({ | ||
users: { $: { where: { val: { $isNull: true } } } }, | ||
}); | ||
const t13 = dummyQuery({ | ||
users: { $: { where: { val: { $not: "a" } } } }, | ||
}); | ||
// ------------------ | ||
@@ -100,2 +109,10 @@ // Bad $ clauses fail | ||
}); | ||
const s4 = dummyQuery({ | ||
// @ts-expect-error | ||
users: { $: { where: { val: { $isNull: "a" } } } }, | ||
}); | ||
const s5 = dummyQuery({ | ||
// @ts-expect-error | ||
users: { $: { where: { val: { $not: { val: "a" } } } } }, | ||
}); | ||
// ---------------- | ||
@@ -102,0 +119,0 @@ // Good Nested queries succeed |
@@ -75,4 +75,3 @@ "use strict"; | ||
function blobAttrs({ attrs }, etype) { | ||
return Object.values(attrs) | ||
.filter((attr) => isBlob(attr) && attr['forward-identity'][1] === etype); | ||
return Object.values(attrs).filter((attr) => isBlob(attr) && attr["forward-identity"][1] === etype); | ||
} | ||
@@ -86,3 +85,3 @@ function getAsObject(store, attrs, e) { | ||
for (const v of vs) { | ||
obj[attr['forward-identity'][2]] = v[2]; | ||
obj[attr["forward-identity"][2]] = v[2]; | ||
} | ||
@@ -402,4 +401,24 @@ } | ||
} | ||
function triplesByValue(m, v) { | ||
function triplesByValue(store, m, v) { | ||
var _a; | ||
const res = []; | ||
if (v === null || v === void 0 ? void 0 : v.hasOwnProperty('$not')) { | ||
for (const candidate of m.keys()) { | ||
if (v.$not !== candidate) { | ||
res.push(m.get(candidate)); | ||
} | ||
} | ||
return res; | ||
} | ||
if (v === null || v === void 0 ? void 0 : v.hasOwnProperty('$isNull')) { | ||
const { attrId, isNull } = v.$isNull; | ||
const aMap = store.aev.get(attrId); | ||
for (const candidate of m.keys()) { | ||
const isValNull = !aMap || ((_a = aMap.get(candidate)) === null || _a === void 0 ? void 0 : _a.get(null)) || !aMap.get(candidate); | ||
if (isNull ? isValNull : !isValNull) { | ||
res.push(m.get(candidate)); | ||
} | ||
} | ||
return res; | ||
} | ||
const values = v.in ? v.in : [v]; | ||
@@ -446,3 +465,3 @@ for (const value of values) { | ||
} | ||
return triplesByValue(aMap, v); | ||
return triplesByValue(store, aMap, v); | ||
} | ||
@@ -456,3 +475,3 @@ case "ev": { | ||
for (const aMap of eMap.values()) { | ||
res.push(...triplesByValue(aMap, v)); | ||
res.push(...triplesByValue(store, aMap, v)); | ||
} | ||
@@ -472,3 +491,3 @@ return res; | ||
for (const eMap of aMap.values()) { | ||
res.push(...triplesByValue(eMap, v)); | ||
res.push(...triplesByValue(store, eMap, v)); | ||
} | ||
@@ -481,3 +500,3 @@ return res; | ||
for (const aMap of eMap.values()) { | ||
res.push(...triplesByValue(aMap, v)); | ||
res.push(...triplesByValue(store, aMap, v)); | ||
} | ||
@@ -484,0 +503,0 @@ } |
{ | ||
"name": "@instantdb/core", | ||
"version": "v0.14.23", | ||
"version": "v0.14.24", | ||
"description": "Instant's core local abstraction", | ||
@@ -12,2 +12,3 @@ "main": "dist/index.js", | ||
"test:ci": "vitest run", | ||
"check": "tsc --noEmit", | ||
"build": "rm -rf dist; npm run build:main && npm run build:module && npm run build:standalone", | ||
@@ -14,0 +15,0 @@ "dev:main": "tsc -p tsconfig.json -w --skipLibCheck", |
@@ -25,2 +25,11 @@ // 1. patternMatch | ||
} | ||
if ( | ||
patternPart.hasOwnProperty("$not") || | ||
patternPart.hasOwnProperty("$isNull") | ||
) { | ||
// If we use `$not` or `$isNull`, we've already done the filtering in | ||
// `getTriples` | ||
return context; | ||
} | ||
return null; | ||
@@ -27,0 +36,0 @@ } |
import { query as datalogQuery } from "./datalog"; | ||
import { uuidCompare } from "./utils/uuid"; | ||
import { getAttrByFwdIdentName, getAttrByReverseIdentName } from "./instaml"; | ||
import * as s from "./store"; | ||
import * as s from "./store"; | ||
@@ -84,13 +84,13 @@ // Pattern variables | ||
? [ | ||
makeVar(fwdEtype, level), | ||
attr.id, | ||
makeVar(revEtype, nextLevel), | ||
wildcard("time"), | ||
] | ||
makeVar(fwdEtype, level), | ||
attr.id, | ||
makeVar(revEtype, nextLevel), | ||
wildcard("time"), | ||
] | ||
: [ | ||
makeVar(fwdEtype, nextLevel), | ||
attr.id, | ||
makeVar(revEtype, level), | ||
wildcard("time"), | ||
]; | ||
makeVar(fwdEtype, nextLevel), | ||
attr.id, | ||
makeVar(revEtype, level), | ||
wildcard("time"), | ||
]; | ||
@@ -113,2 +113,17 @@ const nextEtype = fwdAttr ? revEtype : fwdEtype; | ||
if (v?.hasOwnProperty("$isNull")) { | ||
const idAttr = getAttrByFwdIdentName(store.attrs, valueEtype, "id"); | ||
if (!idAttr) { | ||
throw new AttrNotFoundError( | ||
`No attr for etype = ${valueEtype} label = id value-label`, | ||
); | ||
} | ||
return [ | ||
makeVar(valueEtype, valueLevel), | ||
idAttr.id, | ||
{ $isNull: { attrId: attr.id, isNull: v.$isNull } }, | ||
wildcard("time"), | ||
]; | ||
} | ||
return [makeVar(valueEtype, valueLevel), attr.id, v, wildcard("time")]; | ||
@@ -197,2 +212,20 @@ } | ||
// Given a path, returns a list of paths leading up to this path: | ||
// growPath([1, 2, 3]) -> [[1], [1, 2], [1, 2, 3]] | ||
function growPath(path) { | ||
const ret = []; | ||
for (let i = 1; i <= path.length; i++) { | ||
ret.push(path.slice(0, i)); | ||
} | ||
return ret; | ||
} | ||
// Returns array of pattern arrays that should be grouped in OR | ||
// to capture any intermediate nulls | ||
function whereCondAttrPatsForNullIsTrue(makeVar, store, etype, level, path) { | ||
return growPath(path).map((path) => | ||
whereCondAttrPats(makeVar, store, etype, level, path, { $isNull: true }), | ||
); | ||
} | ||
function parseWhere(makeVar, store, etype, level, where) { | ||
@@ -206,3 +239,45 @@ return Object.entries(where).flatMap(([k, v]) => { | ||
} | ||
const path = k.split("."); | ||
if (v?.hasOwnProperty("$not")) { | ||
// `$not` won't pick up entities that are missing the attr, so we | ||
// add in a `$isNull` to catch those too. | ||
const notPats = whereCondAttrPats(makeVar, store, etype, level, path, v); | ||
const nilPats = whereCondAttrPatsForNullIsTrue( | ||
makeVar, | ||
store, | ||
etype, | ||
level, | ||
path, | ||
); | ||
return [ | ||
{ | ||
or: { | ||
patterns: [notPats, ...nilPats], | ||
joinSym: makeVar(etype, level), | ||
}, | ||
}, | ||
]; | ||
} | ||
if (v?.hasOwnProperty("$isNull") && v.$isNull === true && path.length > 1) { | ||
// Make sure we're capturing all of the intermediate paths that might be null | ||
// by checking for null at each step along the path | ||
return [ | ||
{ | ||
or: { | ||
patterns: whereCondAttrPatsForNullIsTrue( | ||
makeVar, | ||
store, | ||
etype, | ||
level, | ||
path, | ||
), | ||
joinSym: makeVar(etype, level), | ||
}, | ||
}, | ||
]; | ||
} | ||
return whereCondAttrPats(makeVar, store, etype, level, path, v); | ||
@@ -252,3 +327,3 @@ }); | ||
store.cardinalityInference && | ||
store.linkIndex?.[etype]?.[label]?.isSingular, | ||
store.linkIndex?.[etype]?.[label]?.isSingular, | ||
); | ||
@@ -326,8 +401,7 @@ | ||
const aid = idAttr(store, etype).id; | ||
const idVecs = datalogQuery(store, dq) | ||
.sort(([_, tsA], [__, tsB]) => { | ||
return direction === "desc" ? tsB - tsA : tsA - tsB; | ||
}); | ||
const idVecs = datalogQuery(store, dq).sort(([_, tsA], [__, tsB]) => { | ||
return direction === "desc" ? tsB - tsA : tsA - tsB; | ||
}); | ||
let objects = {} | ||
let objects = {}; | ||
const startCursor = pageInfo?.["start-cursor"]; | ||
@@ -334,0 +408,0 @@ const blobAttrs = s.blobAttrs(store, etype); |
@@ -18,3 +18,7 @@ // Query | ||
type WhereArgs = { | ||
/** @deprecated use `$in` instead of `in` */ | ||
in?: (string | number | boolean)[]; | ||
$in?: (string | number | boolean)[]; | ||
$not?: string | number | boolean; | ||
$isNull?: boolean; | ||
}; | ||
@@ -21,0 +25,0 @@ |
@@ -68,2 +68,5 @@ // -------- | ||
}); | ||
const s1_5 = dummyQuery({ | ||
users: { $: { where: { foo: { $in: [1, 2, 3] } } } }, | ||
}); | ||
const t1 = dummyQuery({ | ||
@@ -112,2 +115,10 @@ users: { $: { where: { or: [{ foo: 1 }] } } }, | ||
const t12 = dummyQuery({ | ||
users: { $: { where: { val: { $isNull: true } } } }, | ||
}); | ||
const t13 = dummyQuery({ | ||
users: { $: { where: { val: { $not: "a" } } } }, | ||
}); | ||
// ------------------ | ||
@@ -130,2 +141,12 @@ // Bad $ clauses fail | ||
const s4 = dummyQuery({ | ||
// @ts-expect-error | ||
users: { $: { where: { val: { $isNull: "a" } } } }, | ||
}); | ||
const s5 = dummyQuery({ | ||
// @ts-expect-error | ||
users: { $: { where: { val: { $not: { val: "a" } } } } }, | ||
}); | ||
// ---------------- | ||
@@ -132,0 +153,0 @@ // Good Nested queries succeed |
@@ -73,13 +73,14 @@ import { create } from "mutative"; | ||
export function blobAttrs({ attrs }, etype) { | ||
return Object.values(attrs) | ||
.filter((attr) => isBlob(attr) && attr['forward-identity'][1] === etype); | ||
return Object.values(attrs).filter( | ||
(attr) => isBlob(attr) && attr["forward-identity"][1] === etype, | ||
); | ||
} | ||
export function getAsObject(store, attrs, e) { | ||
const obj = {}; | ||
for (const attr of attrs) { | ||
const obj = {}; | ||
for (const attr of attrs) { | ||
const aMap = store.eav.get(e)?.get(attr.id); | ||
const vs = allMapValues(aMap, 1); | ||
for (const v of vs) { | ||
obj[attr['forward-identity'][2]] = v[2]; | ||
for (const v of vs) { | ||
obj[attr["forward-identity"][2]] = v[2]; | ||
} | ||
@@ -437,4 +438,27 @@ } | ||
function triplesByValue(m, v) { | ||
function triplesByValue(store, m, v) { | ||
const res = []; | ||
if (v?.hasOwnProperty('$not')) { | ||
for (const candidate of m.keys()) { | ||
if (v.$not !== candidate) { | ||
res.push(m.get(candidate)); | ||
} | ||
} | ||
return res; | ||
} | ||
if (v?.hasOwnProperty('$isNull')) { | ||
const { attrId, isNull } = v.$isNull; | ||
const aMap = store.aev.get(attrId); | ||
for (const candidate of m.keys()) { | ||
const isValNull = | ||
!aMap || aMap.get(candidate)?.get(null) || !aMap.get(candidate); | ||
if (isNull ? isValNull : !isValNull) { | ||
res.push(m.get(candidate)); | ||
} | ||
} | ||
return res; | ||
} | ||
const values = v.in ? v.in : [v]; | ||
@@ -483,3 +507,3 @@ | ||
} | ||
return triplesByValue(aMap, v); | ||
return triplesByValue(store, aMap, v); | ||
} | ||
@@ -493,3 +517,3 @@ case "ev": { | ||
for (const aMap of eMap.values()) { | ||
res.push(...triplesByValue(aMap, v)); | ||
res.push(...triplesByValue(store, aMap, v)); | ||
} | ||
@@ -499,3 +523,3 @@ return res; | ||
case "a": { | ||
const aMap = store.aev.get(a) | ||
const aMap = store.aev.get(a); | ||
return allMapValues(aMap, 2); | ||
@@ -510,3 +534,3 @@ } | ||
for (const eMap of aMap.values()) { | ||
res.push(...triplesByValue(eMap, v)); | ||
res.push(...triplesByValue(store, eMap, v)); | ||
} | ||
@@ -519,3 +543,3 @@ return res; | ||
for (const aMap of eMap.values()) { | ||
res.push(...triplesByValue(aMap, v)); | ||
res.push(...triplesByValue(store, aMap, v)); | ||
} | ||
@@ -522,0 +546,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
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 too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
2394419
42479