├── .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(""); 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 | --------------------------------------------------------------------------------