Mercurial > code > home > repos > streamed-graph
view src/layout/Layout.ts @ 114:4b33a479dc2f
fix layout test to match new layout return types. clean up UriPairMap
author | drewp@bigasterisk.com |
---|---|
date | Sat, 19 Mar 2022 16:12:49 -0700 |
parents | 3cdbbd913f1d |
children | 84551452a9c9 |
line wrap: on
line source
// Organize graph data into tables (column orders, etc) for the view layer. import Immutable from "immutable"; // mostly using this for the builtin equals() testing, since NamedNode(x)!=NamedNode(x) import { NamedNode, Quad, Store, Term } from "n3"; import { rdf, rdfs } from "./namespaces"; import { uniqueSortedTerms, UriPairMap } from "./rdf_value"; import { TableDesc, ViewConfig } from "./ViewConfig"; type UriSet = Immutable.Set<NamedNode>; export type TypeToSubjs = Immutable.Map<NamedNode, UriSet>; interface ColumnHeader { rdfType: NamedNode; pred: NamedNode; } export interface AlignedTable { columnHeaders: ColumnHeader[]; rowHeaders: NamedNode[]; rows: Term[][][]; } export interface PredRow { pred: NamedNode; objs: Term[]; } export interface SubjRow { subj: NamedNode; predRows: PredRow[]; } export interface FreeStatements { subjRows: SubjRow[]; } export interface LayoutResult { sections: (AlignedTable | FreeStatements)[]; } class AlignedTableBuilder { subjSet = Immutable.Set<NamedNode>(); predSet = Immutable.Set<NamedNode>(); cell = new UriPairMap(); constructor( public rdfType: NamedNode /* plus join types, sort instructions */ ) {} addQuad(q: Quad) { const subj = q.subject as NamedNode; const pred = q.predicate as NamedNode; this.subjSet = this.subjSet.add(subj); this.predSet = this.predSet.add(pred); this.cell.add(subj, pred, q.object); } _displayedPreds(): NamedNode[] { let preds = uniqueSortedTerms(this.predSet); preds = preds.filter((p) => { return !p.equals(rdf.type); }); const tagged = preds.map((p, i) => { if (p.equals(rdfs.label)) { i = -1; } return { sort: i, val: p }; }); tagged.sort((a, b) => { return a.sort - b.sort; }); preds = tagged.map((e) => e.val); return preds; } value(): AlignedTable { const subjs = uniqueSortedTerms(this.subjSet); const preds = this._displayedPreds(); const outputGrid: Term[][][] = []; for (let subj of subjs) { const row: Term[][] = []; preds.forEach((pred) => { const objs = this.cell.get(subj, pred); const uniq = uniqueSortedTerms(objs); row.push(uniq); }); outputGrid.push(row); } const headers = preds.map((pred) => { return { rdfType: this.rdfType, pred: pred }; }); return { columnHeaders: headers, rowHeaders: subjs, rows: outputGrid }; } } function findTypesNeededForTables(viewConfig?: ViewConfig): UriSet { const typesToGather: NamedNode[] = []; if (viewConfig) { viewConfig.tables.forEach((t: TableDesc) => { typesToGather.push(t.primary); }); } return Immutable.Set(typesToGather); } function findSubjectsWithTypes(graph: Store, typesToGather: UriSet): UriSet { const subjectsToGather: NamedNode[] = []; const ft = typesToGather.toArray()[0]; graph.forEach( (q: Quad) => { if (q.object.equals(ft)) { //typesToGather.has(q.object as NamedNode)) { subjectsToGather.push(q.subject as NamedNode); } }, null, rdf.type, null, null ); return Immutable.Set(subjectsToGather); } function freeStatmentsSection(stmts: Quad[]): FreeStatements { const subjs: NamedNode[] = []; stmts.forEach((q) => { subjs.push(q.subject as NamedNode); }); return { subjRows: uniqueSortedTerms(subjs).map((subj) => { const preds: NamedNode[] = []; let po = Immutable.Map<NamedNode, Term[]>(); stmts.forEach((q) => { if (q.subject.equals(subj)) { const p = q.predicate as NamedNode; preds.push(p); po = po.set(p, po.get(p, [])); po.get(p)?.push(q.object as Term); } }); const rows: PredRow[] = []; uniqueSortedTerms(preds).forEach((p) => { rows.push({ pred: p, objs: uniqueSortedTerms(po.get(p, [])) }); }); return { subj: subj, predRows: rows }; }), }; } // The description of how this page should look: sections, tables, etc. export class Layout { constructor(public viewConfig?: ViewConfig) {} plan(graph: Store): LayoutResult { const typesToTable = findTypesNeededForTables(this.viewConfig); const subjectsToTable = findSubjectsWithTypes(graph, typesToTable); const ungrouped: Quad[] = []; const vc = this.viewConfig; const table = vc && vc.tables.length > 0 ? new AlignedTableBuilder(vc.tables[0].primary) //todo multiple tables : null; graph.forEach( (q: Quad) => { let contains = false; subjectsToTable.forEach((s) => { if (s.equals(q.subject)) { contains = true; } }); // if (subjectsToTable.has(q.subject as NamedNode) && table) { // not working if (contains && table) { table.addQuad(q); } else { ungrouped.push(q); } }, null, null, null, null ); const res: LayoutResult = { sections: [] }; if (table) { console.log("table value"); res.sections.push(table.value()); } res.sections.push(freeStatmentsSection(ungrouped)); return res; } } // // One table of rows with a common rdf:type. // export class MultiSubjsTypeBlockLayout { // subjs: NamedNode[]; // preds: NamedNode[]; // graphCells: Immutable.Map<ISP, Immutable.Set<Term>>; // constructor(graph: Store, byType: TypeToSubjs, table: TableDesc) { // const subjSet = byType.get(table.primary); // this.subjs = subjSet ? Array.from(subjSet) : []; // this.subjs.sort(); // let preds = Immutable.Set<NamedNode>(); // this.graphCells = Immutable.Map<ISP, Immutable.Set<Term>>().withMutations( // (mutGraphCells) => { // this.subjs.forEach((subj: NamedNode) => { // graph.forEach( // (q: Quad) => { // if (!Util.isNamedNode(q.predicate)) { // throw new Error(); // } // const pred = q.predicate as NamedNode; // if (pred.equals(rdf.type)) { // // the whole block is labeled with the type // return; // } // preds = preds.add(pred); // const cellKey = this.makeCellKey(subj, pred); // mutGraphCells.set( // cellKey, // mutGraphCells.get(cellKey, Immutable.Set<Term>()).add(q.object) // ); // }, // subj, // null, // null, // null // ); // }); // } // ); // this.preds = Array.from(preds); // this.preds.splice(this.preds.indexOf(rdf.type), 1); // // also pull out label, which should be used on 1st column // this.preds.sort(); // } // makeCellKey(subj: NamedNode, pred: NamedNode): ISP { // return SP({ // subj: subj, // pred: pred, // }); // } // }