mongo/jstests/with_mongot/common_utils.js

138 lines
6.1 KiB
JavaScript

/**
* Utility functions that are common for both mocked and e2e search tests.
*/
/**
* Checks that the explain object for a sharded search query contains the correct "protocolVersion".
* Checks 'metaPipeline' and 'sortSpec' if it's provided.
* * @param {Object} result the results from running coll.explain().aggregate([[$search: ....],
* ...])
* @param {string} searchType ex. "$search", "$searchMeta"
* @param {Object} metaPipeline
* @param {NumberInt()} protocolVersion To check the value of the protocolVersion, the value
* returned from explain will be wrapped with NumberInt(). The default value to check is 1 since
* mongot currently always returns 1 for its protocolVersion.
* @param {Object} sortSpec
*/
export function verifyShardsPartExplainOutput({
result,
searchType,
metaPipeline = null,
protocolVersion = NumberInt(1),
sortSpec = null,
}) {
// Checks index 0 of 'shardsPart' since $search, $searchMeta need to come first in the pipeline
assert(result.splitPipeline.shardsPart[0][searchType].hasOwnProperty(
"metadataMergeProtocolVersion"));
assert(result.splitPipeline.shardsPart[0][searchType].hasOwnProperty("mergingPipeline"));
assert.eq(
NumberInt(result.splitPipeline.shardsPart[0][searchType].metadataMergeProtocolVersion),
protocolVersion);
if (metaPipeline) {
assert.eq(result.splitPipeline.shardsPart[0][searchType].mergingPipeline, metaPipeline);
}
if (sortSpec) {
assert(result.splitPipeline.shardsPart[0][searchType].hasOwnProperty("sortSpec"));
assert.eq(result.splitPipeline.shardsPart[0][searchType].sortSpec, sortSpec);
}
}
/**
* Helper to create an explain object that getAggPlanStages() can be used on.
*
* @param {Object} unionSubExplain The explain output for the $unionWith sub pipeline. Given an
* unionWithStage, accessed with unionWithStage.$unionWith.pipeline.
*
* @returns explain object that getAggPlanStages() can be called to retrieve stages.
*/
export function prepareUnionWithExplain(unionSubExplain) {
if (unionSubExplain.hasOwnProperty("stages") || unionSubExplain.hasOwnProperty("shards") ||
unionSubExplain.hasOwnProperty("queryPlanner")) {
return unionSubExplain;
}
// In the case that the pipeline doesn't have "stages", "shards", or "queryPlanner", the explain
// output is an array of stages. We return {stages: []} to replicate what a normal explain does.
assert(
Array.isArray(unionSubExplain),
"We expect the input is an array here. If this is not an array, this function needs to be " +
"updated in order to replicate a non-unionWith explain object. " +
tojson(unionSubExplain));
// The first stage of the explain array should be a initial mongot stage.
assert(Object.keys(unionSubExplain[0]).includes("$_internalSearchMongotRemote") ||
Object.keys(unionSubExplain[0]).includes("$searchMeta") ||
Object.keys(unionSubExplain[0]).includes("$vectorSearch"),
"The first stage of the array should be a mongot stage.");
return {stages: unionSubExplain};
}
const mongotStages =
["$_internalSearchMongotRemote", "$searchMeta", "$_internalSearchIdLookup", "$vectorSearch"];
/**
* Validates that the mongot stage from the explain output includes the required execution metrics.
*
* @param {Object} stage The object representing a mongot stage ($_internalSearchMongotRemote,
* $searchMeta, $_internalSearchIdLookup) from explain output.
* @param {string} stageType ex. "$_internalSearchMongotRemote" , "$searchMeta",
* "$_internalSearchIdLookup", "$vectorSearch"
* @param {string} verbosity The verbosity of explain. "nReturned" and "executionTimeMillisEstimate"
* will not be checked for 'queryPlanner' verbosity "
* @param {NumberLong} nReturned The number of documents that should be returned in the
* mongot stage. Optional for "queryPlanner" verbosity.
* @param {Object} explain The explain object that the stage should contain.
* @param {Boolean} isE2E True if this function is being called for an e2e test. Since an e2e test
* uses an actual mongot, we don't know the value of the explain object and cannot check it.
* @param {NumberLong} numFiltered optional, The number of documents filtered out by the idLookup
* stage.
*/
export function validateMongotStageExplainExecutionStats({
stage,
stageType,
verbosity,
nReturned = null,
explain = null,
isE2E = false,
numFiltered = null
}) {
assert(mongotStages.includes(stageType),
"stageType must be a mongot stage found in mongotStages.");
assert(stage[stageType],
"Given stage isn't the expected stage. " + stageType + " is not found.");
const isIdLookup = stageType === "$_internalSearchIdLookup";
if (verbosity != "queryPlanner") {
assert(stage.hasOwnProperty("nReturned"));
assert.eq(nReturned, stage["nReturned"]);
assert(stage.hasOwnProperty("executionTimeMillisEstimate"));
if (isIdLookup) {
const idLookupStage = stage["$_internalSearchIdLookup"];
assert(idLookupStage.hasOwnProperty("totalDocsExamined"), idLookupStage);
assert.eq(idLookupStage["totalDocsExamined"], nReturned, idLookupStage);
assert(idLookupStage.hasOwnProperty("totalKeysExamined"), idLookupStage);
assert.eq(idLookupStage["totalKeysExamined"], nReturned, idLookupStage);
}
}
// Non $_internalSearchIdLookup mongot stages must contain an explain object.
if (!isIdLookup) {
const explainStage = stage[stageType];
assert(explainStage.hasOwnProperty("explain"), explainStage);
// We don't know the actual value of the explain object for e2e tests and can't check it.
if (!isE2E) {
assert(explain, "Explain is null but needs to be provided for initial mongot stage.");
assert.eq(explain, explainStage["explain"]);
if (numFiltered) {
assert(explainStage.hasOwnProperty("numDocsFilteredByIdLookup"), explainStage);
assert.eq(explainStage["numDocsFilteredByIdLookup"], numFiltered);
}
}
}
}