mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-07-02 02:58:10 +02:00
Remove native classes from TS runtime
This commit is contained in:
parent
952daf325d
commit
dca2786828
79 changed files with 7622 additions and 6703 deletions
|
|
@ -8,10 +8,11 @@
|
|||
*/
|
||||
|
||||
import {
|
||||
FlowProcessor,
|
||||
ConsumerSpec,
|
||||
ProducerSpec,
|
||||
makeFlowProcessor,
|
||||
makeConsumerSpec,
|
||||
makeProducerSpec,
|
||||
type ProcessorConfig,
|
||||
type FlowProcessorRuntime,
|
||||
type FlowContext,
|
||||
type FlowResourceNotFoundError,
|
||||
type MessagingDeliveryError,
|
||||
|
|
@ -65,37 +66,34 @@ const onTriplesQueryMessage = Effect.fn("TriplesQueryService.onMessage")(functio
|
|||
});
|
||||
|
||||
export const makeTriplesQuerySpecs = (): ReadonlyArray<Spec<FalkorDBTriplesQueryService>> => [
|
||||
new ConsumerSpec<
|
||||
makeConsumerSpec<
|
||||
TriplesQueryRequest,
|
||||
FlowResourceNotFoundError | MessagingDeliveryError,
|
||||
FalkorDBTriplesQueryService
|
||||
>("triples-request", onTriplesQueryMessage),
|
||||
new ProducerSpec<TriplesQueryResponse>("triples-response"),
|
||||
makeProducerSpec<TriplesQueryResponse>("triples-response"),
|
||||
];
|
||||
|
||||
export class TriplesQueryService extends FlowProcessor<FalkorDBTriplesQueryService> {
|
||||
private readonly query = makeFalkorDBTriplesQueryService();
|
||||
export type TriplesQueryService = FlowProcessorRuntime<FalkorDBTriplesQueryService>;
|
||||
|
||||
constructor(config: ProcessorConfig) {
|
||||
super(config);
|
||||
|
||||
for (const spec of makeTriplesQuerySpecs()) {
|
||||
this.registerSpecification(spec);
|
||||
}
|
||||
|
||||
console.log("[TriplesQuery] Service initialized");
|
||||
}
|
||||
|
||||
override startEffect() {
|
||||
return super.startEffect().pipe(
|
||||
Effect.provideService(
|
||||
FalkorDBTriplesQueryService,
|
||||
FalkorDBTriplesQueryService.of(this.query),
|
||||
export function makeTriplesQueryService(config: ProcessorConfig): TriplesQueryService {
|
||||
const query = makeFalkorDBTriplesQueryService();
|
||||
const service = makeFlowProcessor(config, {
|
||||
specifications: makeTriplesQuerySpecs(),
|
||||
provide: (effect) =>
|
||||
effect.pipe(
|
||||
Effect.provideService(
|
||||
FalkorDBTriplesQueryService,
|
||||
FalkorDBTriplesQueryService.of(query),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
console.log("[TriplesQuery] Service initialized");
|
||||
return service;
|
||||
}
|
||||
|
||||
export const TriplesQueryService = makeTriplesQueryService;
|
||||
|
||||
export const program = makeFlowProcessorProgram<ProcessorConfig & FalkorDBQueryConfig, never, FalkorDBTriplesQueryService>({
|
||||
id: "triples-query",
|
||||
specs: () => makeTriplesQuerySpecs(),
|
||||
|
|
|
|||
|
|
@ -41,35 +41,194 @@ function field(row: unknown, key: string): string {
|
|||
return (row as Record<string, unknown>)?.[key] as string ?? "";
|
||||
}
|
||||
|
||||
export class FalkorDBTriplesQuery {
|
||||
private graph: Graph;
|
||||
private connectPromise: Promise<void>;
|
||||
export interface FalkorDBTriplesQuery {
|
||||
readonly queryTriples: (
|
||||
s?: Term,
|
||||
p?: Term,
|
||||
o?: Term,
|
||||
limit?: number,
|
||||
) => Promise<Triple[]>;
|
||||
}
|
||||
|
||||
constructor(config: FalkorDBQueryConfig = {}) {
|
||||
const url = config.url ?? process.env.FALKORDB_URL ?? "redis://localhost:6379";
|
||||
const database = config.database ?? "falkordb";
|
||||
export function makeFalkorDBTriplesQuery(
|
||||
config: FalkorDBQueryConfig = {},
|
||||
): FalkorDBTriplesQuery {
|
||||
const url = config.url ?? process.env.FALKORDB_URL ?? "redis://localhost:6379";
|
||||
const database = config.database ?? "falkordb";
|
||||
|
||||
const client = createClient({ url });
|
||||
this.graph = new Graph(client, database);
|
||||
this.connectPromise = client.connect().then(() => {
|
||||
console.log(`[FalkorDBTriplesQuery] Connected to ${url}, graph: ${database}`);
|
||||
}).catch((err) => {
|
||||
console.error(`[FalkorDBTriplesQuery] Connection failed:`, err);
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
const client = createClient({ url });
|
||||
const graph = new Graph(client, database);
|
||||
const connectPromise = client.connect().then(() => {
|
||||
console.log(`[FalkorDBTriplesQuery] Connected to ${url}, graph: ${database}`);
|
||||
}).catch((err) => {
|
||||
console.error(`[FalkorDBTriplesQuery] Connection failed:`, err);
|
||||
throw err;
|
||||
});
|
||||
|
||||
private async ensureConnected(): Promise<void> {
|
||||
await this.connectPromise;
|
||||
}
|
||||
const ensureConnected = async (): Promise<void> => {
|
||||
await connectPromise;
|
||||
};
|
||||
|
||||
async queryTriples(
|
||||
const matchPattern = async (
|
||||
out: [string, string, string][],
|
||||
sv: string, pv: string, ov: string, limit: number,
|
||||
): Promise<void> => {
|
||||
for (const destType of ["Literal", "Node"] as const) {
|
||||
const destKey = destType === "Literal" ? "value" : "uri";
|
||||
const result = await graph.query(
|
||||
`MATCH (src:Node {uri: $src})-[rel:Rel {uri: $rel}]->(dest:${destType} {${destKey}: $dest}) ` +
|
||||
`RETURN src.uri LIMIT ${limit}`,
|
||||
{ params: { src: sv, rel: pv, dest: ov } },
|
||||
);
|
||||
for (const _rec of (result.data ?? [])) {
|
||||
out.push([sv, pv, ov]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const matchSP = async (
|
||||
out: [string, string, string][],
|
||||
sv: string, pv: string, limit: number,
|
||||
): Promise<void> => {
|
||||
// Literals
|
||||
const litResult = await graph.query(
|
||||
`MATCH (src:Node {uri: $src})-[rel:Rel {uri: $rel}]->(dest:Literal) ` +
|
||||
`RETURN dest.value as dest LIMIT ${limit}`,
|
||||
{ params: { src: sv, rel: pv } },
|
||||
);
|
||||
for (const rec of (litResult.data ?? [])) {
|
||||
out.push([sv, pv, field(rec, "dest")]);
|
||||
}
|
||||
// Nodes
|
||||
const nodeResult = await graph.query(
|
||||
`MATCH (src:Node {uri: $src})-[rel:Rel {uri: $rel}]->(dest:Node) ` +
|
||||
`RETURN dest.uri as dest LIMIT ${limit}`,
|
||||
{ params: { src: sv, rel: pv } },
|
||||
);
|
||||
for (const rec of (nodeResult.data ?? [])) {
|
||||
out.push([sv, pv, field(rec, "dest")]);
|
||||
}
|
||||
};
|
||||
|
||||
const matchSO = async (
|
||||
out: [string, string, string][],
|
||||
sv: string, ov: string, limit: number,
|
||||
): Promise<void> => {
|
||||
for (const [destType, destKey] of [["Literal", "value"], ["Node", "uri"]] as const) {
|
||||
const result = await graph.query(
|
||||
`MATCH (src:Node {uri: $src})-[rel:Rel]->(dest:${destType} {${destKey}: $dest}) ` +
|
||||
`RETURN rel.uri as rel LIMIT ${limit}`,
|
||||
{ params: { src: sv, dest: ov } },
|
||||
);
|
||||
for (const rec of (result.data ?? [])) {
|
||||
out.push([sv, field(rec, "rel"), ov]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const matchPO = async (
|
||||
out: [string, string, string][],
|
||||
pv: string, ov: string, limit: number,
|
||||
): Promise<void> => {
|
||||
for (const [destType, destKey] of [["Literal", "value"], ["Node", "uri"]] as const) {
|
||||
const result = await graph.query(
|
||||
`MATCH (src:Node)-[rel:Rel {uri: $rel}]->(dest:${destType} {${destKey}: $dest}) ` +
|
||||
`RETURN src.uri as src LIMIT ${limit}`,
|
||||
{ params: { rel: pv, dest: ov } },
|
||||
);
|
||||
for (const rec of (result.data ?? [])) {
|
||||
out.push([field(rec, "src"), pv, ov]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const matchS = async (
|
||||
out: [string, string, string][],
|
||||
sv: string, limit: number,
|
||||
): Promise<void> => {
|
||||
const litResult = await graph.query(
|
||||
`MATCH (src:Node {uri: $src})-[rel:Rel]->(dest:Literal) ` +
|
||||
`RETURN rel.uri as rel, dest.value as dest LIMIT ${limit}`,
|
||||
{ params: { src: sv } },
|
||||
);
|
||||
for (const rec of (litResult.data ?? [])) {
|
||||
out.push([sv, field(rec, "rel"), field(rec, "dest")]);
|
||||
}
|
||||
const nodeResult = await graph.query(
|
||||
`MATCH (src:Node {uri: $src})-[rel:Rel]->(dest:Node) ` +
|
||||
`RETURN rel.uri as rel, dest.uri as dest LIMIT ${limit}`,
|
||||
{ params: { src: sv } },
|
||||
);
|
||||
for (const rec of (nodeResult.data ?? [])) {
|
||||
out.push([sv, field(rec, "rel"), field(rec, "dest")]);
|
||||
}
|
||||
};
|
||||
|
||||
const matchP = async (
|
||||
out: [string, string, string][],
|
||||
pv: string, limit: number,
|
||||
): Promise<void> => {
|
||||
const litResult = await graph.query(
|
||||
`MATCH (src:Node)-[rel:Rel {uri: $rel}]->(dest:Literal) ` +
|
||||
`RETURN src.uri as src, dest.value as dest LIMIT ${limit}`,
|
||||
{ params: { rel: pv } },
|
||||
);
|
||||
for (const rec of (litResult.data ?? [])) {
|
||||
out.push([field(rec, "src"), pv, field(rec, "dest")]);
|
||||
}
|
||||
const nodeResult = await graph.query(
|
||||
`MATCH (src:Node)-[rel:Rel {uri: $rel}]->(dest:Node) ` +
|
||||
`RETURN src.uri as src, dest.uri as dest LIMIT ${limit}`,
|
||||
{ params: { rel: pv } },
|
||||
);
|
||||
for (const rec of (nodeResult.data ?? [])) {
|
||||
out.push([field(rec, "src"), pv, field(rec, "dest")]);
|
||||
}
|
||||
};
|
||||
|
||||
const matchO = async (
|
||||
out: [string, string, string][],
|
||||
ov: string, limit: number,
|
||||
): Promise<void> => {
|
||||
for (const [destType, destKey] of [["Literal", "value"], ["Node", "uri"]] as const) {
|
||||
const result = await graph.query(
|
||||
`MATCH (src:Node)-[rel:Rel]->(dest:${destType} {${destKey}: $dest}) ` +
|
||||
`RETURN src.uri as src, rel.uri as rel LIMIT ${limit}`,
|
||||
{ params: { dest: ov } },
|
||||
);
|
||||
for (const rec of (result.data ?? [])) {
|
||||
out.push([field(rec, "src"), field(rec, "rel"), ov]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const matchAll = async (
|
||||
out: [string, string, string][],
|
||||
limit: number,
|
||||
): Promise<void> => {
|
||||
const litResult = await graph.query(
|
||||
`MATCH (src:Node)-[rel:Rel]->(dest:Literal) ` +
|
||||
`RETURN src.uri as src, rel.uri as rel, dest.value as dest LIMIT ${limit}`,
|
||||
);
|
||||
for (const rec of (litResult.data ?? [])) {
|
||||
out.push([field(rec, "src"), field(rec, "rel"), field(rec, "dest")]);
|
||||
}
|
||||
const nodeResult = await graph.query(
|
||||
`MATCH (src:Node)-[rel:Rel]->(dest:Node) ` +
|
||||
`RETURN src.uri as src, rel.uri as rel, dest.uri as dest LIMIT ${limit}`,
|
||||
);
|
||||
for (const rec of (nodeResult.data ?? [])) {
|
||||
out.push([field(rec, "src"), field(rec, "rel"), field(rec, "dest")]);
|
||||
}
|
||||
};
|
||||
|
||||
const queryTriples = async (
|
||||
s?: Term,
|
||||
p?: Term,
|
||||
o?: Term,
|
||||
limit = 100,
|
||||
): Promise<Triple[]> {
|
||||
await this.ensureConnected();
|
||||
): Promise<Triple[]> => {
|
||||
await ensureConnected();
|
||||
const sv = termToValue(s);
|
||||
const pv = termToValue(p);
|
||||
const ov = termToValue(o);
|
||||
|
|
@ -79,28 +238,28 @@ export class FalkorDBTriplesQuery {
|
|||
// Query both Node and Literal targets for each pattern
|
||||
if (sv !== null && pv !== null && ov !== null) {
|
||||
// SPO — exact match
|
||||
await this.matchPattern(rawTriples, sv, pv, ov, limit);
|
||||
await matchPattern(rawTriples, sv, pv, ov, limit);
|
||||
} else if (sv !== null && pv !== null) {
|
||||
// SP — known subject + predicate
|
||||
await this.matchSP(rawTriples, sv, pv, limit);
|
||||
await matchSP(rawTriples, sv, pv, limit);
|
||||
} else if (sv !== null && ov !== null) {
|
||||
// SO — known subject + object
|
||||
await this.matchSO(rawTriples, sv, ov, limit);
|
||||
await matchSO(rawTriples, sv, ov, limit);
|
||||
} else if (pv !== null && ov !== null) {
|
||||
// PO — known predicate + object
|
||||
await this.matchPO(rawTriples, pv, ov, limit);
|
||||
await matchPO(rawTriples, pv, ov, limit);
|
||||
} else if (sv !== null) {
|
||||
// S only
|
||||
await this.matchS(rawTriples, sv, limit);
|
||||
await matchS(rawTriples, sv, limit);
|
||||
} else if (pv !== null) {
|
||||
// P only
|
||||
await this.matchP(rawTriples, pv, limit);
|
||||
await matchP(rawTriples, pv, limit);
|
||||
} else if (ov !== null) {
|
||||
// O only
|
||||
await this.matchO(rawTriples, ov, limit);
|
||||
await matchO(rawTriples, ov, limit);
|
||||
} else {
|
||||
// Wildcard — all triples
|
||||
await this.matchAll(rawTriples, limit);
|
||||
await matchAll(rawTriples, limit);
|
||||
}
|
||||
|
||||
return rawTriples
|
||||
|
|
@ -111,160 +270,9 @@ export class FalkorDBTriplesQuery {
|
|||
p: createTerm(p),
|
||||
o: createTerm(o),
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
private async matchPattern(
|
||||
out: [string, string, string][],
|
||||
sv: string, pv: string, ov: string, limit: number,
|
||||
): Promise<void> {
|
||||
for (const destType of ["Literal", "Node"] as const) {
|
||||
const destKey = destType === "Literal" ? "value" : "uri";
|
||||
const result = await this.graph.query(
|
||||
`MATCH (src:Node {uri: $src})-[rel:Rel {uri: $rel}]->(dest:${destType} {${destKey}: $dest}) ` +
|
||||
`RETURN src.uri LIMIT ${limit}`,
|
||||
{ params: { src: sv, rel: pv, dest: ov } },
|
||||
);
|
||||
for (const _rec of (result.data ?? [])) {
|
||||
out.push([sv, pv, ov]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async matchSP(
|
||||
out: [string, string, string][],
|
||||
sv: string, pv: string, limit: number,
|
||||
): Promise<void> {
|
||||
// Literals
|
||||
const litResult = await this.graph.query(
|
||||
`MATCH (src:Node {uri: $src})-[rel:Rel {uri: $rel}]->(dest:Literal) ` +
|
||||
`RETURN dest.value as dest LIMIT ${limit}`,
|
||||
{ params: { src: sv, rel: pv } },
|
||||
);
|
||||
for (const rec of (litResult.data ?? [])) {
|
||||
out.push([sv, pv, field(rec, "dest")]);
|
||||
}
|
||||
// Nodes
|
||||
const nodeResult = await this.graph.query(
|
||||
`MATCH (src:Node {uri: $src})-[rel:Rel {uri: $rel}]->(dest:Node) ` +
|
||||
`RETURN dest.uri as dest LIMIT ${limit}`,
|
||||
{ params: { src: sv, rel: pv } },
|
||||
);
|
||||
for (const rec of (nodeResult.data ?? [])) {
|
||||
out.push([sv, pv, field(rec, "dest")]);
|
||||
}
|
||||
}
|
||||
|
||||
private async matchSO(
|
||||
out: [string, string, string][],
|
||||
sv: string, ov: string, limit: number,
|
||||
): Promise<void> {
|
||||
for (const [destType, destKey] of [["Literal", "value"], ["Node", "uri"]] as const) {
|
||||
const result = await this.graph.query(
|
||||
`MATCH (src:Node {uri: $src})-[rel:Rel]->(dest:${destType} {${destKey}: $dest}) ` +
|
||||
`RETURN rel.uri as rel LIMIT ${limit}`,
|
||||
{ params: { src: sv, dest: ov } },
|
||||
);
|
||||
for (const rec of (result.data ?? [])) {
|
||||
out.push([sv, field(rec, "rel"), ov]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async matchPO(
|
||||
out: [string, string, string][],
|
||||
pv: string, ov: string, limit: number,
|
||||
): Promise<void> {
|
||||
for (const [destType, destKey] of [["Literal", "value"], ["Node", "uri"]] as const) {
|
||||
const result = await this.graph.query(
|
||||
`MATCH (src:Node)-[rel:Rel {uri: $rel}]->(dest:${destType} {${destKey}: $dest}) ` +
|
||||
`RETURN src.uri as src LIMIT ${limit}`,
|
||||
{ params: { rel: pv, dest: ov } },
|
||||
);
|
||||
for (const rec of (result.data ?? [])) {
|
||||
out.push([field(rec, "src"), pv, ov]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async matchS(
|
||||
out: [string, string, string][],
|
||||
sv: string, limit: number,
|
||||
): Promise<void> {
|
||||
const litResult = await this.graph.query(
|
||||
`MATCH (src:Node {uri: $src})-[rel:Rel]->(dest:Literal) ` +
|
||||
`RETURN rel.uri as rel, dest.value as dest LIMIT ${limit}`,
|
||||
{ params: { src: sv } },
|
||||
);
|
||||
for (const rec of (litResult.data ?? [])) {
|
||||
out.push([sv, field(rec, "rel"), field(rec, "dest")]);
|
||||
}
|
||||
const nodeResult = await this.graph.query(
|
||||
`MATCH (src:Node {uri: $src})-[rel:Rel]->(dest:Node) ` +
|
||||
`RETURN rel.uri as rel, dest.uri as dest LIMIT ${limit}`,
|
||||
{ params: { src: sv } },
|
||||
);
|
||||
for (const rec of (nodeResult.data ?? [])) {
|
||||
out.push([sv, field(rec, "rel"), field(rec, "dest")]);
|
||||
}
|
||||
}
|
||||
|
||||
private async matchP(
|
||||
out: [string, string, string][],
|
||||
pv: string, limit: number,
|
||||
): Promise<void> {
|
||||
const litResult = await this.graph.query(
|
||||
`MATCH (src:Node)-[rel:Rel {uri: $rel}]->(dest:Literal) ` +
|
||||
`RETURN src.uri as src, dest.value as dest LIMIT ${limit}`,
|
||||
{ params: { rel: pv } },
|
||||
);
|
||||
for (const rec of (litResult.data ?? [])) {
|
||||
out.push([field(rec, "src"), pv, field(rec, "dest")]);
|
||||
}
|
||||
const nodeResult = await this.graph.query(
|
||||
`MATCH (src:Node)-[rel:Rel {uri: $rel}]->(dest:Node) ` +
|
||||
`RETURN src.uri as src, dest.uri as dest LIMIT ${limit}`,
|
||||
{ params: { rel: pv } },
|
||||
);
|
||||
for (const rec of (nodeResult.data ?? [])) {
|
||||
out.push([field(rec, "src"), pv, field(rec, "dest")]);
|
||||
}
|
||||
}
|
||||
|
||||
private async matchO(
|
||||
out: [string, string, string][],
|
||||
ov: string, limit: number,
|
||||
): Promise<void> {
|
||||
for (const [destType, destKey] of [["Literal", "value"], ["Node", "uri"]] as const) {
|
||||
const result = await this.graph.query(
|
||||
`MATCH (src:Node)-[rel:Rel]->(dest:${destType} {${destKey}: $dest}) ` +
|
||||
`RETURN src.uri as src, rel.uri as rel LIMIT ${limit}`,
|
||||
{ params: { dest: ov } },
|
||||
);
|
||||
for (const rec of (result.data ?? [])) {
|
||||
out.push([field(rec, "src"), field(rec, "rel"), ov]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async matchAll(
|
||||
out: [string, string, string][],
|
||||
limit: number,
|
||||
): Promise<void> {
|
||||
const litResult = await this.graph.query(
|
||||
`MATCH (src:Node)-[rel:Rel]->(dest:Literal) ` +
|
||||
`RETURN src.uri as src, rel.uri as rel, dest.value as dest LIMIT ${limit}`,
|
||||
);
|
||||
for (const rec of (litResult.data ?? [])) {
|
||||
out.push([field(rec, "src"), field(rec, "rel"), field(rec, "dest")]);
|
||||
}
|
||||
const nodeResult = await this.graph.query(
|
||||
`MATCH (src:Node)-[rel:Rel]->(dest:Node) ` +
|
||||
`RETURN src.uri as src, rel.uri as rel, dest.uri as dest LIMIT ${limit}`,
|
||||
);
|
||||
for (const rec of (nodeResult.data ?? [])) {
|
||||
out.push([field(rec, "src"), field(rec, "rel"), field(rec, "dest")]);
|
||||
}
|
||||
}
|
||||
return { queryTriples };
|
||||
}
|
||||
|
||||
export class FalkorDBTriplesQueryError extends S.TaggedErrorClass<FalkorDBTriplesQueryError>()(
|
||||
|
|
@ -302,7 +310,7 @@ const falkorDBTriplesQueryError = (operation: string, cause: unknown) =>
|
|||
export const makeFalkorDBTriplesQueryService = (
|
||||
config: FalkorDBQueryConfig = {},
|
||||
): FalkorDBTriplesQueryServiceShape => {
|
||||
const query = new FalkorDBTriplesQuery(config);
|
||||
const query = makeFalkorDBTriplesQuery(config);
|
||||
return {
|
||||
queryTriples: Effect.fn("FalkorDBTriplesQuery.queryTriples")((
|
||||
s: Term | undefined,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue