[SERVER-36794] Non-blocking $text plans with just one search term do not need an OR stage Created: 21/Aug/18  Updated: 29/Oct/23  Resolved: 16/Feb/21

Status: Closed
Project: Core Server
Component/s: Querying, Text Search
Affects Version/s: None
Fix Version/s: 4.9.0

Type: Improvement Priority: Major - P3
Reporter: David Storch Assignee: Hartek Sabharwal
Resolution: Fixed Votes: 2
Labels: neweng, storch
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Related
is related to SERVER-24375 Deduping in OR, SORT_MERGE, and IXSCA... Backlog
is related to SERVER-26534 Text search uses excessive memory Backlog
is related to SERVER-36087 Executing $text statements in conjunc... Closed
is related to SERVER-26833 Permit non-blocking $text queries whe... Closed
Backwards Compatibility: Fully Compatible
Sprint: Query Optimization 2021-02-22
Participants:

 Description   

InĀ SERVER-26534 we made an improvement to make $text queries which do not require text score metadata use the OR stage. This improves $text performance by making the plan non-blocking, and also reduces the memory footprint of the plan by avoiding the special TEXT_OR stage.

The $text execution code generates an OR plan even when it has just one child. This typically happens when the search text consists of a single token:

MongoDB Enterprise > db.c.find({$text: {$search: "foo"}}).explain("queryPlanner").queryPlanner.winningPlan
{
	"stage" : "TEXT",
	"indexPrefix" : {
 
	},
	"indexName" : "words_text",
	"parsedTextQuery" : {
		"terms" : [
			"foo"
		],
		"negatedTerms" : [ ],
		"phrases" : [ ],
		"negatedPhrases" : [ ]
	},
	"textIndexVersion" : 3,
	"inputStage" : {
		"stage" : "TEXT_MATCH",
		"inputStage" : {
			"stage" : "FETCH",
			"inputStage" : {
				"stage" : "OR", // Note that this is an OR of just one child.
				"inputStage" : {
					"stage" : "IXSCAN",
					"keyPattern" : {
						"_fts" : "text",
						"_ftsx" : 1
					},
					"indexName" : "words_text",
					"isMultiKey" : true,
					"isUnique" : false,
					"isSparse" : false,
					"isPartial" : false,
					"indexVersion" : 2,
					"direction" : "backward",
					"indexBounds" : {
 
					}
				}
			}
		}
	}
}

As an optimization, we could eliminate the OR stage when it would have just one child. One major advantage of this change is that it would reduce the memory footprint of the query. The OR stage is known to consume too much memory in some cases, as it keeps a set of RecordIds for deduping (see SERVER-24375).



 Comments   
Comment by Githook User [ 16/Feb/21 ]

Author:

{'name': 'Hartek Sabharwal', 'email': 'hartek.sabharwal@mongodb.com', 'username': 'tectonic8'}

Message: SERVER-36794 Non-blocking plans with just one search term do not need an OR stage
Branch: master
https://github.com/mongodb/mongo/commit/2eb1fbcfc5a10cd777f7e6883b2aeb971f1333ac

Comment by David Storch [ 21/Aug/18 ]

kyle.suarez, because there's no $or match expression here. This is an optimization which we would make in the code which constructs a special execution plan for $text, specifically TextStage::buildTextTree(). The main motivation here is mitigating cases of memory consumption due to OR's deduping (e.g. SERVER-36087).

Comment by Kyle Suarez [ 21/Aug/18 ]

Why isn't this case covered by ListOfMatchExpression::getOptimizer()?

expression_tree.cpp

142
        if (children.size() == 1) {
143
            if ((matchType == AND || matchType == OR || matchType == INTERNAL_SCHEMA_XOR)) {
144
                // Simplify AND/OR/XOR with exactly one operand to an expression consisting of just
145
                // that operand.
146
                MatchExpression* simplifiedExpression = children.front();
147
                children.clear();
148
                return std::unique_ptr<MatchExpression>(simplifiedExpression);
149
            } else if (matchType == NOR) {
150
                // Simplify NOR of exactly one operand to NOT of that operand.
151
                auto simplifiedExpression = stdx::make_unique<NotMatchExpression>(children.front());
152
                children.clear();
153
                return std::move(simplifiedExpression);
154
            }
155
        }

Generated at Thu Feb 08 04:44:06 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.