eyeling 1.27.9 → 1.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/browser/eyeling.browser.js +313 -71
- package/examples/alma-rdf-messages.n3 +202 -0
- package/examples/output/alma-rdf-messages.n3 +0 -0
- package/eyeling.js +320 -72
- package/index.d.ts +3 -2
- package/lib/cli.js +140 -27
- package/lib/engine.js +31 -19
- package/lib/rdfjs.js +142 -25
- package/notes/rdf-message-logs.md +228 -0
- package/notes/rdfjs-integration.md +378 -0
- package/package.json +1 -1
- package/test/api.test.js +75 -20
- package/test/stream_messages.test.js +102 -3
- package/tools/bundle.js +7 -1
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Alma MARC extraction over a remote RDF Message Log.
|
|
2
|
+
#
|
|
3
|
+
# This example is intended to be run with the UGent Alma RDF Message Log URL.
|
|
4
|
+
# The log is larger than 9 GB, so it deliberately remains remote and is not
|
|
5
|
+
# checked into examples/input/.
|
|
6
|
+
#
|
|
7
|
+
# CLI:
|
|
8
|
+
# eyeling -r --stream-messages examples/alma-rdf-messages.n3 https://ugent-lib-opendata-prd.s3.ugent.be/alma-rdf/rdf-messages.20260404.nt
|
|
9
|
+
#
|
|
10
|
+
# Playground:
|
|
11
|
+
# https://eyereasoner.github.io/eyeling/playground?state=g.H4sIAAAAAAAC_02N3QqDMAxG36XXtpEpu_BtUhur0EZJWrYx9u7Tjf1chBz44Jy7ITMY05i0v7mUTQeAGomLTYu360YcsKDdJDjt3GtxngBTRithgv1sJlWMpO7Uns5t3_aOy66sf0rBi4tLmauvSjKuXA7RuGagGwmhrkxycFo4gtCkMBMGhYwLA10xb4n0m_0ludtL3gxFKjVGPpDf8HgCVZUvyeAAAAA
|
|
12
|
+
#
|
|
13
|
+
# Each message is one bibliographic record shaped as a nested list of MARC
|
|
14
|
+
# fields: <record> :record (("245" "1" "0" "a" "Title…") ("650" …) …), where an
|
|
15
|
+
# inner list is (tag ind1 ind2 code value code value …). These backward helper
|
|
16
|
+
# rules (marc:field / marc:subf / marc:map / marc:join / marc:id) walk that
|
|
17
|
+
# structure to pull a subfield's value by (tag, subfield-code); the forward
|
|
18
|
+
# rules at the bottom emit :title / :subject / :type per record.
|
|
19
|
+
#
|
|
20
|
+
|
|
21
|
+
@prefix : <http://example.org/ns#> .
|
|
22
|
+
@prefix list: <http://www.w3.org/2000/10/swap/list#> .
|
|
23
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
24
|
+
@prefix eymsg: <https://eyereasoner.github.io/eyeling/vocab/message#> .
|
|
25
|
+
@prefix string: <http://www.w3.org/2000/10/swap/string#> .
|
|
26
|
+
@prefix math: <http://www.w3.org/2000/10/swap/math#> .
|
|
27
|
+
@prefix marc: <https://codeberg.org/phochste/marcattacks#> .
|
|
28
|
+
|
|
29
|
+
## ---- Helper functions (backward rules) ----
|
|
30
|
+
|
|
31
|
+
# marc:splice - drop the first ?Idx members of a list (here: the tag + indicators)
|
|
32
|
+
{ (?List ?Idx) marc:splice ?Result }
|
|
33
|
+
<=
|
|
34
|
+
{
|
|
35
|
+
( ?X {
|
|
36
|
+
?List list:iterate (?Num ?X).
|
|
37
|
+
?Num math:notLessThan ?Idx.
|
|
38
|
+
} ?Result ) log:collectAllIn _:x.
|
|
39
|
+
}.
|
|
40
|
+
|
|
41
|
+
# marc:join - join a list with a separator
|
|
42
|
+
{ (?List ?Sep) marc:join ?Result }
|
|
43
|
+
<=
|
|
44
|
+
{
|
|
45
|
+
(?List ?Sep "") marc:join ?Result.
|
|
46
|
+
}.
|
|
47
|
+
|
|
48
|
+
{ ( () ?Sep ?Acc ) marc:join ?Acc }
|
|
49
|
+
<= true.
|
|
50
|
+
|
|
51
|
+
{ ( ?List ?Sep ?Acc ) marc:join ?Result }
|
|
52
|
+
<=
|
|
53
|
+
{
|
|
54
|
+
?List list:firstRest (?H ?T).
|
|
55
|
+
?Acc log:equalTo "".
|
|
56
|
+
( ?T ?Sep ?H ) marc:join ?Result .
|
|
57
|
+
}.
|
|
58
|
+
|
|
59
|
+
{ ( ?List ?Sep ?Acc ) marc:join ?Result }
|
|
60
|
+
<=
|
|
61
|
+
{
|
|
62
|
+
?List list:firstRest (?H ?T).
|
|
63
|
+
?Acc log:notEqualTo "".
|
|
64
|
+
( ?Acc ?Sep ?H) string:concatenation ?AccNew.
|
|
65
|
+
( ?T ?Sep ?AccNew ) marc:join ?Result .
|
|
66
|
+
}.
|
|
67
|
+
|
|
68
|
+
# marc:id - return the record id as an IRI (from the 001 control field)
|
|
69
|
+
{ ?Record marc:id ?Result }
|
|
70
|
+
<=
|
|
71
|
+
{
|
|
72
|
+
(?Record "001") marc:field0 ?F001.
|
|
73
|
+
?F001 marc:ctrl ?ID.
|
|
74
|
+
( "http://lib.ugent.be/record/" ?ID ) string:concatenation ?IRI_ID.
|
|
75
|
+
?Result log:uri ?IRI_ID.
|
|
76
|
+
}.
|
|
77
|
+
|
|
78
|
+
# marc:ctrl - return the control value of a field
|
|
79
|
+
{ ?Field marc:ctrl ?Result }
|
|
80
|
+
<=
|
|
81
|
+
{
|
|
82
|
+
(?Field 3) list:memberAt "_" .
|
|
83
|
+
(?Field 4) list:memberAt ?Result.
|
|
84
|
+
}.
|
|
85
|
+
|
|
86
|
+
# marc:subf - return all values matching a subfield regex
|
|
87
|
+
{ ( ?Field ?Regex) marc:subf ?Result }
|
|
88
|
+
<=
|
|
89
|
+
{
|
|
90
|
+
( ?Field 3) marc:splice ?FieldData.
|
|
91
|
+
( ?FieldData ?Regex ()) marc:subf ?Result.
|
|
92
|
+
} .
|
|
93
|
+
|
|
94
|
+
{ ( () ?Regex ?Acc ) marc:subf ?Acc }
|
|
95
|
+
<= true.
|
|
96
|
+
|
|
97
|
+
{ ( ?FieldData ?Regex ?Acc ) marc:subf ?Result }
|
|
98
|
+
<=
|
|
99
|
+
{
|
|
100
|
+
?FieldData list:firstRest (?Subf ?Rest).
|
|
101
|
+
?Rest list:firstRest (?Value ?Tail).
|
|
102
|
+
?Subf string:matches ?Regex.
|
|
103
|
+
( ?Acc (?Value)) list:append ?Acc2.
|
|
104
|
+
( ?Tail ?Regex ?Acc2 ) marc:subf ?Result.
|
|
105
|
+
}.
|
|
106
|
+
|
|
107
|
+
{ ( ?FieldData ?Regex ?Acc ) marc:subf ?Result }
|
|
108
|
+
<=
|
|
109
|
+
{
|
|
110
|
+
?FieldData list:firstRest (?Subf ?Rest).
|
|
111
|
+
?Rest list:firstRest (?Value ?Tail).
|
|
112
|
+
?Subf string:notMatches ?Regex.
|
|
113
|
+
( ?Tail ?Regex ?Acc ) marc:subf ?Result.
|
|
114
|
+
}.
|
|
115
|
+
|
|
116
|
+
# marc:field0 - collect the first row of a marc field
|
|
117
|
+
{ ( ?Record ?Field) marc:field0 ?Result }
|
|
118
|
+
<=
|
|
119
|
+
{
|
|
120
|
+
( ?Record ?Field) marc:field ?F.
|
|
121
|
+
?F list:first ?Result.
|
|
122
|
+
}.
|
|
123
|
+
|
|
124
|
+
# marc:field - collect all data for a marc field
|
|
125
|
+
{ ( ?Record ?Field) marc:field ?Result}
|
|
126
|
+
<=
|
|
127
|
+
{
|
|
128
|
+
( ?Record ?Field ()) marc:field ?Result.
|
|
129
|
+
}.
|
|
130
|
+
|
|
131
|
+
{ ( () ?Field ?Acc ) marc:field ?Acc}
|
|
132
|
+
<= true.
|
|
133
|
+
|
|
134
|
+
{ ( ?L ?Field ?Acc ) marc:field ?Result }
|
|
135
|
+
<=
|
|
136
|
+
{
|
|
137
|
+
?L list:firstRest (?H ?T).
|
|
138
|
+
( ?H 0 ) list:memberAt ?Field.
|
|
139
|
+
( ?Acc (?H) ) list:append ?AccNew.
|
|
140
|
+
(?T ?Field ?AccNew) marc:field ?Result.
|
|
141
|
+
}.
|
|
142
|
+
|
|
143
|
+
{ (?L ?Field ?Acc) marc:field ?Result }
|
|
144
|
+
<=
|
|
145
|
+
{
|
|
146
|
+
?L list:firstRest (?H ?T).
|
|
147
|
+
( ?H 0 ) list:memberAt ?X.
|
|
148
|
+
?Field log:notEqualTo ?X.
|
|
149
|
+
(?T ?Field ?Acc) marc:field ?Result.
|
|
150
|
+
}.
|
|
151
|
+
|
|
152
|
+
# marc:map - join all values of (tag, subfield) into one string
|
|
153
|
+
{ ( ?Record ?Tag ?Subfield ) marc:map ?Result }
|
|
154
|
+
<=
|
|
155
|
+
{
|
|
156
|
+
(?Record ?Tag) marc:field ?FL.
|
|
157
|
+
?FL list:member ?F.
|
|
158
|
+
(?F ?Subfield) marc:subf ?T .
|
|
159
|
+
(?T " ") marc:join ?Result.
|
|
160
|
+
}.
|
|
161
|
+
|
|
162
|
+
## ---- Extraction rules (forward) ----
|
|
163
|
+
|
|
164
|
+
# In --stream-messages mode the current RDF Message payload is exposed as a
|
|
165
|
+
# quoted graph via eymsg:payloadGraph/log:nameOf. Match :record inside that
|
|
166
|
+
# payload graph, then reuse the ordinary MARC helper rules on the bound list.
|
|
167
|
+
|
|
168
|
+
{
|
|
169
|
+
?Envelope eymsg:payloadGraph ?Payload.
|
|
170
|
+
?Payload log:nameOf ?PayloadContext.
|
|
171
|
+
?PayloadContext log:includes { ?X :record ?Record. }.
|
|
172
|
+
?Record marc:id ?ID.
|
|
173
|
+
(?Record "245" "a") marc:map ?Val.
|
|
174
|
+
}
|
|
175
|
+
=>
|
|
176
|
+
{
|
|
177
|
+
?ID :title ?Val.
|
|
178
|
+
}.
|
|
179
|
+
|
|
180
|
+
{
|
|
181
|
+
?Envelope eymsg:payloadGraph ?Payload.
|
|
182
|
+
?Payload log:nameOf ?PayloadContext.
|
|
183
|
+
?PayloadContext log:includes { ?X :record ?Record. }.
|
|
184
|
+
?Record marc:id ?ID.
|
|
185
|
+
(?Record "650" "a") marc:map ?Val.
|
|
186
|
+
}
|
|
187
|
+
=>
|
|
188
|
+
{
|
|
189
|
+
?ID :subject ?Val.
|
|
190
|
+
}.
|
|
191
|
+
|
|
192
|
+
{
|
|
193
|
+
?Envelope eymsg:payloadGraph ?Payload.
|
|
194
|
+
?Payload log:nameOf ?PayloadContext.
|
|
195
|
+
?PayloadContext log:includes { ?X :record ?Record. }.
|
|
196
|
+
?Record marc:id ?ID.
|
|
197
|
+
(?Record "920" "a") marc:map ?Val.
|
|
198
|
+
}
|
|
199
|
+
=>
|
|
200
|
+
{
|
|
201
|
+
?ID :type ?Val.
|
|
202
|
+
}.
|
|
File without changes
|