-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlxl.mjs
148 lines (137 loc) · 3.28 KB
/
lxl.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
export const GRAPH = '@graph'
export const ID = '@id'
export const TYPE = '@type'
export const REVERSE = '@reverse'
export class Index {
constructor(data) {
this.index = index(data)
connect(this.index)
}
values() {
return Object.values(this.index.nodes)
}
get(id) {
return this.index.nodes[id]
}
}
function index(data) {
const flattened = data[GRAPH]
const nodes = {}
const reverses = {}
for (const node of flattened) {
const id = node[ID]
nodes[id] = node
for (const key in node) {
for (const o of asArray(node[key])) {
if (typeof o === 'object' && ID in o) {
const oId = o[ID]
let rev = reverses[oId]
if (rev == null) {
rev = reverses[oId] = {}
}
let subjects = rev[key]
if (subjects == null) {
subjects = rev[key] = []
}
subjects.push(id)
}
}
}
}
return {nodes, reverses}
}
function connect(index) {
for (const node of Object.values(index.nodes)) {
connectNode(node, index)
}
}
function connectNode(node, index) {
const id = node[ID]
for (const key in node) {
let o = node[key]
if (typeof o === 'object') {
if (Array.isArray(o)) {
node[key] = o.map(x =>
typeof x === 'object' && ID in x ?
index.nodes[x[ID]] || x :
connectNode(x, index)
)
} else {
connectNode(o, index)
if (ID in o) {
node[key] = index.nodes[o[ID]] || o
}
}
}
}
const reverses = index.reverses[id]
if (reverses) {
const rev = node[REVERSE] = {}
for (const key in reverses) {
rev[key] = reverses[key].map(ref => index.nodes[ref])
}
}
return node
}
export function asArray(o) {
return Array.isArray(o) ? o : o == null ? [] : [o]
}
export class LD {
constructor(vocab) {
this.termIndex = {}
this.typeBaseIndex = {}
for (const term of vocab[GRAPH]) {
if (ID in term) {
this.termIndex[term[ID]] = term
}
if (term.subClassOf && ID in term.subClassOf) {
this.typeBaseIndex[term[ID]] = term.subClassOf[ID]
}
}
}
getTerm(id) {
return this.termIndex[id]
}
isA(thing, type) {
let t = thing[TYPE]
while (t != null) {
if (t === type) return true
let next = this.typeBaseIndex[t]
if (next === t) break // cycle
t = next
}
return false
}
getTypeLabelFor(thing) {
return this.getTerm(thing[TYPE])?.label
}
groupByType(things, category=null) {
if (things == null) return []
let termByKey = {}
let groupsByKey = {}
for (let thing of things) {
let t = thing[TYPE]
let term = this.getTerm(t)
if (category) {
term = this.findBaseTermInCategory(term, category) || term
}
let key = term[ID]
let g = groupsByKey[key]
if (g == null) {
g = groupsByKey[key] = {term, members: []}
}
g.members.push(thing)
}
return Object.values(groupsByKey)
}
findBaseTermInCategory(term, category) {
let current = term
while (current != null) {
if (current.category === category) break
let next = this.getTerm(this.typeBaseIndex[current[ID]])
if (next === current) break // cycle
current = next
}
return current
}
}