/**
* Copyright (C) 2013-2014 MongoDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/db/exec/text.h"
#include
#include "mongo/db/exec/fetch.h"
#include "mongo/db/exec/filter.h"
#include "mongo/db/exec/index_scan.h"
#include "mongo/db/exec/or.h"
#include "mongo/db/exec/scoped_timer.h"
#include "mongo/db/exec/text_match.h"
#include "mongo/db/exec/text_or.h"
#include "mongo/db/exec/working_set.h"
#include "mongo/db/fts/fts_index_format.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/query/internal_plans.h"
#include "mongo/stdx/memory.h"
namespace mongo {
using std::string;
using std::unique_ptr;
using std::vector;
using stdx::make_unique;
using stdx::make_unique;
using fts::FTSIndexFormat;
using fts::MAX_WEIGHT;
const char* TextStage::kStageType = "TEXT";
TextStage::TextStage(OperationContext* opCtx,
const TextStageParams& params,
WorkingSet* ws,
const MatchExpression* filter)
: PlanStage(kStageType, opCtx), _params(params) {
_children.emplace_back(buildTextTree(opCtx, ws, filter, params.wantTextScore));
_specificStats.indexPrefix = _params.indexPrefix;
_specificStats.indexName = _params.index->indexName();
_specificStats.parsedTextQuery = _params.query.toBSON();
_specificStats.textIndexVersion = _params.index->infoObj()["textIndexVersion"].numberInt();
}
bool TextStage::isEOF() {
return child()->isEOF();
}
PlanStage::StageState TextStage::doWork(WorkingSetID* out) {
if (isEOF()) {
return PlanStage::IS_EOF;
}
return child()->work(out);
}
unique_ptr TextStage::getStats() {
_commonStats.isEOF = isEOF();
unique_ptr ret = make_unique(_commonStats, STAGE_TEXT);
ret->specific = make_unique(_specificStats);
ret->children.emplace_back(child()->getStats());
return ret;
}
const SpecificStats* TextStage::getSpecificStats() const {
return &_specificStats;
}
unique_ptr TextStage::buildTextTree(OperationContext* opCtx,
WorkingSet* ws,
const MatchExpression* filter,
bool wantTextScore) const {
// Get all the index scans for each term in our query.
std::vector> indexScanList;
for (const auto& term : _params.query.getTermsForBounds()) {
IndexScanParams ixparams(opCtx, *_params.index);
ixparams.bounds.startKey = FTSIndexFormat::getIndexKey(
MAX_WEIGHT, term, _params.indexPrefix, _params.spec.getTextIndexVersion());
ixparams.bounds.endKey = FTSIndexFormat::getIndexKey(
0, term, _params.indexPrefix, _params.spec.getTextIndexVersion());
ixparams.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
ixparams.bounds.isSimpleRange = true;
ixparams.direction = -1;
ixparams.shouldDedup = _params.index->isMultikey(opCtx);
indexScanList.push_back(stdx::make_unique(opCtx, ixparams, ws, nullptr));
}
// Build the union of the index scans as a TEXT_OR or an OR stage, depending on whether the
// projection requires the "textScore" $meta field.
std::unique_ptr textMatchStage;
if (wantTextScore) {
// We use a TEXT_OR stage to get the union of the results from the index scans and then
// compute their text scores. This is a blocking operation.
auto textScorer = make_unique(opCtx, _params.spec, ws, filter, _params.index);
textScorer->addChildren(std::move(indexScanList));
textMatchStage = make_unique(
opCtx, std::move(textScorer), _params.query, _params.spec, ws);
} else {
// Because we don't need the text score, we can use a non-blocking OR stage to get the union
// of the index scans.
auto textSearcher = make_unique(opCtx, ws, true, filter);
textSearcher->addChildren(std::move(indexScanList));
// Unlike the TEXT_OR stage, the OR stage does not fetch the documents that it outputs. We
// add our own FETCH stage to satisfy the requirement of the TEXT_MATCH stage that its
// WorkingSetMember inputs have fetched data.
const MatchExpression* emptyFilter = nullptr;
auto fetchStage = make_unique(
opCtx, ws, textSearcher.release(), emptyFilter, _params.index->getCollection());
textMatchStage = make_unique(
opCtx, std::move(fetchStage), _params.query, _params.spec, ws);
}
return textMatchStage;
}
} // namespace mongo