@lde/search-pipeline 0.24.0 → 0.24.2

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.
@@ -33,6 +33,13 @@ export interface ExtractionOptions {
33
33
  * nested template (`{ ?root <…/ref> ?r . ?r <…/field> ?v }`), recursing into
34
34
  * the reference type to the schema’s declared depth. The referent-binding hop
35
35
  * uses the source `path`; the emitted triples use the minted aliases.
36
+ * - **references into a keyed type**: a reference naming a target that declares
37
+ * a `key` gets its branch extended with an `OPTIONAL` hop reading the
38
+ * referent’s key field, emitted under the **target’s** alias for it – so the
39
+ * projection can store the referent’s document key rather than its node IRI.
40
+ * `OPTIONAL`, so a referent with no key candidate keeps its row. The root side
41
+ * needs nothing: a key field is a declared field, so its own branch and
42
+ * template triple are already there.
36
43
  *
37
44
  * Wire the result into a `SparqlConstructReader` (see `searchStages`), which
38
45
  * runs it per batch with the roots injected as VALUES.
@@ -1 +1 @@
1
- {"version":3,"file":"extraction.d.ts","sourceRoot":"","sources":["../src/extraction.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,cAAc,EAIpB,MAAM,2BAA2B,CAAC;AAQnC,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAM5D,2CAA2C;AAC3C,MAAM,WAAW,iBAAiB;IAChC;;;;;;;OAOG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,iBAAsB,GAC9B,cAAc,CAqBhB;AAED,+EAA+E;AAC/E,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,YAAY,EACpB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,MAAM,CAER"}
1
+ {"version":3,"file":"extraction.d.ts","sourceRoot":"","sources":["../src/extraction.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,cAAc,EAIpB,MAAM,2BAA2B,CAAC;AAWnC,OAAO,KAAK,EAIV,YAAY,EACZ,UAAU,EACX,MAAM,aAAa,CAAC;AAMrB,2CAA2C;AAC3C,MAAM,WAAW,iBAAiB;IAChC;;;;;;;OAOG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,iBAAsB,GAC9B,cAAc,CAqBhB;AAED,+EAA+E;AAC/E,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,YAAY,EACpB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,MAAM,CAER"}
@@ -1,7 +1,7 @@
1
1
  import { AstFactory, } from '@traqula/rules-sparql-1-1';
2
2
  import { Parser } from '@traqula/parser-sparql-1-1';
3
3
  import { Generator } from '@traqula/generator-sparql-1-1';
4
- import { irAlias, isInlineReference, referenceTypeNamed, } from '@lde/search/adapter';
4
+ import { fieldNamed, irAlias, isInlineReference, labelSourceNameOf, referenceTypeNamed, rootTypeNamed, } from '@lde/search/adapter';
5
5
  const factory = new AstFactory();
6
6
  const parser = new Parser();
7
7
  const generator = new Generator();
@@ -26,6 +26,13 @@ const generator = new Generator();
26
26
  * nested template (`{ ?root <…/ref> ?r . ?r <…/field> ?v }`), recursing into
27
27
  * the reference type to the schema’s declared depth. The referent-binding hop
28
28
  * uses the source `path`; the emitted triples use the minted aliases.
29
+ * - **references into a keyed type**: a reference naming a target that declares
30
+ * a `key` gets its branch extended with an `OPTIONAL` hop reading the
31
+ * referent’s key field, emitted under the **target’s** alias for it – so the
32
+ * projection can store the referent’s document key rather than its node IRI.
33
+ * `OPTIONAL`, so a referent with no key candidate keeps its row. The root side
34
+ * needs nothing: a key field is a declared field, so its own branch and
35
+ * template triple are already there.
29
36
  *
30
37
  * Wire the result into a `SparqlConstructReader` (see `searchStages`), which
31
38
  * runs it per batch with the roots injected as VALUES.
@@ -83,13 +90,63 @@ function buildFor(searchType, subject, schema, counter) {
83
90
  else {
84
91
  const value = factory.termVariable(`v${counter.next++}`, factory.gen());
85
92
  template.push(factory.triple(subject, alias, value));
86
- branches.push(factory.patternGroup([
93
+ const patterns = [
87
94
  factory.patternBgp([factory.triple(subject, sourcePath, value)], factory.gen()),
88
- ], factory.gen()));
95
+ ];
96
+ const keyed = keyedTargetOf(field, schema);
97
+ if (keyed !== undefined) {
98
+ const built = buildKeyHop(keyed.target, keyed.keyField, value, counter);
99
+ template.push(built.triple);
100
+ patterns.push(built.pattern);
101
+ }
102
+ branches.push(factory.patternGroup(patterns, factory.gen()));
89
103
  }
90
104
  }
91
105
  return { template, branches };
92
106
  }
107
+ /**
108
+ * The keyed Root Type a reference points at, with the field its key is read
109
+ * from: a `lookup`’s `target` or an `idOnly`’s `labelSource`
110
+ * ({@link labelSourceNameOf}) that declares a {@link RootType.key}, or
111
+ * `undefined` for every other field. Naming the target is exactly the boundary
112
+ * the projection re-keys along, so the extraction reads the same declarations
113
+ * rather than a rule of its own: a reference that names no target keeps the
114
+ * node IRI, and needs no hop.
115
+ */
116
+ function keyedTargetOf(field, schema) {
117
+ if (field.kind !== 'reference') {
118
+ return undefined;
119
+ }
120
+ const targetName = labelSourceNameOf(field);
121
+ const target = targetName === undefined ? undefined : rootTypeNamed(schema, targetName);
122
+ if (target?.key === undefined) {
123
+ return undefined;
124
+ }
125
+ // `searchSchema` guarantees a declared, path-bearing key field, so the target
126
+ // – which came out of the schema – always has one.
127
+ const keyField = fieldNamed(target, target.key.field);
128
+ return { target, keyField };
129
+ }
130
+ /**
131
+ * The one-field hop that reads a referent’s key: a template triple emitting the
132
+ * key field under the **target’s** IR Alias, and an `OPTIONAL` branch binding
133
+ * it. The referent recursion an inline reference already performs, for a single
134
+ * field and wrapped in `OPTIONAL` rather than conjoined – so a referent with no
135
+ * key candidate keeps its row and still stores its own IRI, instead of the
136
+ * whole reference dropping out of the CONSTRUCT.
137
+ *
138
+ * It sits inside its own UNION branch (the one the reference contributes), so it
139
+ * multiplies against nothing.
140
+ */
141
+ function buildKeyHop(target, keyField, referent, counter) {
142
+ const key = factory.termVariable(`k${counter.next++}`, factory.gen());
143
+ return {
144
+ triple: factory.triple(referent, factory.termNamed(factory.gen(), irAlias(target, keyField)), key),
145
+ pattern: factory.patternOptional([
146
+ factory.patternBgp([factory.triple(referent, liftPath(keyField.path), key)], factory.gen()),
147
+ ], factory.gen()),
148
+ };
149
+ }
93
150
  /**
94
151
  * Lift a field’s `path` – written in the SPARQL reader adapter’s grammar (a
95
152
  * property path) – into a predicate AST node, by parsing it inside a throwaway
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lde/search-pipeline",
3
- "version": "0.24.0",
3
+ "version": "0.24.2",
4
4
  "description": "Search indexing as an @lde/pipeline instance: a Writer<Quad> that applies the @lde/search projection to extracted RDF and fans each document out to the transactional engine writer for its type's collection. Glue between the pipeline quad world and the search-engine document world; owns no projection or engine logic itself.",
5
5
  "repository": {
6
6
  "url": "git+https://github.com/ldelements/lde.git",
@@ -25,21 +25,21 @@
25
25
  "!**/*.tsbuildinfo"
26
26
  ],
27
27
  "dependencies": {
28
- "@lde/search": "^0.23.0",
28
+ "@lde/search": "^0.23.1",
29
29
  "@traqula/generator-sparql-1-1": "^1.1.8",
30
30
  "@traqula/parser-sparql-1-1": "^1.2.0",
31
31
  "@traqula/rules-sparql-1-1": "^1.2.0",
32
32
  "tslib": "^2.3.0"
33
33
  },
34
34
  "devDependencies": {
35
- "@lde/search-typesense": "^0.27.0",
35
+ "@lde/search-typesense": "^0.27.2",
36
36
  "@rdfjs/types": "^2.0.1",
37
37
  "n3": "^2.1.2",
38
- "testcontainers": "^12.0.3",
38
+ "testcontainers": "^12.1.0",
39
39
  "typesense": "^3.0.6"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@lde/dataset": "^0.7.9",
43
- "@lde/pipeline": "^0.36.0"
43
+ "@lde/pipeline": "^0.36.1"
44
44
  }
45
45
  }