@comunica/actor-optimize-query-operation-prune-empty-source-operations 4.4.0 → 4.4.2-alpha.49.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.
File without changes
File without changes
@@ -2,7 +2,7 @@ import type { IActionOptimizeQueryOperation, IActorOptimizeQueryOperationOutput,
2
2
  import { ActorOptimizeQueryOperation } from '@comunica/bus-optimize-query-operation';
3
3
  import type { IActorTest, TestResult } from '@comunica/core';
4
4
  import type { IActionContext, IQuerySourceWrapper } from '@comunica/types';
5
- import { Algebra, Factory } from 'sparqlalgebrajs';
5
+ import { Algebra, AlgebraFactory } from '@comunica/utils-algebra';
6
6
  /**
7
7
  * A comunica Prune Empty Source Operations Optimize Query Operation Actor.
8
8
  */
@@ -13,10 +13,7 @@ export declare class ActorOptimizeQueryOperationPruneEmptySourceOperations exten
13
13
  run(action: IActionOptimizeQueryOperation): Promise<IActorOptimizeQueryOperationOutput>;
14
14
  protected static hasEmptyOperation(operation: Algebra.Operation): boolean;
15
15
  protected collectMultiOperationInputs(inputs: Algebra.Operation[], collectedOperations: (Algebra.Pattern | Algebra.Link)[], inputType: (Algebra.Pattern | Algebra.Link)['type']): void;
16
- protected mapMultiOperation<O extends Algebra.Union | Algebra.Alt>(operation: O, emptyOperations: Set<Algebra.Operation>, multiOperationFactory: (input: O['input']) => Algebra.Operation): {
17
- result: Algebra.Operation;
18
- recurse: boolean;
19
- };
16
+ protected mapMultiOperation<O extends Algebra.Union | Algebra.Alt>(operationCopy: O, origOp: O, emptyOperations: Set<Algebra.Operation>, multiOperationFactory: (input: O['input']) => Algebra.Operation): Algebra.Operation;
20
17
  /**
21
18
  * Check if the given query operation will produce at least one result in the given source.
22
19
  * @param algebraFactory The algebra factory.
@@ -24,7 +21,7 @@ export declare class ActorOptimizeQueryOperationPruneEmptySourceOperations exten
24
21
  * @param input A query operation.
25
22
  * @param context The query context.
26
23
  */
27
- hasSourceResults(algebraFactory: Factory, source: IQuerySourceWrapper, input: Algebra.Operation, context: IActionContext): Promise<boolean>;
24
+ hasSourceResults(algebraFactory: AlgebraFactory, source: IQuerySourceWrapper, input: Algebra.Operation, context: IActionContext): Promise<boolean>;
28
25
  }
29
26
  export interface IActorOptimizeQueryOperationPruneEmptySourceOperationsArgs extends IActorOptimizeQueryOperationArgs {
30
27
  /**
@@ -4,8 +4,8 @@ exports.ActorOptimizeQueryOperationPruneEmptySourceOperations = void 0;
4
4
  const bus_optimize_query_operation_1 = require("@comunica/bus-optimize-query-operation");
5
5
  const context_entries_1 = require("@comunica/context-entries");
6
6
  const core_1 = require("@comunica/core");
7
+ const utils_algebra_1 = require("@comunica/utils-algebra");
7
8
  const utils_query_operation_1 = require("@comunica/utils-query-operation");
8
- const sparqlalgebrajs_1 = require("sparqlalgebrajs");
9
9
  /**
10
10
  * A comunica Prune Empty Source Operations Optimize Query Operation Actor.
11
11
  */
@@ -21,31 +21,27 @@ class ActorOptimizeQueryOperationPruneEmptySourceOperations extends bus_optimize
21
21
  }
