added lossy view

This commit is contained in:
Ladd Hoffman 2024-12-23 23:30:54 -06:00
parent 42df273052
commit a283649c77
2 changed files with 121 additions and 0 deletions

80
__tests__/lossy.ts Normal file
View File

@ -0,0 +1,80 @@
import Debug from "debug";
import {Lossless, LosslessViewMany} from "../src/lossless";
import {Lossy, firstValueFromLosslessViewOne, firstValueFromCollapsedDelta} from "../src/lossy";
const debug = Debug('test:lossy');
describe('Lossy', () => {
describe('se a provided function to resolve entity views', () => {
const lossless = new Lossless();
const lossy = new Lossy(lossless);
beforeAll(() => {
lossless.ingestDelta({
creator: 'a',
host: 'h',
pointers: [{
localContext: "actor",
target: "keanu",
targetContext: "roles"
}, {
localContext: "role",
target: "neo",
targetContext: "actor"
}, {
localContext: "film",
target: "the_matrix",
targetContext: "cast"
}, {
localContext: "base_salary",
target: 1000000
}, {
localContext: "salary_currency",
target: "usd"
}]
});
});
it('example summary', () => {
type Role = {
actor: string,
film: string,
role: string
};
type Summary = {
roles: Role[];
};
const resolver = (losslessView: LosslessViewMany): Summary => {
const roles: Role[] = [];
debug('resolving roles');
for (const [id, ent] of Object.entries(losslessView)) {
if (ent.referencedAs.includes("role")) {
const {delta, value: actor} = firstValueFromLosslessViewOne(ent, "actor") ?? {};
if (!delta) continue; // TODO: panic
if (!actor) continue; // TODO: panic
const film = firstValueFromCollapsedDelta(delta, "film");
debug(`role ${id}`, {actor, film});
if (!film) continue; // TODO: panic
roles.push({
role: id,
actor,
film
});
}
}
return {roles};
}
const result = lossy.resolve(resolver);
expect(result).toEqual({
roles: [{
film: "the_matrix",
role: "neo",
actor: "keanu"
}]
});
});
});
});

41
src/lossy.ts Normal file
View File

@ -0,0 +1,41 @@
// We have the lossless transformation of the delta stream.
// We want to enable transformations from the lossless view,
// into various possible "lossy" views that combine or exclude some information.
//
// We can achieve this via functional expression, encoded as JSON-Logic.
// Fields in the output can be described as transformations
import {CollapsedDelta, Lossless, LosslessViewMany, LosslessViewOne} from "./lossless";
import {DeltaFilter} from "./types";
type Resolver = (losslessView: LosslessViewMany) => unknown;
export function firstValueFromCollapsedDelta(delta: CollapsedDelta, key: string): string | undefined {
const pointers = delta.pointers;
for (const pointer of pointers || []) {
const [[k, value]] = Object.entries(pointer);
if (k === key && typeof value === "string") {
return value;
}
}
}
export function firstValueFromLosslessViewOne(ent: LosslessViewOne, key: string): {delta: CollapsedDelta, value: string} | undefined {
for (const delta of ent.properties[key] || []) {
const value = firstValueFromCollapsedDelta(delta, key);
if (value) return {delta, value};
}
}
export class Lossy {
lossless: Lossless;
constructor(lossless: Lossless) {
this.lossless = lossless;
}
resolve(fn: Resolver, deltaFilter?: DeltaFilter) {
return fn(this.lossless.view(deltaFilter));
}
}