@azure/cosmos
Version:
Microsoft Azure Cosmos DB Service Node.js SDK for NOSQL API
194 lines (193 loc) • 7.36 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var BaseContinuationTokenManager_exports = {};
__export(BaseContinuationTokenManager_exports, {
BaseContinuationTokenManager: () => BaseContinuationTokenManager
});
module.exports = __toCommonJS(BaseContinuationTokenManager_exports);
var import_CompositeQueryContinuationToken = require("../../documents/ContinuationToken/CompositeQueryContinuationToken.js");
var import_PartitionRangeManager = require("../PartitionRangeManager.js");
class BaseContinuationTokenManager {
ranges = [];
partitionRangeManager = new import_PartitionRangeManager.PartitionRangeManager();
collectionLink;
offset;
limit;
constructor(collectionLink, initialContinuationToken) {
this.collectionLink = collectionLink;
if (initialContinuationToken) {
const token = (0, import_CompositeQueryContinuationToken.parseBaseContinuationToken)(initialContinuationToken);
if (token?.rangeMappings) {
this.ranges.push(...token.rangeMappings);
}
this.offset = token?.offset;
this.limit = token?.limit;
}
}
/**
* Provides controlled access to partition range manager for subclasses.
* This is the only protected access point needed.
*/
get partitionManager() {
return this.partitionRangeManager;
}
/**
* Provides controlled access to ranges for subclasses.
* Made protected to allow subclass range management.
*/
get rangeList() {
return this.ranges;
}
/**
* Processes query results and generates continuation tokens for pagination.
* Handles response data processing, range management, and token generation.
*
* @param pageSize - Maximum number of items to return in this page
* @param isResponseEmpty - Whether the current response contains no data
* @param responseResult - Optional response data containing partition mappings and query-specific data
* @returns Object containing the end index for slicing results and optional continuation token for next page
*/
paginateResults(pageSize, isResponseEmpty, responseResult) {
if (responseResult) {
this.processResponseResult(responseResult);
}
this.removeExhaustedRangesFromRanges();
const result = this.processRangesForPagination(pageSize, isResponseEmpty);
const tokenString = this.generateContinuationTokenString();
this.trimProcessedData(result.processedRanges, result.endIndex);
return {
endIndex: result.endIndex,
continuationToken: tokenString
};
}
/**
* Generates continuation token string using the appropriate serialization function.
* This provides a common implementation that delegates to query-specific logic.
*/
generateContinuationTokenString() {
const token = this.getCurrentContinuationToken();
if (!token) {
return void 0;
}
const serializeFunction = this.getSerializationFunction();
return serializeFunction(token);
}
/**
* Cleans up processed data after a page has been returned.
* Handles both common and query-specific cleanup.
*/
trimProcessedData(processedRanges, endIndex) {
processedRanges.forEach((rangeId) => {
this.partitionRangeManager.removePartitionRangeMapping(rangeId);
});
this.performQuerySpecificDataTrim(processedRanges, endIndex);
}
addPartitionKeyRangeMap(partitionKeyRangeMap) {
this.partitionRangeManager.addPartitionKeyRangeMap(partitionKeyRangeMap);
}
/**
* Processes the entire response result and updates the continuation token manager state.
* This encapsulates all response handling logic in one place.
*/
processResponseResult(responseResult) {
if (responseResult.partitionKeyRangeMap) {
this.addPartitionKeyRangeMap(responseResult.partitionKeyRangeMap);
}
if (responseResult.updatedContinuationRanges) {
this.handlePartitionRangeChanges(responseResult.updatedContinuationRanges);
}
this.processQuerySpecificResponse(responseResult);
}
/**
* Compacts the ranges array in place by keeping only items that satisfy the predicate.
* This preserves the same array reference while removing unwanted entries.
*/
compactRangesInPlace(shouldKeep) {
let writeIndex = 0;
for (let i = 0; i < this.ranges.length; i++) {
const mapping = this.ranges[i];
if (!shouldKeep(mapping)) {
continue;
}
this.ranges[writeIndex++] = mapping;
}
this.ranges.length = writeIndex;
}
removeExhaustedRangesFromRanges() {
if (!this.ranges || !Array.isArray(this.ranges)) {
return;
}
this.compactRangesInPlace(
(mapping) => !!mapping && !(0, import_PartitionRangeManager.isPartitionExhausted)(mapping.continuationToken)
);
}
handlePartitionRangeChanges(updatedContinuationRanges) {
if (updatedContinuationRanges && Object.keys(updatedContinuationRanges).length === 0) {
return;
}
Object.entries(updatedContinuationRanges).forEach(([rangeKey, rangeChange]) => {
this.processRangeChange(rangeKey, rangeChange);
});
}
processRangeChange(_rangeKey, rangeChange) {
const { oldRange, newRanges, continuationToken } = rangeChange;
if (newRanges.length === 1) {
this.handleRangeMerge(oldRange, newRanges[0], continuationToken);
} else {
this.handleRangeSplit(oldRange, newRanges, continuationToken);
}
}
handleRangeMerge(oldRange, newRange, continuationToken) {
const existingMappingIndex = this.ranges.findIndex(
(mapping) => mapping.queryRange.min === oldRange.min && mapping.queryRange.max === oldRange.max
);
if (existingMappingIndex < 0) {
return;
}
const existingMapping = this.ranges[existingMappingIndex];
const updatedQueryRange = {
min: newRange.min,
max: newRange.max
};
existingMapping.queryRange = updatedQueryRange;
existingMapping.continuationToken = continuationToken;
}
handleRangeSplit(oldRange, newRanges, continuationToken) {
this.compactRangesInPlace(
(mapping) => !(mapping.queryRange.min === oldRange.min && mapping.queryRange.max === oldRange.max)
);
newRanges.forEach((newRange) => {
this.createNewRangeMapping(newRange, continuationToken);
});
}
createNewRangeMapping(partitionKeyRange, continuationToken) {
const queryRange = {
min: partitionKeyRange.min,
max: partitionKeyRange.max
};
const newRangeWithToken = {
queryRange,
continuationToken
};
this.ranges.push(newRangeWithToken);
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BaseContinuationTokenManager
});