165 lines
6.1 KiB
TypeScript
165 lines
6.1 KiB
TypeScript
import { create } from "@bufbuild/protobuf";
|
|
import * as wire from "../gen/camino/schema_pb.js";
|
|
import type { QueryArgument, QuerySelection } from "./types.js";
|
|
import type {
|
|
QueryAggregatePredicate,
|
|
QueryExpression,
|
|
QueryPathStep,
|
|
QueryPredicate,
|
|
QueryReduction,
|
|
QueryRelationalPlan,
|
|
QueryRow,
|
|
} from "./relational.js";
|
|
|
|
export function relationalWire(
|
|
argument: (v: QueryArgument) => wire.QueryArgument,
|
|
selection: (v: QuerySelection) => wire.QuerySelection,
|
|
) {
|
|
const path = (p: QueryPathStep): wire.QueryPathStep =>
|
|
create(wire.QueryPathStepSchema, {
|
|
step: p.kind === "relation" ? { case: "relation", value: p } : { case: p.kind, value: true },
|
|
});
|
|
const expression = (e: QueryExpression) =>
|
|
create(wire.QueryExpressionSchema, {
|
|
path: e.path.map(path),
|
|
valueTypeJson: JSON.stringify(e.type),
|
|
leaf:
|
|
e.leaf.kind === "field"
|
|
? { case: "field", value: e.leaf }
|
|
: e.leaf.kind === "ref"
|
|
? { case: "ref", value: e.leaf.interfaceRevisionId }
|
|
: { case: e.leaf.kind, value: true },
|
|
});
|
|
const reduction = (r: QueryReduction) =>
|
|
create(wire.QueryReductionSchema, {
|
|
operator: {
|
|
count: wire.QueryAggregateOperator.COUNT,
|
|
countPresent: wire.QueryAggregateOperator.COUNT_PRESENT,
|
|
sum: wire.QueryAggregateOperator.SUM,
|
|
avg: wire.QueryAggregateOperator.AVG,
|
|
min: wire.QueryAggregateOperator.MIN,
|
|
max: wire.QueryAggregateOperator.MAX,
|
|
}[r.operator],
|
|
operand: r.operand && expression(r.operand),
|
|
valueTypeJson: JSON.stringify(r.type),
|
|
});
|
|
const operand = (v: QueryExpression | QueryReduction) =>
|
|
create(wire.QueryOperandSchema, {
|
|
operand:
|
|
"operator" in v ? { case: "reduction", value: reduction(v) } : { case: "expression", value: expression(v) },
|
|
});
|
|
const predicate = (p: QueryPredicate | QueryAggregatePredicate): wire.QueryPredicate => {
|
|
switch (p.kind) {
|
|
case "and":
|
|
case "or":
|
|
return create(wire.QueryPredicateSchema, {
|
|
predicate: { case: p.kind, value: { children: p.children.map(predicate) } },
|
|
});
|
|
case "not":
|
|
return create(wire.QueryPredicateSchema, { predicate: { case: "not", value: predicate(p.child) } });
|
|
case "compare":
|
|
return create(wire.QueryPredicateSchema, {
|
|
predicate: {
|
|
case: "compare",
|
|
value: {
|
|
operand: operand(p.expression),
|
|
value: argument(p.value),
|
|
operator: {
|
|
eq: wire.QueryComparisonOperator.EQ,
|
|
in: wire.QueryComparisonOperator.IN,
|
|
isNull: wire.QueryComparisonOperator.IS_NULL,
|
|
lt: wire.QueryComparisonOperator.LT,
|
|
lte: wire.QueryComparisonOperator.LTE,
|
|
gt: wire.QueryComparisonOperator.GT,
|
|
gte: wire.QueryComparisonOperator.GTE,
|
|
}[p.operator],
|
|
},
|
|
},
|
|
});
|
|
case "relation":
|
|
return create(wire.QueryPredicateSchema, {
|
|
predicate: {
|
|
case: "relation",
|
|
value: {
|
|
path: p.path.map(path),
|
|
relation: p,
|
|
operator: {
|
|
some: wire.QueryRelationPredicateOperator.SOME,
|
|
none: wire.QueryRelationPredicateOperator.NONE,
|
|
is: wire.QueryRelationPredicateOperator.IS,
|
|
isNull: wire.QueryRelationPredicateOperator.IS_NULL,
|
|
}[p.operator],
|
|
predicate: p.predicate && predicate(p.predicate),
|
|
value: p.value && argument(p.value),
|
|
},
|
|
},
|
|
});
|
|
case "reduce":
|
|
return create(wire.QueryPredicateSchema, {
|
|
predicate: {
|
|
case: "reduce",
|
|
value: {
|
|
path: p.path.map(path),
|
|
relation: p,
|
|
where: p.where && predicate(p.where),
|
|
having: predicate(p.having),
|
|
},
|
|
},
|
|
});
|
|
}
|
|
};
|
|
const row = (r: QueryRow): wire.QueryRow =>
|
|
create(wire.QueryRowSchema, {
|
|
row:
|
|
r.kind === "object"
|
|
? {
|
|
case: "object",
|
|
value: {
|
|
interfaceRevisionId: r.interfaceRevisionId,
|
|
membership: !!r.membership,
|
|
keyType: r.membership?.keyType,
|
|
},
|
|
}
|
|
: { case: "pair", value: { source: row(r.source), target: row(r.target) } },
|
|
});
|
|
const plan = (p: QueryRelationalPlan) =>
|
|
create(wire.QueryRelationalPlanSchema, {
|
|
stages: p.stages.map((s) => {
|
|
const op = s.operation;
|
|
const operation: wire.QueryRelationalStage["operation"] =
|
|
op.kind === "source"
|
|
? { case: "source", value: create(wire.QueryRelationPathSchema, op) }
|
|
: op.kind === "filter"
|
|
? { case: "filter", value: predicate(op.predicate) }
|
|
: op.kind === "expand"
|
|
? {
|
|
case: "expand",
|
|
value: create(wire.QueryExpansionSchema, { path: op.path.map(path), relation: op }),
|
|
}
|
|
: {
|
|
case: "distinct",
|
|
value: create(wire.QueryDistinctSchema, {
|
|
keys: op.keys.map((k) => ({ ...k, expression: expression(k.expression) })),
|
|
}),
|
|
};
|
|
return create(wire.QueryRelationalStageSchema, { id: s.id, input: s.input, row: row(s.row), operation });
|
|
}),
|
|
terminals: p.terminals.map((t) => ({
|
|
stage: t.stage,
|
|
path: t.path,
|
|
groups: t.kind === "groups",
|
|
keys: t.keys.map((k) => ({ ...k, expression: expression(k.expression) })),
|
|
reductions: t.reductions.map((r) => ({ ...r, reduction: reduction(r.reduction) })),
|
|
where: t.where && predicate(t.where),
|
|
having: t.having && predicate(t.having),
|
|
order: t.order.map((o) => ({ operand: operand(o.expression), descending: o.descending })),
|
|
first: t.first && argument(t.first),
|
|
all: t.all && argument(t.all),
|
|
after: t.after && argument(t.after),
|
|
selection: t.selection.map(selection),
|
|
residual: t.residual,
|
|
})),
|
|
});
|
|
return { predicate, plan };
|
|
}
|