Mercurial > code > home > repos > streamed-graph
comparison 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 |
comparison
equal
deleted
inserted
replaced
109:cbcd82d21356 | 110:3cdbbd913f1d |
---|---|
1 // Organize graph data into tables (column orders, etc) for the view layer. | 1 // Organize graph data into tables (column orders, etc) for the view layer. |
2 | 2 |
3 import Immutable from "immutable"; // mostly using this for the builtin equals() testing, since NamedNode(x)!=NamedNode(x) | 3 import Immutable from "immutable"; // mostly using this for the builtin equals() testing, since NamedNode(x)!=NamedNode(x) |
4 import { NamedNode, Quad, Store, Term } from "n3"; | 4 import { NamedNode, Quad, Store, Term } from "n3"; |
5 import { rdf } from "./namespaces"; | 5 import { rdf, rdfs } from "./namespaces"; |
6 import { uniqueSortedTerms } from "./rdf_value"; | 6 import { uniqueSortedTerms } from "./rdf_value"; |
7 import { TableDesc, ViewConfig } from "./ViewConfig"; | 7 import { TableDesc, ViewConfig } from "./ViewConfig"; |
8 | 8 |
9 type UriSet = Immutable.Set<NamedNode>; | 9 type UriSet = Immutable.Set<NamedNode>; |
10 export type TypeToSubjs = Immutable.Map<NamedNode, UriSet>; | 10 export type TypeToSubjs = Immutable.Map<NamedNode, UriSet>; |
14 pred: NamedNode; | 14 pred: NamedNode; |
15 } | 15 } |
16 | 16 |
17 export interface AlignedTable { | 17 export interface AlignedTable { |
18 columnHeaders: ColumnHeader[]; | 18 columnHeaders: ColumnHeader[]; |
19 rows: (Term | null)[][]; // each row is 1 wider than columnHeaders since the 1st element is the subject for that row | 19 rowHeaders: NamedNode[]; |
20 rows: Term[][][]; | |
20 } | 21 } |
21 | 22 |
22 export interface PredRow { | 23 export interface PredRow { |
23 pred: NamedNode; | 24 pred: NamedNode; |
24 objs: Term[]; | 25 objs: Term[]; |
35 | 36 |
36 export interface LayoutResult { | 37 export interface LayoutResult { |
37 sections: (AlignedTable | FreeStatements)[]; | 38 sections: (AlignedTable | FreeStatements)[]; |
38 } | 39 } |
39 | 40 |
41 interface ISP { | |
42 subj: NamedNode; | |
43 pred: NamedNode; | |
44 } | |
45 const makeSP = Immutable.Record<ISP>({ | |
46 subj: new NamedNode(""), | |
47 pred: new NamedNode(""), | |
48 }); | |
49 | |
40 class AlignedTableBuilder { | 50 class AlignedTableBuilder { |
41 columnPreds = Immutable.List<NamedNode>(); | 51 subjSet = Immutable.Set<NamedNode>(); |
42 subjRowIndices = Immutable.Map<NamedNode, number>(); | 52 predSet = Immutable.Set<NamedNode>(); |
43 rows: (Term | null)[][] = []; | 53 cell = Immutable.Map<string, Immutable.Set<Term>>(); |
44 constructor( | 54 constructor( |
45 public rdfType: NamedNode /* plus join types, sort instructions */ | 55 public rdfType: NamedNode /* plus join types, sort instructions */ |
46 ) {} | 56 ) {} |
47 | 57 |
48 addQuad(q: Quad) { | 58 addQuad(q: Quad) { |
59 const subj = q.subject as NamedNode; | |
49 const pred = q.predicate as NamedNode; | 60 const pred = q.predicate as NamedNode; |
50 const omittedColumn = pred.equals(rdf.type); | 61 this.subjSet = this.subjSet.add(subj); |
51 if (!this.columnPreds.contains(pred) && !omittedColumn) { | 62 this.predSet = this.predSet.add(pred); |
52 this.columnPreds = this.columnPreds.push(pred); // this is putting cols in random order | 63 |
53 this.rows.forEach((r) => r.push(null)); | 64 const key =subj.id+pred.id//makeSP({ subj, pred }); |
65 const cur = this.cell.get(key, undefined); | |
66 const newval = | |
67 cur === undefined ? Immutable.Set([q.object]) : cur.add(q.object); | |
68 | |
69 this.cell = this.cell.set(key, newval); | |
70 } | |
71 | |
72 value(): AlignedTable { | |
73 let preds = uniqueSortedTerms(this.predSet); | |
74 const tagged = preds.map((p, i)=>{ | |
75 if (p.equals(rdf.type)) { | |
76 i=999; | |
77 } | |
78 if (p.equals(rdfs.label)) { | |
79 i=-1 | |
80 } | |
81 return {sort:i, val: p} | |
82 }) | |
83 tagged.sort((a,b)=>{ | |
84 return a.sort - b.sort; | |
85 }); | |
86 preds = tagged.map((e)=>e.val); | |
87 | |
88 // const omittedColumn = pred.equals(rdf.type); | |
89 const subjs = uniqueSortedTerms(this.subjSet); | |
90 const outputGrid: Term[][][] = []; | |
91 for (let subj of subjs) { | |
92 const row: Term[][] = []; | |
93 preds.forEach((pred) => { | |
94 const key = subj.id+pred.id;//makeSP({ subj, pred }); | |
95 const objs = this.cell.get(key, Immutable.Set<Term>([])); | |
96 const uniq = uniqueSortedTerms(objs); | |
97 console.log("cell objs", objs.size, uniq.length); | |
98 row.push(uniq); | |
99 }); | |
100 outputGrid.push(row); | |
54 } | 101 } |
55 | 102 |
56 const predIndex = omittedColumn ? null : this.columnPreds.indexOf(pred); | 103 const headers = preds.map((pred) => { |
57 let rowIndex = this.subjRowIndices.get(q.subject as NamedNode); | 104 return { rdfType: this.rdfType, pred: pred }; |
58 if (rowIndex === undefined) { | |
59 const newRow = new Array(1 + this.columnPreds.size).fill(null); | |
60 newRow[0] = q.subject; | |
61 this.rows.push(newRow); | |
62 rowIndex = this.rows.length - 1; | |
63 this.subjRowIndices = this.subjRowIndices.set( | |
64 q.subject as NamedNode, | |
65 rowIndex | |
66 ); | |
67 } | |
68 if (predIndex !== null) { | |
69 this.rows[rowIndex][1 + predIndex] = q.object; | |
70 } | |
71 } | |
72 | |
73 value(): AlignedTable { | |
74 this.rows.sort((a, b) => { | |
75 const uriA = (a[0] as NamedNode).value, | |
76 uriB = (b[0] as NamedNode).value; | |
77 return uriA.localeCompare(uriB); | |
78 }); | 105 }); |
79 const headers = this.columnPreds | 106 return { columnHeaders: headers, rowHeaders: subjs, rows: outputGrid }; |
80 .map((pred) => { | |
81 return { rdfType: this.rdfType, pred: pred }; | |
82 }) | |
83 .toArray(); | |
84 return { columnHeaders: headers, rows: this.rows }; | |
85 } | 107 } |
86 } | 108 } |
87 | 109 |
88 function findTypesNeededForTables(viewConfig?: ViewConfig): UriSet { | 110 function findTypesNeededForTables(viewConfig?: ViewConfig): UriSet { |
89 const typesToGather: NamedNode[] = []; | 111 const typesToGather: NamedNode[] = []; |
93 }); | 115 }); |
94 } | 116 } |
95 return Immutable.Set(typesToGather); | 117 return Immutable.Set(typesToGather); |
96 } | 118 } |
97 | 119 |
98 function findSubjectsWithTypes(graph: Store, typesToGatherSet: UriSet): UriSet { | 120 function findSubjectsWithTypes(graph: Store, typesToGather: UriSet): UriSet { |
99 const subjectsToGather: NamedNode[] = []; | 121 const subjectsToGather: NamedNode[] = []; |
122 const ft = typesToGather.toArray()[0]; | |
100 graph.forEach( | 123 graph.forEach( |
101 (q: Quad) => { | 124 (q: Quad) => { |
102 if (typesToGatherSet.contains(q.object as NamedNode)) { | 125 if (q.object.equals(ft)) { |
126 //typesToGather.has(q.object as NamedNode)) { | |
103 subjectsToGather.push(q.subject as NamedNode); | 127 subjectsToGather.push(q.subject as NamedNode); |
104 } | 128 } |
105 }, | 129 }, |
106 null, | 130 null, |
107 rdf.type, | 131 rdf.type, |
140 | 164 |
141 // The description of how this page should look: sections, tables, etc. | 165 // The description of how this page should look: sections, tables, etc. |
142 export class Layout { | 166 export class Layout { |
143 constructor(public viewConfig?: ViewConfig) {} | 167 constructor(public viewConfig?: ViewConfig) {} |
144 plan(graph: Store): LayoutResult { | 168 plan(graph: Store): LayoutResult { |
145 const typesToGatherSet = findTypesNeededForTables(this.viewConfig); | 169 const typesToTable = findTypesNeededForTables(this.viewConfig); |
146 | 170 |
147 const subjectsToGatherSet = findSubjectsWithTypes(graph, typesToGatherSet); | 171 const subjectsToTable = findSubjectsWithTypes(graph, typesToTable); |
148 const ungrouped: Quad[] = []; | 172 const ungrouped: Quad[] = []; |
149 const vc = this.viewConfig; | 173 const vc = this.viewConfig; |
150 const table = | 174 const table = |
151 vc && vc.tables.length > 0 | 175 vc && vc.tables.length > 0 |
152 ? new AlignedTableBuilder(vc.tables[0].primary) | 176 ? new AlignedTableBuilder(vc.tables[0].primary) //todo multiple tables |
153 : null; | 177 : null; |
154 | 178 |
155 graph.forEach( | 179 graph.forEach( |
156 (q: Quad) => { | 180 (q: Quad) => { |
157 if (!subjectsToGatherSet.contains(q.subject as NamedNode) || !table) { | 181 let contains = false; |
182 subjectsToTable.forEach((s) => { | |
183 if (s.equals(q.subject)) { | |
184 contains = true; | |
185 } | |
186 }); | |
187 | |
188 // if (subjectsToTable.has(q.subject as NamedNode) && table) { // not working | |
189 if (contains && table) { | |
190 table.addQuad(q); | |
191 } else { | |
158 ungrouped.push(q); | 192 ungrouped.push(q); |
159 } else { | |
160 table.addQuad(q); | |
161 } | 193 } |
162 }, | 194 }, |
163 null, | 195 null, |
164 null, | 196 null, |
165 null, | 197 null, |
166 null | 198 null |
167 ); | 199 ); |
168 const res: LayoutResult = { sections: [] }; | 200 const res: LayoutResult = { sections: [] }; |
169 if (table) { | 201 if (table) { |
202 console.log("table value"); | |
170 res.sections.push(table.value()); | 203 res.sections.push(table.value()); |
171 } | 204 } |
172 res.sections.push(freeStatmentsSection(ungrouped)); | 205 res.sections.push(freeStatmentsSection(ungrouped)); |
173 return res; | 206 return res; |
174 } | 207 } |
175 } | 208 } |
176 | |
177 // interface ISP { | |
178 // subj: NamedNode; | |
179 // pred: NamedNode; | |
180 // } | |
181 // const SP = Immutable.Record<ISP>({ | |
182 // subj: new NamedNode(""), | |
183 // pred: new NamedNode(""), | |
184 // }); | |
185 | 209 |
186 // // One table of rows with a common rdf:type. | 210 // // One table of rows with a common rdf:type. |
187 // export class MultiSubjsTypeBlockLayout { | 211 // export class MultiSubjsTypeBlockLayout { |
188 // subjs: NamedNode[]; | 212 // subjs: NamedNode[]; |
189 // preds: NamedNode[]; | 213 // preds: NamedNode[]; |