├── .gitignore
├── README.textile
├── release.sh
└── src
├── build.properties
├── build.xml
├── java
└── com
│ └── marklogic
│ └── semantic
│ ├── Configuration.java
│ ├── ContentFactory.java
│ └── NQuadLoader.java
└── xquery
├── foaf.xqy
├── insert-4.1.xqy
├── insert-property.xqy
├── insert-tuples.xqy
├── insert.xqy
├── owl-inference.xqy
└── semantic.xqy
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | *.class
3 | lib/
4 | publish.sh
5 | src/*.sh
6 | src/simplelogger*
7 | src/test*
--------------------------------------------------------------------------------
/README.textile:
--------------------------------------------------------------------------------
1 | *Note:* MarkLogic 7 introduces support for semantic queries that obsoletes this toolkit,
2 | including support for all of the capabilities in SPARQL 1.0 and many of the capabilities in SPARQL 1.1.
3 | MarkLogic includes rich support for triples, and integrates querying triples with MarkLogic search in
4 | ways that allow you to not only create interesting semantic applications, but to integrate them with search in
5 | a rich way. For details, see the Semantics Developer's Guide.
6 |
7 | If you are using MarkLogic 6 or earlier, you should stick with this toolkit.
8 |
9 |
10 |
11 | For more information see "http://marklogic.github.com/semantic/":http://marklogic.github.io/semantic/
12 |
--------------------------------------------------------------------------------
/release.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 |
4 | set -e
5 |
6 | BASE=`dirname $0`
7 | cd $BASE
8 | pwd
9 | BASE=`basename $PWD`
10 | PAGES=$BASE-gh-pages
11 |
12 | git status
13 |
14 | echo releasing $BASE
15 | (cd src && ant jar) \
16 | && cp lib/$BASE.jar ../$PAGES/ \
17 | && md5sum lib/$BASE.jar ../$PAGES/$BASE.jar \
18 | && (cd ../$PAGES && git commit $BASE.jar -m "new release" \
19 | && git status && git push)
20 |
21 | # release.sh
--------------------------------------------------------------------------------
/src/build.properties:
--------------------------------------------------------------------------------
1 | # build.properties: Ant properties for building the app
2 | #
3 |
4 | java.library.system=/usr/share/java
5 | java.library.user=/home/michael/lib/java
6 |
7 | # end build.properties
8 |
--------------------------------------------------------------------------------
/src/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
54 |
55 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/src/java/com/marklogic/semantic/Configuration.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010 Mark Logic Corporation. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * The use of the Apache License does not indicate that this project is
17 | * affiliated with the Apache Software Foundation.
18 | */
19 | package com.marklogic.semantic;
20 |
21 | /**
22 | * @author Michael Blakeley, Mark Logic Corporation
23 | *
24 | */
25 | public class Configuration extends
26 | com.marklogic.recordloader.Configuration {
27 |
28 | public static final String BATCH_SIZE_KEY = "BATCH_SIZE";
29 |
30 | public static final String BATCH_SIZE_DEFAULT = "" + 1;
31 |
32 | public static final String DUPLICATE_FILTER_LIMIT_KEY = "DUPLICATE_FILTER_LIMIT";
33 |
34 | /**
35 | * limit the memory utilization for duplicate filtering to 500,000 entries,
36 | * or roughly 128-MB.
37 | */
38 | public static final String DUPLICATE_FILTER_LIMIT_DEFAULT = "" + 500 * 1000;
39 |
40 | @Override
41 | public void configure() {
42 | super.configure();
43 |
44 | // subclass-specific options
45 | logger.info("configuring semantic-specific options");
46 | properties.setProperty(CONTENT_FACTORY_CLASSNAME_KEY,
47 | ContentFactory.class.getCanonicalName());
48 | properties.setProperty(LOADER_CLASSNAME_KEY, NQuadLoader.class
49 | .getCanonicalName());
50 | }
51 |
52 | /**
53 | * @return
54 | */
55 | public int getDuplicateFilterLimit() {
56 | return Integer.parseInt(properties
57 | .getProperty(DUPLICATE_FILTER_LIMIT_KEY));
58 | }
59 |
60 | /**
61 | * @return
62 | */
63 | public int getBatchSize() {
64 | return Integer.parseInt(properties.getProperty(BATCH_SIZE_KEY));
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/java/com/marklogic/semantic/ContentFactory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c)2009-2010 Mark Logic Corporation
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * The use of the Apache License does not indicate that this project is
17 | * affiliated with the Apache Software Foundation.
18 | */
19 | package com.marklogic.semantic;
20 |
21 | import java.net.URI;
22 |
23 | import com.marklogic.recordloader.Configuration;
24 | import com.marklogic.recordloader.ContentInterface;
25 | import com.marklogic.recordloader.LoaderException;
26 |
27 | /**
28 | * @author Michael Blakeley, Mark Logic Corporation
29 | *
30 | * This is a dummy class - NQuadLoader doesn't use it.
31 | *
32 | */
33 | public class ContentFactory implements
34 | com.marklogic.recordloader.ContentFactory {
35 |
36 | /*
37 | * (non-Javadoc)
38 | *
39 | * @see com.marklogic.recordloader.ContentFactory#close()
40 | */
41 | public void close() {
42 | // unused
43 | }
44 |
45 | /*
46 | * (non-Javadoc)
47 | *
48 | * @see com.marklogic.recordloader.ContentFactory#getVersionString()
49 | */
50 | public String getVersionString() {
51 | return "n/a";
52 | }
53 |
54 | /*
55 | * (non-Javadoc)
56 | *
57 | * @see
58 | * com.marklogic.recordloader.ContentFactory#newContent(java.lang.String)
59 | */
60 | @SuppressWarnings("unused")
61 | public ContentInterface newContent(String uri) throws LoaderException {
62 | // unused
63 | return null;
64 | }
65 |
66 | /*
67 | * (non-Javadoc)
68 | *
69 | * @see
70 | * com.marklogic.recordloader.ContentFactory#setConfiguration(com.marklogic
71 | * .recordloader.Configuration)
72 | */
73 | @SuppressWarnings("unused")
74 | public void setConfiguration(Configuration configuration)
75 | throws LoaderException {
76 | // unused
77 | }
78 |
79 | /*
80 | * (non-Javadoc)
81 | *
82 | * @see
83 | * com.marklogic.recordloader.ContentFactory#setConnectionUri(java.net.URI)
84 | */
85 | @SuppressWarnings("unused")
86 | public void setConnectionUri(URI uri) throws LoaderException {
87 | // unused
88 | }
89 |
90 | /*
91 | * (non-Javadoc)
92 | *
93 | * @see
94 | * com.marklogic.recordloader.ContentFactory#setFileBasename(java.lang.String
95 | * )
96 | */
97 | @SuppressWarnings("unused")
98 | public void setFileBasename(String name) throws LoaderException {
99 | // unused
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/src/java/com/marklogic/semantic/NQuadLoader.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c)2009-2010 Mark Logic Corporation
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * The use of the Apache License does not indicate that this project is
17 | * affiliated with the Apache Software Foundation.
18 | */
19 | package com.marklogic.semantic;
20 |
21 | import java.io.BufferedReader;
22 | import java.io.FileNotFoundException;
23 | import java.io.IOException;
24 | import java.io.InputStreamReader;
25 | import java.io.OutputStreamWriter;
26 | import java.io.UnsupportedEncodingException;
27 | import java.math.BigDecimal;
28 | import java.net.Authenticator;
29 | import java.net.ConnectException;
30 | import java.net.HttpURLConnection;
31 | import java.net.MalformedURLException;
32 | import java.net.PasswordAuthentication;
33 | import java.net.ProtocolException;
34 | import java.net.URI;
35 | import java.net.URL;
36 | import java.net.URLEncoder;
37 | import java.util.HashMap;
38 | import java.util.LinkedHashMap;
39 | import java.util.Map;
40 | import java.util.Map.Entry;
41 | import java.util.regex.Pattern;
42 |
43 | import org.semanticweb.yars.nx.Node;
44 | import org.semanticweb.yars.nx.parser.NxParser;
45 |
46 | import com.marklogic.ps.Utilities;
47 | import com.marklogic.ps.timing.TimedEvent;
48 | import com.marklogic.recordloader.AbstractLoader;
49 | import com.marklogic.recordloader.FatalException;
50 | import com.marklogic.recordloader.LoaderException;
51 |
52 | /**
53 | * @author Michael Blakeley, Mark Logic Corporation
54 | *
55 | */
56 | public class NQuadLoader extends AbstractLoader {
57 |
58 | protected Configuration config = (Configuration) super.config;
59 |
60 | private static final Long LONG_ONE = new Long(1);
61 |
62 | private static final int OBJECT = 2;
63 |
64 | private static final String VERSION = "2010-08-24.1";
65 |
66 | private boolean detectDecimal = false;
67 |
68 | static String[] names = new String[] { "s", "p", "o", "c" };
69 |
70 | boolean authenticatorIsIntialized = false;
71 |
72 | private Pattern decimalPattern;
73 |
74 | private long count = 0;
75 |
76 | static private Object initMutex = new Object();
77 |
78 | class DuplicateFilter {
79 |
80 | private int limit = config.getDuplicateFilterLimit();
81 |
82 | private Object mutex = new Object();
83 |
84 | Map m = null;
85 |
86 | /**
87 | * @param duplicateFilterLimit
88 | */
89 | public DuplicateFilter(int duplicateFilterLimit) {
90 | m = new LinkedHashMap(limit, 0.75f, true) {
91 | private static final long serialVersionUID = 1L;
92 |
93 | @Override
94 | protected boolean removeEldestEntry(
95 | Entry eldest) {
96 | if (size() <= limit) {
97 | return false;
98 | }
99 | Entry e = eldest;
100 | String k;
101 | int tries = 10;
102 | while (size() > limit) {
103 | k = e.getKey();
104 | if (e.getValue() < 2) {
105 | m.remove(k);
106 | } else {
107 | // tickle it so it won't be eldest anymore
108 | m.get(k);
109 | }
110 | tries--;
111 | if (tries < 0) {
112 | logger.warning("size = " + size()
113 | + ", limit = " + limit);
114 | break;
115 | }
116 | // get the iterator fresh every time,
117 | // because this loop will surely modify it
118 | e = m.entrySet().iterator().next();
119 | }
120 | return false;
121 | }
122 | };
123 | }
124 |
125 | protected boolean exists(String key) {
126 | synchronized (mutex) {
127 | if (m.containsKey(key)) {
128 | m.put(key, m.get(key) + 1);
129 | return true;
130 | }
131 | m.put(key, LONG_ONE);
132 | return false;
133 | }
134 | }
135 |
136 | /**
137 | * @return
138 | */
139 | public int size() {
140 | synchronized (mutex) {
141 | return m.size();
142 | }
143 | }
144 |
145 | /**
146 | * @return the limit
147 | */
148 | public int getLimit() {
149 | synchronized (mutex) {
150 | return limit;
151 | }
152 | }
153 |
154 | }
155 |
156 | static private DuplicateFilter duplicateFilter = null;
157 |
158 | class BatchGroup {
159 |
160 | int size;
161 |
162 | int[] current;
163 |
164 | URI[] uris;
165 |
166 | Map tupleMap = new HashMap();
167 |
168 | /**
169 | * @param _size
170 | * @param _uris
171 | */
172 | public BatchGroup(int _size, URI[] _uris) {
173 | size = _size;
174 | uris = _uris;
175 | current = new int[uris.length];
176 | //logger.info("buckets " + uris.length);
177 | for (int i = 0; i < uris.length; i++) {
178 | tupleMap.put(uris[i], new String[size]);
179 | current[i] = 0;
180 | }
181 | }
182 |
183 | /**
184 | * @param _tuple
185 | * @return
186 | */
187 | public int put(String _tuple) {
188 | int bucket = Math.abs(_tuple.hashCode() % uris.length);
189 | String[] tuples = tupleMap.get(uris[bucket]);
190 | tuples[current[bucket]] = _tuple;
191 | current[bucket]++;
192 | if (current[bucket] < size) {
193 | return -1;
194 | }
195 | //logger.info("batch complete for bucket " + bucket);
196 | return bucket;
197 | }
198 |
199 | /**
200 | * @param _bucket
201 | * @return
202 | */
203 | public String[] tuples(int _bucket) {
204 | return tupleMap.get(uris[_bucket]);
205 | }
206 |
207 | /**
208 | * @param _bucket
209 | */
210 | public void reset(int _bucket) {
211 | current[_bucket] = 0;
212 | }
213 |
214 | /**
215 | * @param _bucket
216 | * @return
217 | */
218 | public URI uri(int _bucket) {
219 | return uris[_bucket];
220 | }
221 |
222 | /**
223 | * @param _bucket
224 | * @return
225 | */
226 | public int size(int _bucket) {
227 | return current[_bucket];
228 | }
229 |
230 | /**
231 | * @return
232 | */
233 | public int length() {
234 | return uris.length;
235 | }
236 |
237 | }
238 |
239 | public void process() throws LoaderException {
240 | super.process();
241 |
242 | // race to initialize
243 | if (null == duplicateFilter) {
244 | synchronized (initMutex) {
245 | // maybe someone else already got it?
246 | if (null == duplicateFilter) {
247 | logger.info("initializing version " + VERSION);
248 | duplicateFilter = new DuplicateFilter(config
249 | .getDuplicateFilterLimit());
250 | logger.info("filter size limit "
251 | + duplicateFilter.getLimit());
252 | // informational message, on initialization only
253 | logger.info("batch size " + config.getBatchSize());
254 | }
255 | }
256 | }
257 |
258 | BatchGroup bg = new BatchGroup(config.getBatchSize(), config
259 | .getConnectionStrings());
260 | decimalPattern = Pattern.compile("^\\d*\\.?\\d+$");
261 | TimedEvent te = null;
262 | long duplicateCount = 0;
263 |
264 | NxParser nxp;
265 | String nextTuple;
266 | int bucket;
267 | int size;
268 |
269 | try {
270 | nxp = new NxParser(input, false);
271 | while (nxp.hasNext()) {
272 | te = new TimedEvent();
273 | nextTuple = processNext(nxp.next());
274 | if (duplicateFilter.exists(nextTuple)) {
275 | // this tuple is a duplicate - skip it
276 | duplicateCount++;
277 | logger.finer("skipping duplicate tuple "
278 | + duplicateCount + ": " + nextTuple);
279 | continue;
280 | }
281 |
282 | bucket = bg.put(nextTuple);
283 | if (0 > bucket) {
284 | continue;
285 | }
286 |
287 | // send these tuples to the database
288 | try {
289 | size = bg.size(bucket);
290 | insert(bg.tuples(bucket), te, bg.uri(bucket), size);
291 | count += size;
292 | } catch (UnsupportedEncodingException e) {
293 | if (config.isFatalErrors()) {
294 | throw e;
295 | }
296 | logger.logException("non-fatal", e);
297 | } catch (LoaderException e) {
298 | if (config.isFatalErrors()) {
299 | throw e;
300 | }
301 | logger.logException("non-fatal", e);
302 | }
303 |
304 | // reset the bucket
305 | bg.reset(bucket);
306 | }
307 |
308 | if (null == te) {
309 | // we never started
310 | return;
311 | }
312 |
313 | // insert any pending tuples
314 | int length = bg.length();
315 | for (int i = 0; i < length; i++) {
316 | size = bg.size(i);
317 | if (size < 1) {
318 | continue;
319 | }
320 | //logger.info("cleaning up " + size + " for bucket " + i);
321 | // no need to try-catch and check isFatalErrors()
322 | // because we are done either way.
323 | insert(bg.tuples(i), te, bg.uri(i), size);
324 | count += size;
325 | // no need to reset the bucket
326 | }
327 | } catch (IOException e) {
328 | throw new LoaderException(e);
329 | }
330 |
331 | }
332 |
333 | /**
334 | * @param ns
335 | * @return
336 | */
337 | private String processNext(Node[] ns) {
338 | Node n;
339 | String name;
340 | String value = null;
341 | boolean isDecimal = false;
342 |
343 | // optionally detect range values in the object
344 | if (detectDecimal) {
345 | value = ns[OBJECT].toString();
346 | isDecimal = decimalPattern.matcher(value).matches();
347 | }
348 |
349 | StringBuilder xml = new StringBuilder("");
350 | for (int i = 0; i < ns.length; i++) {
351 | n = ns[i];
352 | name = names[i];
353 | xml.append("<").append(name);
354 | if (detectDecimal && isDecimal && OBJECT == i) {
355 | xml.append(" dec=\"1\"");
356 | }
357 | xml.append(">").append(Utilities.escapeXml(n.toString()))
358 | .append("").append(name).append(">");
359 | }
360 |
361 | if (detectDecimal && isDecimal) {
362 | // name the new element after the predicate
363 | name = ns[1].toString();
364 | xml.append("")
365 | .append(new BigDecimal(value).toPlainString())
366 | .append("");
367 | }
368 | xml.append("");
369 |
370 | return xml.toString();
371 | }
372 |
373 | /**
374 | * @param urlString
375 | * @param body
376 | * @return
377 | * @throws ActionException
378 | */
379 | protected void doRequest(String urlString, String body)
380 | throws LoaderException {
381 | URL url;
382 | HttpURLConnection conn = null;
383 | logger.fine("url " + urlString);
384 | try {
385 | url = new URL(urlString);
386 | initializeAuthenticator(url);
387 | conn = (HttpURLConnection) url.openConnection();
388 | doRequest(body, conn);
389 | return;
390 | } catch (FileNotFoundException e) {
391 | // HTTP error
392 | try {
393 | if (null != conn) {
394 | StringBuilder sb = new StringBuilder();
395 | Utilities.read(new InputStreamReader(conn
396 | .getErrorStream()), sb);
397 | logger.warning("server responded with: "
398 | + sb.toString());
399 | }
400 | throw new LoaderException(urlString
401 | + ": "
402 | + ((null == conn) ? "null" : conn
403 | .getResponseMessage()), e);
404 | } catch (IOException e1) {
405 | // now we are really in trouble
406 | throw new FatalException(e1);
407 | }
408 | } catch (MalformedURLException e) {
409 | throw new FatalException(urlString, e);
410 | } catch (IOException e) {
411 | throw new LoaderException(e + ": " + urlString, e);
412 | } finally {
413 | if (null != conn) {
414 | conn.disconnect();
415 | }
416 | }
417 | }
418 |
419 | /**
420 | * @param body
421 | * @param conn
422 | * @return
423 | * @throws ProtocolException
424 | * @throws IOException
425 | */
426 | private void doRequest(String body, HttpURLConnection conn)
427 | throws ProtocolException, IOException {
428 | OutputStreamWriter osw = null;
429 | try {
430 | conn.setUseCaches(false);
431 | conn.setRequestMethod("POST");
432 | conn.setRequestProperty("Content-Type",
433 | "application/x-www-form-urlencoded");
434 | // use keep-alive to reduce open sockets
435 | conn.setRequestProperty("Connection", "keep-alive");
436 | conn.setDoOutput(true);
437 | osw = new OutputStreamWriter(conn.getOutputStream());
438 | osw.write(body);
439 | osw.flush();
440 |
441 | BufferedReader in = new BufferedReader(new InputStreamReader(
442 | conn.getInputStream()));
443 | String line;
444 | while ((line = in.readLine()) != null) {
445 | System.out.println(line);
446 | }
447 | in.close();
448 | } finally {
449 | if (null != osw) {
450 | try {
451 | osw.close();
452 | } catch (IOException e) {
453 | logger.logException(e);
454 | }
455 | }
456 | }
457 | }
458 |
459 | /**
460 | * @param url
461 | */
462 | private void initializeAuthenticator(URL url) {
463 | if (authenticatorIsIntialized) {
464 | return;
465 | }
466 | final String[] auth = url.getAuthority().split("@")[0].split(":");
467 | Authenticator.setDefault(new Authenticator() {
468 | protected PasswordAuthentication getPasswordAuthentication() {
469 | return new PasswordAuthentication(auth[0], auth[1]
470 | .toCharArray());
471 | }
472 | });
473 | authenticatorIsIntialized = true;
474 | }
475 |
476 | private void insert(String[] tuples, TimedEvent _event, URI _url,
477 | int _size) throws LoaderException,
478 | UnsupportedEncodingException {
479 | String label = Thread.currentThread().getName() + "=" + count;
480 | StringBuilder body = new StringBuilder();
481 | for (int i = 0; i < _size; i++) {
482 | body.append((0 == i) ? "" : "&").append("xml=").append(
483 | URLEncoder.encode(tuples[i], "UTF-8"));
484 | }
485 | // TODO: move into Content subclass with body bytes?
486 | _event.increment(body.length());
487 | // retry loop
488 | int tries = 0;
489 | int maxTries = 10;
490 | long sleepMillis = Configuration.SLEEP_TIME;
491 | while (tries < maxTries) {
492 | try {
493 | doRequest(_url.toString(), body.toString());
494 | monitor.add(label, _event);
495 | break;
496 | } catch (LoaderException e) {
497 | if (tries < maxTries
498 | && e.getCause() instanceof ConnectException) {
499 | logger.warning("retry " + tries);
500 | Thread.yield();
501 | try {
502 | Thread.sleep(sleepMillis);
503 | } catch (InterruptedException e1) {
504 | // reset interrupt status and throw the error
505 | Thread.interrupted();
506 | logger.warning("interrupted: " + e1.getMessage());
507 | throw e;
508 | }
509 | tries++;
510 | sleepMillis = 2 * sleepMillis;
511 | continue;
512 | }
513 | // no retries left, or the error wasn't a connection error
514 | _event.setError(true);
515 | monitor.add(label, _event);
516 | logger.warning("failed to insert tuples (" + label + "):"
517 | + e.getMessage() + "; " + body.toString());
518 | throw e;
519 | }
520 | }
521 | }
522 |
523 | @Override
524 | public void setConfiguration(
525 | com.marklogic.recordloader.Configuration _config)
526 | throws LoaderException {
527 | super.setConfiguration(_config);
528 | config = (Configuration) _config;
529 | }
530 |
531 | }
532 |
--------------------------------------------------------------------------------
/src/xquery/foaf.xqy:
--------------------------------------------------------------------------------
1 | xquery version "1.0-ml";
2 |
3 | (:
4 | : Copyright (c)2009-2011 Mark Logic Corporation
5 | :
6 | : Licensed under the Apache License, Version 2.0 (the "License");
7 | : you may not use this file except in compliance with the License.
8 | : You may obtain a copy of the License at
9 | :
10 | : http://www.apache.org/licenses/LICENSE-2.0
11 | :
12 | : Unless required by applicable law or agreed to in writing, software
13 | : distributed under the License is distributed on an "AS IS" BASIS,
14 | : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | : See the License for the specific language governing permissions and
16 | : limitations under the License.
17 | :
18 | : The use of the Apache License does not indicate that this project is
19 | : affiliated with the Apache Software Foundation.
20 | :
21 | : foaf example module
22 | :
23 | : @author Michael Blakeley, Mark Logic Corporation
24 | :)
25 | import module namespace sem="http://marklogic.com/semantic"
26 | at "semantic.xqy";
27 |
28 | declare variable $SINDICE-FRIEND := 'http://xmlns.com/foaf/0.1/knows'
29 | ;
30 |
31 | declare variable $SINDICE-FRIEND-HASH := xdmp:hash64($SINDICE-FRIEND)
32 | ;
33 |
34 | let $seeds as xs:string+ := xdmp:get-request-field('seed')
35 | let $filters as xs:string* := xdmp:get-request-field('filter')
36 | let $gen as xs:integer := xs:integer(
37 | xdmp:get-request-field('gen', '6'))
38 | let $m := map:map()
39 | let $do := sem:transitive-closure(
40 | $m, $seeds, $gen, $SINDICE-FRIEND, true(), $filters)
41 | return count(map:keys($m))
42 |
--------------------------------------------------------------------------------
/src/xquery/insert-4.1.xqy:
--------------------------------------------------------------------------------
1 | xquery version "1.0-ml";
2 | (:
3 | : Copyright (c)2009-2011 Mark Logic Corporation
4 | :
5 | : Licensed under the Apache License, Version 2.0 (the "License");
6 | : you may not use this file except in compliance with the License.
7 | : You may obtain a copy of the License at
8 | :
9 | : http://www.apache.org/licenses/LICENSE-2.0
10 | :
11 | : Unless required by applicable law or agreed to in writing, software
12 | : distributed under the License is distributed on an "AS IS" BASIS,
13 | : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | : See the License for the specific language governing permissions and
15 | : limitations under the License.
16 | :
17 | : The use of the Apache License does not indicate that this project is
18 | : affiliated with the Apache Software Foundation.
19 | :
20 | : semantic insert-4.1.xqy
21 | :
22 | : @author Michael Blakeley, Mark Logic Corporation
23 | :)
24 |
25 | declare namespace hs="http://marklogic.com/xdmp/status/host"
26 | ;
27 |
28 | import module namespace sem="http://marklogic.com/semantic"
29 | at "semantic.xqy";
30 |
31 | sem:tuple-insert(
32 | xdmp:unquote(
33 | xdmp:get-request-field('xml') )/t )
34 |
35 | (: semantic insert-4.1.xqy :)
36 |
--------------------------------------------------------------------------------
/src/xquery/insert-property.xqy:
--------------------------------------------------------------------------------
1 | xquery version "1.0-ml";
2 | (:
3 | : Copyright (c)2009-2011 Mark Logic Corporation
4 | :
5 | : Licensed under the Apache License, Version 2.0 (the "License");
6 | : you may not use this file except in compliance with the License.
7 | : You may obtain a copy of the License at
8 | :
9 | : http://www.apache.org/licenses/LICENSE-2.0
10 | :
11 | : Unless required by applicable law or agreed to in writing, software
12 | : distributed under the License is distributed on an "AS IS" BASIS,
13 | : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | : See the License for the specific language governing permissions and
15 | : limitations under the License.
16 | :
17 | : The use of the Apache License does not indicate that this project is
18 | : affiliated with the Apache Software Foundation.
19 | :
20 | : semantic insert-property.xqy
21 | :
22 | : This variant uses so-called "naked" properties, rather than documents.
23 | :
24 | : @author Michael Blakeley, Mark Logic Corporation
25 | :)
26 |
27 | let $skip-existing := true()
28 | let $map := map:map()
29 | let $build := (
30 | for $xml in xdmp:get-request-field('xml')
31 | let $n as element(t) := xdmp:unquote($xml)/t
32 | let $uri := xdmp:integer-to-hex(
33 | xdmp:hash64(
34 | string-join(
35 | (: keep these elements to be in order, for deterministic uris :)
36 | ($n/s, $n/p, $n/o, $n/c), '|')))
37 | where empty(map:get($map, $uri)) and (
38 | not($skip-existing) or empty(xdmp:document-properties($uri)) )
39 | return map:put(
40 | $map, $uri, element t {
41 | $n/@*,
42 | for $n in $n/node()
43 | return typeswitch($n)
44 | case element() return element { node-name($n) } {
45 | attribute h { xdmp:hash64($n) },
46 | $n/node() }
47 | default return $n
48 | } )
49 | )
50 | for $uri in map:keys($map)
51 | return xdmp:document-set-properties(
52 | $uri,
53 | map:get($map, $uri)/*
54 | )
55 |
56 | (: semantic insert-property.xqy :)
57 |
--------------------------------------------------------------------------------
/src/xquery/insert-tuples.xqy:
--------------------------------------------------------------------------------
1 | xquery version "1.0-ml";
2 | (:
3 | : Copyright (c)2009-2011 Mark Logic Corporation
4 | :)
5 |
6 | import module namespace sem="http://marklogic.com/semantic"
7 | at "semantic.xqy";
8 |
9 | declare variable $AS-PROPERTY as xs:boolean external ;
10 |
11 | declare variable $FOREST as xs:unsignedLong external
12 | ;
13 | declare variable $MAP as map:map external
14 | ;
15 |
16 | if ($AS-PROPERTY)
17 | then sem:tuple-insert-as-property(map:get($MAP, xs:string($FOREST)))
18 | else sem:tuple-insert(map:get($MAP, xs:string($FOREST)))
19 |
20 | (: insert-tuples.xqy :)
21 |
--------------------------------------------------------------------------------
/src/xquery/insert.xqy:
--------------------------------------------------------------------------------
1 | xquery version "1.0-ml";
2 | (:
3 | : Copyright (c)2009-2011 Mark Logic Corporation
4 | :
5 | : Licensed under the Apache License, Version 2.0 (the "License");
6 | : you may not use this file except in compliance with the License.
7 | : You may obtain a copy of the License at
8 | :
9 | : http://www.apache.org/licenses/LICENSE-2.0
10 | :
11 | : Unless required by applicable law or agreed to in writing, software
12 | : distributed under the License is distributed on an "AS IS" BASIS,
13 | : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | : See the License for the specific language governing permissions and
15 | : limitations under the License.
16 | :
17 | : The use of the Apache License does not indicate that this project is
18 | : affiliated with the Apache Software Foundation.
19 | :
20 | : semantic insert.xqy
21 | :
22 | : @author Michael Blakeley, Mark Logic Corporation
23 | :)
24 |
25 | declare namespace hs="http://marklogic.com/xdmp/status/host"
26 | ;
27 |
28 | import module namespace sem="http://marklogic.com/semantic"
29 | at "semantic.xqy";
30 |
31 | declare variable $FORESTS as xs:unsignedLong+ := (
32 | (: use local forests only :)
33 | let $forest-key := 'http://marklogic.com/semantic/forests'
34 | let $cached-forests as xs:unsignedLong* := xdmp:get-server-field($forest-key)
35 | return (
36 | if (exists($cached-forests)) then $cached-forests
37 | else (
38 | xdmp:log(text { 'determining local forests' }),
39 | let $db-forests as xs:unsignedLong+ := xdmp:database-forests(
40 | xdmp:database())
41 | let $host-forests as xs:unsignedLong+ := xdmp:host-status(
42 | xdmp:host())/hs:assignments/hs:assignment/hs:forest-id
43 | return xdmp:set-server-field(
44 | $forest-key, $db-forests[ . = $host-forests ])
45 | )
46 | )
47 | );
48 |
49 | declare variable $FOREST-COUNT := count($FORESTS)
50 | ;
51 |
52 | (: map key is forest id, chosen from the local forests.
53 | : map value is a list of the documents to be inserted.
54 | :)
55 | declare variable $MAP as map:map := (
56 | let $m := map:map()
57 | let $build := (
58 | for $n in xdmp:unquote(
59 | xdmp:get-request-field('xml') )/t
60 | let $forest-index := xdmp:document-assign(
61 | sem:uri-for-tuple($n/s, $n/p, $n/o, $n/c),
62 | $FOREST-COUNT )
63 | let $key := xs:string(subsequence($FORESTS, $forest-index, 1))
64 | return map:put($m, $key, (map:get($m, $key), $n))
65 | )
66 | return $m
67 | );
68 |
69 | (: NB - in-forest eval per tuple :)
70 | for $key in map:keys($MAP)
71 | let $forest := xs:unsignedLong($key)
72 | return xdmp:invoke(
73 | 'insert-tuples.xqy',
74 | (xs:QName('AS-PROPERTY'), false(),
75 | xs:QName('FOREST'), $forest,
76 | xs:QName('MAP'), $MAP),
77 | {
78 | element database { $forest } } )
79 |
80 | (: semantic insert.xqy :)
81 |
--------------------------------------------------------------------------------
/src/xquery/owl-inference.xqy:
--------------------------------------------------------------------------------
1 | xquery version "1.0-ml";
2 |
3 | (:
4 | : Copyright (c)2009-2011 Mark Logic Corporation
5 | :
6 | : Licensed under the Apache License, Version 2.0 (the "License");
7 | : you may not use this file except in compliance with the License.
8 | : You may obtain a copy of the License at
9 | :
10 | : http://www.apache.org/licenses/LICENSE-2.0
11 | :
12 | : Unless required by applicable law or agreed to in writing, software
13 | : distributed under the License is distributed on an "AS IS" BASIS,
14 | : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | : See the License for the specific language governing permissions and
16 | : limitations under the License.
17 | :
18 | : The use of the Apache License does not indicate that this project is
19 | : affiliated with the Apache Software Foundation.
20 | :
21 | : @author Li Ding
22 | :)
23 |
24 | import module namespace sem="http://marklogic.com/semantic" at "semantic.xqy";
25 | sem:inf-owl2rl-eq-sym();
26 | import module namespace sem="http://marklogic.com/semantic" at "semantic.xqy";
27 | sem:inf-owl2rl-eq-trans();
28 | import module namespace sem="http://marklogic.com/semantic" at "semantic.xqy";
29 | sem:inf-owl2rl-eq-rep-s();
30 | import module namespace sem="http://marklogic.com/semantic" at "semantic.xqy";
31 | sem:inf-owl2rl-eq-rep-p();
32 | import module namespace sem="http://marklogic.com/semantic" at "semantic.xqy";
33 | sem:inf-owl2rl-eq-rep-o();
34 | import module namespace sem="http://marklogic.com/semantic" at "semantic.xqy";
35 | sem:inf-owl2rl-prp-symp();
36 | import module namespace sem="http://marklogic.com/semantic" at "semantic.xqy";
37 | sem:inf-owl2rl-prp-trp();
38 | import module namespace sem="http://marklogic.com/semantic" at "semantic.xqy";
39 | sem:inf-owl2rl-prp-inv1();
40 | import module namespace sem="http://marklogic.com/semantic" at "semantic.xqy";
41 | sem:inf-owl2rl-prp-inv2();
42 | import module namespace sem="http://marklogic.com/semantic" at "semantic.xqy";
43 | sem:inf-owl2rl-scm-sco();
44 | import module namespace sem="http://marklogic.com/semantic" at "semantic.xqy";
45 | sem:inf-owl2rl-scm-spo();
46 |
47 | import module namespace sem="http://marklogic.com/semantic" at "semantic.xqy";
48 | sem:inf-owl2rl-prp-spo1();
49 | import module namespace sem="http://marklogic.com/semantic" at "semantic.xqy";
50 | sem:inf-owl2rl-cls-hv1();
51 | import module namespace sem="http://marklogic.com/semantic" at "semantic.xqy";
52 | sem:inf-owl2rl-cls-hv2();
53 | import module namespace sem="http://marklogic.com/semantic" at "semantic.xqy";
54 | sem:inf-owl2rl-cax-sco();
55 |
56 | import module namespace sem="http://marklogic.com/semantic" at "semantic.xqy";
57 | sem:inf-owl2rl-scm-cls();
58 | import module namespace sem="http://marklogic.com/semantic" at "semantic.xqy";
59 | sem:inf-owl2rl-scm-op();
60 | import module namespace sem="http://marklogic.com/semantic" at "semantic.xqy";
61 | sem:inf-owl2rl-scm-dp();
62 | ()
63 |
--------------------------------------------------------------------------------
/src/xquery/semantic.xqy:
--------------------------------------------------------------------------------
1 | xquery version "1.0-ml";
2 |
3 | (:
4 | : Copyright (c)2009-2011 Mark Logic Corporation
5 | :
6 | : Licensed under the Apache License, Version 2.0 (the "License");
7 | : you may not use this file except in compliance with the License.
8 | : You may obtain a copy of the License at
9 | :
10 | : http://www.apache.org/licenses/LICENSE-2.0
11 | :
12 | : Unless required by applicable law or agreed to in writing, software
13 | : distributed under the License is distributed on an "AS IS" BASIS,
14 | : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | : See the License for the specific language governing permissions and
16 | : limitations under the License.
17 | :
18 | : The use of the Apache License does not indicate that this project is
19 | : affiliated with the Apache Software Foundation.
20 | :
21 | : library module of semantic functions
22 | :
23 | : @author Michael Blakeley, Mark Logic Corporation
24 | : @author Hsiao Su, Mark Logic Corporation
25 | : @author Li Ding
26 | :)
27 |
28 | module namespace sem = "http://marklogic.com/semantic";
29 |
30 | declare default function namespace "http://www.w3.org/2005/xpath-functions";
31 |
32 | declare default collation 'http://marklogic.com/collation/codepoint';
33 |
34 | declare variable $sem:DEBUG := false()
35 | ;
36 |
37 | declare variable $sem:LEXICON-OPTIONS := (
38 | 'any'
39 | );
40 |
41 | declare variable $sem:QN-S := xs:QName('s')
42 | ;
43 |
44 | declare variable $sem:QN-O := xs:QName('o')
45 | ;
46 |
47 | declare variable $sem:QN-P := xs:QName('p')
48 | ;
49 |
50 | declare variable $sem:QN-C := xs:QName('c')
51 | ;
52 |
53 | declare variable $sem:O-OWL-CLASS :=
54 | 'http://www.w3.org/2002/07/owl#Class'
55 | ;
56 |
57 | declare variable $sem:O-OWL-OBJECT-PROPERTY :=
58 | 'http://www.w3.org/2002/07/owl#ObjectProperty'
59 | ;
60 |
61 | declare variable $sem:O-RDF-NIL :=
62 | 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'
63 | ;
64 |
65 | declare variable $sem:P-OWL-INTERSECTION :=
66 | 'http://www.w3.org/2002/07/owl#intersectionOf'
67 | ;
68 |
69 | declare variable $sem:P-OWL-ON-PROPERTY :=
70 | 'http://www.w3.org/2002/07/owl#onProperty'
71 | ;
72 |
73 | declare variable $sem:P-RDF-FIRST :=
74 | 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first'
75 | ;
76 |
77 | declare variable $sem:P-RDF-LABEL :=
78 | 'http://www.w3.org/2000/01/rdf-schema#label'
79 | ;
80 |
81 | declare variable $sem:P-RDF-REST :=
82 | 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest'
83 | ;
84 |
85 | declare variable $sem:P-RDF-SUBCLASS :=
86 | 'http://www.w3.org/2000/01/rdf-schema#subClassOf'
87 | ;
88 |
89 | declare variable $sem:P-RDF-SUBPROPERTY :=
90 | 'http://www.w3.org/2000/01/rdf-schema#subPropertyOf'
91 | ;
92 |
93 | declare variable $sem:P-RDF-TYPE :=
94 | 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'
95 | ;
96 |
97 | (: RangeQuery - returns a cts:element-range-query with the equal operator between $qn and $v :)
98 | declare private function sem:rq(
99 | $qn as xs:QName+, $v as xs:string+)
100 | as cts:query
101 | {
102 | cts:element-range-query($qn, '=', $v)
103 | };
104 |
105 | (: EValuate - evaluates $query using cts:element-values and return qualified names specified in $qn from matching documents :)
106 | declare private function sem:ev(
107 | $qn as xs:QName+, $query as cts:query)
108 | as xs:string*
109 | {
110 | if (not($sem:DEBUG)) then ()
111 | else xdmp:log(text { 'sem:ev', xdmp:describe($qn), xdmp:quote($query) })
112 | ,
113 | cts:element-values(
114 | $qn, (), $sem:LEXICON-OPTIONS, $query)
115 | };
116 |
117 | (: PredicateQuery - returns a cts:query that matches predicates in $p :)
118 | declare private function sem:pq(
119 | $p as xs:string+)
120 | as cts:query
121 | {
122 | sem:rq($sem:QN-P, $p)
123 | };
124 |
125 | (: ObjectQuery - returns a cts:query that matches objects in $o :)
126 | declare private function sem:oq(
127 | $o as xs:string+)
128 | as cts:query
129 | {
130 | sem:rq($sem:QN-O, $o)
131 | };
132 |
133 | (: SubjectQuery - returns a cts:query that matches subjects in $s :)
134 | declare private function sem:sq(
135 | $s as xs:string+)
136 | as cts:query
137 | {
138 | sem:rq($sem:QN-S, $s)
139 | };
140 |
141 | (: ObjectPredicateQuery - returns a cts:query that matches objects in $o and predicates in $p:)
142 | declare private function sem:opq(
143 | $o as xs:string+, $p as xs:string+)
144 | as cts:query
145 | {
146 | cts:and-query((sem:rq($sem:QN-O, $o), sem:pq($p)))
147 | };
148 |
149 | (: SubjectPredicateQuery - returns a cts:query that matches subjects in $s and predicates in $p:)
150 | declare private function sem:spq(
151 | $s as xs:string+, $p as xs:string+)
152 | as cts:query
153 | {
154 | cts:and-query((sem:rq($sem:QN-S, $s), sem:pq($p)))
155 | };
156 |
157 | (: SubjectObjectPredicateQuery - returns a cts:query that matches subjects in $s, objects in $o, and predicates in $p:)
158 | declare private function sem:sopq(
159 | $s as xs:string+, $o as xs:string+, $p as xs:string+)
160 | as cts:query
161 | {
162 | cts:and-query((sem:rq($sem:QN-S, $s), sem:rq($sem:QN-O, $o), sem:pq($p)))
163 | };
164 |
165 | (: returns objects that matches predicates in $p :)
166 | declare function sem:object-for-predicate(
167 | $p as xs:string+)
168 | as xs:string*
169 | {
170 | if (empty($p)) then ()
171 | else sem:ev($sem:QN-O, sem:pq($p))
172 | };
173 |
174 | (: returns subjects that matches predicates in $p :)
175 | declare function sem:subject-for-predicate(
176 | $p as xs:string+)
177 | as xs:string*
178 | {
179 | if (empty($p)) then ()
180 | else sem:ev($sem:QN-S, sem:pq($p))
181 | };
182 |
183 | (: returns objects that matches subjects in $s, and predicates in $p :)
184 | declare function sem:object-for-object-predicate(
185 | $s as xs:string*, $p as xs:string+)
186 | as xs:string*
187 | {
188 | if (empty($s)) then ()
189 | else sem:ev($sem:QN-O, sem:opq($s, $p))
190 | };
191 |
192 | (: returns objects that matches subjects in $s and predicates in $p :)
193 | declare function sem:object-for-subject-predicate(
194 | $s as xs:string*, $p as xs:string+)
195 | as xs:string*
196 | {
197 | if (empty($s)) then ()
198 | else sem:ev($sem:QN-O, sem:spq($s, $p))
199 | };
200 |
201 | (: returns subjects that matches objects in $o and predicates in $p :)
202 | declare function sem:subject-for-object-predicate(
203 | $o as xs:string*, $p as xs:string+)
204 | as xs:string*
205 | {
206 | if (not($sem:DEBUG)) then ()
207 | else xdmp:log(text { 'sem:subject-for-object-predicate', $o, $p })
208 | ,
209 | if (empty($o)) then ()
210 | else sem:ev($sem:QN-S, sem:opq($o, $p))
211 | };
212 |
213 | (: returns subjects that matches subjects in $s and predicates in $p :)
214 | declare function sem:subject-for-subject-predicate(
215 | $s as xs:string*, $p as xs:string+)
216 | as xs:string*
217 | {
218 | if (empty($s)) then ()
219 | else sem:ev($sem:QN-S, sem:spq($s, $p))
220 | };
221 |
222 | (: returns objects that matches subjects in $s, objects in $o, and predicates in $p :)
223 | declare function sem:object-by-subject-object-predicate(
224 | $s as xs:string+,
225 | $o as xs:string+,
226 | $p as xs:string+)
227 | as xs:string*
228 | {
229 | sem:ev($sem:QN-O, sem:sopq($s, $o, $p))
230 | };
231 |
232 | (: returns subjects that matches subjects in $s, objects in $o, and predicates in $p :)
233 | declare function sem:subject-by-subject-object-predicate(
234 | $s as xs:string+,
235 | $o as xs:string+,
236 | $p as xs:string+)
237 | as xs:string*
238 | {
239 | sem:ev($sem:QN-S, sem:sopq($s, $o, $p))
240 | };
241 |
242 | (:
243 | Take the list of $candidates, and filter them through $filters
244 | (predicates). Filters are applied as AND-conditions. Candidates that
245 | pass all filters are stored inside the map $m, with the key =
246 | candidate, and value = $gen.
247 | :)
248 | declare private function sem:transitive-closure-filter(
249 | $m as map:map, $candidates as xs:string*,
250 | $filters as xs:string*, $gen as xs:integer)
251 | as xs:string*
252 | {
253 | (: use lexicons to filter :)
254 | (: are we done yet? :)
255 | if (empty($candidates)) then ()
256 | else if (empty($filters)) then (
257 | if (not($sem:DEBUG)) then ()
258 | else xdmp:log(text {
259 | 'transitive-closure-filter put', $gen, count($candidates) }),
260 | (: update the map :)
261 | for $c in $candidates
262 | where empty(map:get($m, $c))
263 | return (
264 | map:put($m, $c, $gen),
265 | (: yields sequence of filtered candidates from this generation :)
266 | $c
267 | )
268 | )
269 | else (
270 | let $this := $filters[1]
271 | let $rest := subsequence($filters, 2)
272 | let $next := sem:subject-for-subject-predicate($candidates, $this)
273 | let $d := (
274 | if (not($sem:DEBUG)) then ()
275 | else xdmp:log(text {
276 | 'transitive-closure-filter gen',
277 | $gen, count($candidates), count($filters) })
278 | )
279 | where exists($next)
280 | return sem:transitive-closure-filter($m, $next, $rest, $gen)
281 | )
282 | };
283 |
284 | (:
285 | Find the transitive-closure, starting from $seeds, using $relation as the predicate for traversing edges.
286 | $m - This is used to store filtered results, where key = name, value = generation count
287 | $seeds - This stores unfiltered results, and it's used recursively for finding the next generation of friends
288 | $gen - An integer for counting generations
289 | $relation - the predicate used for finding relationships
290 | $direction - If true, we traverse from subject to object. If false, we traverse from object to subject
291 | $filter - Used to filter out results. Note that filter only essentially apply to the end result.
292 | Friends that match the filter is still used to find the next generation of friends.
293 | :)
294 | declare function sem:transitive-closure(
295 | $m as map:map, $seeds as xs:string*, $gen as xs:integer,
296 | $relation as xs:string, $direction as xs:boolean, $filters as xs:string*)
297 | as empty-sequence()
298 | {
299 | if (not($sem:DEBUG)) then ()
300 | else xdmp:log(text {
301 | 'transitive-closure start of gen',
302 | $gen, count($seeds), count(map:keys($m)) }),
303 | (: apply dummy empty filter, on bootstrap generation only :)
304 | if (exists(map:keys($m))) then () else (
305 | let $do := sem:transitive-closure-filter($m, $seeds, (), $gen)
306 | return ()
307 | ),
308 | (: are we done yet? :)
309 | if ($gen lt 1 or empty($seeds)) then (
310 | if (not($sem:DEBUG)) then ()
311 | else xdmp:log(text {
312 | 'transitive-closure end at gen',
313 | $gen, count($seeds), count(map:keys($m)) })
314 | )
315 | else (
316 | (: get the next generation of friends :)
317 | let $new-friends := (
318 | if ($direction) then sem:object-for-subject-predicate($seeds, $relation)
319 | else sem:subject-for-object-predicate($seeds, $relation)
320 | )
321 | let $d := (
322 | if (not($sem:DEBUG)) then ()
323 | else xdmp:log(text {
324 | 'transitive-closure gen',
325 | $gen, count($seeds), 'new', count($new-friends) })
326 | )
327 | let $next-gen := $gen - 1
328 | (: transitive-closure-filter does the map:put, so always call it :)
329 | let $new-friends := sem:transitive-closure-filter(
330 | $m, $new-friends, $filters, $next-gen)
331 | let $d := (
332 | if (not($sem:DEBUG)) then ()
333 | else xdmp:log(text {
334 | 'transitive-closure gen',
335 | $gen, count($seeds), 'filtered', count($new-friends) })
336 | )
337 | where exists($new-friends) and $next-gen gt 0
338 | return sem:transitive-closure(
339 | $m, $new-friends, $next-gen, $relation, $direction, $filters)
340 | )
341 | };
342 |
343 | declare function sem:serialize(
344 | $m as map:map, $max-gen as xs:integer)
345 | as item()+
346 | {
347 | let $keys := map:keys($m)
348 | let $keys-count := count($keys)
349 | let $d := (
350 | if (not($sem:DEBUG)) then ()
351 | else xdmp:log(text { 'serialize', $max-gen, $keys-count })
352 | )
353 | return (
354 | $keys-count,
355 | for $k in $keys
356 | let $gen := $max-gen - map:get($m, $k)
357 | order by $gen descending, $k
358 | return text { $gen, $k }
359 | ),
360 | if (not($sem:DEBUG)) then ()
361 | else xdmp:log(text { 'serialize end', $max-gen })
362 | };
363 |
364 | (:
365 | returns a sem:join element that joins objects in $o and predicates in
366 | $p, sem:join is used in *-for-join functions
367 | :)
368 | declare function sem:object-predicate-join(
369 | $o as xs:string*,
370 | $p as xs:string* )
371 | as element(sem:join)?
372 | {
373 | if (empty($p) and empty($o)) then ()
374 | else element sem:join {
375 | for $i in $p return element sem:p { $i },
376 | for $i in $o return element sem:o { $i } }
377 | };
378 |
379 | (:
380 | returns a sem:join element that joins predicates in $p, sem:join is
381 | used in *-for-join functions
382 | :)
383 | declare function sem:predicate-join(
384 | $p as xs:string* )
385 | as element(sem:join)?
386 | {
387 | if (empty($p)) then ()
388 | else element sem:join {
389 | for $i in $p return element sem:p { $i } }
390 | };
391 |
392 | (:
393 | returns a sem:join element that joins subjects in $s and predicates in
394 | $p, sem:join is used in *-for-join functions
395 | :)
396 | declare function sem:subject-predicate-join(
397 | $s as xs:string*,
398 | $p as xs:string* )
399 | as element(sem:join)?
400 | {
401 | if (empty($s) and empty($p)) then ()
402 | else element sem:join {
403 | for $i in $s return element sem:s { $i },
404 | for $i in $p return element sem:p { $i } }
405 | };
406 |
407 | (:
408 | returns a sem:join element that joins objects in $type and predicates
409 | in $sem:P-RDF-TYPE
410 | :)
411 | declare function sem:type-join(
412 | $type as xs:string+)
413 | as element(sem:join)
414 | {
415 | sem:object-predicate-join($type, $sem:P-RDF-TYPE)
416 | };
417 |
418 | (: returns objects that matches the sem:join conditions in $joins :)
419 | (: substitute function calls for flwor, to maintain streaming :)
420 | declare function sem:object-for-join(
421 | $joins as element(sem:join)+)
422 | as xs:string*
423 | {
424 | if (not($sem:DEBUG)) then ()
425 | else xdmp:log(text { 'sem:object-for-join', count($joins) })
426 | ,
427 | if (count($joins, 2) gt 1) then sem:object-for-join(
428 | sem:object-for-join($joins[1]),
429 | subsequence($joins, 2) )
430 | (: single join :)
431 | else if ($joins/sem:o) then sem:object-for-object-predicate(
432 | $joins/sem:o, $joins/sem:p)
433 | else if ($joins/sem:s) then sem:object-for-subject-predicate(
434 | $joins/sem:s, $joins/sem:p)
435 | else if ($joins/sem:select) then (
436 | if ($joins/sem:select/@type
437 | eq 'subject') then sem:object-for-subject-predicate(
438 | sem:select($joins/sem:select), $joins/sem:p)
439 | else if ($joins/sem:select/@type
440 | eq 'object') then sem:object-for-object-predicate(
441 | sem:select($joins/sem:select), $joins/sem:p)
442 | else error(
443 | (), 'SEM-UNEXPECTED', text {
444 | 'select type must be subject or object' })
445 | )
446 | (: TODO handle other join cases? :)
447 | else error(
448 | (), 'SEM-UNEXPECTED',
449 | text { 'cannot join without object-predicate or subject-predicate' })
450 | };
451 |
452 | (: returns objects that matches the sem:join conditions in $joins, search is limited to triples that match elements in $seeds :)
453 | (: substitute function calls for flwor, to maintain streaming :)
454 | declare function sem:object-for-join(
455 | $seeds as xs:string*,
456 | $joins as element(sem:join)* )
457 | as xs:string*
458 | {
459 | if (not($sem:DEBUG)) then ()
460 | else xdmp:log(text {
461 | 'sem:object-for-join', count($seeds), count($joins) })
462 | ,
463 | if (empty($seeds) or empty($joins)) then $seeds
464 | else sem:object-for-join($seeds, $joins[1], subsequence($joins, 2))
465 | };
466 |
467 |
468 | (:
469 | returns objects that matches the sem:join conditions in $first, and
470 | then $joins, search is limited to triples that match elements in
471 | $seeds
472 | :)
473 | declare private function sem:object-for-join(
474 | $seeds as xs:string*,
475 | $first as element(sem:join),
476 | $joins as element(sem:join)* )
477 | as xs:string*
478 | {
479 | sem:object-for-join(
480 | $seeds, $first/sem:s, $first/sem:o, $first/sem:p, $joins)
481 | };
482 |
483 | (:
484 | returns objects that matches subjects in $s, objects in $o, predicates
485 | in $p, and then $joins. Search is limited to triples that match
486 | element in $seeds.
487 |
488 | Note: It's unclear what is the correct return value if all 3 $s, $o,
489 | and $p are specified. But it seems that if $o and $p are specified,
490 | then $seeds has to be a subject. If $s and $p are specified, then
491 | $seeds have to be an object.
492 | :)
493 | declare private function sem:object-for-join(
494 | $seeds as xs:string*,
495 | $s as xs:string*,
496 | $o as xs:string*,
497 | $p as xs:string*,
498 | $joins as element(sem:join)* )
499 | as xs:string*
500 | {
501 | sem:object-for-join(
502 | if ($o and $p) then sem:subject-by-subject-object-predicate(
503 | $seeds, $o, $p)
504 | (: seeds will be objects for the relation :)
505 | else if ($s and $p) then sem:object-by-subject-object-predicate(
506 | $s, $seeds, $p)
507 | (: TODO handle other join cases? :)
508 | else error(
509 | (), 'SEM-UNEXPECTED',
510 | text { 'cannot join without object-predicate or subject-predicate' })
511 | ,
512 | $joins
513 | )
514 | };
515 |
516 | (: Same as object-for-join, except this returns objects instead of subjects :)
517 | (: substitute function calls for flwor, to maintain streaming :)
518 | declare function sem:subject-for-join(
519 | $joins as element(sem:join)+)
520 | as xs:string*
521 | {
522 | if (not($sem:DEBUG)) then ()
523 | else xdmp:log(text { 'sem:subject-for-join', count($joins) })
524 | ,
525 | if (count($joins, 2) gt 1) then sem:subject-for-join(
526 | sem:subject-for-join($joins[1]),
527 | subsequence($joins, 2) )
528 | (: single join :)
529 | else if ($joins/sem:o) then sem:subject-for-object-predicate(
530 | $joins/sem:o, $joins/sem:p)
531 | else if ($joins/sem:s) then sem:subject-for-subject-predicate(
532 | $joins/sem:s, $joins/sem:p)
533 | else if ($joins/sem:select) then (
534 | if ($joins/sem:select/@type
535 | eq 'subject') then sem:subject-for-subject-predicate(
536 | sem:select($joins/sem:select), $joins/sem:p)
537 | else if ($joins/sem:select/@type
538 | eq 'object') then sem:subject-for-object-predicate(
539 | sem:select($joins/sem:select), $joins/sem:p)
540 | else error(
541 | (), 'SEM-UNEXPECTED', text {
542 | 'select type must be subject or object' })
543 | )
544 | (: TODO handle other join cases? :)
545 | else error(
546 | (), 'SEM-UNEXPECTED',
547 | text { 'cannot join without object-predicate or subject-predicate' })
548 | };
549 |
550 | (: Same as object-for-join, except this returns objects instead of subjects :)
551 | (: substitute function calls for flwor, to maintain streaming :)
552 | declare function sem:subject-for-join(
553 | $seeds as xs:string*,
554 | $joins as element(sem:join)* )
555 | as xs:string*
556 | {
557 | if (not($sem:DEBUG)) then ()
558 | else xdmp:log(text {
559 | 'sem:subject-for-join', count($seeds), count($joins) })
560 | ,
561 | if (empty($seeds) or empty($joins)) then $seeds
562 | else sem:subject-for-join($seeds, $joins[1], subsequence($joins, 2))
563 | };
564 |
565 |
566 | (: Same as object-for-join, except this returns objects instead of subjects :)
567 | declare private function sem:subject-for-join(
568 | $seeds as xs:string*,
569 | $first as element(sem:join),
570 | $joins as element(sem:join)* )
571 | as xs:string*
572 | {
573 | sem:subject-for-join(
574 | $seeds, $first/sem:s, $first/sem:o, $first/sem:p, $joins)
575 | };
576 |
577 | (: Same as object-for-join, except this returns objects instead of subjects :)
578 | declare private function sem:subject-for-join(
579 | $seeds as xs:string*,
580 | $s as xs:string*,
581 | $o as xs:string*,
582 | $p as xs:string*,
583 | $joins as element(sem:join)* )
584 | as xs:string*
585 | {
586 | sem:subject-for-join(
587 | if ($o and $p) then sem:subject-by-subject-object-predicate(
588 | $seeds, $o, $p)
589 | (: seeds will be objects for the relation :)
590 | else if ($s and $p) then sem:object-by-subject-object-predicate(
591 | $s, $seeds, $p)
592 | (: TODO handle other join cases? :)
593 | else error(
594 | (), 'SEM-UNEXPECTED',
595 | text { 'cannot join without object-predicate or subject-predicate' })
596 | ,
597 | $joins
598 | )
599 | };
600 |
601 | declare function sem:owl-on-property(
602 | $prop as xs:string*)
603 | as xs:string*
604 | {
605 | if (empty($prop)) then () else
606 | sem:subject-for-object-predicate($prop, $sem:P-OWL-ON-PROPERTY)
607 | };
608 |
609 | declare function sem:owl-subclasses(
610 | $class as xs:string*)
611 | as xs:string*
612 | {
613 | if (empty($class)) then () else
614 | sem:rdf-subclasses((
615 | sem:subject-for-object-predicate(
616 | sem:subject-for-object-predicate($class, $sem:P-RDF-FIRST),
617 | $sem:P-OWL-INTERSECTION ),
618 | sem:owl-subclasses-implicit($class) ))
619 | };
620 |
621 | declare private function sem:owl-subclasses-implicit(
622 | $class as xs:string*)
623 | as xs:string*
624 | {
625 | $class,
626 | let $inter := sem:object-for-subject-predicate(
627 | $class, $sem:P-OWL-INTERSECTION)
628 | let $req-1 := sem:object-for-subject-predicate($inter, $sem:P-RDF-FIRST)
629 | let $req-2 := sem:object-for-subject-predicate($inter, $sem:P-RDF-REST)
630 | let $req-2 := sem:subject-for-join(
631 | $req-2, (sem:object-predicate-join($sem:O-RDF-NIL, $sem:P-RDF-REST)) )
632 | let $req-2 := sem:object-for-subject-predicate($req-2, $sem:P-RDF-FIRST)
633 | let $req-2 := sem:object-for-subject-predicate(
634 | $req-2, $sem:P-OWL-ON-PROPERTY)
635 | let $req-2 := sem:rdf-subclasses(sem:owl-on-property($req-2))
636 | return sem:subject-for-join(
637 | $req-2,
638 | (sem:object-predicate-join($req-1, $sem:P-RDF-SUBCLASS)) )
639 | };
640 |
641 | declare function sem:rdf-subclasses(
642 | $class as xs:string*)
643 | as xs:string*
644 | {
645 | if (empty($class)) then () else
646 | let $sub := sem:subject-for-object-predicate($class, $sem:P-RDF-SUBCLASS)
647 | return (
648 | (: time to stop? :)
649 | if (empty($sub)) then $class else (
650 | $class,
651 | sem:rdf-subclasses($sub)
652 | )
653 | )
654 | };
655 |
656 | declare function sem:rdf-subproperties(
657 | $prop as xs:string*)
658 | as xs:string*
659 | {
660 | if (empty($prop)) then () else
661 | let $sub := sem:subject-for-object-predicate($prop, $sem:P-RDF-SUBPROPERTY)
662 | return (
663 | (: time to stop? :)
664 | if (empty($sub)) then $prop else (
665 | $prop,
666 | sem:rdf-subproperties($sub)
667 | )
668 | )
669 | };
670 |
671 | declare function sem:relate(
672 | $a as xs:QName,
673 | $b as xs:QName,
674 | $a-seed as xs:string*,
675 | $b-seed as xs:string*,
676 | $join as element(sem:join)* )
677 | as map:map
678 | {
679 | sem:relate(
680 | $a, $b,
681 | sem:relate-query($a, $b, $a-seed, $b-seed, $join),
682 | map:map()
683 | )
684 | };
685 |
686 | declare private function sem:relate-query(
687 | $a as xs:QName,
688 | $b as xs:QName,
689 | $a-seed as xs:string*,
690 | $b-seed as xs:string*,
691 | $join as element(sem:join)* )
692 | as cts:query
693 | {
694 | cts:and-query(
695 | (if (empty($a-seed)) then () else sem:rq($a, $a-seed),
696 | if (empty($b-seed)) then () else sem:rq($b, $b-seed),
697 | for $j in $join
698 | return (
699 | if ($j/sem:o and $j/sem:p) then error((), 'UNIMPLEMENTED')
700 | else if ($j/sem:s and $j/sem:p) then error((), 'UNIMPLEMENTED')
701 | else if ($j/sem:p) then sem:pq($j/sem:p)
702 | else error((), 'SEM-UNEXPECTED')
703 | ) ) )
704 | };
705 |
706 | declare private function sem:relate(
707 | $a as xs:QName, $b as xs:QName,
708 | $query as cts:query,
709 | $m as map:map)
710 | as map:map
711 | {
712 | sem:relate(
713 | $m, cts:element-value-co-occurrences(
714 | $a, $b, $sem:LEXICON-OPTIONS, $query) ),
715 | if (not($sem:DEBUG)) then () else xdmp:log(
716 | text { 'sem:relate', count(map:keys($m)) } ),
717 | $m
718 | };
719 |
720 | declare private function sem:relate(
721 | $m as map:map, $co as element(cts:co-occurrence) )
722 | as empty-sequence()
723 | {
724 | map:put($m, $co/cts:value[1], $co/cts:value[2]/string())
725 | };
726 |
727 | declare private function sem:relate-join(
728 | $a as xs:QName, $b as xs:QName,
729 | $query as cts:query )
730 | as element(cts:co-occurrence)*
731 | {
732 | cts:element-value-co-occurrences(
733 | $a, $b, $sem:LEXICON-OPTIONS, $query)
734 | };
735 |
736 | declare function sem:relate-join(
737 | $a as xs:QName,
738 | $b as xs:QName,
739 | $a-seed as xs:string*,
740 | $b-seed as xs:string*,
741 | $join as element(sem:join)* )
742 | as element(cts:co-occurrence)*
743 | {
744 | sem:relate-join(
745 | $a, $b,
746 | sem:relate-query($a, $b, $a-seed, $b-seed, $join)
747 | )
748 | };
749 |
750 | declare function sem:uri-for-tuple(
751 | $s as xs:string,
752 | $p as xs:string,
753 | $o as xs:string,
754 | $c as xs:string?)
755 | as xs:string
756 | {
757 | (: build a deterministic uri for a triple or quad :)
758 | xdmp:integer-to-hex(
759 | xdmp:hash64(
760 | string-join(($s, $p, $o, $c), '|') ) )
761 | };
762 |
763 | declare function sem:uri-for-tuple(
764 | $t as element(t) )
765 | as xs:string
766 | {
767 | sem:uri-for-tuple($t/s, $t/p, $t/o, $t/c)
768 | };
769 |
770 | declare function sem:tuple(
771 | $s as xs:string,
772 | $p as xs:string,
773 | $o as xs:string,
774 | $c as xs:string?)
775 | as element(t)
776 | {
777 | element t {
778 | element s {
779 | $s },
780 | element p {
781 | $p },
782 | element o {
783 | $o },
784 | if (empty($c)) then ()
785 | else element c {
786 | $c }
787 | }
788 | };
789 |
790 | declare function sem:tuple-insert-as-property(
791 | $s as xs:string,
792 | $p as xs:string,
793 | $o as xs:string,
794 | $c as xs:string?)
795 | as empty-sequence()
796 | {
797 | xdmp:document-add-properties(
798 | sem:uri-for-tuple($s, $p, $o, $c),
799 | sem:tuple($s, $p, $o, $c)/* )
800 | };
801 |
802 | declare function sem:tuple-insert-as-property(
803 | $t as element(t))
804 | as empty-sequence()
805 | {
806 | xdmp:document-add-properties(
807 | sem:uri-for-tuple($t/s, $t/p, $t/o, $t/c),
808 | sem:tuple($t/s, $t/p, $t/o, $t/c)/* )
809 | };
810 |
811 | declare function sem:tuple-insert(
812 | $s as xs:string,
813 | $p as xs:string,
814 | $o as xs:string,
815 | $c as xs:string?)
816 | as empty-sequence()
817 | {
818 | xdmp:document-insert(
819 | sem:uri-for-tuple($s, $p, $o, $c),
820 | sem:tuple($s, $p, $o, $c) )
821 | };
822 |
823 | declare function sem:tuple-insert(
824 | $t as element(t))
825 | as empty-sequence()
826 | {
827 | xdmp:document-insert(
828 | sem:uri-for-tuple($t/s, $t/p, $t/o, $t/c),
829 | sem:tuple($t/s, $t/p, $t/o, $t/c) )
830 | };
831 |
832 | declare function sem:tuples-for-query(
833 | $q as cts:query )
834 | as element(t)*
835 | {
836 | cts:search(/t, $q, 'unfiltered')
837 | };
838 |
839 | declare function sem:tuples-for-predicate(
840 | $p as xs:string+ )
841 | as element(t)*
842 | {
843 | sem:tuples-for-query(sem:pq($p))
844 | };
845 |
846 | declare function sem:tuples-for-subject(
847 | $s as xs:string+ )
848 | as element(t)*
849 | {
850 | sem:tuples-for-query(sem:sq($s))
851 | };
852 |
853 | declare function sem:tuples-for-object(
854 | $o as xs:string+ )
855 | as element(t)*
856 | {
857 | sem:tuples-for-query(sem:oq($o))
858 | };
859 |
860 | declare function sem:select(
861 | $s as element(sem:select))
862 | {
863 | sem:select($s/@type, $s/sem:join)
864 | };
865 |
866 | declare function sem:select(
867 | $type as xs:string,
868 | $join as element(sem:join)+)
869 | {
870 | if ($type eq 'subject') then sem:subject-for-join($join)
871 | else if ($type eq 'object') then sem:object-for-join($join)
872 | else error((), 'SEM-UNEXPECTED', text {
873 | 'select must have type subject or object, not',
874 | xdmp:describe($type) })
875 | };
876 |
877 | (:
878 | : -------------------------------------
879 | : PART 2 enhancement
880 | : -------------------------------------
881 | :)
882 |
883 | declare variable $sem:P-OWL-INVERSE :=
884 | 'http://www.w3.org/2002/07/owl#inverseOf'
885 | ;
886 |
887 | declare variable $sem:C-OWL-TRANSITIVE-PROPERTY :=
888 | 'http://www.w3.org/2002/07/owl#TransitiveProperty'
889 | ;
890 |
891 | declare variable $sem:C-OWL-SYMMETRIC-PROPERTY :=
892 | 'http://www.w3.org/2002/07/owl#SymmetricProperty'
893 | ;
894 |
895 | declare variable $sem:P-OWL-SAMEAS :=
896 | 'http://www.w3.org/2002/07/owl#sameAs'
897 | ;
898 |
899 |
900 | declare variable $sem:P-OWL-HAS-VALUE :=
901 | 'http://www.w3.org/2002/07/owl#hasValue'
902 | ;
903 |
904 |
905 | declare variable $sem:O-OWL-DATATYPE-PROPERTY :=
906 | 'http://www.w3.org/2002/07/owl#DatatypeProperty'
907 | ;
908 |
909 |
910 | (:
911 | : -------------------------------------
912 | : PART 2.1 wrappers
913 | : - query constructor
914 | : - query evaluation
915 | : -------------------------------------
916 | :)
917 |
918 | (: shortcut for sem:rq, create a query pattern on subject :)
919 | declare function sem:query-s(
920 | $s as xs:string*)
921 | as cts:query
922 | {
923 | if (empty($s))
924 | then sem:rq($sem:QN-S, '')
925 | else sem:rq($sem:QN-S, $s)
926 | };
927 |
928 | (: shortcut for sem:rq, create a query pattern on object :)
929 | declare function sem:query-o(
930 | $o as xs:string*)
931 | as cts:query
932 | {
933 | if (empty($o))
934 | then sem:rq($sem:QN-O, '')
935 | else sem:rq($sem:QN-O, $o)
936 | };
937 |
938 | (: shortcut for sem:rq, create a query pattern on predicate :)
939 | declare function sem:query-p(
940 | $p as xs:string*)
941 | as cts:query
942 | {
943 | if (empty($p))
944 | then sem:rq($sem:QN-P, '')
945 | else sem:rq($sem:QN-P, $p)
946 | };
947 |
948 | (: shortcut for sem:rq, create a query pattern on context (named graph) :)
949 | declare function sem:query-c(
950 | $c as xs:string*)
951 | as cts:query
952 | {
953 | if (empty($c))
954 | then sem:rq($sem:QN-C, '')
955 | else sem:rq($sem:QN-C, $c)
956 | };
957 |
958 |
959 | (: evaluate a list of queries, return a list of strings :)
960 | declare function sem:ev1(
961 | $qn as xs:QName,
962 | $query as cts:query*)
963 | as xs:string*
964 | {
965 | if (empty($query))
966 | then ()
967 | else sem:ev($qn, cts:and-query( $query) )
968 | };
969 |
970 | (: evaluate a list of queries, return a list of triples :)
971 | declare function sem:evT(
972 | $query as cts:query*)
973 | as element(t)*
974 | {
975 | if (empty($query))
976 | then ()
977 | else sem:tuples-for-query(cts:and-query( $query) )
978 | };
979 |
980 |
981 |
982 |
983 | (:
984 | : -------------------------------------
985 | : PART 2.2 Set Operations
986 | : -------------------------------------
987 | :)
988 |
989 | declare function sem:setop-union(
990 | $seq1 as item()*,
991 | $seq2 as item()*)
992 | as item()*
993 | {
994 | if (empty($seq1)) then $seq2
995 | else if (empty($seq2)) then $seq1
996 | else
997 | for $x in $seq1
998 | for $y in $seq2
999 | return ($x, $y)
1000 | };
1001 |
1002 | declare function sem:setop-distinct-element($seq as element()*, $m as map:map) {
1003 | for $e in $seq
1004 | return map:put($m, $e,$e)
1005 | };
1006 |
1007 | declare function sem:setop-distinct-element($seq as element()*) {
1008 | let $m := map:map()
1009 | let $x := sem:setop-distinct-element($seq, $m)
1010 | for $y in map:keys($m)
1011 | return map:get($m,$y)
1012 | };
1013 |
1014 | declare function sem:setop-intersect($m as map:map, $seq1 as element()*, $seq2 as element()*) {
1015 | for $e1 in $seq1
1016 | for $e2 in $seq2
1017 | where deep-equal($e1,$e2)
1018 | return map:put($m, $e1,$e1)
1019 | };
1020 |
1021 |
1022 |
1023 | (:
1024 | : -------------------------------------
1025 | : PART 2.3 dynamic inference
1026 | : -------------------------------------
1027 | :)
1028 |
1029 |
1030 | (: list properties which are direct inverse of the input properties :)
1031 | declare function sem:list-direct-owl-inverse(
1032 | $p as xs:string*)
1033 | as xs:string*
1034 | {
1035 | if (empty($p)) then () else
1036 | sem:setop-union(
1037 | sem:subject-for-object-predicate($p, $sem:P-OWL-INVERSE),
1038 | sem:object-for-subject-predicate($p, $sem:P-OWL-INVERSE)
1039 | )
1040 | };
1041 |
1042 | (: list classes which are direct sub-classes of the input classes. Results include the input :)
1043 | declare function sem:list-direct-subclasses(
1044 | $class as xs:string*)
1045 | as xs:string*
1046 | {
1047 | if (empty($class)) then () else
1048 | let $sub := sem:subject-for-object-predicate($class, $sem:P-RDF-SUBCLASS)
1049 | return (
1050 | (: time to stop? :)
1051 | if (empty($sub)) then $class else (
1052 | $class,
1053 | $sub
1054 | )
1055 | )
1056 | };
1057 |
1058 | (: list properties which are direct sub-properties of the input properties. Results include the input :)
1059 | declare function sem:list-direct-subproperties(
1060 | $prop as xs:string*)
1061 | as xs:string*
1062 | {
1063 | if (empty($prop)) then () else
1064 | let $sub := sem:subject-for-object-predicate($prop, $sem:P-RDF-SUBPROPERTY)
1065 | return (
1066 | (: time to stop? :)
1067 | if (empty($sub)) then $prop else (
1068 | $prop,
1069 | $sub
1070 | )
1071 | )
1072 | };
1073 |
1074 |
1075 |
1076 |
1077 | (:
1078 | : -------------------------------------
1079 | : PART 2.4 forward chaining materialization inference
1080 | : -------------------------------------
1081 | :)
1082 |
1083 |
1084 | (: Forward-Chaining Inference -- general :)
1085 |
1086 | (: insert a couple of documents encoded in map in form of "document key" => "document content" :)
1087 | declare function sem:inf-tuple-insert($m as map:map)
1088 | as empty-sequence()
1089 | {
1090 | for $key in map:keys($m)
1091 | return xdmp:document-insert($key, map:get($m, $key))
1092 | };
1093 |
1094 |
1095 | (: Forward-Chaining Inference -- add owl2 ontology :)
1096 |
1097 | declare function sem:inf-owl2-ontology()
1098 | as xs:integer*{
1099 | (
1100 | sem:tuple-insert( 'http://www.w3.org/2002/07/owl#SymmetricProperty' , 'http://www.w3.org/2000/01/rdf-schema#subClassOf' , 'http://www.w3.org/2002/07/owl#ObjectProperty', 'http://www.w3.org/2002/07/owl' ),
1101 | sem:tuple-insert( 'http://www.w3.org/2002/07/owl#TransitiveProperty' , 'http://www.w3.org/2000/01/rdf-schema#subClassOf' , 'http://www.w3.org/2002/07/owl#ObjectProperty', 'http://www.w3.org/2002/07/owl' ),
1102 | ()
1103 | )
1104 | };
1105 |
1106 |
1107 |
1108 | (: Forward-Chaining Inference -- OWL2RL rules http://www.w3.org/TR/owl2-profiles/#OWL_2_RL :)
1109 |
1110 |
1111 | (: owl2rl | eq-sym | sameAs :)
1112 | declare function sem:inf-owl2rl-eq-sym()
1113 | as xs:integer
1114 | {
1115 | let $m := map:map()
1116 | let $query := sem:inf-owl2rl-eq-sym($m)
1117 | let $exec := sem:inf-tuple-insert($m)
1118 | return map:count($m)
1119 | };
1120 |
1121 | declare private function sem:inf-owl2rl-eq-sym($m as map:map)
1122 | as empty-sequence()
1123 | {
1124 | for $t_x_y in sem:evT( sem:query-p( $sem:P-OWL-SAMEAS ) )
1125 | , $x in $t_x_y/s/text()
1126 | , $y in $t_x_y/o/text()
1127 | let $key := sem:uri-for-tuple($y, $sem:P-OWL-SAMEAS, $x, '')
1128 | where (($x != $y) and (map:count($m) lt 10000) and ( not (fn:doc-available($key)) ) )
1129 | return map:put(
1130 | $m,
1131 | $key,
1132 | sem:tuple($y, $sem:P-OWL-SAMEAS, $x, ''))
1133 | };
1134 |
1135 |
1136 |
1137 | (: owl2rl | eq-trans | sameAs :)
1138 | declare function sem:inf-owl2rl-eq-trans()
1139 | as xs:integer
1140 | {
1141 | let $m := map:map()
1142 | let $query := sem:inf-owl2rl-eq-trans($m)
1143 | let $exec := sem:inf-tuple-insert($m)
1144 | return map:count($m)
1145 | };
1146 |
1147 | declare private function sem:inf-owl2rl-eq-trans($m as map:map)
1148 | as empty-sequence()
1149 | {
1150 | for $t_x_y in sem:evT( sem:query-p( $sem:P-OWL-SAMEAS ) )
1151 | , $x in $t_x_y/s/text()
1152 | , $y in $t_x_y/o/text()
1153 | for $z in sem:ev1( $sem:QN-O, (sem:query-s( $y ), sem:query-p( $sem:P-OWL-SAMEAS ) ))
1154 | let $key := sem:uri-for-tuple($x, $sem:P-OWL-SAMEAS, $z, '')
1155 | where (($x != $y) and ($y != $z) and (map:count($m) lt 10000) and ( not (fn:doc-available($key)) ) )
1156 | return map:put(
1157 | $m,
1158 | $key,
1159 | sem:tuple($x, $sem:P-OWL-SAMEAS, $z, ''))
1160 | };
1161 |
1162 |
1163 |
1164 | (: owl2rl | eq-rep-s | sameAs :)
1165 | declare function sem:inf-owl2rl-eq-rep-s()
1166 | as xs:integer
1167 | {
1168 | let $m := map:map()
1169 | let $query := sem:inf-owl2rl-eq-rep-s($m)
1170 | let $exec := sem:inf-tuple-insert($m)
1171 | return map:count($m)
1172 | };
1173 |
1174 | declare private function sem:inf-owl2rl-eq-rep-s($m as map:map)
1175 | as empty-sequence()
1176 | {
1177 | for $t_x_y in sem:evT( sem:query-p( $sem:P-OWL-SAMEAS ) )
1178 | , $x in $t_x_y/s/text()
1179 | , $y in $t_x_y/o/text()
1180 | for $t_p_o in sem:evT( sem:query-s( $x ) )
1181 | , $p in $t_p_o/p/text()
1182 | , $o in $t_p_o/o/text()
1183 | let $key := sem:uri-for-tuple($y, $p, $o, '')
1184 | where (($x != $y) and (map:count($m) lt 10000) and ( not (fn:doc-available($key)) ) )
1185 | return map:put(
1186 | $m,
1187 | $key,
1188 | sem:tuple($y, $p, $o, ''))
1189 | };
1190 |
1191 |
1192 |
1193 | (: owl2rl | eq-rep-p | sameAs :)
1194 | declare function sem:inf-owl2rl-eq-rep-p()
1195 | as xs:integer
1196 | {
1197 | let $m := map:map()
1198 | let $query := sem:inf-owl2rl-eq-rep-p($m)
1199 | let $exec := sem:inf-tuple-insert($m)
1200 | return map:count($m)
1201 | };
1202 |
1203 | declare private function sem:inf-owl2rl-eq-rep-p($m as map:map)
1204 | as empty-sequence()
1205 | {
1206 | for $t_x_y in sem:evT( sem:query-p( $sem:P-OWL-SAMEAS ) )
1207 | , $x in $t_x_y/s/text()
1208 | , $y in $t_x_y/o/text()
1209 | for $t_s_o in sem:evT( sem:query-p( $x ) )
1210 | , $s in $t_s_o/s/text()
1211 | , $o in $t_s_o/o/text()
1212 | let $key := sem:uri-for-tuple($s, $y, $o, '')
1213 | where (($x != $y) and (map:count($m) lt 10000) and ( not (fn:doc-available($key)) ) )
1214 | return map:put(
1215 | $m,
1216 | $key,
1217 | sem:tuple($s, $y, $o, ''))
1218 | };
1219 |
1220 |
1221 |
1222 | (: owl2rl | eq-rep-o | sameAs :)
1223 | declare function sem:inf-owl2rl-eq-rep-o()
1224 | as xs:integer
1225 | {
1226 | let $m := map:map()
1227 | let $query := sem:inf-owl2rl-eq-rep-o($m)
1228 | let $exec := sem:inf-tuple-insert($m)
1229 | return map:count($m)
1230 | };
1231 |
1232 | declare private function sem:inf-owl2rl-eq-rep-o($m as map:map)
1233 | as empty-sequence()
1234 | {
1235 | for $t_x_y in sem:evT( sem:query-p( $sem:P-OWL-SAMEAS ) )
1236 | , $x in $t_x_y/s/text()
1237 | , $y in $t_x_y/o/text()
1238 | for $t_s_p in sem:evT( sem:query-o( $x ) )
1239 | , $s in $t_s_p/s/text()
1240 | , $p in $t_s_p/p/text()
1241 | let $key := sem:uri-for-tuple($s, $p, $y, '')
1242 | where (($x != $y) and (map:count($m) lt 10000) and ( not (fn:doc-available($key)) ) )
1243 | return map:put(
1244 | $m,
1245 | $key,
1246 | sem:tuple($s, $p, $y, ''))
1247 | };
1248 |
1249 |
1250 |
1251 |
1252 | (: owl2rl | prp-symp | SymmetricProperty :)
1253 | declare function sem:inf-owl2rl-prp-symp()
1254 | as xs:integer
1255 | {
1256 | let $m := map:map()
1257 | let $query := sem:inf-owl2rl-prp-symp($m)
1258 | let $exec := sem:inf-tuple-insert($m)
1259 | return map:count($m)
1260 | };
1261 |
1262 | declare private function sem:inf-owl2rl-prp-symp($m as map:map)
1263 | as empty-sequence()
1264 | {
1265 | for $p in sem:ev1( $sem:QN-S, (sem:query-p( $sem:P-RDF-TYPE ), sem:query-o( $sem:C-OWL-SYMMETRIC-PROPERTY)) )
1266 | for $t_x_y in sem:evT( sem:query-p( $p ) )
1267 | , $x in $t_x_y/s/text()
1268 | , $y in $t_x_y/o/text()
1269 | let $key := sem:uri-for-tuple($y, $p, $x, '')
1270 | where ( ($x != $y) and (map:count($m) lt 10000) and ( not (fn:doc-available($key)) ) )
1271 | return map:put(
1272 | $m,
1273 | $key,
1274 | sem:tuple($y, $p, $x, ''))
1275 | };
1276 |
1277 |
1278 |
1279 |
1280 | (: owl2rl | prp-trp | TransitiveProperty :)
1281 | declare function sem:inf-owl2rl-prp-trp()
1282 | as xs:integer
1283 | {
1284 | let $m := map:map()
1285 | let $query := sem:inf-owl2rl-prp-trp($m)
1286 | let $exec := sem:inf-tuple-insert($m)
1287 | return map:count($m)
1288 | };
1289 |
1290 | declare private function sem:inf-owl2rl-prp-trp($m as map:map)
1291 | as empty-sequence()
1292 | {
1293 | for $p in sem:ev1( $sem:QN-S, (sem:query-p( $sem:P-RDF-TYPE ), sem:query-o( $sem:C-OWL-TRANSITIVE-PROPERTY )) )
1294 | for $t_x_y in sem:evT( sem:query-p( $p ) )
1295 | , $x in $t_x_y/s/text()
1296 | , $y in $t_x_y/o/text()
1297 | for $z in sem:ev1( $sem:QN-O, (sem:query-s( $y ), sem:query-p( $p ) ))
1298 | let $key := sem:uri-for-tuple($x, $p, $z, '')
1299 | where (($x != $y) and ($y != $z) and (map:count($m) lt 10000) and ( not (fn:doc-available($key)) ) )
1300 | return map:put(
1301 | $m,
1302 | $key,
1303 | sem:tuple($x, $p, $z, ''))
1304 | };
1305 |
1306 |
1307 | (: owl2rl | prp-spo1 | subPropertyOf inference :)
1308 | declare function sem:inf-owl2rl-prp-spo1()
1309 | as xs:integer
1310 | {
1311 | let $m := map:map()
1312 | let $query := sem:inf-owl2rl-prp-spo1($m)
1313 | let $exec := sem:inf-tuple-insert($m)
1314 | return map:count($m)
1315 | };
1316 |
1317 | declare private function sem:inf-owl2rl-prp-spo1($m as map:map)
1318 | as empty-sequence()
1319 | {
1320 | for $t_p1_p2 in sem:evT( sem:query-p( $sem:P-RDF-SUBPROPERTY ) )
1321 | , $p1 in $t_p1_p2/s/text()
1322 | , $p2 in $t_p1_p2/o/text()
1323 | for $t_x_y in sem:evT( sem:query-p( $p1 ) )
1324 | , $x in $t_x_y/s/text()
1325 | , $y in $t_x_y/o/text()
1326 | let $key := sem:uri-for-tuple($x, $p2, $y, '')
1327 | where ( ($p1 != $p2 ) and (map:count($m) lt 10000) and ( not (fn:doc-available($key)) ) )
1328 | return map:put(
1329 | $m,
1330 | $key,
1331 | sem:tuple($x, $p2, $y, ''))
1332 | };
1333 |
1334 |
1335 |
1336 | (: owl2rl | prp-inv1 | inverseOf:)
1337 | declare function sem:inf-owl2rl-prp-inv1()
1338 | as xs:integer
1339 | {
1340 | let $m := map:map()
1341 | let $query := sem:inf-owl2rl-prp-inv1($m)
1342 | let $exec := sem:inf-tuple-insert($m)
1343 | return map:count($m)
1344 | };
1345 |
1346 | declare private function sem:inf-owl2rl-prp-inv1($m as map:map)
1347 | as empty-sequence()
1348 | {
1349 | for $t_x_y in sem:evT( sem:query-p( $sem:P-OWL-INVERSE ) )
1350 | , $x in $t_x_y/s/text()
1351 | , $y in $t_x_y/o/text()
1352 | for $t_s_o in sem:evT( sem:query-p( $x ) )
1353 | , $s in $t_s_o/s/text()
1354 | , $o in $t_s_o/o/text()
1355 | let $key := sem:uri-for-tuple($o, $y, $s, '')
1356 | where ( ($x != $y) and (map:count($m) lt 10000) and ( not (fn:doc-available($key)) ) )
1357 | return map:put(
1358 | $m,
1359 | $key,
1360 | sem:tuple($o, $y, $s, ''))
1361 | };
1362 |
1363 |
1364 |
1365 |
1366 | (: owl2rl | prp-inv2 | inverseOf:)
1367 | declare function sem:inf-owl2rl-prp-inv2()
1368 | as xs:integer
1369 | {
1370 | let $m := map:map()
1371 | let $query := sem:inf-owl2rl-prp-inv2($m)
1372 | let $exec := sem:inf-tuple-insert($m)
1373 | return map:count($m)
1374 | };
1375 |
1376 | declare private function sem:inf-owl2rl-prp-inv2($m as map:map)
1377 | as empty-sequence()
1378 | {
1379 | for $t_x_y in sem:evT( sem:query-p( $sem:P-OWL-INVERSE ) )
1380 | , $x in $t_x_y/s/text()
1381 | , $y in $t_x_y/o/text()
1382 | for $t_s_o in sem:evT( sem:query-p( $y ) )
1383 | , $s in $t_s_o/s/text()
1384 | , $o in $t_s_o/o/text()
1385 | let $key := sem:uri-for-tuple($o, $x, $s, '')
1386 | where ( ($x != $y) and (map:count($m) lt 10000) and ( not (fn:doc-available($key)) ) )
1387 | return map:put(
1388 | $m,
1389 | $key,
1390 | sem:tuple($o, $x, $s, ''))
1391 | };
1392 |
1393 |
1394 | (: owl2rl | cls-hv1 | owl:hasValue :)
1395 | declare function sem:inf-owl2rl-cls-hv1()
1396 | as xs:integer
1397 | {
1398 | let $m := map:map()
1399 | let $query := sem:inf-owl2rl-cls-hv1($m)
1400 | let $exec := sem:inf-tuple-insert($m)
1401 | return map:count($m)
1402 | };
1403 |
1404 | declare private function sem:inf-owl2rl-cls-hv1($m as map:map)
1405 | as empty-sequence()
1406 | {
1407 | for $t_x_y in sem:evT( sem:query-p( $sem:P-OWL-HAS-VALUE ) )
1408 | , $x in $t_x_y/s/text()
1409 | , $y in $t_x_y/o/text()
1410 | for $p in sem:ev1( $sem:QN-O, (sem:query-p( $sem:P-OWL-ON-PROPERTY ), sem:query-s( $x )) )
1411 | for $u in sem:ev1( $sem:QN-S, (sem:query-p( $sem:P-RDF-TYPE ), sem:query-o( $x )) )
1412 | let $key := sem:uri-for-tuple($u, $p, $y, '')
1413 | where ( (map:count($m) lt 10000) and ( not (fn:doc-available($key)) ) )
1414 | return map:put(
1415 | $m,
1416 | $key,
1417 | sem:tuple($u, $p, $y, ''))
1418 | };
1419 |
1420 |
1421 | (: owl2rl | cls-hv2 | owl:hasValue :)
1422 | declare function sem:inf-owl2rl-cls-hv2()
1423 | as xs:integer
1424 | {
1425 | let $m := map:map()
1426 | let $query := sem:inf-owl2rl-cls-hv2($m)
1427 | let $exec := sem:inf-tuple-insert($m)
1428 | return map:count($m)
1429 | };
1430 |
1431 | declare private function sem:inf-owl2rl-cls-hv2($m as map:map)
1432 | as empty-sequence()
1433 | {
1434 | for $t_x_y in sem:evT( sem:query-p( $sem:P-OWL-HAS-VALUE ) )
1435 | , $x in $t_x_y/s/text()
1436 | , $y in $t_x_y/o/text()
1437 | for $p in sem:ev1( $sem:QN-O, (sem:query-p( $sem:P-OWL-ON-PROPERTY ), sem:query-s( $x )) )
1438 | for $u in sem:ev1( $sem:QN-S, (sem:query-p( $p ), sem:query-o( $x )) )
1439 | let $key := sem:uri-for-tuple($u, $sem:P-RDF-TYPE, $x, '')
1440 | where ( (map:count($m) lt 10000) and ( not (fn:doc-available($key)) ) )
1441 | return map:put(
1442 | $m,
1443 | $key,
1444 | sem:tuple($u, $sem:P-RDF-TYPE, $x, ''))
1445 | };
1446 |
1447 |
1448 |
1449 | (: owl2rl | cax-sco | subClassOf inference :)
1450 | declare function sem:inf-owl2rl-cax-sco()
1451 | as xs:integer
1452 | {
1453 | let $m := map:map()
1454 | let $query := sem:inf-owl2rl-cax-sco($m)
1455 | let $exec := sem:inf-tuple-insert($m)
1456 | return map:count($m)
1457 | };
1458 |
1459 | declare private function sem:inf-owl2rl-cax-sco($m as map:map)
1460 | as empty-sequence()
1461 | {
1462 | for $t_c1_c2 in sem:evT( sem:query-p( $sem:P-RDF-SUBCLASS ) )
1463 | , $c1 in $t_c1_c2/s/text()
1464 | , $c2 in $t_c1_c2/o/text()
1465 | for $x in sem:ev1( $sem:QN-S, (sem:query-o( $c1 ), sem:query-p( $sem:P-RDF-TYPE ) ))
1466 | let $key := sem:uri-for-tuple($x, $sem:P-RDF-TYPE , $c2, '')
1467 | where ( ($c1 != $c2) and (map:count($m) lt 10000) and ( not (fn:doc-available($key)) ) )
1468 | return map:put(
1469 | $m,
1470 | $key,
1471 | sem:tuple($x, $sem:P-RDF-TYPE , $c2, ''))
1472 | };
1473 |
1474 |
1475 |
1476 | (: owl2rl | scm-cls | class inference TODO PARTIAL only first of four possible triples are generated :)
1477 | declare function sem:inf-owl2rl-scm-cls()
1478 | as xs:integer
1479 | {
1480 | let $m := map:map()
1481 | let $query := sem:inf-owl2rl-scm-cls($m)
1482 | let $exec := sem:inf-tuple-insert($m)
1483 | return map:count($m)
1484 | };
1485 |
1486 | declare private function sem:inf-owl2rl-scm-cls($m as map:map)
1487 | as empty-sequence()
1488 | {
1489 | for $c in sem:ev1( $sem:QN-S, (sem:query-p( $sem:P-RDF-TYPE ), sem:query-o( $sem:O-OWL-CLASS)) )
1490 | let $key := sem:uri-for-tuple($c, $sem:P-RDF-SUBCLASS, $c, '')
1491 | where ( (map:count($m) lt 10000) and ( not (fn:doc-available($key)) ) )
1492 | return map:put(
1493 | $m,
1494 | $key,
1495 | sem:tuple($c, $sem:P-RDF-SUBCLASS, $c, ''))
1496 | };
1497 |
1498 |
1499 |
1500 | (: owl2rl | scm-sco | subClassOf :)
1501 | declare function sem:inf-owl2rl-scm-sco()
1502 | as xs:integer
1503 | {
1504 | let $m := map:map()
1505 | let $query := sem:inf-owl2rl-scm-sco($m)
1506 | let $exec := sem:inf-tuple-insert($m)
1507 | return map:count($m)
1508 | };
1509 |
1510 | declare private function sem:inf-owl2rl-scm-sco($m as map:map)
1511 | as empty-sequence()
1512 | {
1513 | for $t_x_y in sem:evT( sem:query-p( $sem:P-RDF-SUBCLASS ) )
1514 | , $x in $t_x_y/s/text()
1515 | , $y in $t_x_y/o/text()
1516 | for $z in sem:ev1( $sem:QN-O, (sem:query-s( $y ), sem:query-p( $sem:P-RDF-SUBCLASS ) ))
1517 | let $key := sem:uri-for-tuple($x, $sem:P-RDF-SUBCLASS, $z, '')
1518 | where ( ($x != $y) and ($y != $z) and (map:count($m) lt 10000) and ( not (fn:doc-available($key)) ) )
1519 | return map:put(
1520 | $m,
1521 | $key,
1522 | sem:tuple($x, $sem:P-RDF-SUBCLASS, $z, ''))
1523 | };
1524 |
1525 |
1526 |
1527 | (: owl2rl | scm-op | Property inference TODO PARTIAL only first of two possible triples are generated:)
1528 | declare function sem:inf-owl2rl-scm-op()
1529 | as xs:integer
1530 | {
1531 | let $m := map:map()
1532 | let $query := sem:inf-owl2rl-scm-op($m)
1533 | let $exec := sem:inf-tuple-insert($m)
1534 | return map:count($m)
1535 | };
1536 |
1537 | declare private function sem:inf-owl2rl-scm-op($m as map:map)
1538 | as empty-sequence()
1539 | {
1540 | for $p in sem:ev1( $sem:QN-S, (sem:query-p( $sem:P-RDF-TYPE ), sem:query-o( $sem:O-OWL-OBJECT-PROPERTY)) )
1541 | let $key := sem:uri-for-tuple($p, $sem:P-RDF-SUBPROPERTY, $p, '')
1542 | where ( (map:count($m) lt 10000) and ( not (fn:doc-available($key)) ) )
1543 | return map:put(
1544 | $m,
1545 | $key,
1546 | sem:tuple($p, $sem:P-RDF-SUBPROPERTY, $p, ''))
1547 | };
1548 |
1549 |
1550 | (: owl2rl | scm-dp | Property inference TODO PARTIAL only first of two possible triples are generated:)
1551 | declare function sem:inf-owl2rl-scm-dp()
1552 | as xs:integer
1553 | {
1554 | let $m := map:map()
1555 | let $query := sem:inf-owl2rl-scm-dp($m)
1556 | let $exec := sem:inf-tuple-insert($m)
1557 | return map:count($m)
1558 | };
1559 |
1560 | declare private function sem:inf-owl2rl-scm-dp($m as map:map)
1561 | as empty-sequence()
1562 | {
1563 | for $p in sem:ev1( $sem:QN-S, (sem:query-p( $sem:P-RDF-TYPE ), sem:query-o( $sem:O-OWL-DATATYPE-PROPERTY)) )
1564 | let $key := sem:uri-for-tuple($p, $sem:P-RDF-SUBPROPERTY, $p, '')
1565 | where ( (map:count($m) lt 10000) and ( not (fn:doc-available($key)) ) )
1566 | return map:put(
1567 | $m,
1568 | $key,
1569 | sem:tuple($p, $sem:P-RDF-SUBPROPERTY, $p, ''))
1570 | };
1571 |
1572 |
1573 | (: owl2rl | scm-spo | subPropertyOf :)
1574 | declare function sem:inf-owl2rl-scm-spo()
1575 | as xs:integer
1576 | {
1577 | let $m := map:map()
1578 | let $query := sem:inf-owl2rl-scm-spo($m)
1579 | let $exec := sem:inf-tuple-insert($m)
1580 | return map:count($m)
1581 | };
1582 |
1583 | declare private function sem:inf-owl2rl-scm-spo($m as map:map)
1584 | as empty-sequence()
1585 | {
1586 | for $t_x_y in sem:evT( sem:query-p( $sem:P-RDF-SUBPROPERTY ) )
1587 | , $x in $t_x_y/s/text()
1588 | , $y in $t_x_y/o/text()
1589 | for $z in sem:ev1( $sem:QN-O, (sem:query-s( $y ), sem:query-p( $sem:P-RDF-SUBPROPERTY ) ))
1590 | let $key := sem:uri-for-tuple($x, $sem:P-RDF-SUBPROPERTY, $z, '')
1591 | where ( ($x != $y) and ($y != $z) and (map:count($m) lt 10000) and ( not (fn:doc-available($key)) ) )
1592 | return map:put(
1593 | $m,
1594 | $key,
1595 | sem:tuple($x, $sem:P-RDF-SUBPROPERTY, $z, ''))
1596 | };
1597 |
1598 | (: semantic.xqy :)
1599 |
--------------------------------------------------------------------------------