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.
@@ -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