ravendb
Version:
RavenDB client for Node.js
348 lines (346 loc) • 16.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.QueryOperation = void 0;
const Stopwatch_js_1 = require("../../../Utility/Stopwatch.js");
const LogUtil_js_1 = require("../../../Utility/LogUtil.js");
const QueryCommand_js_1 = require("../../Commands/QueryCommand.js");
const index_js_1 = require("../../../Exceptions/index.js");
const Constants_js_1 = require("../../../Constants.js");
const TypeUtil_js_1 = require("../../../Utility/TypeUtil.js");
const DocumentQuery_js_1 = require("../DocumentQuery.js");
const TimeSeriesAggregationResult_js_1 = require("../../Queries/TimeSeries/TimeSeriesAggregationResult.js");
const TimeSeriesRawResult_js_1 = require("../../Queries/TimeSeries/TimeSeriesRawResult.js");
const TimeSeriesRangeAggregation_js_1 = require("../../Queries/TimeSeries/TimeSeriesRangeAggregation.js");
const ObjectUtil_js_1 = require("../../../Utility/ObjectUtil.js");
const TimeSeriesEntry_js_1 = require("../TimeSeries/TimeSeriesEntry.js");
const StringBuilder_js_1 = require("../../../Utility/StringBuilder.js");
const DateUtil_js_1 = require("../../../Utility/DateUtil.js");
const log = (0, LogUtil_js_1.getLogger)({ module: "QueryOperation" });
const facetResultFields = ["Name", "Values", "RemainingHits", "RemainingTermsCount", "RemainingTerms"];
// noinspection ExceptionCaughtLocallyJS
class QueryOperation {
_session;
_indexName;
_indexQuery;
_metadataOnly;
_indexEntriesOnly;
_isProjectInto;
_currentQueryResults;
_fieldsToFetch;
_sp;
_noTracking;
constructor(session, indexName, indexQuery, fieldsToFetch, disableEntitiesTracking, metadataOnly, indexEntriesOnly, isProjectInto) {
this._session = session;
this._indexName = indexName;
this._indexQuery = indexQuery;
this._fieldsToFetch = fieldsToFetch;
this._noTracking = disableEntitiesTracking;
this._metadataOnly = metadataOnly;
this._indexEntriesOnly = indexEntriesOnly;
this._isProjectInto = isProjectInto;
}
createRequest() {
this._session.incrementRequestCount();
this.logQuery();
return new QueryCommand_js_1.QueryCommand(this._session, this._indexQuery, {
metadataOnly: this._metadataOnly,
indexEntriesOnly: this._indexEntriesOnly
});
}
getCurrentQueryResults() {
return this._currentQueryResults;
}
setResult(queryResult) {
this.ensureIsAcceptableAndSaveResult(queryResult, null);
}
_startTiming() {
this._sp = Stopwatch_js_1.Stopwatch.createStarted();
}
logQuery() {
log.info("Executing query '" + this._indexQuery.query + "'"
+ (this._indexName ? "' on index '" + this._indexName + "'" : "")
+ " in " + this._session.storeIdentifier);
}
/* TDB 4.1
public enterQueryContext(): IDisposable {
this._startTiming();
if (!this._indexQuery.waitForNonStaleResults) {
return null;
}
return this._session.documentStore.disableAggressiveCaching(this._session.databaseName);
}
*/
complete(documentType) {
const queryResult = this._currentQueryResults.createSnapshot();
const result = [];
this._completeInternal(documentType, queryResult, x => result.push(x));
return result;
}
_completeInternal(documentType, queryResult, addToResult) {
if (!this._noTracking) {
this._session.registerIncludes(queryResult.includes);
}
try {
for (const document of queryResult.results) {
if (document[`${Constants_js_1.CONSTANTS.Documents.Metadata.KEY}.${Constants_js_1.CONSTANTS.Documents.Metadata.NESTED_OBJECT_TYPES}`]) {
document[Constants_js_1.CONSTANTS.Documents.Metadata.KEY][Constants_js_1.CONSTANTS.Documents.Metadata.NESTED_OBJECT_TYPES]
= document[`${Constants_js_1.CONSTANTS.Documents.Metadata.KEY}.${Constants_js_1.CONSTANTS.Documents.Metadata.NESTED_OBJECT_TYPES}`];
}
const metadata = document[Constants_js_1.CONSTANTS.Documents.Metadata.KEY];
try {
const idNode = metadata[Constants_js_1.CONSTANTS.Documents.Metadata.ID];
let id = null;
if (idNode && TypeUtil_js_1.TypeUtil.isString(idNode)) {
id = idNode;
}
addToResult(QueryOperation.deserialize(id, document, metadata, this._fieldsToFetch, this._noTracking, this._session, documentType, this._isProjectInto, queryResult.timeSeriesFields || []));
}
catch (e) {
if (Object.keys(document).length !== facetResultFields.length) {
throw e;
}
for (const prop of facetResultFields) {
if (!(prop in document)) {
throw e;
}
}
(0, index_js_1.throwError)("InvalidArgumentException", "Raw query with aggregation by facet should be called by executeAggregation method.");
}
}
}
catch (err) {
log.warn(err, "Unable to read query result JSON.");
(0, index_js_1.throwError)("RavenException", "Unable to read json.", err);
}
if (!this._noTracking) {
this._session.registerMissingIncludes(queryResult.results, queryResult.includes, queryResult.includedPaths);
if (queryResult.counterIncludes) {
this._session.registerCounters(queryResult.counterIncludes, queryResult.includedCounterNames);
}
if (queryResult.timeSeriesIncludes) {
this._session.registerTimeSeries(queryResult.timeSeriesIncludes);
}
if (queryResult.compareExchangeValueIncludes) {
const clusterSession = this._session.clusterSession;
clusterSession.registerCompareExchangeIncludes(queryResult.compareExchangeValueIncludes, false);
}
if (queryResult.revisionIncludes) {
this._session.registerRevisionIncludes(queryResult.revisionIncludes);
}
}
}
static deserialize(id, document, metadata, fieldsToFetch, disableEntitiesTracking, session, clazz, isProjectInto, timeSeriesFields) {
const { conventions } = session;
const projection = metadata["@projection"];
if (TypeUtil_js_1.TypeUtil.isNullOrUndefined(projection) || projection === false) {
const entityType = conventions.getJsTypeByDocumentType(clazz);
return session.trackEntity(entityType, id, document, metadata, disableEntitiesTracking);
}
const singleField = fieldsToFetch
&& fieldsToFetch.projections
&& (!clazz || clazz === TimeSeriesAggregationResult_js_1.TimeSeriesAggregationResult || clazz === TimeSeriesRawResult_js_1.TimeSeriesRawResult)
&& (fieldsToFetch.projections.length === 1 || (fieldsToFetch.projections.includes(DocumentQuery_js_1.NESTED_OBJECT_TYPES_PROJECTION_FIELD) && fieldsToFetch.projections.length === 2));
// return primitives only if type was not passed at all AND fields count is 1
// if type was passed then use that even if it's only 1 field
if (singleField) {
// we only select a single field
let projectionField = fieldsToFetch.projections.find(x => x !== DocumentQuery_js_1.NESTED_OBJECT_TYPES_PROJECTION_FIELD);
if (fieldsToFetch.sourceAlias) {
if (projectionField.startsWith(fieldsToFetch.sourceAlias)) {
// remove source-alias from projection name
projectionField = projectionField.substring(fieldsToFetch.sourceAlias.length + 1);
}
if (projectionField.startsWith("'")) {
projectionField = projectionField.substring(1, projectionField.length - 1);
}
}
if (conventions.serverToLocalFieldNameConverter) {
projectionField = conventions.serverToLocalFieldNameConverter(projectionField);
}
const jsonNode = document[projectionField];
if (TypeUtil_js_1.TypeUtil.isNullOrUndefined(jsonNode)) {
return null;
}
if (TypeUtil_js_1.TypeUtil.isPrimitive(jsonNode)) {
return jsonNode || null;
}
const isTimeSeriesField = fieldsToFetch.projections[0].startsWith(Constants_js_1.TIME_SERIES.QUERY_FUNCTION);
if (!isProjectInto || isTimeSeriesField) {
if (isTimeSeriesField || fieldsToFetch.fieldsToFetch[0] === fieldsToFetch.projections[0]) {
if (TypeUtil_js_1.TypeUtil.isObject(jsonNode)) { // extraction from original type
document = jsonNode;
}
}
}
}
const projType = conventions.getJsTypeByDocumentType(clazz);
const documentRef = {
value: document
};
session.onBeforeConversionToEntityInvoke(id, clazz, documentRef);
document = documentRef.value;
const raw = conventions.objectMapper.fromObjectLiteral(document);
const result = projType ? new (Function.prototype.bind.apply(projType)) : {};
const mapper = conventions.objectMapper;
if (result instanceof TimeSeriesAggregationResult_js_1.TimeSeriesAggregationResult) {
Object.assign(result, QueryOperation._reviveTimeSeriesAggregationResult(raw));
}
else if (result instanceof TimeSeriesRawResult_js_1.TimeSeriesRawResult) {
Object.assign(result, QueryOperation._reviveTimeSeriesRawResult(raw));
}
else {
if (fieldsToFetch && fieldsToFetch.projections && fieldsToFetch.projections.length) {
const keys = conventions.serverToLocalFieldNameConverter
? fieldsToFetch.projections.map(x => conventions.serverToLocalFieldNameConverter(x))
: fieldsToFetch.projections;
const nestedTypes = raw[DocumentQuery_js_1.NESTED_OBJECT_TYPES_PROJECTION_FIELD];
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const mapped = mapper.fromObjectLiteral(raw, {
typeName: "object",
nestedTypes
});
result[key] = mapped[key];
}
}
else {
if (conventions.serverToLocalFieldNameConverter) {
const options = {
recursive: true,
arrayRecursive: true,
ignorePaths: [
Constants_js_1.CONSTANTS.Documents.Metadata.IGNORE_CASE_TRANSFORM_REGEX,
//
],
defaultTransform: conventions.serverToLocalFieldNameConverter
};
Object.assign(result, ObjectUtil_js_1.ObjectUtil.transformObjectKeys(raw, options));
}
else {
Object.assign(result, raw);
}
}
}
if (timeSeriesFields && timeSeriesFields.length) {
for (const timeSeriesField of timeSeriesFields) {
const value = document[timeSeriesField];
if (value) {
const newValue = QueryOperation._detectTimeSeriesResultType(value);
if (newValue instanceof TimeSeriesAggregationResult_js_1.TimeSeriesAggregationResult) {
Object.assign(newValue, QueryOperation._reviveTimeSeriesAggregationResult(value));
}
else if (newValue instanceof TimeSeriesRawResult_js_1.TimeSeriesRawResult) {
Object.assign(newValue, QueryOperation._reviveTimeSeriesRawResult(value));
}
result[timeSeriesField] = newValue;
}
}
}
session.onAfterConversionToEntityInvoke(id, document, result);
return result;
}
static _detectTimeSeriesResultType(raw) {
const results = raw.Results || [];
// duck typing
if (results.length && results[0].From && results[0].To) {
return new TimeSeriesAggregationResult_js_1.TimeSeriesAggregationResult();
}
return new TimeSeriesRawResult_js_1.TimeSeriesRawResult();
}
static _reviveTimeSeriesAggregationResult(raw) {
const rawLower = ObjectUtil_js_1.ObjectUtil.transformObjectKeys(raw, { defaultTransform: ObjectUtil_js_1.ObjectUtil.camel });
const { results, ...otherProps } = rawLower;
const mappedResults = results.map(r => {
const { from, to, ...otherRangeProps } = r;
const overrides = {
from: DateUtil_js_1.DateUtil.utc.parse(from),
to: DateUtil_js_1.DateUtil.utc.parse(to)
};
return Object.assign(new TimeSeriesRangeAggregation_js_1.TimeSeriesRangeAggregation(), otherRangeProps, overrides);
});
return {
...otherProps,
results: mappedResults
};
}
static _reviveTimeSeriesRawResult(raw) {
const rawLower = ObjectUtil_js_1.ObjectUtil.transformObjectKeys(raw, { defaultTransform: ObjectUtil_js_1.ObjectUtil.camel });
const { results, ...otherProps } = rawLower;
const mappedResults = results.map(r => {
const { timestamp, ...otherRangeProps } = r;
const overrides = {
timestamp: DateUtil_js_1.DateUtil.utc.parse(timestamp),
};
return Object.assign(new TimeSeriesEntry_js_1.TimeSeriesEntry(), otherRangeProps, overrides);
});
return {
...otherProps,
results: mappedResults
};
}
get noTracking() {
return this._noTracking;
}
set noTracking(value) {
this._noTracking = value;
}
ensureIsAcceptableAndSaveResult(result, duration) {
if (TypeUtil_js_1.TypeUtil.isNullOrUndefined(duration)) {
if (this._sp) {
duration = this._sp.elapsed;
}
else {
duration = null;
}
}
if (!result) {
(0, index_js_1.throwError)("IndexDoesNotExistException", `Could not find index ${this._indexName}.`);
}
QueryOperation.ensureIsAcceptable(result, this._indexQuery.waitForNonStaleResults, duration, this._session);
this._saveQueryResult(result);
}
_saveQueryResult(result) {
this._currentQueryResults = result;
// logging
const isStale = result.isStale ? " stale " : " ";
const parameters = new StringBuilder_js_1.StringBuilder();
if (this._indexQuery.queryParameters
&& this._indexQuery.queryParameters.length) {
parameters.append("(parameters: ");
let first = true;
const queryParameters = this._indexQuery.queryParameters;
for (const parameterKey of Object.keys(queryParameters)) {
const parameterValue = queryParameters[parameterKey];
if (!first) {
parameters.append(", ");
}
parameters.append(parameterKey)
.append(" = ")
.append(parameterValue);
first = false;
}
parameters.append(") ");
}
log.info("Query '"
+ this._indexQuery.query + "' "
+ parameters.toString()
+ "returned "
+ result.results.length + isStale + "results (total index results: " + result.totalResults + ")");
// end logging
}
static ensureIsAcceptable(result, waitForNonStaleResults, duration, session) {
if (duration instanceof Stopwatch_js_1.Stopwatch) {
duration.stop();
return QueryOperation.ensureIsAcceptable(result, waitForNonStaleResults, duration.elapsed, session);
}
if (waitForNonStaleResults && result.isStale) {
const msg = "Waited for " + duration.toString() + " for the query to return non stale result.";
(0, index_js_1.throwError)("TimeoutException", msg);
}
}
get indexQuery() {
return this._indexQuery;
}
}
exports.QueryOperation = QueryOperation;
//# sourceMappingURL=QueryOperation.js.map