UNPKG

@dialog-db/query

Version:

Datalog query engine inspired by Datomic

119 lines (103 loc) 2.68 kB
import * as Variable from '../variable.js' import * as API from '../api.js' import * as Cursor from '../cursor.js' import * as Match from '../match.js' import { _, $ } from '../$.js' export class Select { /** * @param {API.SelectSyntax} source * @param {API.Cursor} cursor * @param {API.MatchFrame} parameters */ constructor(source, cursor, parameters) { this.source = source this.cursor = cursor this.parameters = parameters } get selector() { return this.source.match } get recurs() { return null } /** * Base execution cost of the select operation. */ get cost() { return 100 } /** * @template {API.Scalar} T * @param {API.Term<T>|undefined} term * @param {API.MatchFrame} bindings * @returns {API.Term<T>|undefined} */ resolve(term, bindings) { if (Variable.is(term)) { return Cursor.get(bindings, this.cursor, term) ?? term } else { return term } } /** * @param {API.EvaluationContext} context */ *evaluate({ source, selection }) { const { selector } = this const matches = [] for (const frame of selection) { const the = selector.the ? Match.resolve(frame, selector.the) : _ const of = selector.of ? Match.resolve(frame, selector.of) : _ const is = selector.is ? Match.resolve(frame, selector.is) : _ // Note: We expect that there will be LRUCache wrapping the db // so calling scan over and over again will not actually cause new scans. /** @type {API.FactsSelector} */ const query = {} if (!Variable.is(of)) { query.of = of } if (!Variable.is(the)) { query.the = the } if (!Variable.is(is)) { query.is = is } const facts = yield* source.select(query) for (const { the, of, is } of facts) { let match = Match.clone(frame) if (Variable.is(selector.the)) { let { ok } = Match.set(match, selector.the, the) if (ok) { match = ok } else { continue } } if (Variable.is(selector.of)) { let { ok } = Match.set(match, selector.of, of) if (ok) { match = ok } else { continue } } if (Variable.is(selector.is)) { let { ok } = Match.set(match, selector.is, is) if (ok) { match = ok } else { continue } } matches.push(match) } } return matches } toJSON() { return this.source.toJSON() } toDebugString() { return this.source.toDebugString() } }