22
22
  async run(action) {
23
23
  const dataFactory = action.context.getSafe(context_entries_1.KeysInitQuery.dataFactory);
24
- const algebraFactory = new sparqlalgebrajs_1.Factory(dataFactory);
24
+ const algebraFactory = new utils_algebra_1.AlgebraFactory(dataFactory);
25
25
  let operation = action.operation;
26
26
  // Collect all operations with source types
27
27
  // Only consider unions of patterns or alts of links, since these are created during exhaustive source assignment.
28
28
  const collectedOperations = [];
29
- // eslint-disable-next-line ts/no-this-alias
30
- const self = this;
31
- sparqlalgebrajs_1.Util.recurseOperation(operation, {
32
- [sparqlalgebrajs_1.Algebra.types.UNION](subOperation) {
33
- self.collectMultiOperationInputs(subOperation.input, collectedOperations, sparqlalgebrajs_1.Algebra.types.PATTERN);
34
- return true;
35
- },
36
- [sparqlalgebrajs_1.Algebra.types.ALT](subOperation) {
37
- self.collectMultiOperationInputs(subOperation.input, collectedOperations, sparqlalgebrajs_1.Algebra.types.LINK);
38
- return false;
39
- },
40
- [sparqlalgebrajs_1.Algebra.types.SERVICE]() {
41
- return false;
42
- },
29
+ utils_algebra_1.algebraUtils.visitOperation(operation, {
30
+ [utils_algebra_1.Algebra.Types.UNION]: { preVisitor: (subOperation) => {
31
+ this.collectMultiOperationInputs(subOperation.input, collectedOperations, utils_algebra_1.Algebra.Types.PATTERN);
32
+ return {};
33
+ } },
34
+ [utils_algebra_1.Algebra.Types.ALT]: { preVisitor: (subOperation) => {
35
+ this.collectMultiOperationInputs(subOperation.input, collectedOperations, utils_algebra_1.Algebra.Types.LINK);
36
+ return { continue: false };
37
+ } },
38
+ [utils_algebra_1.Algebra.Types.SERVICE]: { preVisitor: () => ({ continue: false }) },
43
39
  });
44
40
  // Determine in an async manner whether or not these sources return non-empty results
45
41
  const emptyOperations = new Set();
46
42
  await Promise.all(collectedOperations.map(async (collectedOperation) => {
47
- const checkOperation = collectedOperation.type === 'link' ?
48
- algebraFactory.createPattern(dataFactory.variable('s'), collectedOperation.iri, dataFactory.variable('o')) :
43
+ const checkOperation = collectedOperation.type === utils_algebra_1.Algebra.Types.LINK ?
44
+ algebraFactory.createPattern(dataFactory.variable('s'), collectedOperation.iri, dataFactory.variable('?o')) :
49
45
  collectedOperation;
50
46
  if (!await this.hasSourceResults(algebraFactory, (0, utils_query_operation_1.getOperationSource)(collectedOperation), checkOperation, action.context)) {
51
47
  emptyOperations.add(collectedOperation);
@@ -55,43 +51,27 @@ class ActorOptimizeQueryOperationPruneEmptySourceOperations extends bus_optimize
55
51
  if (emptyOperations.size > 0) {
56
52
  this.logDebug(action.context, `Pruning ${emptyOperations.size} source-specific operations`);
57
53
  // Rewrite operations by removing the empty children
58
- operation = sparqlalgebrajs_1.Util.mapOperation(operation, {
59
- [sparqlalgebrajs_1.Algebra.types.UNION](subOperation, factory) {
60
- return self.mapMultiOperation(subOperation, emptyOperations, children => factory.createUnion(children));
54
+ operation = utils_algebra_1.algebraUtils.mapOperation(operation, {
55
+ [utils_algebra_1.Algebra.Types.UNION]: { transform: (subOperation, origOp) => this.mapMultiOperation(subOperation, origOp, emptyOperations, children => algebraFactory.createUnion(children)) },
56
+ [utils_algebra_1.Algebra.Types.ALT]: { transform: (subOperation, origOp) => this.mapMultiOperation(subOperation, origOp, emptyOperations, children => algebraFactory.createAlt(children)) },
57
+ // Remove operations that have become empty now due to missing variables
58
+ [utils_algebra_1.Algebra.Types.PROJECT]: {
59
+ transform: (subOperation) => {
60
+ // Remove projections that have become empty now due to missing variables
61
+ if (ActorOptimizeQueryOperationPruneEmptySourceOperations.hasEmptyOperation(subOperation)) {
62
+ return algebraFactory.createUnion([]);
63
+ }
64
+ return subOperation;
65
+ },
61
66
  },
62
- [sparqlalgebrajs_1.Algebra.types.ALT](subOperation, factory) {
63
- return self.mapMultiOperation(subOperation, emptyOperations, children => factory.createAlt(children));
64
- },
65
- }, algebraFactory);
66
- // Identify and remove operations that have become empty now due to missing variables
67
- operation = sparqlalgebrajs_1.Util.mapOperation(operation, {
68
- [sparqlalgebrajs_1.Algebra.types.PROJECT](subOperation, factory) {
69
- // Remove projections that have become empty now due to missing variables
70
- if (ActorOptimizeQueryOperationPruneEmptySourceOperations.hasEmptyOperation(subOperation)) {
71
- return {
72
- recurse: false,
73
- result: factory.createUnion([]),
74
- };
75
- }
76
- return {
77
- recurse: true,
78
- result: subOperation,
79
- };
80
- },
81
- [sparqlalgebrajs_1.Algebra.types.LEFT_JOIN](subOperation) {
82
- // Remove left joins with empty right operation
83
- if (ActorOptimizeQueryOperationPruneEmptySourceOperations.hasEmptyOperation(subOperation.input[1])) {
84
- return {
85
- recurse: true,
86
- result: subOperation.input[0],
87
- };
88
- }
89
- return {
90
- recurse: true,
91
- result: subOperation,
92
- };
93
- },
94
- }, algebraFactory);
67
+ [utils_algebra_1.Algebra.Types.LEFT_JOIN]: { transform: (subOperation) => {
68
+ // Remove left joins with empty right operation
69
+ if (ActorOptimizeQueryOperationPruneEmptySourceOperations.hasEmptyOperation(subOperation.input[1])) {
70
+ return subOperation.input[0];
71
+ }
72
+ return subOperation;
73
+ } },
74
+ });
95
75
  }
96
76
  return { operation, context: action.context };
97
77
  }
@@ -100,51 +80,59 @@ class ActorOptimizeQueryOperationPruneEmptySourceOperations extends bus_optimize
100
80
  // But if we find a union with multiple children,
101
81
  // *all* of the children must be empty before the full operation is considered empty.
102
82
  let emptyOperation = false;
103
- sparqlalgebrajs_1.Util.recurseOperation(operation, {
104
- [sparqlalgebrajs_1.Algebra.types.UNION](subOperation) {
105
- if (subOperation.input.every(subSubOperation => ActorOptimizeQueryOperationPruneEmptySourceOperations
106
- .hasEmptyOperation(subSubOperation))) {
107
- emptyOperation = true;
108
- }
109
- return false;
110
- },
111
- [sparqlalgebrajs_1.Algebra.types.ALT](subOperation) {
112
- if (subOperation.input.length === 0) {
113
- emptyOperation = true;
114
- }
115
- return false;
116
- },
117
- [sparqlalgebrajs_1.Algebra.types.LEFT_JOIN](subOperation) {
118
- // Only recurse into left part of left-join
119
- if (ActorOptimizeQueryOperationPruneEmptySourceOperations.hasEmptyOperation(subOperation.input[0])) {
120
- emptyOperation = true;
121
- }
122
- return false;
123
- },
83
+ utils_algebra_1.algebraUtils.visitOperation(operation, {
84
+ [utils_algebra_1.Algebra.Types.UNION]: { preVisitor: (unionOp) => {
85
+ if (unionOp.input.every(subSubOperation => ActorOptimizeQueryOperationPruneEmptySourceOperations
86
+ .hasEmptyOperation(subSubOperation))) {
87
+ emptyOperation = true;
88
+ return { shortcut: true };
89
+ }
90
+ return { continue: false };
91
+ } },
92
+ [utils_algebra_1.Algebra.Types.LEFT_JOIN]: { preVisitor: (leftJoinOp) => {
93
+ // Only recurse into left part of left-join
94
+ if (ActorOptimizeQueryOperationPruneEmptySourceOperations.hasEmptyOperation(leftJoinOp.input[0])) {
95
+ emptyOperation = true;
96
+ return { shortcut: true };
97
+ }
98
+ return { continue: false };
99
+ } },
100
+ [utils_algebra_1.Algebra.Types.ALT]: { preVisitor: (altOp) => {
101
+ if (altOp.input.length === 0) {
102
+ emptyOperation = true;
103
+ return { shortcut: true };
104
+ }
105
+ return { continue: false };
106
+ } },
124
107
  });
125
108
  return emptyOperation;
126
109
  }
127
110
  collectMultiOperationInputs(inputs, collectedOperations, inputType) {
128
111
  for (const input of inputs) {
129
- if ((0, utils_query_operation_1.getOperationSource)(input) && input.type === inputType) {
112
+ if ((0, utils_query_operation_1.getOperationSource)(input) && (0, utils_algebra_1.isKnownOperation)(input, inputType)) {
130
113
  collectedOperations.push(input);
131
114
  }
132
115
  }
133
116
  }
134
- mapMultiOperation(operation, emptyOperations, multiOperationFactory) {
117
+ mapMultiOperation(operationCopy, origOp, emptyOperations, multiOperationFactory) {
135
118
  // Determine which operations return non-empty results
136
- const nonEmptyInputs = operation.input.filter(input => !emptyOperations.has(input));
119
+ const nonEmptyInputs = [];
120
+ for (const [idx, input] of operationCopy.input.entries()) {
121
+ if (!emptyOperations.has(origOp.input[idx])) {
122
+ nonEmptyInputs.push(input);
123
+ }
124
+ }
137
125
  // Remove empty operations
138
- if (nonEmptyInputs.length === operation.input.length) {
139
- return { result: operation, recurse: true };
126
+ if (nonEmptyInputs.length === operationCopy.input.length) {
127
+ return operationCopy;
140
128
  }
141
129
  if (nonEmptyInputs.length === 0) {
142
- return { result: multiOperationFactory([]), recurse: false };
130
+ return multiOperationFactory([]);
143
131
  }
144
132
  if (nonEmptyInputs.length === 1) {
145
- return { result: nonEmptyInputs[0], recurse: true };
133
+ return nonEmptyInputs[0];
146
134
  }
147
- return { result: multiOperationFactory(nonEmptyInputs), recurse: true };
135
+ return multiOperationFactory(nonEmptyInputs);
148
136
  }
149
137
  /**
150
138
  * Check if the given query operation will produce at least one result in the given source.
@@ -155,6 +143,7 @@ class ActorOptimizeQueryOperationPruneEmptySourceOperations extends bus_optimize
155
143
  */
156
144
  async hasSourceResults(algebraFactory, source, input, context) {
157
145
  const mergedContext = source.context ? context.merge(source.context) : context;
146
+ const wildcardAcceptAllExtensionFunctions = mergedContext.get(context_entries_1.KeysInitQuery.extensionFunctionsAlwaysPushdown);
158
147
  // Traversal contexts should never be considered empty at optimization time.
159
148
  if (mergedContext.get(context_entries_1.KeysQuerySourceIdentify.traverse)) {
160
149
  return true;
@@ -162,7 +151,7 @@ class ActorOptimizeQueryOperationPruneEmptySourceOperations extends bus_optimize
162
151
  // Prefer ASK over COUNT when instructed to, and the source allows it
163
152
  if (this.useAskIfSupported) {
164
153
  const askOperation = algebraFactory.createAsk(input);
165
- const askSupported = (0, utils_query_operation_1.doesShapeAcceptOperation)(await source.source.getSelectorShape(context), askOperation);
154
+ const askSupported = (0, utils_query_operation_1.doesShapeAcceptOperation)(await source.source.getSelectorShape(context), askOperation, { wildcardAcceptAllExtensionFunctions });
166
155
  if (askSupported) {
167
156
  return source.source.queryBoolean(askOperation, mergedContext);
168
157
  }
@@ -181,7 +170,7 @@ class ActorOptimizeQueryOperationPruneEmptySourceOperations extends bus_optimize
181
170
  // Since the VoID estimators in Comunica cannot produce false negatives, only positive assignments must be verified.
182
171
  if (cardinality.type === 'estimate' && cardinality.value > 0) {
183
172
  const askOperation = algebraFactory.createAsk(input);
184
- const askSupported = (0, utils_query_operation_1.doesShapeAcceptOperation)(await source.source.getSelectorShape(context), askOperation);
173
+ const askSupported = (0, utils_query_operation_1.doesShapeAcceptOperation)(await source.source.getSelectorShape(context), askOperation, { wildcardAcceptAllExtensionFunctions });
185
174
  if (askSupported) {
186
175
  return source.source.queryBoolean(askOperation, mergedContext);
187
176
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ActorOptimizeQueryOperationPruneEmptySourceOperations.js","sourceRoot":"","sources":["ActorOptimizeQueryOperationPruneEmptySourceOperations.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n IActionOptimizeQueryOperation,\n IActorOptimizeQueryOperationOutput,\n IActorOptimizeQueryOperationArgs,\n} from '@comunica/bus-optimize-query-operation';\nimport { ActorOptimizeQueryOperation } from '@comunica/bus-optimize-query-operation';\nimport { KeysInitQuery, KeysQuerySourceIdentify } from '@comunica/context-entries';\nimport type { IActorTest, TestResult } from '@comunica/core';\nimport { failTest, passTestVoid } from '@comunica/core';\nimport type {\n ComunicaDataFactory,\n IActionContext,\n IQuerySourceWrapper,\n MetadataBindings,\n QueryResultCardinality,\n} from '@comunica/types';\nimport { doesShapeAcceptOperation, getOperationSource } from '@comunica/utils-query-operation';\nimport { Algebra, Factory, Util } from 'sparqlalgebrajs';\n\n/**\n * A comunica Prune Empty Source Operations Optimize Query Operation Actor.\n */\nexport class ActorOptimizeQueryOperationPruneEmptySourceOperations extends ActorOptimizeQueryOperation {\n private readonly useAskIfSupported: boolean;\n\n public constructor(args: IActorOptimizeQueryOperationPruneEmptySourceOperationsArgs) {\n super(args);\n }\n\n public async test(action: IActionOptimizeQueryOperation): Promise<TestResult<IActorTest>> {\n if (getOperationSource(action.operation)) {\n return failTest(`Actor ${this.name} does not work with top-level operation sources.`);\n }\n return passTestVoid();\n }\n\n public async run(action: IActionOptimizeQueryOperation): Promise<IActorOptimizeQueryOperationOutput> {\n const dataFactory: ComunicaDataFactory = action.context.getSafe(KeysInitQuery.dataFactory);\n const algebraFactory = new Factory(dataFactory);\n\n let operation = action.operation;\n\n // Collect all operations with source types\n // Only consider unions of patterns or alts of links, since these are created during exhaustive source assignment.\n const collectedOperations: (Algebra.Pattern | Algebra.Link)[] = [];\n // eslint-disable-next-line ts/no-this-alias\n const self = this;\n Util.recurseOperation(operation, {\n [Algebra.types.UNION](subOperation) {\n self.collectMultiOperationInputs(subOperation.input, collectedOperations, Algebra.types.PATTERN);\n return true;\n },\n [Algebra.types.ALT](subOperation) {\n self.collectMultiOperationInputs(subOperation.input, collectedOperations, Algebra.types.LINK);\n return false;\n },\n [Algebra.types.SERVICE]() {\n return false;\n },\n });\n\n // Determine in an async manner whether or not these sources return non-empty results\n const emptyOperations: Set<Algebra.Operation> = new Set();\n await Promise.all(collectedOperations.map(async(collectedOperation) => {\n const checkOperation = collectedOperation.type === 'link' ?\n algebraFactory.createPattern(dataFactory.variable('s'), collectedOperation.iri, dataFactory.variable('o')) :\n collectedOperation;\n if (!await this.hasSourceResults(\n algebraFactory,\n getOperationSource(collectedOperation)!,\n checkOperation,\n action.context,\n )) {\n emptyOperations.add(collectedOperation);\n }\n }));\n\n // Only perform next mapping if we have at least one empty operation\n if (emptyOperations.size > 0) {\n this.logDebug(action.context, `Pruning ${emptyOperations.size} source-specific operations`);\n // Rewrite operations by removing the empty children\n operation = Util.mapOperation(operation, {\n [Algebra.types.UNION](subOperation, factory) {\n return self.mapMultiOperation(subOperation, emptyOperations, children => factory.createUnion(children));\n },\n [Algebra.types.ALT](subOperation, factory) {\n return self.mapMultiOperation(subOperation, emptyOperations, children => factory.createAlt(children));\n },\n }, algebraFactory);\n\n // Identify and remove operations that have become empty now due to missing variables\n operation = Util.mapOperation(operation, {\n [Algebra.types.PROJECT](subOperation, factory) {\n // Remove projections that have become empty now due to missing variables\n if (ActorOptimizeQueryOperationPruneEmptySourceOperations.hasEmptyOperation(subOperation)) {\n return {\n recurse: false,\n result: factory.createUnion([]),\n };\n }\n return {\n recurse: true,\n result: subOperation,\n };\n },\n [Algebra.types.LEFT_JOIN](subOperation) {\n // Remove left joins with empty right operation\n if (ActorOptimizeQueryOperationPruneEmptySourceOperations.hasEmptyOperation(subOperation.input[1])) {\n return {\n recurse: true,\n result: subOperation.input[0],\n };\n }\n return {\n recurse: true,\n result: subOperation,\n };\n },\n }, algebraFactory);\n }\n\n return { operation, context: action.context };\n }\n\n protected static hasEmptyOperation(operation: Algebra.Operation): boolean {\n // If union (or alt) is empty, consider it empty (`Array.every` on an empty array always returns true)\n // But if we find a union with multiple children,\n // *all* of the children must be empty before the full operation is considered empty.\n let emptyOperation = false;\n Util.recurseOperation(operation, {\n [Algebra.types.UNION](subOperation) {\n if (subOperation.input.every(subSubOperation => ActorOptimizeQueryOperationPruneEmptySourceOperations\n .hasEmptyOperation(subSubOperation))) {\n emptyOperation = true;\n }\n return false;\n },\n [Algebra.types.ALT](subOperation) {\n if (subOperation.input.length === 0) {\n emptyOperation = true;\n }\n return false;\n },\n [Algebra.types.LEFT_JOIN](subOperation) {\n // Only recurse into left part of left-join\n if (ActorOptimizeQueryOperationPruneEmptySourceOperations.hasEmptyOperation(subOperation.input[0])) {\n emptyOperation = true;\n }\n return false;\n },\n });\n return emptyOperation;\n }\n\n protected collectMultiOperationInputs(\n inputs: Algebra.Operation[],\n collectedOperations: (Algebra.Pattern | Algebra.Link)[],\n inputType: (Algebra.Pattern | Algebra.Link)['type'],\n ): void {\n for (const input of inputs) {\n if (getOperationSource(input) && input.type === inputType) {\n collectedOperations.push(input);\n }\n }\n }\n\n protected mapMultiOperation<O extends Algebra.Union | Algebra.Alt>(\n operation: O,\n emptyOperations: Set<Algebra.Operation>,\n multiOperationFactory: (input: O['input']) => Algebra.Operation,\n ): {\n result: Algebra.Operation;\n recurse: boolean;\n } {\n // Determine which operations return non-empty results\n const nonEmptyInputs = operation.input.filter(input => !emptyOperations.has(input));\n\n // Remove empty operations\n if (nonEmptyInputs.length === operation.input.length) {\n return { result: operation, recurse: true };\n }\n if (nonEmptyInputs.length === 0) {\n return { result: multiOperationFactory([]), recurse: false };\n }\n if (nonEmptyInputs.length === 1) {\n return { result: nonEmptyInputs[0], recurse: true };\n }\n return { result: multiOperationFactory(nonEmptyInputs), recurse: true };\n }\n\n /**\n * Check if the given query operation will produce at least one result in the given source.\n * @param algebraFactory The algebra factory.\n * @param source A query source.\n * @param input A query operation.\n * @param context The query context.\n */\n public async hasSourceResults(\n algebraFactory: Factory,\n source: IQuerySourceWrapper,\n input: Algebra.Operation,\n context: IActionContext,\n ): Promise<boolean> {\n const mergedContext = source.context ? context.merge(source.context) : context;\n\n // Traversal contexts should never be considered empty at optimization time.\n if (mergedContext.get(KeysQuerySourceIdentify.traverse)) {\n return true;\n }\n\n // Prefer ASK over COUNT when instructed to, and the source allows it\n if (this.useAskIfSupported) {\n const askOperation = algebraFactory.createAsk(input);\n const askSupported = doesShapeAcceptOperation(await source.source.getSelectorShape(context), askOperation);\n if (askSupported) {\n return source.source.queryBoolean(askOperation, mergedContext);\n }\n }\n\n // Fall back to sending the full operation, and extracting the cardinality from metadata\n const bindingsStream = source.source.queryBindings(input, mergedContext);\n const cardinality = await new Promise<QueryResultCardinality>((resolve, reject) => {\n bindingsStream.on('error', reject);\n bindingsStream.getProperty('metadata', (metadata: MetadataBindings) => {\n bindingsStream.destroy();\n resolve(metadata.cardinality);\n });\n });\n\n // If the cardinality is an estimate, such as from a VoID description,\n // verify it using ASK if the source supports it.\n // Since the VoID estimators in Comunica cannot produce false negatives, only positive assignments must be verified.\n if (cardinality.type === 'estimate' && cardinality.value > 0) {\n const askOperation = algebraFactory.createAsk(input);\n const askSupported = doesShapeAcceptOperation(await source.source.getSelectorShape(context), askOperation);\n if (askSupported) {\n return source.source.queryBoolean(askOperation, mergedContext);\n }\n }\n\n return cardinality.value > 0;\n }\n}\n\nexport interface IActorOptimizeQueryOperationPruneEmptySourceOperationsArgs extends IActorOptimizeQueryOperationArgs {\n /**\n * If true, ASK queries will be sent to the source instead of COUNT queries to check emptiness for patterns.\n * This will only be done for sources that accept ASK queries.\n * @default {false}\n */\n useAskIfSupported: boolean;\n}\n"]}
1
+ {"version":3,"file":"ActorOptimizeQueryOperationPruneEmptySourceOperations.js","sourceRoot":"","sources":["ActorOptimizeQueryOperationPruneEmptySourceOperations.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n IActionOptimizeQueryOperation,\n IActorOptimizeQueryOperationOutput,\n IActorOptimizeQueryOperationArgs,\n} from '@comunica/bus-optimize-query-operation';\nimport { ActorOptimizeQueryOperation } from '@comunica/bus-optimize-query-operation';\nimport { KeysInitQuery, KeysQuerySourceIdentify } from '@comunica/context-entries';\nimport type { IActorTest, TestResult } from '@comunica/core';\nimport { failTest, passTestVoid } from '@comunica/core';\nimport type {\n ComunicaDataFactory,\n IActionContext,\n IQuerySourceWrapper,\n MetadataBindings,\n QueryResultCardinality,\n} from '@comunica/types';\nimport { Algebra, AlgebraFactory, algebraUtils, isKnownOperation } from '@comunica/utils-algebra';\nimport { doesShapeAcceptOperation, getOperationSource } from '@comunica/utils-query-operation';\n\n/**\n * A comunica Prune Empty Source Operations Optimize Query Operation Actor.\n */\nexport class ActorOptimizeQueryOperationPruneEmptySourceOperations extends ActorOptimizeQueryOperation {\n private readonly useAskIfSupported: boolean;\n\n public constructor(args: IActorOptimizeQueryOperationPruneEmptySourceOperationsArgs) {\n super(args);\n }\n\n public async test(action: IActionOptimizeQueryOperation): Promise<TestResult<IActorTest>> {\n if (getOperationSource(action.operation)) {\n return failTest(`Actor ${this.name} does not work with top-level operation sources.`);\n }\n return passTestVoid();\n }\n\n public async run(action: IActionOptimizeQueryOperation): Promise<IActorOptimizeQueryOperationOutput> {\n const dataFactory: ComunicaDataFactory = action.context.getSafe(KeysInitQuery.dataFactory);\n const algebraFactory = new AlgebraFactory(dataFactory);\n\n let operation = action.operation;\n\n // Collect all operations with source types\n // Only consider unions of patterns or alts of links, since these are created during exhaustive source assignment.\n const collectedOperations: (Algebra.Pattern | Algebra.Link)[] = [];\n algebraUtils.visitOperation(operation, {\n [Algebra.Types.UNION]: { preVisitor: (subOperation) => {\n this.collectMultiOperationInputs(subOperation.input, collectedOperations, Algebra.Types.PATTERN);\n return {};\n } },\n [Algebra.Types.ALT]: { preVisitor: (subOperation) => {\n this.collectMultiOperationInputs(subOperation.input, collectedOperations, Algebra.Types.LINK);\n return { continue: false };\n } },\n [Algebra.Types.SERVICE]: { preVisitor: () => ({ continue: false }) },\n });\n\n // Determine in an async manner whether or not these sources return non-empty results\n const emptyOperations: Set<Algebra.Operation> = new Set();\n await Promise.all(collectedOperations.map(async(collectedOperation) => {\n const checkOperation = collectedOperation.type === Algebra.Types.LINK ?\n algebraFactory.createPattern(dataFactory.variable('s'), collectedOperation.iri, dataFactory.variable('?o')) :\n collectedOperation;\n if (!await this.hasSourceResults(\n algebraFactory,\n getOperationSource(collectedOperation)!,\n checkOperation,\n action.context,\n )) {\n emptyOperations.add(collectedOperation);\n }\n }));\n\n // Only perform next mapping if we have at least one empty operation\n if (emptyOperations.size > 0) {\n this.logDebug(action.context, `Pruning ${emptyOperations.size} source-specific operations`);\n // Rewrite operations by removing the empty children\n operation = algebraUtils.mapOperation(operation, {\n [Algebra.Types.UNION]: { transform: (subOperation, origOp) =>\n this.mapMultiOperation(subOperation, origOp, emptyOperations, children =>\n algebraFactory.createUnion(children)) },\n [Algebra.Types.ALT]: { transform: (subOperation, origOp) =>\n this.mapMultiOperation(subOperation, origOp, emptyOperations, children =>\n algebraFactory.createAlt(children)) },\n\n // Remove operations that have become empty now due to missing variables\n [Algebra.Types.PROJECT]: {\n transform: (subOperation) => {\n // Remove projections that have become empty now due to missing variables\n if (ActorOptimizeQueryOperationPruneEmptySourceOperations.hasEmptyOperation(subOperation)) {\n return algebraFactory.createUnion([]);\n }\n return subOperation;\n },\n },\n [Algebra.Types.LEFT_JOIN]: { transform: (subOperation) => {\n // Remove left joins with empty right operation\n if (ActorOptimizeQueryOperationPruneEmptySourceOperations.hasEmptyOperation(subOperation.input[1])) {\n return subOperation.input[0];\n }\n return subOperation;\n } },\n });\n }\n\n return { operation, context: action.context };\n }\n\n protected static hasEmptyOperation(operation: Algebra.Operation): boolean {\n // If union (or alt) is empty, consider it empty (`Array.every` on an empty array always returns true)\n // But if we find a union with multiple children,\n // *all* of the children must be empty before the full operation is considered empty.\n let emptyOperation = false;\n algebraUtils.visitOperation(operation, {\n [Algebra.Types.UNION]: { preVisitor: (unionOp) => {\n if (unionOp.input.every(subSubOperation => ActorOptimizeQueryOperationPruneEmptySourceOperations\n .hasEmptyOperation(subSubOperation))) {\n emptyOperation = true;\n return { shortcut: true };\n }\n return { continue: false };\n } },\n [Algebra.Types.LEFT_JOIN]: { preVisitor: (leftJoinOp) => {\n // Only recurse into left part of left-join\n if (ActorOptimizeQueryOperationPruneEmptySourceOperations.hasEmptyOperation(leftJoinOp.input[0])) {\n emptyOperation = true;\n return { shortcut: true };\n }\n return { continue: false };\n } },\n [Algebra.Types.ALT]: { preVisitor: (altOp) => {\n if (altOp.input.length === 0) {\n emptyOperation = true;\n return { shortcut: true };\n }\n return { continue: false };\n } },\n });\n return emptyOperation;\n }\n\n protected collectMultiOperationInputs(\n inputs: Algebra.Operation[],\n collectedOperations: (Algebra.Pattern | Algebra.Link)[],\n inputType: (Algebra.Pattern | Algebra.Link)['type'],\n ): void {\n for (const input of inputs) {\n if (getOperationSource(input) && isKnownOperation(input, inputType)) {\n collectedOperations.push(input);\n }\n }\n }\n\n protected mapMultiOperation<O extends Algebra.Union | Algebra.Alt>(\n operationCopy: O,\n origOp: O,\n emptyOperations: Set<Algebra.Operation>,\n multiOperationFactory: (input: O['input']) => Algebra.Operation,\n ): Algebra.Operation {\n // Determine which operations return non-empty results\n const nonEmptyInputs: Algebra.Operation[] = [];\n for (const [ idx, input ] of operationCopy.input.entries()) {\n if (!emptyOperations.has(origOp.input[idx])) {\n nonEmptyInputs.push(input);\n }\n }\n\n // Remove empty operations\n if (nonEmptyInputs.length === operationCopy.input.length) {\n return operationCopy;\n }\n if (nonEmptyInputs.length === 0) {\n return multiOperationFactory([]);\n }\n if (nonEmptyInputs.length === 1) {\n return nonEmptyInputs[0];\n }\n return multiOperationFactory(nonEmptyInputs);\n }\n\n /**\n * Check if the given query operation will produce at least one result in the given source.\n * @param algebraFactory The algebra factory.\n * @param source A query source.\n * @param input A query operation.\n * @param context The query context.\n */\n public async hasSourceResults(\n algebraFactory: AlgebraFactory,\n source: IQuerySourceWrapper,\n input: Algebra.Operation,\n context: IActionContext,\n ): Promise<boolean> {\n const mergedContext = source.context ? context.merge(source.context) : context;\n const wildcardAcceptAllExtensionFunctions = mergedContext.get(KeysInitQuery.extensionFunctionsAlwaysPushdown);\n\n // Traversal contexts should never be considered empty at optimization time.\n if (mergedContext.get(KeysQuerySourceIdentify.traverse)) {\n return true;\n }\n\n // Prefer ASK over COUNT when instructed to, and the source allows it\n if (this.useAskIfSupported) {\n const askOperation = algebraFactory.createAsk(input);\n const askSupported = doesShapeAcceptOperation(\n await source.source.getSelectorShape(context),\n askOperation,\n { wildcardAcceptAllExtensionFunctions },\n );\n if (askSupported) {\n return source.source.queryBoolean(askOperation, mergedContext);\n }\n }\n\n // Fall back to sending the full operation, and extracting the cardinality from metadata\n const bindingsStream = source.source.queryBindings(input, mergedContext);\n const cardinality = await new Promise<QueryResultCardinality>((resolve, reject) => {\n bindingsStream.on('error', reject);\n bindingsStream.getProperty('metadata', (metadata: MetadataBindings) => {\n bindingsStream.destroy();\n resolve(metadata.cardinality);\n });\n });\n\n // If the cardinality is an estimate, such as from a VoID description,\n // verify it using ASK if the source supports it.\n // Since the VoID estimators in Comunica cannot produce false negatives, only positive assignments must be verified.\n if (cardinality.type === 'estimate' && cardinality.value > 0) {\n const askOperation = algebraFactory.createAsk(input);\n const askSupported = doesShapeAcceptOperation(\n await source.source.getSelectorShape(context),\n askOperation,\n { wildcardAcceptAllExtensionFunctions },\n );\n if (askSupported) {\n return source.source.queryBoolean(askOperation, mergedContext);\n }\n }\n\n return cardinality.value > 0;\n }\n}\n\nexport interface IActorOptimizeQueryOperationPruneEmptySourceOperationsArgs extends IActorOptimizeQueryOperationArgs {\n /**\n * If true, ASK queries will be sent to the source instead of COUNT queries to check emptiness for patterns.\n * This will only be done for sources that accept ASK queries.\n * @default {false}\n */\n useAskIfSupported: boolean;\n}\n"]}
package/lib/index.d.ts CHANGED
File without changes
package/lib/index.js CHANGED
File without changes
package/lib/index.js.map CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comunica/actor-optimize-query-operation-prune-empty-source-operations",
3
- "version": "4.4.0",
3
+ "version": "4.4.2-alpha.49.0",
4
4
  "description": "A prune-empty-source-operations optimize-query-operation actor",
5
5
  "lsd:module": true,
6
6
  "license": "MIT",
@@ -41,12 +41,12 @@
41
41
  "build:components": "componentsjs-generator"
42
42
  },
43
43
  "dependencies": {
44
- "@comunica/bus-optimize-query-operation": "^4.4.0",
45
- "@comunica/context-entries": "^4.4.0",
46
- "@comunica/core": "^4.4.0",
47
- "@comunica/types": "^4.4.0",
48
- "@comunica/utils-query-operation": "^4.4.0",
49
- "sparqlalgebrajs": "^4.3.8"
44
+ "@comunica/bus-optimize-query-operation": "4.4.2-alpha.49.0",
45
+ "@comunica/context-entries": "4.4.2-alpha.49.0",
46
+ "@comunica/core": "4.4.2-alpha.49.0",
47
+ "@comunica/types": "4.4.2-alpha.49.0",
48
+ "@comunica/utils-algebra": "4.4.2-alpha.49.0",
49
+ "@comunica/utils-query-operation": "4.4.2-alpha.49.0"
50
50
  },
51
- "gitHead": "27acde56dfe0c1ff1162c1f46818ad752296ca88"
51
+ "gitHead": "ef6f96cfd8faf7c37955bb7e0fe9f6fc6a994bdf"
52
52
  }