Skip to content

Commit a544a3a

Browse files
committed
Add optimization for finding the best CURIE.
- Build a map for searching for a matching IRI when computing the inverse context. Each letter of an IRI can be used to key into the map to find the best set of partial matches (which can be used to create CURIEs). - This approach is a faster alternative to trying each possible term in the active context as a possible CURIE, linearly, one at a time.
1 parent 0a99dd3 commit a544a3a

File tree

1 file changed

+92
-26
lines changed

1 file changed

+92
-26
lines changed

js/jsonld.js

Lines changed: 92 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5565,31 +5565,37 @@ function _compactIri(activeCtx, iri, value, relativeTo, reverse) {
55655565

55665566
// no term or @vocab match, check for possible CURIEs
55675567
var choice = null;
5568-
for(var term in activeCtx.mappings) {
5569-
var definition = activeCtx.mappings[term];
5570-
// skip null definitions and terms with colons, they can't be prefixes
5571-
if(!definition || definition._termHasColon) {
5572-
continue;
5573-
}
5574-
// skip entries with @ids that are not partial matches
5575-
if(!(iri.length > definition['@id'].length &&
5576-
iri.indexOf(definition['@id']) === 0)) {
5577-
continue;
5578-
}
5579-
5580-
// a CURIE is usable if:
5581-
// 1. it has no mapping, OR
5582-
// 2. value is null, which means we're not compacting an @value, AND
5583-
// the mapping matches the IRI)
5584-
var curie = term + ':' + iri.substr(definition['@id'].length);
5585-
var isUsableCurie = (!(curie in activeCtx.mappings) ||
5586-
(value === null && activeCtx.mappings[curie]['@id'] === iri));
5587-
5588-
// select curie if it is shorter or the same length but lexicographically
5589-
// less than the current choice
5590-
if(isUsableCurie && (choice === null ||
5591-
_compareShortestLeast(curie, choice) < 0)) {
5592-
choice = curie;
5568+
var idx = 0;
5569+
var partialMatches = [];
5570+
var iriMap = activeCtx.fastCurieMap;
5571+
// check for partial matches of against `iri`, which means look until
5572+
// iri.length - 1, not full length
5573+
var maxPartialLength = iri.length - 1;
5574+
for(; idx < maxPartialLength && iri[idx] in iriMap; ++idx) {
5575+
iriMap = iriMap[iri[idx]];
5576+
if('' in iriMap) {
5577+
partialMatches.push(iriMap[''][0]);
5578+
}
5579+
}
5580+
// check partial matches in reverse order to prefer longest ones first
5581+
for(var i = partialMatches.length - 1; i >= 0; --i) {
5582+
var entry = partialMatches[i];
5583+
var terms = entry.terms;
5584+
for(var ti = 0; ti < terms.length; ++ti) {
5585+
// a CURIE is usable if:
5586+
// 1. it has no mapping, OR
5587+
// 2. value is null, which means we're not compacting an @value, AND
5588+
// the mapping matches the IRI
5589+
var curie = terms[ti] + ':' + iri.substr(entry.iri.length);
5590+
var isUsableCurie = (!(curie in activeCtx.mappings) ||
5591+
(value === null && activeCtx.mappings[curie]['@id'] === iri));
5592+
5593+
// select curie if it is shorter or the same length but lexicographically
5594+
// less than the current choice
5595+
if(isUsableCurie && (choice === null ||
5596+
_compareShortestLeast(curie, choice) < 0)) {
5597+
choice = curie;
5598+
}
55935599
}
55945600
}
55955601

@@ -6212,6 +6218,10 @@ function _getInitialContext(options) {
62126218
}
62136219
var inverse = activeCtx.inverse = {};
62146220

6221+
// variables for building fast CURIE map
6222+
var fastCurieMap = activeCtx.fastCurieMap = {};
6223+
var irisToTerms = {};
6224+
62156225
// handle default language
62166226
var defaultLanguage = activeCtx['@language'] || '@none';
62176227

@@ -6236,10 +6246,25 @@ function _getInitialContext(options) {
62366246
for(var ii = 0; ii < ids.length; ++ii) {
62376247
var iri = ids[ii];
62386248
var entry = inverse[iri];
6249+
var isKeyword = _isKeyword(iri);
62396250

6240-
// initialize entry
62416251
if(!entry) {
6252+
// initialize entry
62426253
inverse[iri] = entry = {};
6254+
6255+
if(!isKeyword && !mapping._termHasColon) {
6256+
// init IRI to term map and fast CURIE prefixes
6257+
irisToTerms[iri] = [term];
6258+
var fastCurieEntry = {iri: iri, terms: irisToTerms[iri]};
6259+
if(iri[0] in fastCurieMap) {
6260+
fastCurieMap[iri[0]].push(fastCurieEntry);
6261+
} else {
6262+
fastCurieMap[iri[0]] = [fastCurieEntry];
6263+
}
6264+
}
6265+
} else if(!isKeyword && !mapping._termHasColon) {
6266+
// add IRI to term match
6267+
irisToTerms[iri].push(term);
62436268
}
62446269

62456270
// add new entry
@@ -6274,9 +6299,50 @@ function _getInitialContext(options) {
62746299
}
62756300
}
62766301

6302+
// build fast CURIE map
6303+
for(var key in fastCurieMap) {
6304+
_buildIriMap(fastCurieMap, key, 1);
6305+
}
6306+
62776307
return inverse;
62786308
}
62796309

6310+
/**
6311+
* Runs a recursive algorithm to build a lookup map for quickly finding
6312+
* potential CURIEs.
6313+
*
6314+
* @param iriMap the map to build.
6315+
* @param key the current key in the map to work on.
6316+
* @param idx the index into the IRI to compare.
6317+
*/
6318+
function _buildIriMap(iriMap, key, idx) {
6319+
var entries = iriMap[key];
6320+
var next = iriMap[key] = {};
6321+
6322+
var iri;
6323+
var letter;
6324+
for(var i = 0; i < entries.length; ++i) {
6325+
iri = entries[i].iri;
6326+
if(idx >= iri.length) {
6327+
letter = '';
6328+
} else {
6329+
letter = iri[idx];
6330+
}
6331+
if(letter in next) {
6332+
next[letter].push(entries[i]);
6333+
} else {
6334+
next[letter] = [entries[i]];
6335+
}
6336+
}
6337+
6338+
for(var key in next) {
6339+
if(key === '') {
6340+
continue;
6341+
}
6342+
_buildIriMap(next, key, idx + 1);
6343+
}
6344+
}
6345+
62806346
/**
62816347
* Adds the term for the given entry if not already added.
62826348
*

0 commit comments

Comments
 (0)