n3 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/N3Parser.js CHANGED
@@ -6,9 +6,9 @@ var RDF_PREFIX = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
6
6
  RDF_FIRST = RDF_PREFIX + 'first',
7
7
  RDF_REST = RDF_PREFIX + 'rest';
8
8
 
9
- var absoluteIRI = /:/,
10
- documentPart = /[^\/]*$/,
11
- rootIRI = /^(?:[^:]+:\/*)?[^\/]*/;
9
+ var absoluteIRI = /^[a-z][a-z0-9+.-]*:/i,
10
+ schemeAuthority = /^(?:([a-z][a-z0-9+.-]*:))?(?:\/\/[^\/]*)?/i,
11
+ dotSegments = /(?:^|\/)\.\.?(?:$|[\/#?])/;
12
12
 
13
13
  // The next ID for new blank nodes
14
14
  var blankNodePrefix = 0, blankNodeCount = 0;
@@ -22,17 +22,7 @@ function N3Parser(options) {
22
22
 
23
23
  // Set the document IRI.
24
24
  options = options || {};
25
- if (!options.documentIRI) {
26
- this._baseIRI = null;
27
- this._baseIRIPath = null;
28
- }
29
- else {
30
- if (options.documentIRI.indexOf('#') > 0)
31
- throw new Error('Invalid document IRI');
32
- this._baseIRI = options.documentIRI;
33
- this._baseIRIPath = this._baseIRI.replace(documentPart, '');
34
- this._baseIRIRoot = this._baseIRI.match(rootIRI)[0];
35
- }
25
+ this._setBase(options.documentIRI);
36
26
 
37
27
  // Set supported features depending on the format.
38
28
  var format = (typeof options.format === 'string') && options.format.match(/\w*$/)[0].toLowerCase(),
@@ -44,12 +34,14 @@ function N3Parser(options) {
44
34
  this._supportsQuads = !(isTurtle || isTriG || isNTriples);
45
35
  // Disable relative IRIs in N-Triples or N-Quads mode
46
36
  if (isLineMode) {
47
- this._baseIRI = '';
37
+ this._base = '';
48
38
  this._resolveIRI = function (token) {
49
39
  this._error('Disallowed relative IRI', token);
50
40
  return this._callback = noop, this._subject = null;
51
41
  };
52
42
  }
43
+ this._blankNodePrefix = typeof options.blankNodePrefix !== 'string' ? '' :
44
+ '_:' + options.blankNodePrefix.replace(/^_:/, '');
53
45
  this._lexer = options.lexer || new N3Lexer({ lineMode: isLineMode });
54
46
  }
55
47
 
@@ -63,6 +55,21 @@ N3Parser._resetBlankNodeIds = function () {
63
55
  N3Parser.prototype = {
64
56
  // ## Private methods
65
57
 
58
+ // ### `_setBase` sets the base IRI to resolve relative IRIs.
59
+ _setBase: function (baseIRI) {
60
+ if (!baseIRI)
61
+ baseIRI = null;
62
+ else if (baseIRI.indexOf('#') >= 0)
63
+ throw new Error('Invalid base IRI ' + baseIRI);
64
+ // Set base IRI and its components
65
+ if (this._base = baseIRI) {
66
+ this._basePath = baseIRI.replace(/[^\/?]*(?:\?.*)?$/, '');
67
+ baseIRI = baseIRI.match(schemeAuthority);
68
+ this._baseRoot = baseIRI[0];
69
+ this._baseScheme = baseIRI[1];
70
+ }
71
+ },
72
+
66
73
  // ### `_readInTopContext` reads a token when in the top context.
67
74
  _readInTopContext: function (token) {
68
75
  switch (token.type) {
@@ -94,9 +101,8 @@ N3Parser.prototype = {
94
101
  return this._readSubject;
95
102
  }
96
103
  case 'GRAPH':
97
- if (this._supportsNamedGraphs) {
104
+ if (this._supportsNamedGraphs)
98
105
  return this._readNamedGraphLabel;
99
- }
100
106
  // Otherwise, the next token must be a subject.
101
107
  default:
102
108
  return this._readSubject(token);
@@ -108,7 +114,7 @@ N3Parser.prototype = {
108
114
  this._predicate = null;
109
115
  switch (token.type) {
110
116
  case 'IRI':
111
- if (this._baseIRI === null || absoluteIRI.test(token.value))
117
+ if (this._base === null || absoluteIRI.test(token.value))
112
118
  this._subject = token.value;
113
119
  else
114
120
  this._subject = this._resolveIRI(token);
@@ -145,21 +151,18 @@ N3Parser.prototype = {
145
151
  switch (type) {
146
152
  case 'IRI':
147
153
  case 'abbreviation':
148
- if (this._baseIRI === null || absoluteIRI.test(token.value))
154
+ if (this._base === null || absoluteIRI.test(token.value))
149
155
  this._predicate = token.value;
150
156
  else
151
157
  this._predicate = this._resolveIRI(token);
152
158
  break;
153
159
  case 'prefixed':
154
- if (token.prefix === '_') {
160
+ if (token.prefix === '_')
155
161
  return this._error('Disallowed blank node as predicate', token);
156
- }
157
- else {
158
- var prefix = this._prefixes[token.prefix];
159
- if (prefix === undefined)
160
- return this._error('Undefined prefix "' + token.prefix + ':"', token);
161
- this._predicate = prefix + token.value;
162
- }
162
+ var prefix = this._prefixes[token.prefix];
163
+ if (prefix === undefined)
164
+ return this._error('Undefined prefix "' + token.prefix + ':"', token);
165
+ this._predicate = prefix + token.value;
163
166
  break;
164
167
  case '.':
165
168
  case ']':
@@ -183,7 +186,7 @@ N3Parser.prototype = {
183
186
  _readObject: function (token) {
184
187
  switch (token.type) {
185
188
  case 'IRI':
186
- if (this._baseIRI === null || absoluteIRI.test(token.value))
189
+ if (this._base === null || absoluteIRI.test(token.value))
187
190
  this._object = token.value;
188
191
  else
189
192
  this._object = this._resolveIRI(token);
@@ -234,8 +237,10 @@ N3Parser.prototype = {
234
237
  this._subject = null;
235
238
  return this._readBlankNodeTail(token);
236
239
  }
237
- this._predicate = null;
238
- return this._readPredicate(token);
240
+ else {
241
+ this._predicate = null;
242
+ return this._readPredicate(token);
243
+ }
239
244
  },
240
245
 
241
246
  // ### `_readBlankNodeTail` reads the end of a blank node.
@@ -271,7 +276,7 @@ N3Parser.prototype = {
271
276
  case 'type':
272
277
  var value;
273
278
  if (token.prefix === '') {
274
- if (this._baseIRI === null || absoluteIRI.test(token.value))
279
+ if (this._base === null || absoluteIRI.test(token.value))
275
280
  value = token.value;
276
281
  else
277
282
  value = this._resolveIRI(token);
@@ -303,7 +308,10 @@ N3Parser.prototype = {
303
308
 
304
309
  switch (token.type) {
305
310
  case 'IRI':
306
- item = token.value;
311
+ if (this._base === null || absoluteIRI.test(token.value))
312
+ item = token.value;
313
+ else
314
+ item = this._resolveIRI(token);
307
315
  break;
308
316
  case 'prefixed':
309
317
  var prefix = this._prefixes[token.prefix];
@@ -420,7 +428,7 @@ N3Parser.prototype = {
420
428
  // An IRI means this is a quad (only allowed if not already inside a graph).
421
429
  case 'IRI':
422
430
  if (this._supportsQuads && this._graph === null) {
423
- if (this._baseIRI === null || absoluteIRI.test(token.value))
431
+ if (this._base === null || absoluteIRI.test(token.value))
424
432
  graph = token.value;
425
433
  else
426
434
  graph = this._resolveIRI(token);
@@ -493,7 +501,7 @@ N3Parser.prototype = {
493
501
  if (token.type !== 'IRI')
494
502
  return this._error('Expected IRI to follow prefix "' + this._prefix + ':"', token);
495
503
  var prefixIRI;
496
- if (this._baseIRI === null || absoluteIRI.test(token.value))
504
+ if (this._base === null || absoluteIRI.test(token.value))
497
505
  prefixIRI = token.value;
498
506
  else
499
507
  prefixIRI = this._resolveIRI(token);
@@ -506,14 +514,11 @@ N3Parser.prototype = {
506
514
  _readBaseIRI: function (token) {
507
515
  if (token.type !== 'IRI')
508
516
  return this._error('Expected IRI to follow base declaration', token);
509
- if (token.value.indexOf('#') > 0)
510
- return this._error('Invalid base IRI', token);
511
- if (this._baseIRI === null || absoluteIRI.test(token.value))
512
- this._baseIRI = token.value;
513
- else
514
- this._baseIRI = this._resolveIRI(token);
515
- this._baseIRIPath = this._baseIRI.replace(documentPart, '');
516
- this._baseIRIRoot = this._baseIRI.match(rootIRI)[0];
517
+ try {
518
+ this._setBase(this._base === null ||
519
+ absoluteIRI.test(token.value) ? token.value : this._resolveIRI(token));
520
+ }
521
+ catch (error) { this._error(error.message, token); }
517
522
  return this._readDeclarationPunctuation;
518
523
  },
519
524
 
@@ -568,28 +573,89 @@ N3Parser.prototype = {
568
573
  this._callback(new Error(message + ' at line ' + token.line + '.'));
569
574
  },
570
575
 
571
- // ### `_resolveIRI` resolves an IRI token against the base path
576
+ // ### `_resolveIRI` resolves a relative IRI token against the base path,
577
+ // assuming that a base path has been set and that the IRI is indeed relative.
572
578
  _resolveIRI: function (token) {
573
579
  var iri = token.value;
574
580
  switch (iri[0]) {
575
581
  // An empty relative IRI indicates the base IRI
576
- case undefined:
577
- return this._baseIRI;
582
+ case undefined: return this._base;
578
583
  // Resolve relative fragment IRIs against the base IRI
579
- case '#':
580
- return this._baseIRI + iri;
584
+ case '#': return this._base + iri;
581
585
  // Resolve relative query string IRIs by replacing the query string
582
- case '?':
583
- return this._baseIRI.replace(/(?:\?.*)?$/, iri);
584
- // Resolve root relative IRIs at the root of the base IRI
586
+ case '?': return this._base.replace(/(?:\?.*)?$/, iri);
587
+ // Resolve root-relative IRIs at the root of the base IRI
585
588
  case '/':
586
- return this._baseIRIRoot + iri;
589
+ // Resolve scheme-relative IRIs to the scheme
590
+ return (iri[1] === '/' ? this._baseScheme : this._baseRoot) + this._removeDotSegments(iri);
587
591
  // Resolve all other IRIs at the base IRI's path
588
592
  default:
589
- return this._baseIRIPath + iri;
593
+ return this._removeDotSegments(this._basePath + iri);
590
594
  }
591
595
  },
592
596
 
597
+ // ### `_removeDotSegments` resolves './' and '../' path segments in an IRI as per RFC3986.
598
+ _removeDotSegments: function (iri) {
599
+ // Don't modify the IRI if it does not contain any dot segments
600
+ if (!dotSegments.test(iri))
601
+ return iri;
602
+
603
+ // Start with an imaginary slash before the IRI in order to resolve trailing './' and '../'
604
+ var result = '', length = iri.length, i = -1, pathStart = -1, segmentStart = 0, next = '/';
605
+
606
+ while (i < length) {
607
+ switch (next) {
608
+ // The path starts with the first slash after the authority
609
+ case ':':
610
+ if (pathStart < 0) {
611
+ // Skip two slashes before the authority
612
+ if (iri[++i] === '/' && iri[++i] === '/')
613
+ // Skip to slash after the authority
614
+ while ((pathStart = i + 1) < length && iri[pathStart] !== '/')
615
+ i = pathStart;
616
+ }
617
+ break;
618
+ // Don't modify a query string or fragment
619
+ case '?':
620
+ case '#':
621
+ i = length;
622
+ break;
623
+ // Handle '/.' or '/..' path segments
624
+ case '/':
625
+ if (iri[i + 1] === '.') {
626
+ next = iri[++i + 1];
627
+ switch (next) {
628
+ // Remove a '/.' segment
629
+ case '/':
630
+ result += iri.substring(segmentStart, i - 1);
631
+ segmentStart = i + 1;
632
+ break;
633
+ // Remove a trailing '/.' segment
634
+ case undefined:
635
+ case '?':
636
+ case '#':
637
+ return result + iri.substring(segmentStart, i) + iri.substr(i + 1);
638
+ // Remove a '/..' segment
639
+ case '.':
640
+ next = iri[++i + 1];
641
+ if (next === undefined || next === '/' || next === '?' || next === '#') {
642
+ result += iri.substring(segmentStart, i - 2);
643
+ // Try to remove the parent path from result
644
+ if ((segmentStart = result.lastIndexOf('/')) >= pathStart)
645
+ result = result.substr(0, segmentStart);
646
+ // Remove a trailing '/..' segment
647
+ if (next !== '/')
648
+ return result + '/' + iri.substr(i + 1);
649
+ segmentStart = i + 1;
650
+ }
651
+ }
652
+ }
653
+ }
654
+ next = iri[++i];
655
+ }
656
+ return result + iri.substring(segmentStart);
657
+ },
658
+
593
659
  // ## Public methods
594
660
 
595
661
  // ### `parse` parses the N3 input and emits each parsed triple through the callback.
@@ -598,7 +664,7 @@ N3Parser.prototype = {
598
664
  // We start reading in the top context.
599
665
  this._readCallback = this._readInTopContext;
600
666
  this._prefixes = Object.create(null);
601
- this._prefixes._ = '_:b' + blankNodePrefix++ + '_';
667
+ this._prefixes._ = this._blankNodePrefix || '_:b' + blankNodePrefix++ + '_';
602
668
 
603
669
  // If the input argument is not given, shift parameters
604
670
  if (typeof input === 'function')
@@ -622,7 +688,7 @@ N3Parser.prototype = {
622
688
  this.addChunk = this._lexer.addChunk;
623
689
  this.end = this._lexer.end;
624
690
  }
625
- }
691
+ },
626
692
  };
627
693
 
628
694
  // The empty function
package/lib/N3Store.js CHANGED
@@ -11,22 +11,25 @@ function N3Store(triples, options) {
11
11
  this._size = 0;
12
12
  // `_graphs` contains subject, predicate, and object indexes per graph.
13
13
  this._graphs = Object.create(null);
14
- // `_entities` maps entities such as `http://xmlns.com/foaf/0.1/name` to numbers.
15
- // This saves memory, since only the numbers have to be stored in `_graphs`.
16
- this._entities = Object.create(null);
17
- this._entities['><'] = 0; // Dummy entry, so the first actual key is non-zero
18
- this._entityCount = 0;
19
- // `_blankNodeIndex` is the index of the last created blank node that was automatically named
14
+ // `_ids` maps entities such as `http://xmlns.com/foaf/0.1/name` to numbers,
15
+ // saving memory by using only numbers as keys in `_graphs`.
16
+ this._id = 0;
17
+ this._ids = Object.create(null);
18
+ this._ids['><'] = 0; // dummy entry, so the first actual key is non-zero
19
+ this._entities = Object.create(null); // inverse of `_ids`
20
+ // `_blankNodeIndex` is the index of the last automatically named blank node
20
21
  this._blankNodeIndex = 0;
21
22
 
22
23
  // Shift parameters if `triples` is not given
23
24
  if (!options && triples && !triples[0])
24
25
  options = triples, triples = null;
26
+ options = options || {};
25
27
 
26
28
  // Add triples and prefixes if passed
27
29
  this._prefixes = Object.create(null);
28
- if (options && options.prefixes)
30
+ if (options.prefixes)
29
31
  this.addPrefixes(options.prefixes);
32
+ this.defaultGraph = options.defaultGraph || 'http://example.org/#defaultGraph';
30
33
  if (triples)
31
34
  this.addTriples(triples);
32
35
  }
@@ -53,12 +56,16 @@ N3Store.prototype = {
53
56
  // ## Private methods
54
57
 
55
58
  // ### `_addToIndex` adds a triple to a three-layered index.
59
+ // Returns if the index has changed, if the entry did not already exist.
56
60
  _addToIndex: function (index0, key0, key1, key2) {
57
61
  // Create layers as necessary.
58
62
  var index1 = index0[key0] || (index0[key0] = {});
59
63
  var index2 = index1[key1] || (index1[key1] = {});
60
- // Setting the key to _any_ value signalizes the presence of the triple.
61
- index2[key2] = null;
64
+ // Setting the key to _any_ value signals the presence of the triple.
65
+ var existed = key2 in index2;
66
+ if (!existed)
67
+ index2[key2] = null;
68
+ return !existed;
62
69
  },
63
70
 
64
71
  // ### `_removeFromIndex` removes a triple from a three-layered index.
@@ -82,7 +89,9 @@ N3Store.prototype = {
82
89
  // (for instance: _subject_, _predicate_, and _object_).
83
90
  // Finally, `graph` will be the graph of the created triples.
84
91
  _findInIndex: function (index0, key0, key1, key2, name0, name1, name2, graph) {
85
- var results = [], entityKeys = Object.keys(this._entities), tmp, index1, index2;
92
+ var results = [], tmp, index1, index2, varCount = !key0 + !key1 + !key2,
93
+ // depending on the number of variables, keys or reverse index are faster
94
+ entityKeys = varCount > 1 ? Object.keys(this._ids) : this._entities;
86
95
 
87
96
  // If a key is specified, use only that part of index 0.
88
97
  if (key0) (tmp = index0, index0 = {})[key0] = tmp[key0];
@@ -123,7 +132,6 @@ N3Store.prototype = {
123
132
  if (key0) (tmp = index0, index0 = {})[key0] = tmp[key0];
124
133
  for (var value0 in index0) {
125
134
  if (index1 = index0[value0]) {
126
-
127
135
  // If a key is specified, count only that part of index 1.
128
136
  if (key1) (tmp = index1, index1 = {})[key1] = tmp[key1];
129
137
  for (var value1 in index1) {
@@ -142,6 +150,7 @@ N3Store.prototype = {
142
150
  // ## Public methods
143
151
 
144
152
  // ### `addTriple` adds a new N3 triple to the store.
153
+ // Returns if the triple index has changed, if the triple did not already exist.
145
154
  addTriple: function (subject, predicate, object, graph) {
146
155
  // Shift arguments if a triple object is given instead of components
147
156
  if (!predicate)
@@ -149,15 +158,11 @@ N3Store.prototype = {
149
158
  predicate = subject.predicate, subject = subject.subject;
150
159
 
151
160
  // Find the graph that will contain the triple.
152
- graph = graph || '';
161
+ graph = graph || this.defaultGraph;
153
162
  var graphItem = this._graphs[graph];
154
163
  // Create the graph if it doesn't exist yet.
155
164
  if (!graphItem) {
156
- graphItem = this._graphs[graph] = {
157
- subjects: {},
158
- predicates: {},
159
- objects: {}
160
- };
165
+ graphItem = this._graphs[graph] = { subjects: {}, predicates: {}, objects: {} };
161
166
  // Freezing a graph helps subsequent `add` performance,
162
167
  // and properties will never be modified anyway.
163
168
  Object.freeze(graphItem);
@@ -166,17 +171,19 @@ N3Store.prototype = {
166
171
  // Since entities can often be long IRIs, we avoid storing them in every index.
167
172
  // Instead, we have a separate index that maps entities to numbers,
168
173
  // which are then used as keys in the other indexes.
174
+ var ids = this._ids;
169
175
  var entities = this._entities;
170
- subject = entities[subject] || (entities[subject] = ++this._entityCount);
171
- predicate = entities[predicate] || (entities[predicate] = ++this._entityCount);
172
- object = entities[object] || (entities[object] = ++this._entityCount);
176
+ subject = ids[subject] || (ids[entities[++this._id] = subject] = this._id);
177
+ predicate = ids[predicate] || (ids[entities[++this._id] = predicate] = this._id);
178
+ object = ids[object] || (ids[entities[++this._id] = object] = this._id);
173
179
 
174
- this._addToIndex(graphItem.subjects, subject, predicate, object);
180
+ var changed = this._addToIndex(graphItem.subjects, subject, predicate, object);
175
181
  this._addToIndex(graphItem.predicates, predicate, object, subject);
176
182
  this._addToIndex(graphItem.objects, object, subject, predicate);
177
183
 
178
184
  // The cached triple count is now invalid.
179
185
  this._size = null;
186
+ return changed;
180
187
  },
181
188
 
182
189
  // ### `addTriples` adds multiple N3 triples to the store.
@@ -190,7 +197,7 @@ N3Store.prototype = {
190
197
  this._prefixes[prefix] = iri;
191
198
  },
192
199
 
193
- // ### `addPrefixex` adds support for querying with the given prefixes
200
+ // ### `addPrefixes` adds support for querying with the given prefixes
194
201
  addPrefixes: function (prefixes) {
195
202
  for (var prefix in prefixes)
196
203
  this.addPrefix(prefix, prefixes[prefix]);
@@ -202,20 +209,20 @@ N3Store.prototype = {
202
209
  if (!predicate)
203
210
  graph = subject.graph, object = subject.object,
204
211
  predicate = subject.predicate, subject = subject.subject;
205
- graph = graph || '';
212
+ graph = graph || this.defaultGraph;
206
213
 
207
214
  // Find internal identifiers for all components.
208
- var graphItem, entities = this._entities, graphs = this._graphs;
209
- if (!(subject = entities[subject])) return;
210
- if (!(predicate = entities[predicate])) return;
211
- if (!(object = entities[object])) return;
212
- if (!(graphItem = graphs[graph])) return;
215
+ var graphItem, ids = this._ids, graphs = this._graphs;
216
+ if (!(subject = ids[subject])) return false;
217
+ if (!(predicate = ids[predicate])) return false;
218
+ if (!(object = ids[object])) return false;
219
+ if (!(graphItem = graphs[graph])) return false;
213
220
 
214
221
  // Verify that the triple exists.
215
222
  var subjects, predicates;
216
- if (!(subjects = graphItem.subjects[subject])) return;
217
- if (!(predicates = subjects[predicate])) return;
218
- if (!(object in predicates)) return;
223
+ if (!(subjects = graphItem.subjects[subject])) return false;
224
+ if (!(predicates = subjects[predicate])) return false;
225
+ if (!(object in predicates)) return false;
219
226
 
220
227
  // Remove it from all indexes.
221
228
  this._removeFromIndex(graphItem.subjects, subject, predicate, object);
@@ -224,8 +231,9 @@ N3Store.prototype = {
224
231
  if (this._size !== null) this._size--;
225
232
 
226
233
  // Remove the graph if it is empty.
227
- for (subject in graphItem.subjects) return;
234
+ for (subject in graphItem.subjects) return true;
228
235
  delete graphs[graph];
236
+ return true;
229
237
  },
230
238
 
231
239
  // ### `removeTriples` removes multiple N3 triples from the store.
@@ -235,8 +243,7 @@ N3Store.prototype = {
235
243
  },
236
244
 
237
245
  // ### `find` finds a set of triples matching a pattern, expanding prefixes as necessary.
238
- // Setting `subject`, `predicate`, or `object` to `null` means an _anything_ wildcard.
239
- // Setting `graph` to `null` means the default graph.
246
+ // Setting `subject`, `predicate`, `object` or `graph` to a falsy value means an _anything_ wildcard.
240
247
  find: function (subject, predicate, object, graph) {
241
248
  var prefixes = this._prefixes;
242
249
  return this.findByIRI(
@@ -248,44 +255,51 @@ N3Store.prototype = {
248
255
  },
249
256
 
250
257
  // ### `findByIRI` finds a set of triples matching a pattern.
251
- // Setting `subject`, `predicate`, or `object` to a falsy value means an _anything_ wildcard.
252
- // Setting `graph` to a falsy value means the default graph.
258
+ // Setting `subject`, `predicate`, `object` or `graph` to a falsy value means an _anything_ wildcard.
253
259
  findByIRI: function (subject, predicate, object, graph) {
254
- graph = graph || '';
255
- var graphItem = this._graphs[graph], entities = this._entities;
256
-
257
- // If the specified graph contain no triples, there are no results.
258
- if (!graphItem) return [];
259
-
260
- // Translate IRIs to internal index keys.
261
- // Optimization: if the entity doesn't exist, no triples with it exist.
262
- if (subject && !(subject = entities[subject])) return [];
263
- if (predicate && !(predicate = entities[predicate])) return [];
264
- if (object && !(object = entities[object])) return [];
265
-
266
- // Choose the optimal index, based on what fields are present
267
- if (subject) {
268
- if (object)
269
- // If subject and object are given, the object index will be the fastest.
270
- return this._findInIndex(graphItem.objects, object, subject, predicate,
271
- 'object', 'subject', 'predicate', graph);
272
- else
273
- // If only subject and possibly predicate are given, the subject index will be the fastest.
274
- return this._findInIndex(graphItem.subjects, subject, predicate, null,
275
- 'subject', 'predicate', 'object', graph);
276
- }
277
- else if (predicate)
278
- // If only predicate and possibly object are given, the predicate index will be the fastest.
279
- return this._findInIndex(graphItem.predicates, predicate, object, null,
280
- 'predicate', 'object', 'subject', graph);
281
- else if (object)
282
- // If only object is given, the object index will be the fastest.
283
- return this._findInIndex(graphItem.objects, object, null, null,
284
- 'object', 'subject', 'predicate', graph);
260
+ var quads = [], graphs = {}, graphContents,
261
+ ids = this._ids, subjectId, predicateId, objectId;
262
+ // Either loop over all graphs, or over just one selected graph.
263
+ if (!graph)
264
+ graphs = this._graphs;
285
265
  else
286
- // If nothing is given, iterate subjects and predicates first
287
- return this._findInIndex(graphItem.subjects, null, null, null,
288
- 'subject', 'predicate', 'object', graph);
266
+ graphs[graph] = this._graphs[graph];
267
+
268
+ for (var graphId in graphs) {
269
+ // Only if the specified graph contains triples, there can be results
270
+ if (graphContents = graphs[graphId]) {
271
+ // Translate IRIs to internal index keys.
272
+ // Optimization: if the entity doesn't exist, no triples with it exist.
273
+ if (subject && !(subjectId = ids[subject])) return quads;
274
+ if (predicate && !(predicateId = ids[predicate])) return quads;
275
+ if (object && !(objectId = ids[object])) return quads;
276
+
277
+ // Choose the optimal index, based on what fields are present
278
+ if (subjectId) {
279
+ if (objectId)
280
+ // If subject and object are given, the object index will be the fastest.
281
+ quads.push(this._findInIndex(graphContents.objects, objectId, subjectId, predicateId,
282
+ 'object', 'subject', 'predicate', graphId));
283
+ else
284
+ // If only subject and possibly predicate are given, the subject index will be the fastest.
285
+ quads.push(this._findInIndex(graphContents.subjects, subjectId, predicateId, null,
286
+ 'subject', 'predicate', 'object', graphId));
287
+ }
288
+ else if (predicateId)
289
+ // If only predicate and possibly object are given, the predicate index will be the fastest.
290
+ quads.push(this._findInIndex(graphContents.predicates, predicateId, objectId, null,
291
+ 'predicate', 'object', 'subject', graphId));
292
+ else if (objectId)
293
+ // If only object is given, the object index will be the fastest.
294
+ quads.push(this._findInIndex(graphContents.objects, objectId, null, null,
295
+ 'object', 'subject', 'predicate', graphId));
296
+ else
297
+ // If nothing is given, iterate subjects and predicates first
298
+ quads.push(this._findInIndex(graphContents.subjects, null, null, null,
299
+ 'subject', 'predicate', 'object', graphId));
300
+ }
301
+ }
302
+ return quads.length === 1 ? quads[0] : quads.concat.apply([], quads);
289
303
  },
290
304
 
291
305
  // ### `count` returns the number of triples matching a pattern, expanding prefixes as necessary.
@@ -305,17 +319,17 @@ N3Store.prototype = {
305
319
  // Setting `subject`, `predicate`, or `object` to `null` means an _anything_ wildcard.
306
320
  // Setting `graph` to `null` means the default graph.
307
321
  countByIRI: function (subject, predicate, object, graph) {
308
- graph = graph || '';
309
- var graphItem = this._graphs[graph], entities = this._entities;
322
+ graph = graph || this.defaultGraph;
323
+ var graphItem = this._graphs[graph], ids = this._ids;
310
324
 
311
325
  // If the specified graph contain no triples, there are no results.
312
326
  if (!graphItem) return 0;
313
327
 
314
328
  // Translate IRIs to internal index keys.
315
329
  // Optimization: if the entity doesn't exist, no triples with it exist.
316
- if (subject && !(subject = entities[subject])) return 0;
317
- if (predicate && !(predicate = entities[predicate])) return 0;
318
- if (object && !(object = entities[object])) return 0;
330
+ if (subject && !(subject = ids[subject])) return 0;
331
+ if (predicate && !(predicate = ids[predicate])) return 0;
332
+ if (object && !(object = ids[object])) return 0;
319
333
 
320
334
  // Choose the optimal index, based on what fields are present
321
335
  if (subject) {
@@ -338,18 +352,20 @@ N3Store.prototype = {
338
352
 
339
353
  // ### `createBlankNode` creates a new blank node, returning its name.
340
354
  createBlankNode: function (suggestedName) {
341
- var name;
355
+ var name, index;
356
+ // Generate a name based on the suggested name
342
357
  if (suggestedName) {
343
- name = suggestedName = '_:' + suggestedName;
344
- var index = 1;
345
- while (this._entities[name])
358
+ name = suggestedName = '_:' + suggestedName, index = 1;
359
+ while (this._ids[name])
346
360
  name = suggestedName + index++;
347
361
  }
362
+ // Generate a generic blank node name
348
363
  else {
349
364
  do { name = '_:b' + this._blankNodeIndex++; }
350
- while (this._entities[name]);
365
+ while (this._ids[name]);
351
366
  }
352
- this._entities[name] = this._entityCount++;
367
+ // Add the blank node to the entities, avoiding the generation of duplicates
368
+ this._ids[name] = ++this._id;
353
369
  return name;
354
370
  },
355
371
  };