Skip to content

Commit a5308ec

Browse files
gkelloggdavidlehn
authored andcommitted
* Indexing on an arbitrary property, not just @index.
1 parent 9215905 commit a5308ec

File tree

6 files changed

+100
-35
lines changed

6 files changed

+100
-35
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
### Changed
1313
- Default processing mode changed to json-ld-1.1. Allows a 1.1 context to be
1414
used after non-1.1 contexts.
15-
- `@vocab` can be relative or a Compact IRI in 1.1, resolved against either a previous `@vocab`,
16-
`@base` or document base.
15+
- Indexing on an arbitrary property, not just "@index".
16+
- `@vocab` can be relative or a Compact IRI in 1.1, resolved against either
17+
a previous `@vocab`, `@base` or document base.
1718
- Better checking of absolute IRIs.
1819
- Terms that begin with a ':' are not considered absolute or compact IRIs.
1920
- Don't use terms with `"@prefix": false` or expanded term definitions to construct compact IRIs.

lib/compact.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,31 @@ api.compact = async ({
521521
}
522522
key = expandedItem['@language'];
523523
} else if(container.includes('@index')) {
524-
key = expandedItem['@index'];
524+
const indexKey = _getContextValue(activeCtx, itemActiveProperty, '@index') || '@index';
525+
const containerKey = api.compactIri({activeCtx, iri: indexKey, vocab: true});
526+
if(indexKey === '@index') {
527+
key = expandedItem['@index'];
528+
delete compactedItem[containerKey];
529+
} else {
530+
let others;
531+
[key, ...others] = _asArray(compactedItem[indexKey] || []);
532+
if(!_isString(key)) {
533+
// Will use @none if it isn't a string.
534+
key = null;
535+
} else {
536+
switch(others.length) {
537+
case 0:
538+
delete compactedItem[indexKey];
539+
break;
540+
case 1:
541+
compactedItem[indexKey] = others[0];
542+
break;
543+
default:
544+
compactedItem[indexKey] = others;
545+
break;
546+
}
547+
}
548+
}
525549
} else if(container.includes('@id')) {
526550
const idKey = api.compactIri({activeCtx, iri: '@id', vocab: true});
527551
key = compactedItem[idKey];

lib/context.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ api.createTermDefinition = (
375375

376376
// JSON-LD 1.1 support
377377
if(api.processingMode(activeCtx, 1.1)) {
378-
validKeys.push('@context', '@nest', '@prefix', '@protected');
378+
validKeys.push('@context', '@index', '@nest', '@prefix', '@protected');
379379
}
380380

381381
for(const kw in value) {
@@ -609,6 +609,21 @@ api.createTermDefinition = (
609609
mapping['@container'] = container;
610610
}
611611

612+
// property indexing
613+
if('@index' in value) {
614+
if (!('@container' in value) || !mapping['@container'].includes('@index')) {
615+
throw new JsonLdError(
616+
'Invalid JSON-LD syntax; @index without @index in @container: ${value["@index"]} on term ${term}.', 'jsonld.SyntaxError',
617+
{code: 'invalid term definition', context: localCtx});
618+
}
619+
if (!_isString(value['@index']) || value['@index'].indexOf('@') === 0) {
620+
throw new JsonLdError(
621+
'Invalid JSON-LD syntax; "@index must expand to an IRI: ${value["@index"]} on term ${term}.', 'jsonld.SyntaxError',
622+
{code: 'invalid term definition', context: localCtx});
623+
}
624+
mapping['@index'] = value['@index']
625+
}
626+
612627
// scoped contexts
613628
if('@context' in value) {
614629
mapping['@context'] = value['@context'];

lib/expand.js

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -610,14 +610,19 @@ async function _expandObject({
610610
} else if(container.includes('@index') && _isObject(value)) {
611611
// handle index container (skip if value is not an object)
612612
const asGraph = container.includes('@graph');
613+
const indexKey = _getContextValue(termCtx, key, '@index') || '@index';
614+
const propertyIndex = indexKey !== '@index' &&
615+
_expandIri(activeCtx, indexKey, {vocab: true}, options);
616+
613617
expandedValue = await _expandIndexMap({
614618
activeCtx: termCtx,
615619
options,
616620
activeProperty: key,
617621
value,
618622
expansionMap,
619623
asGraph,
620-
indexKey: '@index'
624+
indexKey: indexKey,
625+
propertyIndex
621626
});
622627
} else if(container.includes('@id') && _isObject(value)) {
623628
// handle id container (skip if value is not an object)
@@ -890,7 +895,7 @@ function _expandLanguageMap(activeCtx, languageMap, options) {
890895

891896
async function _expandIndexMap(
892897
{activeCtx, options, activeProperty, value, expansionMap, asGraph,
893-
indexKey}) {
898+
indexKey, propertyIndex}) {
894899
const rval = [];
895900
const keys = Object.keys(value).sort();
896901
const isTypeIndex = indexKey === '@type';
@@ -913,15 +918,6 @@ async function _expandIndexMap(
913918
val = [val];
914919
}
915920

916-
// expand for @type, but also for @none
917-
const expandedKey = _expandIri(activeCtx, key, {vocab: true}, options);
918-
if(indexKey === '@id') {
919-
// expand document relative
920-
key = _expandIri(activeCtx, key, {base: true}, options);
921-
} else if(isTypeIndex) {
922-
key = expandedKey;
923-
}
924-
925921
val = await api.expand({
926922
activeCtx,
927923
activeProperty,
@@ -931,6 +927,26 @@ async function _expandIndexMap(
931927
insideIndex: true,
932928
expansionMap
933929
});
930+
931+
// expand for @type, but also for @none
932+
let expandedKey;
933+
if(propertyIndex) {
934+
if(key === '@none') {
935+
expandedKey = '@none'
936+
} else {
937+
expandedKey =_expandValue({activeCtx, activeProperty: indexKey, value: key, options});
938+
}
939+
} else {
940+
expandedKey = _expandIri(activeCtx, key, {vocab: true}, options);
941+
}
942+
943+
if(indexKey === '@id') {
944+
// expand document relative
945+
key = _expandIri(activeCtx, key, {base: true}, options);
946+
} else if(isTypeIndex) {
947+
key = expandedKey;
948+
}
949+
934950
for(let item of val) {
935951
// If this is also a @graph container, turn items into graphs
936952
if(asGraph && !_isGraph(item)) {
@@ -944,6 +960,20 @@ async function _expandIndexMap(
944960
} else {
945961
item['@type'] = [key];
946962
}
963+
} else if(_isValue(item) && !['@language', '@type', '@index'].includes(indexKey)) {
964+
throw new JsonLdError(
965+
'Invalid JSON-LD syntax; Attempt to add illegal key to value object: ${indexKey}.',
966+
'jsonld.SyntaxError',
967+
{code: 'invalid value object', value: item});
968+
} else if(propertyIndex) {
969+
// index is a property to be expanded, and values interpreted for that property
970+
if(expandedKey !== '@none') {
971+
// expand key as a value
972+
_addValue(item, propertyIndex, expandedKey, {
973+
propertyIsArray: true,
974+
prependValue: true
975+
});
976+
}
947977
} else if(expandedKey !== '@none' && !(indexKey in item)) {
948978
item[indexKey] = key;
949979
}

lib/util.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ api.hasValue = (subject, property, value) => {
234234
* an array (lists) (default: false).
235235
* [allowDuplicate] true to allow duplicates, false not to (uses a
236236
* simple shallow comparison of subject ID or value) (default: true).
237+
* [prependValue] false to prepend value to any existing values. (default: false)
237238
*/
238239
api.addValue = (subject, property, value, options) => {
239240
options = options || {};
@@ -246,6 +247,9 @@ api.addValue = (subject, property, value, options) => {
246247
if(!('allowDuplicate' in options)) {
247248
options.allowDuplicate = true;
248249
}
250+
if(!('prependValue' in options)) {
251+
options.prependValue = false;
252+
}
249253

250254
if(options.valueIsArray) {
251255
subject[property] = value;
@@ -254,6 +258,10 @@ api.addValue = (subject, property, value, options) => {
254258
!subject.hasOwnProperty(property)) {
255259
subject[property] = [];
256260
}
261+
if(options.prependValue) {
262+
value = value.concat(subject[property]);
263+
subject[property] = [];
264+
}
257265
for(let i = 0; i < value.length; ++i) {
258266
api.addValue(subject, property, value[i], options);
259267
}
@@ -270,7 +278,11 @@ api.addValue = (subject, property, value, options) => {
270278

271279
// add new value
272280
if(!hasValue) {
273-
subject[property].push(value);
281+
if(options.prependValue) {
282+
subject[property].unshift(value)
283+
} else {
284+
subject[property].push(value);
285+
}
274286
}
275287
} else {
276288
// add new value as set or single value

tests/test-common.js

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,8 @@ const TEST_TYPES = {
3939
/compact-manifest.jsonld#ttn01$/,
4040
/compact-manifest.jsonld#ttn02$/,
4141
/compact-manifest.jsonld#ttn03$/,
42-
// property-valued indexes
43-
/compact-manifest.jsonld#tpi01$/,
44-
/compact-manifest.jsonld#tpi02$/,
45-
/compact-manifest.jsonld#tpi03$/,
46-
/compact-manifest.jsonld#tpi04$/,
47-
/compact-manifest.jsonld#tpi05$/,
48-
/compact-manifest.jsonld#tpi06$/,
42+
// IRI confusion
43+
/compact-manifest.jsonld#te002$/,
4944
// @propogate
5045
/compact-manifest.jsonld#tc026$/,
5146
/compact-manifest.jsonld#tc027$/,
@@ -138,18 +133,6 @@ const TEST_TYPES = {
138133
/expand-manifest.jsonld#thc05$/,
139134
// @type: @none
140135
/expand-manifest.jsonld#ttn02$/,
141-
// property index maps
142-
/expand-manifest.jsonld#tpi01$/,
143-
/expand-manifest.jsonld#tpi02$/,
144-
/expand-manifest.jsonld#tpi03$/,
145-
/expand-manifest.jsonld#tpi04$/,
146-
/expand-manifest.jsonld#tpi05$/,
147-
/expand-manifest.jsonld#tpi06$/,
148-
/expand-manifest.jsonld#tpi07$/,
149-
/expand-manifest.jsonld#tpi08$/,
150-
/expand-manifest.jsonld#tpi09$/,
151-
/expand-manifest.jsonld#tpi10$/,
152-
/expand-manifest.jsonld#tpi11$/,
153136
// misc
154137
/expand-manifest.jsonld#te043$/,
155138
/expand-manifest.jsonld#te044$/,

0 commit comments

Comments
 (0)