UNPKG

@azure/cosmos

Version:
152 lines 7 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. import { BaseContinuationTokenManager } from "./BaseContinuationTokenManager.js"; import { parseOrderByQueryContinuationToken, serializeOrderByQueryContinuationToken, } from "../../documents/ContinuationToken/OrderByQueryContinuationToken.js"; import { convertRangeMappingToQueryRange } from "../../documents/ContinuationToken/CompositeQueryContinuationToken.js"; /** * Manages continuation tokens for ORDER BY queries using single-range sequential processing. * Uses OrderByQueryContinuationToken for tracking ORDER BY items and skip counts. * @internal */ export class OrderByQueryContinuationTokenManager extends BaseContinuationTokenManager { orderByItemsArray; // OrderBy-specific token state (computed into token on demand) tokenRangeMappings; tokenOrderByItems; tokenSkipCount = 0; tokenDocumentRid = ""; tokenHashedLastResult; constructor(collectionLink, initialContinuationToken) { super(collectionLink, initialContinuationToken); this.orderByItemsArray = []; if (initialContinuationToken) { const parsed = parseOrderByQueryContinuationToken(initialContinuationToken); this.tokenRangeMappings = parsed.rangeMappings; this.tokenOrderByItems = parsed.orderByItems; this.tokenSkipCount = parsed.skipCount; this.tokenDocumentRid = parsed.documentRid; this.tokenHashedLastResult = parsed.hashedLastResult; } } processQuerySpecificResponse(responseResult) { // Clear existing items and add new ones without reassigning the array reference this.orderByItemsArray.length = 0; if (responseResult.orderByItems) { this.orderByItemsArray.push(...responseResult.orderByItems); } } performQuerySpecificDataTrim(_processedRanges, endIndex) { this.sliceOrderByItemsArray(endIndex); } sliceOrderByItemsArray(endIndex) { if (endIndex === 0 || endIndex >= this.orderByItemsArray.length) { // Clear the array without reassigning this.orderByItemsArray.length = 0; } else { // Remove items from 0 to endIndex-1, keeping items from endIndex onwards this.orderByItemsArray.splice(0, endIndex); } } processRangesForPagination(pageSize, isResponseEmpty = false) { // Handle empty response case - preserve previous token state with updated range if (isResponseEmpty && this.tokenRangeMappings) { let rangeProcessingResult; if (this.rangeList.length === 0) { rangeProcessingResult = this.partitionManager.processOrderByRanges(pageSize); } else { rangeProcessingResult = this.partitionManager.processEmptyOrderByRanges(this.rangeList); } const { lastRangeBeforePageLimit } = rangeProcessingResult; if (lastRangeBeforePageLimit) { this.tokenRangeMappings = [convertRangeMappingToQueryRange(lastRangeBeforePageLimit)]; } else { this.clearTokenState(); } return { endIndex: rangeProcessingResult.endIndex, processedRanges: rangeProcessingResult.processedRanges, }; } // Normal processing path - handle non-empty responses const rangeProcessingResult = this.partitionManager.processOrderByRanges(pageSize); const { lastRangeBeforePageLimit } = rangeProcessingResult; // Check if we have a valid range to continue with if (!lastRangeBeforePageLimit) { this.clearTokenState(); return { endIndex: rangeProcessingResult.endIndex, processedRanges: rangeProcessingResult.processedRanges, }; } const queryRange = convertRangeMappingToQueryRange(lastRangeBeforePageLimit); // Extract ORDER BY items from the last item on the page let lastOrderByItems; let documentRid; let skipCount = 0; if (rangeProcessingResult.endIndex > 0 && this.orderByItemsArray.length > 0) { const lastItemIndexOnPage = rangeProcessingResult.endIndex - 1; if (lastItemIndexOnPage < this.orderByItemsArray.length) { lastOrderByItems = this.orderByItemsArray[lastItemIndexOnPage].orderByItems; documentRid = this.orderByItemsArray[lastItemIndexOnPage]._rid; // Calculate skip count: count how many documents in the page have the same RID // This handles JOIN queries where multiple documents can have the same RID skipCount = 0; for (let i = 0; i <= lastItemIndexOnPage; i++) { if (this.orderByItemsArray[i]._rid === documentRid) { skipCount++; } } } } // If we don't have valid ORDER BY items, we cannot create a proper continuation token // This can happen when the response doesn't contain ORDER BY metadata or when there are no results if (!lastOrderByItems || lastOrderByItems.length === 0) { this.clearTokenState(); return { endIndex: rangeProcessingResult.endIndex, processedRanges: rangeProcessingResult.processedRanges, }; } // Update token state fields this.tokenRangeMappings = [queryRange]; this.tokenOrderByItems = lastOrderByItems; this.tokenSkipCount = skipCount; this.tokenDocumentRid = documentRid; this.offset = lastRangeBeforePageLimit.offset; this.limit = lastRangeBeforePageLimit.limit; this.tokenHashedLastResult = lastRangeBeforePageLimit.hashedLastResult; return { endIndex: rangeProcessingResult.endIndex, processedRanges: rangeProcessingResult.processedRanges, }; } getCurrentContinuationToken() { if (!this.tokenRangeMappings || !this.tokenOrderByItems) { return undefined; } return { rid: this.collectionLink, rangeMappings: this.tokenRangeMappings, orderByItems: this.tokenOrderByItems, skipCount: this.tokenSkipCount, documentRid: this.tokenDocumentRid, offset: this.offset, limit: this.limit, hashedLastResult: this.tokenHashedLastResult, }; } getSerializationFunction() { return serializeOrderByQueryContinuationToken; } clearTokenState() { this.tokenRangeMappings = undefined; this.tokenOrderByItems = undefined; this.tokenSkipCount = 0; this.tokenDocumentRid = ""; this.tokenHashedLastResult = undefined; } } //# sourceMappingURL=OrderByQueryContinuationTokenManager.js.map