Mercurial > code > home > repos > streamed-graph
comparison src/layout/Layout.ts @ 118:c2923b20bf5c
support multi labels per column
author | drewp@bigasterisk.com |
---|---|
date | Sun, 20 Mar 2022 00:54:19 -0700 |
parents | 069c1f70afa5 |
children | 8715633f5213 |
comparison
equal
deleted
inserted
replaced
117:069c1f70afa5 | 118:c2923b20bf5c |
---|---|
7 import { ViewConfig } from "./ViewConfig"; | 7 import { ViewConfig } from "./ViewConfig"; |
8 | 8 |
9 type UriSet = Immutable.Set<NamedNode>; | 9 type UriSet = Immutable.Set<NamedNode>; |
10 | 10 |
11 interface ColumnHeader { | 11 interface ColumnHeader { |
12 rdfType: NamedNode; | 12 rdfTypes: NamedNode[]; |
13 pred: NamedNode; | 13 pred: NamedNode; |
14 } | 14 } |
15 | 15 |
16 export interface AlignedTable { | 16 export interface AlignedTable { |
17 columnHeaders: ColumnHeader[]; | 17 columnHeaders: ColumnHeader[]; |
38 } | 38 } |
39 | 39 |
40 class AlignedTableBuilder { | 40 class AlignedTableBuilder { |
41 subjSet: UriSet = Immutable.Set(); | 41 subjSet: UriSet = Immutable.Set(); |
42 predSet: UriSet = Immutable.Set(); | 42 predSet: UriSet = Immutable.Set(); |
43 subjsSeenWithPred: Immutable.Map<NamedNode, NamedNode[]> = Immutable.Map(); | |
44 typesSeenForSubj: Immutable.Map<NamedNode, NamedNode[]> = Immutable.Map(); | |
45 | |
43 cell = new UriPairMap(); | 46 cell = new UriPairMap(); |
44 constructor( | 47 constructor(public primaryType: NamedNode, public joinTypes: NamedNode[]) {} |
45 public rdfType: NamedNode /* plus join types, sort instructions */ | 48 |
46 ) {} | 49 showsType(rdfType: NamedNode): boolean { |
50 return ( | |
51 this.primaryType.equals(rdfType) || | |
52 Immutable.Set<NamedNode>(this.joinTypes).has(rdfType) | |
53 ); | |
54 } | |
47 | 55 |
48 addQuad(q: Quad) { | 56 addQuad(q: Quad) { |
49 const subj = q.subject as NamedNode; | 57 const subj = q.subject as NamedNode; |
50 const pred = q.predicate as NamedNode; | 58 const pred = q.predicate as NamedNode; |
51 this.subjSet = this.subjSet.add(subj); | 59 this.subjSet = this.subjSet.add(subj); |
52 this.predSet = this.predSet.add(pred); | 60 this.predSet = this.predSet.add(pred); |
53 this.cell.add(subj, pred, q.object); | 61 this.cell.add(subj, pred, q.object); |
62 | |
63 if (pred.equals(rdf.type)) { | |
64 this.trackTypes(subj, q.object as NamedNode); | |
65 } | |
66 this.trackSubjs(subj, pred); | |
67 } | |
68 | |
69 private trackTypes(subj: NamedNode<string>, rdfType: NamedNode) { | |
70 let cur = this.typesSeenForSubj.get(subj, undefined); | |
71 if (cur === undefined) { | |
72 cur = []; | |
73 this.typesSeenForSubj = this.typesSeenForSubj.set(subj, cur); | |
74 } | |
75 cur.push(rdfType); | |
76 } | |
77 | |
78 private trackSubjs(subj: NamedNode, pred: NamedNode<string>) { | |
79 let cur = this.subjsSeenWithPred.get(pred, undefined); | |
80 if (cur === undefined) { | |
81 cur = []; | |
82 this.subjsSeenWithPred = this.subjsSeenWithPred.set(pred, cur); | |
83 } | |
84 cur.push(subj); | |
54 } | 85 } |
55 | 86 |
56 _displayedPreds(): NamedNode[] { | 87 _displayedPreds(): NamedNode[] { |
57 let preds = uniqueSortedTerms(this.predSet); | 88 let preds = uniqueSortedTerms(this.predSet); |
58 preds = preds.filter((p) => { | 89 preds = preds.filter((p) => { |
68 return a.sort - b.sort; | 99 return a.sort - b.sort; |
69 }); | 100 }); |
70 preds = tagged.map((e) => e.val); | 101 preds = tagged.map((e) => e.val); |
71 return preds; | 102 return preds; |
72 } | 103 } |
104 | |
73 gotStatements(): boolean { | 105 gotStatements(): boolean { |
74 return !this.subjSet.isEmpty(); | 106 return !this.subjSet.isEmpty(); |
75 } | 107 } |
108 | |
76 value(): AlignedTable { | 109 value(): AlignedTable { |
77 const subjs = uniqueSortedTerms(this.subjSet); | 110 const subjs = uniqueSortedTerms(this.subjSet); |
78 const preds = this._displayedPreds(); | 111 const preds = this._displayedPreds(); |
79 const outputGrid: Term[][][] = []; | 112 const outputGrid: Term[][][] = []; |
80 for (let subj of subjs) { | 113 for (let subj of subjs) { |
85 row.push(uniq); | 118 row.push(uniq); |
86 }); | 119 }); |
87 outputGrid.push(row); | 120 outputGrid.push(row); |
88 } | 121 } |
89 | 122 |
90 const headers = preds.map((pred) => { | 123 const headers: ColumnHeader[] = preds.map((pred) => { |
91 return { rdfType: this.rdfType, pred: pred }; | 124 const subs = this.subjsSeenWithPred.get(pred, []); |
125 const types: NamedNode[] = []; | |
126 subs.forEach((s) => { | |
127 this.typesSeenForSubj.get(s, []).forEach((t) => { | |
128 types.push(t); | |
129 }); | |
130 }); | |
131 | |
132 return { rdfTypes: uniqueSortedTerms(types), pred: pred }; | |
92 }); | 133 }); |
93 return { columnHeaders: headers, rowHeaders: subjs, rows: outputGrid }; | 134 return { columnHeaders: headers, rowHeaders: subjs, rows: outputGrid }; |
94 } | 135 } |
95 } | 136 } |
96 | 137 |
105 ): SubjectTableBuilders { | 146 ): SubjectTableBuilders { |
106 let out: SubjectTableBuilders = Immutable.Map(); | 147 let out: SubjectTableBuilders = Immutable.Map(); |
107 graph.forEach( | 148 graph.forEach( |
108 (q: Quad) => { | 149 (q: Quad) => { |
109 const s = q.subject as NamedNode; | 150 const s = q.subject as NamedNode; |
151 const rdfType = q.object as NamedNode; | |
110 tableBuilders.forEach((tb) => { | 152 tableBuilders.forEach((tb) => { |
111 if (tb.rdfType.equals(q.object)) { | 153 if (tb.showsType(rdfType)) { |
112 let cur = out.get(s); | 154 let cur = out.get(s, undefined); |
113 if (cur === undefined) { | 155 if (cur === undefined) { |
114 cur = []; | 156 cur = []; |
115 out = out.set(s, cur); | 157 out = out.set(s, cur); |
116 } | 158 } |
117 cur.push(tb); | 159 cur.push(tb); |
163 ungrouped: Quad[] | 205 ungrouped: Quad[] |
164 ) { | 206 ) { |
165 graph.forEach( | 207 graph.forEach( |
166 (q: Quad) => { | 208 (q: Quad) => { |
167 const tables = tablesWantingSubject.get(q.subject as NamedNode); | 209 const tables = tablesWantingSubject.get(q.subject as NamedNode); |
210 | |
168 if (tables && tables.length) { | 211 if (tables && tables.length) { |
169 tables.forEach((t: AlignedTableBuilder) => t.addQuad(q)); | 212 tables.forEach((t: AlignedTableBuilder) => t.addQuad(q)); |
170 } else { | 213 } else { |
171 ungrouped.push(q); | 214 ungrouped.push(q); |
172 } | 215 } |
180 | 223 |
181 plan(graph: Store): LayoutResult { | 224 plan(graph: Store): LayoutResult { |
182 const ungrouped: Quad[] = []; | 225 const ungrouped: Quad[] = []; |
183 | 226 |
184 const tableBuilders = this.viewConfig | 227 const tableBuilders = this.viewConfig |
185 ? this.viewConfig.tables.map((t) => new AlignedTableBuilder(t.primary)) | 228 ? this.viewConfig.tables.map( |
229 (t) => new AlignedTableBuilder(t.primary, t.joins) | |
230 ) | |
186 : []; | 231 : []; |
187 | 232 |
188 const tablesWantingSubject = subjectsToTablesMap(graph, tableBuilders); | 233 const tablesWantingSubject = subjectsToTablesMap(graph, tableBuilders); |
189 this._groupAllStatements(graph, tablesWantingSubject, ungrouped); | 234 this._groupAllStatements(graph, tablesWantingSubject, ungrouped); |
190 const res: LayoutResult = { sections: [] }; | 235 const res: LayoutResult = { sections: [] }; |