jinaga
Version:
Data management for web and mobile applications.
198 lines • 9.09 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SpecificationRunner = void 0;
const storage_1 = require("../storage");
const fn_1 = require("../util/fn");
class SpecificationRunner {
constructor(source) {
this.source = source;
}
read(start, specification) {
return __awaiter(this, void 0, void 0, function* () {
if (start.length !== specification.given.length) {
throw new Error(`The number of start references (${start.length}) must match the number of given facts (${specification.given.length}).`);
}
const references = start.reduce((references, reference, index) => (Object.assign(Object.assign({}, references), { [specification.given[index].name]: {
type: reference.type,
hash: reference.hash
} })), {});
const products = yield this.executeMatchesAndProjection(references, specification.matches, specification.projection);
return products;
});
}
executeMatchesAndProjection(references, matches, projection) {
return __awaiter(this, void 0, void 0, function* () {
const tuples = yield this.executeMatches(references, matches);
const products = (0, fn_1.mapAsync)(tuples, tuple => this.createProduct(tuple, projection));
return products;
});
}
executeMatches(references, matches) {
return __awaiter(this, void 0, void 0, function* () {
let results = [references];
for (const match of matches) {
results = yield (0, fn_1.flattenAsync)(results, tuple => this.executeMatch(tuple, match));
}
return results;
});
}
executeMatch(references, match) {
return __awaiter(this, void 0, void 0, function* () {
let results = [];
if (match.conditions.length === 0) {
throw new Error("A match must have at least one condition.");
}
const firstCondition = match.conditions[0];
if (firstCondition.type === "path") {
const result = yield this.executePathCondition(references, match.unknown, firstCondition);
results = result.map(reference => (Object.assign(Object.assign({}, references), { [match.unknown.name]: {
type: reference.type,
hash: reference.hash
} })));
}
else {
throw new Error("The first condition must be a path condition.");
}
const remainingConditions = match.conditions.slice(1);
for (const condition of remainingConditions) {
results = yield this.filterByCondition(references, match.unknown, results, condition);
}
return results;
});
}
executePathCondition(references, unknown, pathCondition) {
return __awaiter(this, void 0, void 0, function* () {
if (!references.hasOwnProperty(pathCondition.labelRight)) {
throw new Error(`The label ${pathCondition.labelRight} is not defined.`);
}
const start = references[pathCondition.labelRight];
let results = [start];
for (const role of pathCondition.rolesRight) {
results = yield this.executePredecessorStep(results, role.name, role.predecessorType);
}
const invertedRoles = invertRoles(pathCondition.rolesLeft, unknown.type);
for (const role of invertedRoles) {
results = yield this.executeSuccessorStep(results, role.name, role.successorType);
}
return results;
});
}
executePredecessorStep(set, name, predecessorType) {
return (0, fn_1.flattenAsync)(set, reference => this.source.getPredecessors(reference, name, predecessorType));
}
executeSuccessorStep(set, name, successorType) {
return (0, fn_1.flattenAsync)(set, reference => this.source.getSuccessors(reference, name, successorType));
}
filterByCondition(references, unknown, results, condition) {
return __awaiter(this, void 0, void 0, function* () {
if (condition.type === "path") {
const otherResults = yield this.executePathCondition(references, unknown, condition);
return results.filter(result => otherResults.some((0, storage_1.factReferenceEquals)(result[unknown.name])));
}
else if (condition.type === "existential") {
const matchingReferences = [];
for (const result of results) {
const matches = yield this.executeMatches(result, condition.matches);
const include = condition.exists ?
matches.length > 0 :
matches.length === 0;
if (include) {
matchingReferences.push(result);
}
}
return matchingReferences;
}
else {
const _exhaustiveCheck = condition;
throw new Error(`Unknown condition type: ${_exhaustiveCheck.type}`);
}
});
}
createProduct(tuple, projection) {
return __awaiter(this, void 0, void 0, function* () {
if (projection.type === "composite") {
let result = {};
for (const component of projection.components) {
result = Object.assign(Object.assign({}, result), { [component.name]: yield this.createComponent(tuple, component) });
}
return {
tuple,
result
};
}
else {
const result = yield this.createSingularProduct(tuple, projection);
return {
tuple,
result
};
}
});
}
createComponent(tuple, component) {
return __awaiter(this, void 0, void 0, function* () {
if (component.type === "specification") {
return yield this.executeMatchesAndProjection(tuple, component.matches, component.projection);
}
else {
return yield this.createSingularProduct(tuple, component);
}
});
}
createSingularProduct(tuple, projection) {
return __awaiter(this, void 0, void 0, function* () {
if (projection.type === "fact") {
if (!tuple.hasOwnProperty(projection.label)) {
throw new Error(`The label ${projection.label} is not defined.`);
}
const reference = tuple[projection.label];
return yield this.source.hydrate(reference);
}
else if (projection.type === "field") {
if (!tuple.hasOwnProperty(projection.label)) {
throw new Error(`The label ${projection.label} is not defined.`);
}
const reference = tuple[projection.label];
const fact = yield this.source.findFact(reference);
if (fact === null) {
throw new Error(`The fact ${reference} is not defined.`);
}
const value = fact.fields[projection.field];
return value;
}
else if (projection.type === "hash") {
if (!tuple.hasOwnProperty(projection.label)) {
throw new Error(`The label ${projection.label} is not defined.`);
}
const reference = tuple[projection.label];
return reference.hash;
}
else {
const _exhaustiveCheck = projection;
throw new Error(`Unexpected child projection type: ${_exhaustiveCheck}`);
}
});
}
}
exports.SpecificationRunner = SpecificationRunner;
function invertRoles(roles, type) {
const results = [];
for (const role of roles) {
results.push({
name: role.name,
successorType: type
});
type = role.predecessorType;
}
return results.reverse();
}
//# sourceMappingURL=specification-runner.js.map