@comunica/actor-abstract-path
Version:
An abstract actor for handling mediatypes
135 lines • 5.57 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PathVariableObjectIterator = void 0;
const utils_query_operation_1 = require("@comunica/utils-query-operation");
const asynciterator_1 = require("asynciterator");
const rdf_string_1 = require("rdf-string");
/**
* An iterator that implements the multi-length property path operation (* and +)
* for a fixed subject and predicate, and a variable object.
*/
class PathVariableObjectIterator extends asynciterator_1.BufferedIterator {
constructor(algebraFactory, subject, predicate, graph, context, mediatorQueryOperation, emitFirstSubject, maxRunningOperations = 16) {
super({ autoStart: false });
this.algebraFactory = algebraFactory;
this.subject = subject;
this.predicate = predicate;
this.graph = graph;
this.context = context;
this.mediatorQueryOperation = mediatorQueryOperation;
this.maxRunningOperations = maxRunningOperations;
this.termHashes = new Map();
this.runningOperations = [];
this.pendingOperations = [];
this.started = false;
// Push the subject as starting point
this._push(this.subject, emitFirstSubject);
}
getProperty(propertyName, callback) {
// Kickstart iterator when metadata is requested
if (!this.started && propertyName === 'metadata') {
this.startNextOperation(false)
.catch(error => this.emit('error', error));
}
return super.getProperty(propertyName, callback);
}
_end(destroy) {
// Close all running iterators
for (const it of this.runningOperations) {
it.destroy();
}
super._end(destroy);
}
_push(item, pushAsResult = true) {
let termString;
if (pushAsResult) {
// Don't push if this subject was already found
termString = (0, rdf_string_1.termToString)(item);
if (this.termHashes.has(termString)) {
return false;
}
}
// Add a pending path operation for this item
const variable = this.algebraFactory.dataFactory.variable('b');
this.pendingOperations.push({
variable,
operation: this.algebraFactory.createPath(item, this.predicate, variable, this.graph),
});
// Otherwise, push the subject
if (termString) {
this.termHashes.set(termString, item);
super._push(item);
}
return true;
}
async startNextOperation(fillBuffer) {
this.started = true;
const pendingOperation = this.pendingOperations.pop();
const results = (0, utils_query_operation_1.getSafeBindings)(await this.mediatorQueryOperation.mediate({ operation: pendingOperation.operation, context: this.context }));
const runningOperation = results.bindingsStream.map(bindings => bindings.get(pendingOperation.variable));
if (!runningOperation.done) {
this.runningOperations.push(runningOperation);
runningOperation.on('error', error => this.destroy(error));
runningOperation.on('readable', () => {
if (fillBuffer) {
this._fillBufferAsync();
}
this.readable = true;
});
runningOperation.on('end', () => {
this.runningOperations.splice(this.runningOperations.indexOf(runningOperation), 1);
if (fillBuffer) {
this._fillBufferAsync();
}
this.readable = true;
});
}
if (!this.getProperty('metadata')) {
this.setProperty('metadata', results.metadata);
}
}
_read(count, done) {
// eslint-disable-next-line ts/no-this-alias
const self = this;
(async function () {
// Open as many operations as possible
while (self.runningOperations.length < self.maxRunningOperations) {
if (self.pendingOperations.length === 0) {
break;
}
await self.startNextOperation(true);
}
// Try to read `count` items (based on UnionIterator)
let lastCount = 0;
let item;
let pushSucceeded = true;
// eslint-disable-next-line no-cond-assign
while (!pushSucceeded || lastCount !== (lastCount = count)) {
pushSucceeded = true;
// Prioritize the operations that have been added first
for (let i = 0; i < self.runningOperations.length && count > 0; i++) {
// eslint-disable-next-line no-cond-assign
if ((item = self.runningOperations[i].read()) !== null) {
if (self._push(item)) {
count--;
}
else {
pushSucceeded = false;
}
}
}
}
// Close if everything has been read
self.closeIfNeeded();
})().then(() => {
done();
}, error => this.destroy(error));
}
closeIfNeeded() {
if (this.runningOperations.length === 0 && this.pendingOperations.length === 0) {
this.close();
}
}
}
exports.PathVariableObjectIterator = PathVariableObjectIterator;
//# sourceMappingURL=PathVariableObjectIterator.js.map