From 28ea665212c0a71553c7340c839198c97a773649 Mon Sep 17 00:00:00 2001 From: rowan Date: Tue, 26 Nov 2024 11:54:57 -0600 Subject: [PATCH] allow multiple component matching and entity id binding --- src/query-parser/match.js | 2 +- src/query-parser/types.js | 47 ++++++++++++++++++++------------------- src/query.js | 34 +++------------------------- tests/query.test.js | 1 + 4 files changed, 29 insertions(+), 55 deletions(-) diff --git a/src/query-parser/match.js b/src/query-parser/match.js index e100e5e..1aa600d 100644 --- a/src/query-parser/match.js +++ b/src/query-parser/match.js @@ -46,7 +46,7 @@ export const node = map( export const edge = map( x => new Edge(x), - bracketed(components, Bracket.Square) + bracketed(component, Bracket.Square) ) const arrowRight = map(() => Direction.Right, seq(Hyphen, Bracket.Angle.Right)) diff --git a/src/query-parser/types.js b/src/query-parser/types.js index e5a428e..beecf88 100644 --- a/src/query-parser/types.js +++ b/src/query-parser/types.js @@ -1,4 +1,3 @@ -import { pipe } from 'bitecs' import { assoc, assocPath, is, nth } from '../fn.js' class Value { @@ -8,6 +7,10 @@ class Value { this.#value = value } + toString() { + return this.#value.toString() + } + get value() { return this.#value?.value ?? this.#value } @@ -28,10 +31,6 @@ export class Identifier extends Value { super(value) } - toString() { - return this.value.toString() - } - from(component) { return component } @@ -189,33 +188,20 @@ export class ReturnValues { #forComponentType(value, type) { if (this.#isTag(type)) { - return () => ({}) + return {} } else if (typeof type === 'function') { return type } else { - return value.from(type) - //return Object.fromEntries( - // Object.entries(type).map(([k, v]) => [k, v[entity]]) - //) + return entity => value.from(type)[entity] } } // TODO: adjust froms to accent entity, return data with built up objects as necessary from(components) { - /* - * const { entity, query: { components } } = node - * const returns2 = components.map(c => ({ ...c, returnValue: returns.find(x => x.equals(c.name)) })) - * returns2.map(({ returnValue, type }) => getValue(returnValue, type, entity)) - * const values = returns2.filter(({ name }) => returns.includes(name)) - * .map(({ name, type, returnValue }) => ({ [name]: getValue(returnValue, type, entity) })) - * const values = components.filter(({ name }) => returns.includes(name)) - * .map(({ name, type }) => ({ [name]: getValue(type, entity) })) - * return Object.assign(obj, ...values) - */ + const values = components.map(cmp => ({ ...cmp, rv: this.findName(cmp.name) })) + .map(({ type, rv }) => new ReturnValue(rv, this.#forComponentType(rv, type))) - const values = components.map(component => ({ ...component, returnValue: this.findName(component.name) })) - .map(({ name, type, returnValue }) => ([returnValue.toString(), this.#forComponentType(returnValue, type)])) - console.log(this, values) + return entity => values.reduce((acc, rv) => Object.assign(acc, rv.withEntity(entity)), {}) } find(fn) { @@ -235,6 +221,21 @@ export class ReturnValues { } } +class ReturnValue { + #value + #getter + + constructor(value, getter) { + this.#value = value + this.#getter = getter + } + + withEntity(entity) { + return assocPath(this.#value.toString(), this.#getter(entity), {}) + } +} + + export class KeyValuePair { constructor(key, value) { this.key = key diff --git a/src/query.js b/src/query.js index f564d9d..5aa033b 100644 --- a/src/query.js +++ b/src/query.js @@ -26,7 +26,7 @@ const prepareQuery = (query, component) => { } else { const components = match.components.map(nodeComponent(component)) const types = components.filter(notEntity).map(prop('type')) - return { components, relationship, query: defineQuery(types), schema: query } + return { components, relationship, getReturns: returnValues.from(components), query: defineQuery(types), schema: query } } } @@ -95,38 +95,10 @@ const executeQuery = curry((query, world) => { } }) -const isTag = type => ( - type && Object.getOwnPropertySymbols(type).find( - (s) => s.description === 'tagStore' - ) || false -) - -const getValue = (returnValue, type, entity) => { - if (isTag(type)) { - return {} - } else if (typeof type === 'function') { - return type(entity) - } else { - return returnValue.from(type) - //return Object.fromEntries( - // Object.entries(type).map(([k, v]) => [k, v[entity]]) - //) - } -} const resolveNode = curry((node, obj) => { - const returns = path('query.schema.returnValues', node) - const { entity, query: { components } } = node - // TODO: do this work in prepareQuery - returns.from(components) - - //const returns2 = components.map(c => ({ ...c, returnValue: returns.find(x => x.equals(c.name)) })) - //returns2.map(({ returnValue, type }) => getValue(returnValue, type, entity)) - //const values = returns2.filter(({ name }) => returns.includes(name)) - // .map(({ name, type, returnValue }) => ({ [name]: getValue(returnValue, type, entity) })) - //const values = components.filter(({ name }) => returns.includes(name)) - // .map(({ name, type }) => ({ [name]: getValue(type, entity) })) - //return Object.assign(obj, ...values) + const { entity, query: { getReturns } } = node + return { ...obj, ...getReturns(entity) } }) const resolveRelationship = edges => { diff --git a/tests/query.test.js b/tests/query.test.js index 009e44c..1543656 100644 --- a/tests/query.test.js +++ b/tests/query.test.js @@ -55,6 +55,7 @@ describe('query', () => { relate(world, b, Knows, a) // 9 relate(world, c, Knows, player) // 10 + // tag components should return an object object assert.deepEqual( query('MATCH (player:Player) RETURN player', engine).unwrap(), [{ player: {} }]