Mercurial > code > home > repos > streamed-graph
view src/layout/Layout.ts @ 110:3cdbbd913f1d
table displays now just barely
author | drewp@bigasterisk.com |
---|---|
date | Fri, 18 Mar 2022 23:41:24 -0700 |
parents | 5e6840229a05 |
children | 4b33a479dc2f |
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 } 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)[]; } interface ISP { subj: NamedNode; pred: NamedNode; } const makeSP = Immutable.Record<ISP>({ subj: new NamedNode(""), pred: new NamedNode(""), }); class AlignedTableBuilder { subjSet = Immutable.Set<NamedNode>(); predSet = Immutable.Set<NamedNode>(); cell = Immutable.Map<string, Immutable.Set<Term>>(); 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); const key =subj.id+pred.id//makeSP({ subj, pred }); const cur = this.cell.get(key, undefined); const newval = cur === undefined ? Immutable.Set([q.object]) : cur.add(q.object); this.cell = this.cell.set(key, newval); } value(): AlignedTable { let preds = uniqueSortedTerms(this.predSet); const tagged = preds.map((p, i)=>{ if (p.equals(rdf.type)) { i=999; } 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); // const omittedColumn = pred.equals(rdf.type); const subjs = uniqueSortedTerms(this.subjSet); const outputGrid: Term[][][] = []; for (let subj of subjs) { const row: Term[][] = []; preds.forEach((pred) => { const key = subj.id+pred.id;//makeSP({ subj, pred }); const objs = this.cell.get(key, Immutable.Set<Term>([])); const uniq = uniqueSortedTerms(objs); console.log("cell objs", objs.size, uniq.length); 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, // }); // } // }