blob: c331bb82cb536e1ae5fccbc5bbf60d2b3bbc5a25 [file]
// Copyright (C) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "icing/query/query-processor.h"
#include <cstdint>
#include <memory>
#include <unordered_set>
#include <utility>
#include <vector>
#include "icing/text_classifier/lib3/utils/base/statusor.h"
#include "icing/absl_ports/canonical_errors.h"
#include "icing/absl_ports/str_cat.h"
#include "icing/feature-flags.h"
#include "icing/index/embed/embedding-index.h"
#include "icing/index/index.h"
#include "icing/index/iterator/doc-hit-info-iterator-all-document-id.h"
#include "icing/index/iterator/doc-hit-info-iterator-and.h"
#include "icing/index/iterator/doc-hit-info-iterator-by-uri.h"
#include "icing/index/iterator/doc-hit-info-iterator-filter.h"
#include "icing/index/iterator/doc-hit-info-iterator-section-restrict.h"
#include "icing/index/iterator/doc-hit-info-iterator.h"
#include "icing/index/numeric/numeric-index.h"
#include "icing/join/join-children-fetcher.h"
#include "icing/proto/logging.pb.h"
#include "icing/proto/search.pb.h"
#include "icing/query/advanced_query_parser/abstract-syntax-tree.h"
#include "icing/query/advanced_query_parser/lexer.h"
#include "icing/query/advanced_query_parser/parser.h"
#include "icing/query/advanced_query_parser/query-visitor.h"
#include "icing/query/query-features.h"
#include "icing/query/query-results.h"
#include "icing/query/query-utils.h"
#include "icing/schema/schema-store.h"
#include "icing/store/document-store.h"
#include "icing/tokenization/language-segmenter.h"
#include "icing/tokenization/tokenizer-factory.h"
#include "icing/tokenization/tokenizer.h"
#include "icing/transform/normalizer.h"
#include "icing/util/clock.h"
#include "icing/util/status-macros.h"
namespace icing {
namespace lib {
libtextclassifier3::StatusOr<std::unique_ptr<QueryProcessor>>
QueryProcessor::Create(Index* index, const NumericIndex<int64_t>* numeric_index,
const EmbeddingIndex* embedding_index,
const LanguageSegmenter* language_segmenter,
const Normalizer* normalizer,
const DocumentStore* document_store,
const SchemaStore* schema_store,
const JoinChildrenFetcher* join_children_fetcher,
const Clock* clock, const FeatureFlags* feature_flags) {
ICING_RETURN_ERROR_IF_NULL(index);
ICING_RETURN_ERROR_IF_NULL(numeric_index);
ICING_RETURN_ERROR_IF_NULL(embedding_index);
ICING_RETURN_ERROR_IF_NULL(language_segmenter);
ICING_RETURN_ERROR_IF_NULL(normalizer);
ICING_RETURN_ERROR_IF_NULL(document_store);
ICING_RETURN_ERROR_IF_NULL(schema_store);
ICING_RETURN_ERROR_IF_NULL(clock);
ICING_RETURN_ERROR_IF_NULL(feature_flags);
return std::unique_ptr<QueryProcessor>(new QueryProcessor(
index, numeric_index, embedding_index, language_segmenter, normalizer,
document_store, schema_store, join_children_fetcher, clock,
feature_flags));
}
QueryProcessor::QueryProcessor(
Index* index, const NumericIndex<int64_t>* numeric_index,
const EmbeddingIndex* embedding_index,
const LanguageSegmenter* language_segmenter, const Normalizer* normalizer,
const DocumentStore* document_store, const SchemaStore* schema_store,
const JoinChildrenFetcher* join_children_fetcher, const Clock* clock,
const FeatureFlags* feature_flags)
: index_(*index),
numeric_index_(*numeric_index),
embedding_index_(*embedding_index),
language_segmenter_(*language_segmenter),
normalizer_(*normalizer),
document_store_(*document_store),
schema_store_(*schema_store),
join_children_fetcher_(join_children_fetcher),
clock_(*clock),
feature_flags_(*feature_flags) {}
libtextclassifier3::StatusOr<QueryResults> QueryProcessor::ParseSearch(
const SearchSpecProto& search_spec,
ScoringSpecProto::RankingStrategy::Code ranking_strategy,
int64_t current_time_ms, QueryStatsProto::SearchStats* search_stats) {
ICING_ASSIGN_OR_RETURN(QueryResults results,
ParseAdvancedQuery(search_spec, ranking_strategy,
current_time_ms, search_stats));
// Check that all new features used in the search have been enabled in the
// SearchSpec.
const std::unordered_set<Feature> enabled_features(
search_spec.enabled_features().begin(),
search_spec.enabled_features().end());
for (const Feature feature : results.features_in_use) {
if (enabled_features.find(feature) == enabled_features.end()) {
return absl_ports::InvalidArgumentError(absl_ports::StrCat(
"Attempted use of unenabled feature ", feature,
". Please make sure that you have explicitly set all advanced query "
"features used in this query as enabled in the SearchSpec."));
}
}
std::vector<std::unique_ptr<DocHitInfoIterator>> iterators;
if (search_spec.document_uri_filters_size() > 0) {
ICING_ASSIGN_OR_RETURN(
std::unique_ptr<DocHitInfoIteratorByUri> uri_iterator,
DocHitInfoIteratorByUri::Create(&document_store_, search_spec));
iterators.push_back(std::move(uri_iterator));
}
if (results.root_iterator != nullptr) {
iterators.push_back(std::move(results.root_iterator));
}
if (iterators.empty()) {
iterators.push_back(std::make_unique<DocHitInfoIteratorAllDocumentId>(
document_store_.last_added_document_id()));
}
results.root_iterator = CreateAndIterator(std::move(iterators));
DocHitInfoIteratorFilter::Options options =
GetFilterOptions(search_spec, document_store_, schema_store_);
results.root_iterator = std::make_unique<DocHitInfoIteratorFilter>(
std::move(results.root_iterator), &document_store_, &schema_store_,
options, current_time_ms);
if (!search_spec.type_property_filters().empty()) {
results.root_iterator =
DocHitInfoIteratorSectionRestrict::ApplyRestrictions(
std::move(results.root_iterator), &document_store_, &schema_store_,
search_spec, current_time_ms);
}
return results;
}
libtextclassifier3::StatusOr<QueryResults> QueryProcessor::ParseAdvancedQuery(
const SearchSpecProto& search_spec,
ScoringSpecProto::RankingStrategy::Code ranking_strategy,
int64_t current_time_ms, QueryStatsProto::SearchStats* search_stats) const {
std::unique_ptr<Timer> lexer_timer = clock_.GetNewTimer();
Lexer lexer(search_spec.query(), Lexer::Language::QUERY);
ICING_ASSIGN_OR_RETURN(std::vector<Lexer::LexerToken> lexer_tokens,
std::move(lexer).ExtractTokens());
if (search_stats != nullptr) {
search_stats->set_query_processor_lexer_extract_token_latency_ms(
lexer_timer->GetElapsedMilliseconds());
}
std::unique_ptr<Timer> parser_timer = clock_.GetNewTimer();
Parser parser = Parser::Create(std::move(lexer_tokens));
ICING_ASSIGN_OR_RETURN(std::unique_ptr<Node> tree_root,
parser.ConsumeQuery());
if (search_stats != nullptr) {
search_stats->set_query_processor_parser_consume_query_latency_ms(
parser_timer->GetElapsedMilliseconds());
}
if (tree_root == nullptr) {
return QueryResults{/*root_iterator=*/nullptr};
}
ICING_ASSIGN_OR_RETURN(
std::unique_ptr<Tokenizer> plain_tokenizer,
tokenizer_factory::CreateIndexingTokenizer(
StringIndexingConfig::TokenizerType::PLAIN, &language_segmenter_));
DocHitInfoIteratorFilter::Options options =
GetFilterOptions(search_spec, document_store_, schema_store_);
bool needs_term_frequency_info =
ranking_strategy == ScoringSpecProto::RankingStrategy::RELEVANCE_SCORE;
std::unique_ptr<Timer> query_visitor_timer = clock_.GetNewTimer();
QueryVisitor query_visitor(
&index_, &numeric_index_, &embedding_index_, &document_store_,
&schema_store_, &normalizer_, plain_tokenizer.get(),
join_children_fetcher_, search_spec, std::move(options),
needs_term_frequency_info, &feature_flags_, current_time_ms);
tree_root->Accept(&query_visitor);
ICING_ASSIGN_OR_RETURN(QueryResults results,
std::move(query_visitor).ConsumeResults());
if (search_stats != nullptr) {
search_stats->set_query_processor_query_visitor_latency_ms(
query_visitor_timer->GetElapsedMilliseconds());
}
return results;
}
} // namespace lib
} // namespace icing