public async listIds()

in source/packages/services/command-and-control/src/commands/commands.service.ts [102:224]


    public async listIds(
        tags: Tags,
        exclusiveStart?: CommandListPaginationKey,
        count = this.MAX_LIST_RESULTS
    ): Promise<[string[], CommandListPaginationKey]> {
        logger.debug(
            `commands.service listIds: in: tags:${JSON.stringify(
                tags
            )}, exclusiveStart:${JSON.stringify(exclusiveStart)}, count:${count}`
        );

        if (count) {
            count = Number(count);
        }

        // convert tags map to arrays to make referencing them later easier
        const tagKeys = Object.keys(tags);
        const tagValues = Object.values(tags);
        const tagCount = tagKeys.length;

        // retrieve the first page of results for each tag
        const resultsForTagsFutures: Promise<[string[], CommandListIdsByTagPaginationKey]>[] =
            new Array(tagCount);
        for (let tagIndex = 0; tagIndex < tagCount; tagIndex++) {
            const paginationKey: CommandListIdsByTagPaginationKey = {
                commandId: exclusiveStart?.commandId,
                tagKey: tagKeys[tagIndex],
                tagValue: tagValues[tagIndex],
            };
            resultsForTagsFutures[tagIndex] = this.commandDao.listIds(
                tagKeys[tagIndex],
                tagValues[tagIndex],
                paginationKey,
                count
            );
        }
        const resultsForTags = await Promise.all(resultsForTagsFutures);
        const idsForTags = resultsForTags.map(([ids, _paginationKey]) => ids);

        // if any of the initial results are empty, then we can exit immediately as no common matches
        for (let tagIndex = 0; tagIndex < tagCount; tagIndex++) {
            if ((idsForTags[tagIndex]?.length ?? 0) === 0) {
                return [undefined, undefined];
            }
        }

        // this inline function will populate new pages of command ids for a specific tag
        const getNextPageOfResults = async (tagIndex: number): Promise<boolean> => {
            const paginationKey = resultsForTags[tagIndex]?.[1];
            if (paginationKey === undefined) {
                // no more to process
                return false;
            }
            resultsForTags[tagIndex] = await this.commandDao.listIds(
                tagKeys[tagIndex],
                tagValues[tagIndex],
                paginationKey,
                count
            );
            if ((resultsForTags[tagIndex]?.[0]?.length ?? 0) === 0) {
                // no more to process
                return false;
            } else {
                // store the new page of tags, and reset its pointer
                idsForTags[tagIndex] = resultsForTags[tagIndex]?.[0];
                listPointers[tagIndex] = 0;
                return true;
            }
        };

        // process each list of commandIds per tag, saving where the commandId is found across all tags
        const matched: string[] = [];
        let keepGoing = true;
        const listPointers = new Array(tagCount).fill(0);
        while (keepGoing && matched.length < count) {
            for (let tagIndex = 0; tagIndex < tagCount; tagIndex++) {
                let currentTagCommandId = idsForTags[tagIndex][listPointers[tagIndex]];
                if (currentTagCommandId === undefined) {
                    keepGoing = await getNextPageOfResults(tagIndex);
                    if (!keepGoing) break;
                    currentTagCommandId = idsForTags[tagIndex][listPointers[tagIndex]];
                }
                // if we reached the last tag index, it means we found a match across all tags
                if (tagIndex === tagCount - 1) {
                    // add the matched id to the result
                    matched.push(currentTagCommandId);
                    // increment all the pointers
                    listPointers.forEach((_value, index) => listPointers[index]++);
                } else {
                    // check for matching commandIds between this and the next tag being compared
                    let nextTagCommandId = idsForTags[tagIndex + 1][listPointers[tagIndex + 1]];
                    if (nextTagCommandId === undefined) {
                        keepGoing = await getNextPageOfResults(tagIndex + 1);
                        if (!keepGoing) break;
                        nextTagCommandId = idsForTags[tagIndex + 1][listPointers[tagIndex + 1]];
                    }
                    if (currentTagCommandId === nextTagCommandId) {
                        // commands match, so move onto checking the next tag pair
                        continue;
                    } else if (currentTagCommandId < nextTagCommandId) {
                        // this tag has a lower command id, therefore increment this tags index
                        listPointers[tagIndex]++;
                        break;
                    } else {
                        // the next tag has a lower command id, therefore increment the next tags index
                        listPointers[tagIndex + 1]++;
                        break;
                    }
                }
            }
        }

        let paginationKey: CommandListPaginationKey;
        if (matched.length === count) {
            paginationKey = {
                commandId: matched[count - 1],
            };
        }

        const result: [string[], CommandListPaginationKey] = [matched, paginationKey];
        logger.debug(`commands.service list: exit: result:${JSON.stringify(result)}`);
        return result;
    }