1 // exports 2 exports.Store = {}; 3 4 /** 5 * @namespace 6 * 7 * The Store module defines the public interface to the RDF store. 8 */ 9 var Store = exports.Store; 10 11 // imports 12 var QueryEngine = require("./../../js-query-engine/src/query_engine").QueryEngine; 13 var QuadBackend = require("./../../js-rdf-persistence/src/quad_backend").QuadBackend; 14 var Lexicon = require("./../../js-rdf-persistence/src/lexicon").Lexicon; 15 var RDFJSInterface = require("./../../js-query-engine/src/rdf_js_interface").RDFJSInterface; 16 var RDFStoreClient = require("./../../js-connection/src/rdfstore_client").RDFStoreClient; 17 var Worker = require('webworker'); 18 19 /** 20 * Version of the store 21 */ 22 Store.VERSION = "0.4.15"; 23 24 /** 25 * Create a new RDFStore instance that will be 26 * executed in a web worker in the browser or a new process 27 * in Node.js. 28 * <br/> 29 * <br/> 30 * The first argument to this function is the URL/FS location 31 * of the store script. 32 * <br/> 33 * <br/> 34 * This parameter is mandatory in the browser. It is safe to 35 * ignore this parameter in Node.js. 36 * <br/> 37 * <br/> 38 * If support for web workers is not present, a regular 39 * store object will be initialized and returned. 40 * <br/> 41 * <br/> 42 * 43 * @param {String} [scriptPath] URL of the RDFStore script 44 * @param {Object[]} [args] Arguments to be passed to the store that will be created 45 * @param {Function} callback Callback function that will be invoked with an error flag and the connection/store object. 46 */ 47 Store.connect = function() { 48 var path, args, callback; 49 if(arguments.length == 1) { 50 path = __dirname; 51 args = {}; 52 callback = arguments[0]; 53 } if(arguments.length == 2) { 54 if(typeof(arguments[0]) === 'string') { 55 path = arguments[0]; 56 args = {}; 57 } else { 58 path = __dirname+"/index.js"; 59 args = arguments[0]; 60 } 61 callback = arguments[1]; 62 } else { 63 path = arguments[0]; 64 args = arguments[1]; 65 callback = arguments[2]; 66 } 67 try { 68 if(!!Worker) { 69 new RDFStoreClient.RDFStoreClient(path, args, function(success,connection) { 70 callback(success, connection); 71 }); 72 } else { 73 Store.create(args,function(connection){ 74 callback(false, connection); 75 }); 76 } 77 } catch(e) { 78 Store.create(args,function(connection){ 79 callback(false, connection); 80 }); 81 } 82 }; 83 84 /** 85 * Creates a new instance of the store. 86 * 87 * The function accepts two optional arguments. 88 * <br/> 89 * If only one argument is passed it must be a 90 * callback function that will be invoked when the 91 * store had been created.<br/> 92 * <br/> 93 * If two arguments are passed the first one must 94 * be a map of configuration parameters for the 95 * store, and the second one the callback function.<br/> 96 * <br/> 97 * Take a look at the Store constructor function for 98 * a detailed list of possible configuration parameters.<br/> 99 * 100 * @param {Object[]} [args] Arguments to be passed to the store that will be created 101 * @param {Function} [callback] Callback function that will be invoked with an error flag and the connection/store object. 102 */ 103 Store.create = function(){ 104 if(arguments.length == 1) { 105 return new Store.Store(arguments[0]); 106 } else if(arguments.length == 2) { 107 return new Store.Store(arguments[0], arguments[1]); 108 } else { 109 return new Store.Store(); 110 }; 111 }; 112 113 /** 114 * Creates a new store.<br/> 115 * <br/> 116 * It accepts two optional arguments, a map of configuration 117 * options for the store and a callback function.<br/> 118 * 119 * @constructor 120 * @param {Function} [callback] Callback that will be invoked when the store has been created 121 * @param {Object} [params] 122 * <ul> 123 * <li> persistent: should use persistence? </li> 124 * <li> name: if using persistence, the name for this store </li> 125 * <li> maxCacheSize: if using persistence, maximum size of the index cache </li> 126 * </ul> 127 */ 128 Store.Store = function(arg1, arg2) { 129 var callback = null; 130 var params = null; 131 132 if(arguments.length == 0) { 133 params ={}; 134 } else if(arguments.length == 1) { 135 params = {}; 136 callback = arg1; 137 } else if(arguments.length > 1) { 138 params = arg1; 139 callback = arg2; 140 } else { 141 throw("An optional argument map and a callback must be provided"); 142 } 143 144 if(params['treeOrder'] == null) { 145 params['treeOrder'] = 15; 146 } 147 148 this.functionMap = {}; 149 150 var that = this; 151 new Lexicon.Lexicon(function(lexicon){ 152 if(params['overwrite'] === true) { 153 // delete lexicon values 154 lexicon.clear(); 155 } 156 new QuadBackend.QuadBackend(params, function(backend){ 157 if(params['overwrite'] === true) { 158 // delete index values 159 backend.clear(); 160 } 161 params.backend = backend; 162 params.lexicon =lexicon; 163 that.engine = new QueryEngine.QueryEngine(params); 164 if(callback) { 165 callback(that); 166 } 167 }); 168 }, 169 params['name']); 170 }; 171 172 173 /** 174 * An instance of RDF JS Interface <code>RDFEnvironment</code> 175 * associated to this graph instance. 176 */ 177 Store.Store.prototype.rdf = RDFJSInterface.rdf; 178 179 /** 180 * Executes a query in the store.<br/> 181 * <br/> 182 * There are two possible ways of invoking this function, 183 * providing a pair of arrays of namespaces that will be 184 * used to compute the union of the default and named 185 * dataset, or without them. 186 * <br/> 187 * <br/> 188 * Both invocations receive as an optional last parameter 189 * a callback function that will receive the return status 190 * of the query and the results. 191 * <br/> 192 * <br/> 193 * Results can have different formats: 194 * <ul> 195 * <li> SELECT queries: array of binding maps </li> 196 * <li> CONSTRUCT queries: RDF JS Interface Graph object </li> 197 * <li> ASK queries: JS boolean value </li> 198 * <li> LOAD/INSERT... queries: Number of triples modified/inserted </li> 199 * </ul> 200 * 201 * @arguments: 202 * @param {String} query 203 * @param {String} [defaultURIs] default namespaces 204 * @param {String} [namespacesURIs] named namespaces 205 * @param {Function} [callback] 206 */ 207 Store.Store.prototype.execute = function() { 208 if(arguments.length === 3) { 209 this.executeWithEnvironment(arguments[0], 210 arguments[1], 211 arguments[2]); 212 } else if(arguments.length === 4) { 213 this.executeWithEnvironment(arguments[0], 214 arguments[1], 215 arguments[2], 216 arguments[3]); 217 } else { 218 219 var queryString; 220 var callback; 221 222 if(arguments.length === 1) { 223 queryString = arguments[0]; 224 var callback = function(){}; 225 } else if(arguments.length === 2) { 226 queryString = arguments[0]; 227 callback = arguments [1]; 228 } 229 this.engine.execute(queryString, callback); 230 } 231 }; 232 233 /** 234 * A variation of the execute function that expects 235 * arguments containing values for the default and named 236 * graphs that will be used in the query. 237 * 238 * 239 * @arguments: 240 * @param {String} query 241 * @param {String} URIs default namespaces 242 * @param {String} URIs named namespaces 243 * @param {Function} [callback] 244 */ 245 Store.Store.prototype.executeWithEnvironment = function() { 246 var queryString, defaultGraphs, namedGraphs; 247 248 if(arguments.length === 3) { 249 queryString = arguments[0]; 250 // JSDoc fails if this is pushed outside 251 var callback = function(){}; 252 defaultGraphs = arguments[1]; 253 namedGraphs = arguments[2]; 254 } else if(arguments.length === 4) { 255 queryString = arguments[0]; 256 var callback = arguments [3]; 257 defaultGraphs = arguments[1]; 258 namedGraphs = arguments[2]; 259 } 260 this.engine.execute(queryString, callback, defaultGraphs, namedGraphs); 261 }; 262 263 /** 264 * Retrieves all the quads belonging to a certain graph 265 * in the store as a RDF JS Interface Graph object.<br/> 266 * <br/> 267 * The function accepts as mandatory parameter a callback 268 * function that will receive the returned graph.<br/> 269 * <br/> 270 * Optionally, the URI of the graph can also be passed as 271 * the first argument. If no graph is specified, the 272 * default graph will be returned.<br/> 273 * 274 * @arguments 275 * @param {String} [graphURI] If this parameter is missing, the default graph will be returned 276 * @param {Functon} callback 277 */ 278 Store.Store.prototype.graph = function() { 279 var graphUri = null; 280 var callback = null; 281 if(arguments.length === 1) { 282 callback = arguments[0] || function(){}; 283 graphUri = this.engine.lexicon.defaultGraphUri; 284 } else if(arguments.length === 2) { 285 callback = arguments[1] || function(){}; 286 graphUri = arguments[0]; 287 } else { 288 throw("An optional graph URI and a callback function must be provided"); 289 } 290 291 if(this.rdf.resolve(graphUri) != null) { 292 graphUri = this.rdf.resolve(graphUri); 293 } 294 295 this.engine.execute("CONSTRUCT { ?s ?p ?o } WHERE { GRAPH <" + graphUri + "> { ?s ?p ?o } }", callback); 296 }; 297 298 /** 299 * Retrieves all the quads belonging to a certain node 300 * in the store as a RDF JS Interface Graph object containing 301 * the collection of triples whose subject is the provided 302 * node URI.<br/> 303 * <br/> 304 * The function accepts as mandatory parameters the node URI and 305 * a callback unction that will receive the returned node.<br/> 306 * <br/> 307 * Optionally, the URI of the graph where the node is contained 308 * can also be passed as the first argument. <br/> 309 * <br/> 310 * If no graph is specified, the node will be looked into the 311 * default graph.<br/> 312 * 313 * @arguments 314 * @param {String} nodeURI URI of the node to look for 315 * @param {String} [graphURI] If this parameter is missing, the node will be looked into the default graph 316 * @param {Functon} callback 317 */ 318 Store.Store.prototype.node = function() { 319 var graphUri = null; 320 var callback = null; 321 var nodeUri = null; 322 if(arguments.length === 2) { 323 nodeUri = arguments[0]; 324 callback = arguments[1] || function(){}; 325 graphUri = this.engine.lexicon.defaultGraphUri; 326 } else if(arguments.length === 3) { 327 nodeUri = arguments[0]; 328 graphUri = arguments[1]; 329 callback = arguments[2] || function(){}; 330 } else { 331 throw("An optional graph URI, node URI and a callback function must be provided"); 332 } 333 334 if(this.rdf.resolve(graphUri) != null) { 335 graphUri = this.rdf.resolve(graphUri); 336 } 337 338 if(this.rdf.resolve(nodeUri) != null) { 339 nodeUri = this.rdf.resolve(nodeUri); 340 } 341 342 this.engine.execute("CONSTRUCT { <" + nodeUri + "> ?p ?o } WHERE { GRAPH <" + graphUri + "> { <" + nodeUri + "> ?p ?o } }", callback); 343 }; 344 345 /** 346 * Associates an event listener function to a node URI. Every time the collection 347 * of triples whose subject is the specified node URI changes, because an 348 * insertion or deletion, the provided callback function will be invoked 349 * receiving as a parameter a RDF JS Interface Graph object with the new 350 * collection of triples.<br/> 351 * <br/> 352 * The function accepts two mandatory arguments, the URI of the node to observe 353 * and the function that will receive the event notifications. An optional 354 * third parameter, consisting of a callback function, can be passed and will be invoked 355 * once the store had correctly configured the event listener.<br/> 356 *<br/> 357 * LOAD queries, batch loading data into the store, do not 358 * trigger events by default. If you wish to be notified 359 * by changes triggered by this kind of queries, invoke 360 * the *setBatchLoadEvents* function with a true argument.<br/> 361 *<br/> 362 * The event listener function can be removed using the stopObservingNode function. 363 * 364 * @arguments 365 * @param {String} nodeURI URI of the node to observe 366 * @param {Function} eventListener Function that will be notified with the events 367 * @param {Function} [callback] Function that will be invoked, once the event listener had been correctly set up. 368 */ 369 Store.Store.prototype.startObservingNode = function() { 370 var uri, graphUri, callback; 371 372 if(arguments.length === 2) { 373 uri = arguments[0]; 374 callback = arguments[1]; 375 this.engine.callbacksBackend.observeNode(uri, callback, function(){}); 376 } else if(arguments.length === 3) { 377 uri = arguments[0]; 378 graphUri = arguments[1]; 379 callback = arguments[2]; 380 this.engine.callbacksBackend.observeNode(uri, graphUri, callback, function(){}); 381 } 382 }; 383 384 /** 385 * Removes a callback function associated to a node.<br/> 386 * The event listener function object must be passed as an argument.<br/> 387 * 388 * @arguments 389 * @param {Function} eventListener The event listener function to remove, the same passed as an argument to startObservingNode 390 */ 391 Store.Store.prototype.stopObservingNode = function(callback) { 392 this.engine.callbacksBackend.stopObservingNode(callback); 393 }; 394 395 /** 396 * Associates an event listener function to a SPARQL SELECT or 397 * CONSTRUCT query.<br/> 398 * Every time an update (insert, delete...) query modified the 399 * triples in the store in a way that modifies the output of the 400 * query, the event listener will be invoked with an updated 401 * result.<br/> 402 *<br/> 403 * LOAD queries, batch loading data into the store, do not 404 * trigger events by default. If you wish to be notified 405 * by changes triggered by this kind of queries, invoke 406 * the <code>setBatchLoadEvents</code> function with a true argument.<br/> 407 *<br/> 408 * The event listener function can be removed invoking the 409 * <code>stopObservingQuery</code> function. 410 * 411 * @arguments 412 * @param {String} query SELECT or CONSTRUCT SPARQL query 413 * @param {Function} eventListener the function that will receive the notifications 414 * @param {Function} [callback] optional function that will be invoked when the stored had set up the event listener function. 415 */ 416 Store.Store.prototype.startObservingQuery = function() { 417 var query = arguments[0]; 418 var callback = arguments[1]; 419 var endCallback = arguments[2]; 420 if(endCallback!=null) { 421 this.engine.callbacksBackend.observeQuery(query, callback, endCallback); 422 } else { 423 this.engine.callbacksBackend.observeQuery(query, callback, function(){}); 424 } 425 }; 426 427 /** 428 * Removes a callback function associated to a SPARQL query.<br/> 429 * The event listener function object must be passed as an argument. 430 * 431 * @arguments 432 * @param {Function} eventListener The event listener function to remove, the same passed as an argument to startObservingQuery 433 */ 434 Store.Store.prototype.stopObservingQuery = function(query) { 435 this.engine.callbacksBackend.stopObservingQuery(query); 436 }; 437 438 /** 439 * Associates an event listener to a pattern expressed as the 440 * subject, predicate, object and graph string parameters passed 441 * to the function. To match any value in that position, a <code>null</code> 442 * value can be passed as an argument. e.g. <code>subscribe(null, null, null, g, cb)</code>, 443 * will be notified with any change in the g graph.<br/> 444 * The graph component of the pattern does not support a <code>null</code> value.<br/> 445 *<br/> 446 * Results will be notified as an Array of RDF JS Interface 447 * <code>Triple</code> objects.<br/> 448 *<br/> 449 * LOAD queries, batch loading data into the store, do not 450 * trigger events by default. If you wish to be notified 451 * by changes triggered by this kind of queries, invoke 452 * the <code>setBatchLoadEvents</code> function with a true argument. 453 * 454 * @arguments 455 * @param {String} s subject or null for any subject 456 * @param {String} p predicate or null for any predicate 457 * @param {String} o object or null for any object 458 * @param {String} g graph or null for any graph 459 * @param {Function} event listener function that will be notified when a change occurs 460 */ 461 Store.Store.prototype.subscribe = function(s, p, o, g, callback) { 462 var adapterCb = function(event,triples){ 463 var acum = []; 464 var queryEnv = {blanks:{}, outCache:{}}; 465 var bindings = []; 466 467 for(var i=0; i<triples.length; i++) { 468 var triple = triples[i]; 469 var s = RDFJSInterface.buildRDFResource(triple.subject,bindings,this.engine,queryEnv); 470 var p = RDFJSInterface.buildRDFResource(triple.predicate,bindings,this.engine,queryEnv); 471 var o = RDFJSInterface.buildRDFResource(triple.object,bindings,this.engine,queryEnv); 472 if(s!=null && p!=null && o!=null) { 473 triple = new RDFJSInterface.Triple(s,p,o); 474 acum.push(triple); 475 } 476 } 477 478 callback(event,acum); 479 }; 480 481 this.functionMap[callback] = adapterCb; 482 this.engine.callbacksBackend.subscribe(s,p,o,g,adapterCb,function(){}); 483 }; 484 485 /** 486 * Removes an event listener associated to a certain pattern. 487 * The function passed as an argument to <code>subscribe</code> must be 488 * passed as an argument. 489 * 490 * @arguments 491 * @param {Function} callback The event listener to be removed 492 */ 493 Store.Store.prototype.unsubscribe = function(callback) { 494 var adapterCb = this.functionMap[callback]; 495 this.engine.callbacksBackend.unsubscribe(adapterCb); 496 delete this.functionMap[callback]; 497 }; 498 499 /** 500 * Register a combination of prefix and URI fragment in the default instance 501 * of the RDF JS Interface API <code>RDFEnvironment</code> object associated 502 * to the store and available through the <code>storeInstance.rdf</code> property. 503 * 504 * @arguments 505 * @param {String} prefix The prefix to be associated 506 * @param {String} URIFragment URI fragment the provided prefix will be resolved 507 */ 508 Store.Store.prototype.setPrefix = function(prefix, uri) { 509 this.rdf.setPrefix(prefix, uri); 510 }; 511 512 /** 513 * Defines the URI that will be used by default by the RDF JS Interface 514 * API <code>RDFEnvironment</code> object associated to the store and available 515 * through the <code>storeInstance.rdf</code> property. 516 * 517 * @arguments 518 * @param {String} URIFragment The URI fragment will be used by default 519 */ 520 Store.Store.prototype.setDefaultPrefix = function(uri) { 521 this.rdf.setDefaultPrefix(uri); 522 }; 523 524 /** 525 * Inserts a RDF JS Interface API <code>Graph</code> object into the store. 526 * The function receives a mandatory <code>Graph</code> object whose triples 527 * will be inserted. Optionally, a URI string for a graph and a 528 * callback function can be passed as arguments.<br/> 529 * <br/> 530 * If no graph URI is specified, triples will be inserted into the 531 * default graph.<br/> 532 * <br/> 533 * If the callback function is specified, it will be invoked when all the 534 * triples had been inserted into the store.<br/> 535 * 536 * @arguments 537 * @param {RDFJSInterface.Graph} triples a RDF JS Interface <code>Graph</code> object 538 * @param {String} [graphURI] URI of the graph where the triples will be inserted. If it is missing, triples will be inserted in the default graph 539 * @param {String} [callback] A callback function that will be invoked with a success notification and the number of triples inserted 540 */ 541 Store.Store.prototype.insert = function() { 542 var graph; 543 var triples; 544 var callback; 545 if(arguments.length === 1) { 546 triples = arguments[0]; 547 } else if(arguments.length === 2) { 548 graph = this.rdf.createNamedNode(this.engine.lexicon.defaultGraphUri); 549 triples = arguments[0]; 550 callback= arguments[1] || function(){}; 551 } else if(arguments.length === 3) { 552 triples = arguments[0]; 553 graph = this.rdf.createNamedNode(arguments[1]); 554 callback= arguments[2] || function(){}; 555 } else { 556 throw("The triples to insert, an optional graph and callback must be provided"); 557 } 558 559 var query = ""; 560 var that = this; 561 triples.forEach(function(triple) { 562 query = query + that._nodeToQuery(triple.subject) + that._nodeToQuery(triple.predicate) + that._nodeToQuery(triple.object) + "."; 563 }); 564 565 if(graph != null) { 566 query = "INSERT DATA { GRAPH " + this._nodeToQuery(graph) +" { "+ query + " } }"; 567 } else { 568 query = "INSERT DATA { " + this._nodeToQuery(graph) +" { "+ query + " }"; 569 } 570 571 this.engine.execute(query, callback); 572 }; 573 574 Store.Store.prototype._nodeToQuery = function(term) { 575 if(term.interfaceName === 'NamedNode') { 576 var resolvedUri = this.rdf.resolve(term.valueOf()); 577 if(resolvedUri != null) { 578 return "<" + resolvedUri + ">"; 579 } else { 580 return "<" + term.valueOf() + ">"; 581 } 582 } else if(term.interfaceName === '') { 583 return term.toString(); 584 } else { 585 if(term.lang != null) { 586 return "\""+term.valueOf()+"\"@"+term.lang; 587 } else if(term.datatype != null) { 588 return "\""+term.valueOf()+"\"^^<"+term.datatype+">"; 589 } 590 return term.toString(); 591 } 592 }; 593 594 /** 595 * Removes the triples in a RDF JS Interface API <code>Graph</code> object from the store. 596 * The function receives a mandatory <code>Graph</code> object whose triples 597 * will be removed. Optionally, a URI string for a graph and a 598 * callback function can be passed as arguments.<br/> 599 * <br/> 600 * If no graph URI is specified, triples will be removed from the 601 * default graph.<br/> 602 * <br/> 603 * If the callback function is specified, it will be invoked when all the 604 * triples had been removed from the store. 605 * 606 * @arguments 607 * @param {RDFJSInterface.Graph} triples a RDF JS Interface <code>Graph</code> object 608 * @param {String} [graphURI] URI of the graph where the triples will be removed from. If it is missing, triples will be removed from the default graph 609 * @param {String} [callback] A callback function that will be invoked with a success notification 610 */ 611 Store.Store.prototype.delete = function() { 612 613 var graph; 614 var triples; 615 var callback; 616 if(arguments.length === 1) { 617 triples = arguments[0]; 618 } else if(arguments.length === 2) { 619 graph = this.rdf.createNamedNode(this.engine.lexicon.defaultGraphUri); 620 triples = arguments[0]; 621 callback= arguments[1] || function(){}; 622 } else if(arguments.length === 3) { 623 triples = arguments[0]; 624 graph = this.rdf.createNamedNode(arguments[1]); 625 callback= arguments[2] || function(){}; 626 } else { 627 throw("The triples to delete, an optional graph and callback must be provided"); 628 } 629 630 var query = ""; 631 var that = this; 632 triples.forEach(function(triple) { 633 query = query + that._nodeToQuery(triple.subject) + that._nodeToQuery(triple.predicate) + that._nodeToQuery(triple.object) + "."; 634 }); 635 636 if(graph != null) { 637 query = "DELETE DATA { GRAPH " + this._nodeToQuery(graph) +" { "+ query + " } }"; 638 } else { 639 query = "DELETE DATA { " + this._nodeToQuery(graph) +" { "+ query + " }"; 640 } 641 642 this.engine.execute(query, callback); 643 }; 644 645 /** 646 * Removes all the triples stored in a graph. 647 * 648 * The URI of the graph and a callback function can be 649 * optinally passed as parameters.<br/> 650 * <br/> 651 * If no graph URI is specified, all triples in the 652 * default graph will be removed. 653 * 654 * @arguments 655 * @param {String} [graph] the URI of the graph the triples must be removed from 656 * @param {Function} [callback] a function that will be invoked with a success notification 657 */ 658 Store.Store.prototype.clear = function() { 659 var graph; 660 var callback; 661 662 if(arguments.length === 0) { 663 graph = this.rdf.createNamedNode(this.engine.lexicon.defaultGraphUri); 664 var callback= function(){}; 665 } else if(arguments.length === 1) { 666 graph = this.rdf.createNamedNode(this.engine.lexicon.defaultGraphUri); 667 callback= arguments[0] || function(){}; 668 } else if(arguments.length === 2) { 669 graph = this.rdf.createNamedNode(arguments[0]); 670 callback= arguments[1] || function(){}; 671 } else { 672 throw("The optional graph and a callback must be provided"); 673 } 674 675 var query = "CLEAR GRAPH " + this._nodeToQuery(graph); 676 this.engine.execute(query, callback); 677 }; 678 679 /** 680 * Boolean value determining if loading RDF must produce 681 * triple add events and fire callbacks.<br/> 682 * Default value is false. 683 * 684 * @arguments 685 * @param {boolean} mustFireEvents true/false value. 686 */ 687 Store.Store.prototype.setBatchLoadEvents = function(mustFireEvents){ 688 this.engine.eventsOnBatchLoad = mustFireEvents; 689 }; 690 691 /** 692 * Registers a namespace prefix that will be automatically declared 693 * in all the queries.<br/> 694 * <br/> 695 * The prefix will also be inserte in the default <code>RDFEnvironment</code> object 696 * associated to the <code>rdf</code> property of the store instance. 697 * 698 * @arguments 699 * @param {String} ns the name space to be regsitered 700 * @param {String} prefix the URI fragment associated to the name space 701 */ 702 Store.Store.prototype.registerDefaultNamespace = function(ns, prefix) { 703 this.rdf.prefixes.set(ns,prefix); 704 this.engine.registerDefaultNamespace(ns,prefix); 705 }; 706 707 /** 708 * Registers the default namespaces declared in the RDF JS Interfaces 709 * specification in the default Profile. 710 */ 711 Store.Store.prototype.registerDefaultProfileNamespaces = function() { 712 var defaultNsMap = this.rdf.prefixes.values(); 713 for (var p in defaultNsMap) { 714 this.registerDefaultNamespace(p,defaultNsMap[p]); 715 } 716 }; 717 718 /** 719 * Load triples into a graph in the store. Data can be passed directly to the method 720 * or a remote URI speifying where the data is located can be used.<br/> 721 *<br/> 722 * If the data is passed directly to the load function, the media type stating the format 723 * of the data must also be passed to the function.<br/> 724 *<br/> 725 * If an URI is passed as a parameter, the store will attempt to perform content negotiation 726 * with the remote server and get a representation for the RDF data matching one of the 727 * the RDF parsers registered in the store. In this case, the media type parameter must be 728 * set to the <code>'remote'</code> value.<br/> 729 *<br/> 730 * An additional URI for the graph where the parsed data will be loaded and a callback function 731 * can be also passed as parameters. If no graph is specified, triples will be loaded in the 732 * default graph.<br/> 733 *<br/> 734 * By default loading data will not trigger notification through the events API. If events needs to 735 * be trigger, the functio <code>setBatchLoadEvents</code> must be invoked with a true parameter. 736 * 737 * @arguments 738 * @param {String} mediaType Media type (application/json, text/n3...) of the data to be parsed or the value <code>'remote'</code> if a URI for the data is passed instead 739 * @param {String} [graph] Graph where the parsed triples will be inserted. If it is not specified, triples will be loaded in the default graph 740 * @param {String} data RDF data to be parsed and loaded or an URI where the data will be retrieved after performing content negotiation 741 * @param {Function} callback that will be invoked with a success notification and the number of triples loaded. 742 */ 743 Store.Store.prototype.load = function(){ 744 var mediaType; 745 var data; 746 var graph; 747 var callback; 748 749 if(arguments.length === 3) { 750 graph = this.rdf.createNamedNode(this.engine.lexicon.defaultGraphUri); 751 mediaType = arguments[0]; 752 data = arguments[1]; 753 callback= arguments[2] || function(){}; 754 } else if(arguments.length === 4) { 755 mediaType = arguments[0]; 756 data = arguments[1]; 757 graph = this.rdf.createNamedNode(arguments[2]); 758 callback= arguments[3] || function(){}; 759 } else if(arguments.length === 2) { 760 throw("The mediaType of the parser, the data a callback and an optional graph must be provided"); 761 } 762 763 if(mediaType === 'remote') { 764 data = this.rdf.createNamedNode(data); 765 var query = "LOAD <"+data.valueOf()+"> INTO GRAPH <"+graph.valueOf()+">"; 766 767 this.engine.execute(query, callback); 768 } else if(data && typeof(data)==='string' && data.indexOf('file://')=== 0) { 769 var parser = this.engine.rdfLoader.parsers[mediaType]; 770 771 var that = this; 772 this.engine.rdfLoader.loadFromFile(parser, {'token':'uri', 'value':graph.valueOf()}, data, function(success, quads) { 773 if(success) { 774 that.engine.batchLoad(quads,callback); 775 } else { 776 callback(success, quads); 777 } 778 }); 779 780 781 } else { 782 var parser = this.engine.rdfLoader.parsers[mediaType]; 783 784 var that = this; 785 786 this.engine.rdfLoader.tryToParse(parser, {'token':'uri', 'value':graph.valueOf()}, data, function(success, quads) { 787 if(success) { 788 that.engine.batchLoad(quads,callback); 789 } else { 790 callback(success, quads); 791 } 792 }); 793 } 794 }; 795 796 /** 797 * Registers a new parser associated to the provided media type. If there is a parser already registered for 798 * that media type, the new parser will replace the old one.<br/> 799 *<br/> 800 * Parsers must implement a function *parse* accepting the data to be parsed as the 801 * first parameter and the destination graph URI as the second one. 802 * They must return an array of objects with properties: 'subject', 'predicate', 'object' 803 * and 'graph' containing lexical representations for these values: 804 *<br/> 805 *<ul> 806 * <li><code>{literal: '"literal"'}</code></li> 807 * <li><code>{literal: ''"literal"^^<datatype>'}</code></li> 808 * <li><code>{literal: '"literal"@lang'}</code></li> 809 * <li><code>{uri: 'uri'}</code></li> 810 * <li><code>{blank: '_:label'}</code></li> 811 *</ul> 812 *<br/> 813 * The provided media type will be used to perform content negotiation when dealing with remote 814 * resources, or to select the parser in the <code>load</code> function. 815 * 816 * @arguments 817 * @param {String} mediaType the media type for this parser 818 * @param {String} parser an object containing the *parse* function with the parser logic 819 */ 820 Store.Store.prototype.registerParser = function(mediaType, parser) { 821 this.engine.rdfLoader.registerParser(mediaType,parser); 822 }; 823 824 /** 825 * Returns the URI of all the graphs currently contained 826 * in the store 827 * 828 * @arguments: 829 * @param {Function} callback function that will receive a success notification and the array of graph URIs 830 */ 831 Store.Store.prototype.registeredGraphs = function(callback) { 832 var graphs = this.engine.lexicon.registeredGraphs(true); 833 var acum = []; 834 for(var i=0; i<graphs.length; i++) { 835 var graph = graphs[i]; 836 var uri = new RDFJSInterface.NamedNode(graph); 837 acum.push(uri); 838 } 839 840 return callback(true, acum); 841 }; 842 843 /** @private */ 844 Store.Store.prototype._nodeToQuery = function(term) { 845 if(term.interfaceName === 'NamedNode') { 846 var resolvedUri = this.rdf.resolve(term.valueOf()); 847 if(resolvedUri != null) { 848 return "<" + resolvedUri + ">"; 849 } else { 850 return "<" + term.valueOf() + ">"; 851 } 852 } else if(term.interfaceName === '') { 853 return term.toString(); 854 } else { 855 return term.toString(); 856 } 857 }; 858 859 /** 860 * Returns the current network transport being used by the 861 * the store. 862 * 863 * The default transport uses TCP sockets in the Node.js version 864 * and relies on jQuery in the browser version. This can be overriden 865 * using the <code>setNetworkTransport</code> function. 866 */ 867 Store.Store.prototype.getNetworkTransport = function() { 868 return NetworkTransport; 869 }; 870 871 /** 872 * Sets the network transport used by the store.<br/> 873 * <br/> 874 * Network transport consist of an object implementing the <code>load</code> 875 * function, receiving the URI to load, a string with the value 876 * of the HTTP 'Accept' header for the store registered parsers, 877 * a callback function where the retrieved data and the success notification 878 * must be returned.<br/> 879 *<br/> 880 * Different examples with implementations of different transports can be found 881 * in the source code of the store: 882 *<ul> 883 * <li>src/js-communication/src/tcp_transport.js</li> 884 * <li>src/js-communication/src/ajax_transport.js</li> 885 *</ul> 886 * @arguments 887 * @param networkTransportImpl object implementing the transport *load* function. 888 */ 889 Store.Store.prototype.setNetworkTransport = function(networkTransportImpl) { 890 NetworkTransport = networkTransportImpl; 891 }; 892