xref: /aosp_15_r20/external/icing/icing/query/advanced_query_parser/query-visitor.cc (revision 8b6cd535a057e39b3b86660c4aa06c99747c2136)
1 // Copyright (C) 2022 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "icing/query/advanced_query_parser/query-visitor.h"
16 
17 #include <algorithm>
18 #include <cstdint>
19 #include <iterator>
20 #include <limits>
21 #include <memory>
22 #include <set>
23 #include <string>
24 #include <string_view>
25 #include <unordered_map>
26 #include <unordered_set>
27 #include <utility>
28 #include <vector>
29 
30 #include "icing/text_classifier/lib3/utils/base/status.h"
31 #include "icing/text_classifier/lib3/utils/base/statusor.h"
32 #include "icing/absl_ports/annotate.h"
33 #include "icing/absl_ports/canonical_errors.h"
34 #include "icing/absl_ports/str_cat.h"
35 #include "icing/absl_ports/str_join.h"
36 #include "icing/index/embed/doc-hit-info-iterator-embedding.h"
37 #include "icing/index/embed/embedding-query-results.h"
38 #include "icing/index/iterator/doc-hit-info-iterator-all-document-id.h"
39 #include "icing/index/iterator/doc-hit-info-iterator-and.h"
40 #include "icing/index/iterator/doc-hit-info-iterator-filter.h"
41 #include "icing/index/iterator/doc-hit-info-iterator-match-score-expression.h"
42 #include "icing/index/iterator/doc-hit-info-iterator-none.h"
43 #include "icing/index/iterator/doc-hit-info-iterator-not.h"
44 #include "icing/index/iterator/doc-hit-info-iterator-or.h"
45 #include "icing/index/iterator/doc-hit-info-iterator-property-in-document.h"
46 #include "icing/index/iterator/doc-hit-info-iterator-property-in-schema.h"
47 #include "icing/index/iterator/doc-hit-info-iterator-section-restrict.h"
48 #include "icing/index/iterator/doc-hit-info-iterator.h"
49 #include "icing/index/property-existence-indexing-handler.h"
50 #include "icing/query/advanced_query_parser/abstract-syntax-tree.h"
51 #include "icing/query/advanced_query_parser/function.h"
52 #include "icing/query/advanced_query_parser/lexer.h"
53 #include "icing/query/advanced_query_parser/param.h"
54 #include "icing/query/advanced_query_parser/parser.h"
55 #include "icing/query/advanced_query_parser/pending-value.h"
56 #include "icing/query/advanced_query_parser/util/string-util.h"
57 #include "icing/query/query-features.h"
58 #include "icing/query/query-results.h"
59 #include "icing/schema/property-util.h"
60 #include "icing/schema/section.h"
61 #include "icing/scoring/advanced_scoring/score-expression-util.h"
62 #include "icing/scoring/advanced_scoring/score-expression.h"
63 #include "icing/tokenization/token.h"
64 #include "icing/tokenization/tokenizer.h"
65 #include "icing/transform/normalizer.h"
66 #include "icing/util/embedding-util.h"
67 #include "icing/util/status-macros.h"
68 
69 namespace icing {
70 namespace lib {
71 
72 namespace {
73 
74 struct CreateList {
operator ()icing::lib::__anon413c4ecb0111::CreateList75   libtextclassifier3::StatusOr<PendingValue> operator()(
76       std::vector<PendingValue>&& args) const {
77     std::vector<std::string> values;
78     values.reserve(args.size());
79     for (PendingValue& arg : args) {
80       QueryTerm string_val = std::move(arg).string_val().ValueOrDie();
81       values.push_back(std::move(string_val.term));
82     }
83     return PendingValue(std::move(values));
84   }
85 };
86 
IsNumericComparator(std::string_view operator_text)87 bool IsNumericComparator(std::string_view operator_text) {
88   if (operator_text.length() < 1 || operator_text.length() > 2) {
89     return false;
90   }
91   // TODO(tjbarron) decide how/if to support !=
92   return operator_text == "<" || operator_text == ">" ||
93          operator_text == "==" || operator_text == "<=" ||
94          operator_text == ">=";
95 }
96 
IsSupportedNaryOperator(std::string_view operator_text)97 bool IsSupportedNaryOperator(std::string_view operator_text) {
98   return IsNumericComparator(operator_text) || operator_text == "AND" ||
99          operator_text == "OR" || operator_text == ":";
100 }
101 
102 struct Int64Range {
103   int64_t low;
104   int64_t high;
105 };
106 
GetInt64Range(std::string_view operator_text,int64_t int_value)107 libtextclassifier3::StatusOr<Int64Range> GetInt64Range(
108     std::string_view operator_text, int64_t int_value) {
109   Int64Range range = {std::numeric_limits<int64_t>::min(),
110                       std::numeric_limits<int64_t>::max()};
111   if (operator_text == "<") {
112     if (int_value == std::numeric_limits<int64_t>::min()) {
113       return absl_ports::InvalidArgumentError(
114           "Cannot specify < INT64_MIN in query expression.");
115     }
116     range.high = int_value - 1;
117   } else if (operator_text == "<=") {
118     range.high = int_value;
119   } else if (operator_text == "==") {
120     range.high = int_value;
121     range.low = int_value;
122   } else if (operator_text == ">=") {
123     range.low = int_value;
124   } else if (operator_text == ">") {
125     if (int_value == std::numeric_limits<int64_t>::max()) {
126       return absl_ports::InvalidArgumentError(
127           "Cannot specify > INT64_MAX in query expression.");
128     }
129     range.low = int_value + 1;
130   }
131   return range;
132 }
133 
134 }  // namespace
135 
AddValidRestricts(std::set<std::string> new_restricts)136 void QueryVisitor::PendingPropertyRestricts::AddValidRestricts(
137     std::set<std::string> new_restricts) {
138   if (!has_active_property_restricts()) {
139     pending_property_restricts_.push_back(std::move(new_restricts));
140     return;
141   }
142 
143   // There is an active property restrict already in effect. To determine the
144   // updated active property restrict being applied at this level, we need to
145   // calculate the intersection of new_restricts and
146   // active_property_restricts.
147   const std::set<std::string>& active_restricts = active_property_restricts();
148   auto active_restricts_itr = active_restricts.begin();
149   for (auto new_restricts_itr = new_restricts.begin();
150        new_restricts_itr != new_restricts.end();) {
151     while (active_restricts_itr != active_restricts.end() &&
152            *active_restricts_itr < *new_restricts_itr) {
153       // new_restricts_itr is behind active_restricts_itr.
154       ++active_restricts_itr;
155     }
156     if (active_restricts_itr == active_restricts.end()) {
157       // There's nothing left in active restricts. Everything at
158       // new_restricts_itr and beyond should be removed
159       new_restricts_itr =
160           new_restricts.erase(new_restricts_itr, new_restricts.end());
161     } else if (*active_restricts_itr > *new_restricts_itr) {
162       // new_restricts_itr points to elements not present in
163       // active_restricts_itr
164       new_restricts_itr = new_restricts.erase(new_restricts_itr);
165     } else {
166       // the element that new_restricts_itr points to is present in
167       // active_restricts_itr.
168       ++new_restricts_itr;
169     }
170   }
171   pending_property_restricts_.push_back(std::move(new_restricts));
172 }
173 
174 libtextclassifier3::StatusOr<std::unique_ptr<DocHitInfoIterator>>
CreateTermIterator(const QueryTerm & query_term)175 QueryVisitor::CreateTermIterator(const QueryTerm& query_term) {
176   if (query_term.is_prefix_val) {
177     // '*' prefix operator was added in list filters
178     features_.insert(kListFilterQueryLanguageFeature);
179   }
180   TermMatchType::Code match_type = GetTermMatchType(query_term.is_prefix_val);
181   int unnormalized_term_start =
182       query_term.raw_term.data() - search_spec_.query().c_str();
183   if (!processing_not_) {
184     // 1. Add term to property_query_terms_map
185     if (pending_property_restricts_.has_active_property_restricts()) {
186       for (const std::string& property_restrict :
187            pending_property_restricts_.active_property_restricts()) {
188         property_query_terms_map_[property_restrict].insert(query_term.term);
189       }
190     } else {
191       property_query_terms_map_[""].insert(query_term.term);
192     }
193 
194     // 2. If needed add term iterator to query_term_iterators_ map.
195     if (needs_term_frequency_info_) {
196       ICING_ASSIGN_OR_RETURN(
197           std::unique_ptr<DocHitInfoIterator> term_iterator,
198           index_.GetIterator(query_term.term, unnormalized_term_start,
199                              query_term.raw_term.length(), kSectionIdMaskAll,
200                              search_spec_.term_match_type(),
201                              needs_term_frequency_info_));
202       query_term_iterators_[query_term.term] =
203           std::make_unique<DocHitInfoIteratorFilter>(
204               std::move(term_iterator), &document_store_, &schema_store_,
205               filter_options_, current_time_ms_);
206     }
207   }
208 
209   // 3. Add the term iterator.
210   return index_.GetIterator(query_term.term, unnormalized_term_start,
211                             query_term.raw_term.length(), kSectionIdMaskAll,
212                             match_type, needs_term_frequency_info_);
213 }
214 
RegisterFunctions()215 void QueryVisitor::RegisterFunctions() {
216   // std::vector<std::string> createList(std::string...);
217   Function create_list_function_ =
218       Function::Create(DataType::kStringList, "createList",
219                        {Param(DataType::kString, Cardinality::kRequired),
220                         Param(DataType::kString, Cardinality::kVariable)},
221                        CreateList())
222           .ValueOrDie();
223   registered_functions_.insert(
224       {create_list_function_.name(), std::move(create_list_function_)});
225 
226   // DocHitInfoIterator search(std::string);
227   // DocHitInfoIterator search(std::string, std::vector<std::string>);
228   auto search_eval = [this](std::vector<PendingValue>&& args) {
229     return this->SearchFunction(std::move(args));
230   };
231   Function search_function =
232       Function::Create(DataType::kDocumentIterator, "search",
233                        {Param(DataType::kString),
234                         Param(DataType::kStringList, Cardinality::kOptional)},
235                        std::move(search_eval))
236           .ValueOrDie();
237   registered_functions_.insert(
238       {search_function.name(), std::move(search_function)});
239 
240   // DocHitInfoIterator propertyDefined(std::string);
241   auto property_defined = [this](std::vector<PendingValue>&& args) {
242     return this->PropertyDefinedFunction(std::move(args));
243   };
244   Function property_defined_function =
245       Function::Create(DataType::kDocumentIterator, "propertyDefined",
246                        {Param(DataType::kString)}, std::move(property_defined))
247           .ValueOrDie();
248   registered_functions_.insert(
249       {property_defined_function.name(), std::move(property_defined_function)});
250 
251   // DocHitInfoIterator hasProperty(std::string);
252   auto has_property = [this](std::vector<PendingValue>&& args) {
253     return this->HasPropertyFunction(std::move(args));
254   };
255   Function has_property_function =
256       Function::Create(DataType::kDocumentIterator, "hasProperty",
257                        {Param(DataType::kString)}, std::move(has_property))
258           .ValueOrDie();
259   registered_functions_.insert(
260       {has_property_function.name(), std::move(has_property_function)});
261 
262   // vector_index getEmbeddingParameter(long);
263   auto get_embedding_parameter = [](std::vector<PendingValue>&& args) {
264     return PendingValue::CreateVectorIndexPendingValue(
265         args.at(0).long_val().ValueOrDie());
266   };
267   Function get_embedding_parameter_function =
268       Function::Create(DataType::kVectorIndex, "getEmbeddingParameter",
269                        {Param(DataType::kLong)},
270                        std::move(get_embedding_parameter))
271           .ValueOrDie();
272   registered_functions_.insert({get_embedding_parameter_function.name(),
273                                 get_embedding_parameter_function});
274 
275   // DocHitInfoIterator semanticSearch(vector_index, double, double, string);
276   auto semantic_search = [this](std::vector<PendingValue>&& args) {
277     return this->SemanticSearchFunction(std::move(args));
278   };
279   Function semantic_search_function =
280       Function::Create(DataType::kDocumentIterator, "semanticSearch",
281                        {Param(DataType::kVectorIndex),
282                         Param(DataType::kDouble, Cardinality::kOptional),
283                         Param(DataType::kDouble, Cardinality::kOptional),
284                         Param(DataType::kString, Cardinality::kOptional)},
285                        std::move(semantic_search))
286           .ValueOrDie();
287   registered_functions_.insert(
288       {semantic_search_function.name(), std::move(semantic_search_function)});
289 
290   // DocHitInfoIterator getSearchStringParameter(long);
291   auto get_search_string_parameter = [this](std::vector<PendingValue>&& args) {
292     return this->GetSearchStringParameterFunction(std::move(args));
293   };
294   Function get_search_string_parameter_function =
295       Function::Create(DataType::kDocumentIterator, "getSearchStringParameter",
296                        {Param(DataType::kLong)},
297                        std::move(get_search_string_parameter))
298           .ValueOrDie();
299   registered_functions_.insert(
300       {get_search_string_parameter_function.name(),
301        std::move(get_search_string_parameter_function)});
302 
303   // DocHitInfoIterator matchScoreExpression(std::string, double, double)
304   auto match_score_expression = [this](std::vector<PendingValue>&& args) {
305     return this->MatchScoreExpressionFunction(std::move(args));
306   };
307   Function match_score_expression_function =
308       Function::Create(DataType::kDocumentIterator, "matchScoreExpression",
309                        {Param(DataType::kString), Param(DataType::kDouble),
310                         Param(DataType::kDouble, Cardinality::kOptional)},
311                        std::move(match_score_expression))
312           .ValueOrDie();
313   registered_functions_.insert({match_score_expression_function.name(),
314                                 std::move(match_score_expression_function)});
315 }
316 
SearchFunction(std::vector<PendingValue> && args)317 libtextclassifier3::StatusOr<PendingValue> QueryVisitor::SearchFunction(
318     std::vector<PendingValue>&& args) {
319   // The second arg (if present) is a list of sections to restrict to.
320   if (args.size() == 2) {
321     std::set<std::string> new_restricts;
322     std::vector<std::string> property_restricts =
323         std::move(args.at(1)).string_vals().ValueOrDie();
324     for (std::string& property_restrict : property_restricts) {
325       new_restricts.insert(std::move(property_restrict));
326     }
327     pending_property_restricts_.AddValidRestricts(std::move(new_restricts));
328     if (pending_property_restricts_.active_property_restricts().empty()) {
329       pending_property_restricts_.PopRestricts();
330       return PendingValue(std::make_unique<DocHitInfoIteratorNone>());
331     }
332   }
333 
334   // The first arg is guaranteed to be a STRING at this point. It should be safe
335   // to call ValueOrDie.
336   const QueryTerm* query = args.at(0).string_val().ValueOrDie();
337   Lexer lexer(query->term, Lexer::Language::QUERY);
338   ICING_ASSIGN_OR_RETURN(std::vector<Lexer::LexerToken> lexer_tokens,
339                          std::move(lexer).ExtractTokens());
340 
341   Parser parser = Parser::Create(std::move(lexer_tokens));
342   ICING_ASSIGN_OR_RETURN(std::unique_ptr<Node> tree_root,
343                          parser.ConsumeQuery());
344 
345   std::unique_ptr<DocHitInfoIterator> iterator;
346   QueryResults query_result;
347   if (tree_root == nullptr) {
348     iterator = std::make_unique<DocHitInfoIteratorAllDocumentId>(
349         document_store_.last_added_document_id());
350   } else {
351     QueryVisitor query_visitor(
352         &index_, &numeric_index_, &embedding_index_, &document_store_,
353         &schema_store_, &normalizer_, &tokenizer_, join_children_fetcher_,
354         search_spec_, filter_options_, needs_term_frequency_info_,
355         &feature_flags_, pending_property_restricts_, processing_not_,
356         current_time_ms_);
357     tree_root->Accept(&query_visitor);
358     ICING_ASSIGN_OR_RETURN(query_result,
359                            std::move(query_visitor).ConsumeResults());
360     iterator = std::move(query_result.root_iterator);
361   }
362 
363   // Update members based on results of processing the query.
364   if (args.size() == 2 &&
365       pending_property_restricts_.has_active_property_restricts()) {
366     iterator = DocHitInfoIteratorSectionRestrict::ApplyRestrictions(
367         std::move(iterator), &document_store_, &schema_store_,
368         pending_property_restricts_.active_property_restricts(),
369         current_time_ms_);
370     pending_property_restricts_.PopRestricts();
371   }
372   if (!processing_not_) {
373     std::move(
374         query_result.query_term_iterators.begin(),
375         query_result.query_term_iterators.end(),
376         std::inserter(query_term_iterators_, query_term_iterators_.end()));
377 
378     std::move(query_result.query_terms.begin(), query_result.query_terms.end(),
379               std::inserter(property_query_terms_map_,
380                             property_query_terms_map_.end()));
381   }
382   std::move(query_result.features_in_use.begin(),
383             query_result.features_in_use.end(),
384             std::inserter(features_, features_.end()));
385   return PendingValue(std::move(iterator));
386 }
387 
388 libtextclassifier3::StatusOr<PendingValue>
PropertyDefinedFunction(std::vector<PendingValue> && args)389 QueryVisitor::PropertyDefinedFunction(std::vector<PendingValue>&& args) {
390   // The first arg is guaranteed to be a STRING at this point. It should be safe
391   // to call ValueOrDie.
392   const QueryTerm* member = args.at(0).string_val().ValueOrDie();
393 
394   std::unique_ptr<DocHitInfoIterator> all_docs_iterator =
395       std::make_unique<DocHitInfoIteratorAllDocumentId>(
396           document_store_.last_added_document_id());
397 
398   std::set<std::string> target_sections = {std::move(member->term)};
399   std::unique_ptr<DocHitInfoIterator> property_in_schema_iterator =
400       std::make_unique<DocHitInfoIteratorPropertyInSchema>(
401           std::move(all_docs_iterator), &document_store_, &schema_store_,
402           std::move(target_sections), current_time_ms_);
403 
404   features_.insert(kListFilterQueryLanguageFeature);
405 
406   return PendingValue(std::move(property_in_schema_iterator));
407 }
408 
HasPropertyFunction(std::vector<PendingValue> && args)409 libtextclassifier3::StatusOr<PendingValue> QueryVisitor::HasPropertyFunction(
410     std::vector<PendingValue>&& args) {
411   // The first arg is guaranteed to be a STRING at this point. It should be safe
412   // to call ValueOrDie.
413   const std::string& property_path = args.at(0).string_val().ValueOrDie()->term;
414 
415   // Perform an exact search for the property existence metadata token.
416   ICING_ASSIGN_OR_RETURN(
417       std::unique_ptr<DocHitInfoIterator> meta_hit_iterator,
418       index_.GetIterator(
419           absl_ports::StrCat(kPropertyExistenceTokenPrefix, property_path),
420           /*term_start_index=*/0,
421           /*unnormalized_term_length=*/0, kSectionIdMaskAll,
422           TermMatchType::EXACT_ONLY,
423           /*need_hit_term_frequency=*/false));
424 
425   std::unique_ptr<DocHitInfoIterator> property_in_document_iterator =
426       std::make_unique<DocHitInfoIteratorPropertyInDocument>(
427           std::move(meta_hit_iterator));
428 
429   features_.insert(kHasPropertyFunctionFeature);
430 
431   return PendingValue(std::move(property_in_document_iterator));
432 }
433 
SemanticSearchFunction(std::vector<PendingValue> && args)434 libtextclassifier3::StatusOr<PendingValue> QueryVisitor::SemanticSearchFunction(
435     std::vector<PendingValue>&& args) {
436   int64_t vector_index = args.at(0).vector_index_val().ValueOrDie();
437   if (vector_index < 0 ||
438       vector_index >= search_spec_.embedding_query_vectors_size()) {
439     return absl_ports::OutOfRangeError("Got invalid vector search index!");
440   }
441 
442   // Handle default values for the optional arguments.
443   double low = -std::numeric_limits<double>::infinity();
444   double high = std::numeric_limits<double>::infinity();
445   SearchSpecProto::EmbeddingQueryMetricType::Code metric_type =
446       search_spec_.embedding_query_metric_type();
447   if (args.size() >= 2) {
448     low = args.at(1).double_val().ValueOrDie();
449   }
450   if (args.size() >= 3) {
451     high = args.at(2).double_val().ValueOrDie();
452   }
453   if (low > high) {
454     return absl_ports::InvalidArgumentError(
455         "The lower bound cannot be greater than the upper bound.");
456   }
457   if (args.size() >= 4) {
458     const std::string& metric = args.at(3).string_val().ValueOrDie()->term;
459     ICING_ASSIGN_OR_RETURN(
460         metric_type,
461         embedding_util::GetEmbeddingQueryMetricTypeFromName(metric));
462   }
463 
464   // Create and return iterator.
465   EmbeddingQueryResults::EmbeddingQueryScoreMap* score_map =
466       &embedding_query_results_.result_scores[vector_index][metric_type];
467   ICING_ASSIGN_OR_RETURN(
468       std::unique_ptr<DocHitInfoIterator> iterator,
469       DocHitInfoIteratorEmbedding::Create(
470           &search_spec_.embedding_query_vectors(vector_index), metric_type, low,
471           high, score_map, &embedding_index_, &document_store_, &schema_store_,
472           current_time_ms_));
473   return PendingValue(std::move(iterator));
474 }
475 
476 libtextclassifier3::StatusOr<PendingValue>
GetSearchStringParameterFunction(std::vector<PendingValue> && args)477 QueryVisitor::GetSearchStringParameterFunction(
478     std::vector<PendingValue>&& args) {
479   int64_t string_index = args.at(0).long_val().ValueOrDie();
480   if (string_index < 0 ||
481       string_index >= search_spec_.query_parameter_strings_size()) {
482     return absl_ports::OutOfRangeError("Got invalid string search index!");
483   }
484   const std::string& string_value =
485       search_spec_.query_parameter_strings(string_index);
486   // the prefix operator cannot be used here.
487   QueryTerm text_value = {string_value, string_value, /*is_prefix_val=*/false};
488   ICING_ASSIGN_OR_RETURN(std::unique_ptr<DocHitInfoIterator> iterator,
489                          ProduceTextTokenIterators(std::move(text_value)));
490   return PendingValue(std::move(iterator));
491 }
492 
493 libtextclassifier3::StatusOr<PendingValue>
MatchScoreExpressionFunction(std::vector<PendingValue> && args)494 QueryVisitor::MatchScoreExpressionFunction(std::vector<PendingValue>&& args) {
495   const std::string& scoring_expression_str =
496       args.at(0).string_val().ValueOrDie()->term;
497   libtextclassifier3::StatusOr<std::unique_ptr<ScoreExpression>>
498       scoring_expression = score_expression_util::GetScoreExpression(
499           scoring_expression_str,
500           /*default_score=*/-std::numeric_limits<double>::infinity(),
501           SearchSpecProto::EmbeddingQueryMetricType::UNKNOWN, &document_store_,
502           &schema_store_, current_time_ms_, join_children_fetcher_,
503           /*embedding_query_results=*/nullptr, /*section_weights=*/nullptr,
504           /*bm25f_calculator=*/nullptr, /*schema_type_alias_map=*/nullptr,
505           &feature_flags_, &scoring_feature_types_enabled_);
506   if (!scoring_expression.ok()) {
507     return absl_ports::Annotate(
508         scoring_expression.status(),
509         absl_ports::StrCat(
510             "matchScoreExpression: Failed to handle the score expression ",
511             scoring_expression_str));
512   }
513   double low = args.at(1).double_val().ValueOrDie();
514   double high = std::numeric_limits<double>::infinity();
515   if (args.size() == 3) {
516     high = args.at(2).double_val().ValueOrDie();
517   }
518   if (low > high) {
519     return absl_ports::InvalidArgumentError(
520         "The lower bound cannot be greater than the upper bound.");
521   }
522 
523   std::unique_ptr<DocHitInfoIterator> iterator =
524       std::make_unique<DocHitInfoIteratorMatchScoreExpression>(
525           document_store_.last_added_document_id(),
526           std::move(scoring_expression).ValueOrDie(), low, high);
527   features_.insert(kMatchScoreExpressionFunctionFeature);
528   return PendingValue(std::move(iterator));
529 }
530 
PopPendingIntValue()531 libtextclassifier3::StatusOr<int64_t> QueryVisitor::PopPendingIntValue() {
532   if (pending_values_.empty()) {
533     return absl_ports::InvalidArgumentError("Unable to retrieve int value.");
534   }
535   ICING_ASSIGN_OR_RETURN(int64_t int_value, pending_values_.top().long_val());
536   pending_values_.pop();
537   return int_value;
538 }
539 
PopPendingStringValue()540 libtextclassifier3::StatusOr<QueryTerm> QueryVisitor::PopPendingStringValue() {
541   if (pending_values_.empty()) {
542     return absl_ports::InvalidArgumentError("Unable to retrieve string value.");
543   }
544   ICING_ASSIGN_OR_RETURN(QueryTerm string_value,
545                          std::move(pending_values_.top()).string_val());
546   pending_values_.pop();
547   return string_value;
548 }
549 
PopPendingTextValue()550 libtextclassifier3::StatusOr<QueryTerm> QueryVisitor::PopPendingTextValue() {
551   if (pending_values_.empty()) {
552     return absl_ports::InvalidArgumentError("Unable to retrieve text value.");
553   }
554   ICING_ASSIGN_OR_RETURN(QueryTerm text_value,
555                          std::move(pending_values_.top()).text_val());
556   pending_values_.pop();
557   return text_value;
558 }
559 
560 libtextclassifier3::StatusOr<std::unique_ptr<DocHitInfoIterator>>
ProduceTextTokenIterators(QueryTerm text_value)561 QueryVisitor::ProduceTextTokenIterators(QueryTerm text_value) {
562   ICING_ASSIGN_OR_RETURN(std::unique_ptr<Tokenizer::Iterator> token_itr,
563                          tokenizer_.Tokenize(text_value.term));
564   Normalizer::NormalizedTerm normalized_term;
565   std::vector<std::unique_ptr<DocHitInfoIterator>> iterators;
566   // raw_text is the portion of text_value.raw_term that hasn't yet been
567   // matched to any of the tokens that we've processed. escaped_token will
568   // hold the portion of raw_text that corresponds to the current token that
569   // is being processed.
570   std::string_view raw_text = text_value.raw_term;
571   std::string_view raw_token;
572   bool reached_final_token = !token_itr->Advance();
573   // If the term is different then the raw_term, then there must have been some
574   // escaped characters that we will need to handle.
575   while (!reached_final_token) {
576     std::vector<Token> tokens = token_itr->GetTokens();
577     if (tokens.size() > 1) {
578       // The tokenizer iterator iterates between token groups. In practice,
579       // the tokenizer used with QueryVisitor (PlainTokenizer) will always
580       // only produce a single token per token group.
581       return absl_ports::InvalidArgumentError(
582           "Encountered unexpected token group with >1 tokens.");
583     }
584 
585     reached_final_token = !token_itr->Advance();
586     const Token& token = tokens.at(0);
587     if (reached_final_token && token.text.length() == raw_text.length()) {
588       // Unescaped tokens are strictly smaller than their escaped counterparts
589       // This means that if we're at the final token and token.length equals
590       // raw_text, then all of raw_text must correspond to this token.
591       raw_token = raw_text;
592     } else {
593       ICING_ASSIGN_OR_RETURN(
594           raw_token, string_util::FindEscapedToken(raw_text, token.text));
595     }
596     normalized_term = normalizer_.NormalizeTerm(token.text);
597     bool should_prefix_match = reached_final_token && text_value.is_prefix_val;
598     QueryTerm term_value{std::move(normalized_term.text), raw_token,
599                          should_prefix_match};
600     ICING_ASSIGN_OR_RETURN(std::unique_ptr<DocHitInfoIterator> iterator,
601                            CreateTermIterator(std::move(term_value)));
602     iterators.push_back(std::move(iterator));
603 
604     // Remove escaped_token from raw_text now that we've processed
605     // raw_text.
606     const char* escaped_token_end = raw_token.data() + raw_token.length();
607     raw_text = raw_text.substr(escaped_token_end - raw_text.data());
608   }
609   // Finally, create an And Iterator. If there's only a single term here, then
610   // it will just return that term iterator. Otherwise, segmented text is
611   // treated as a group of terms AND'd together.
612   return CreateAndIterator(std::move(iterators));
613 }
614 
615 libtextclassifier3::StatusOr<std::unique_ptr<DocHitInfoIterator>>
PopPendingIterator()616 QueryVisitor::PopPendingIterator() {
617   if (pending_values_.empty() || pending_values_.top().is_placeholder()) {
618     return absl_ports::InvalidArgumentError("Unable to retrieve iterator.");
619   }
620   if (pending_values_.top().data_type() == DataType::kDocumentIterator) {
621     std::unique_ptr<DocHitInfoIterator> iterator =
622         std::move(pending_values_.top()).iterator().ValueOrDie();
623     pending_values_.pop();
624     return iterator;
625   } else if (pending_values_.top().data_type() == DataType::kString) {
626     features_.insert(kVerbatimSearchFeature);
627     ICING_ASSIGN_OR_RETURN(QueryTerm string_value, PopPendingStringValue());
628     return CreateTermIterator(std::move(string_value));
629   } else {
630     ICING_ASSIGN_OR_RETURN(QueryTerm text_value, PopPendingTextValue());
631     return ProduceTextTokenIterators(std::move(text_value));
632   }
633 }
634 
635 libtextclassifier3::StatusOr<std::vector<std::unique_ptr<DocHitInfoIterator>>>
PopAllPendingIterators()636 QueryVisitor::PopAllPendingIterators() {
637   std::vector<std::unique_ptr<DocHitInfoIterator>> iterators;
638   while (!pending_values_.empty() && !pending_values_.top().is_placeholder()) {
639     ICING_ASSIGN_OR_RETURN(std::unique_ptr<DocHitInfoIterator> itr,
640                            PopPendingIterator());
641     iterators.push_back(std::move(itr));
642   }
643   if (pending_values_.empty()) {
644     return absl_ports::InvalidArgumentError(
645         "Unable to retrieve expected iterators.");
646   }
647   // Iterators will be in reverse order because we retrieved them from the
648   // stack. Reverse them to get back to the original ordering.
649   std::reverse(iterators.begin(), iterators.end());
650   return iterators;
651 }
652 
ProcessNumericComparator(const NaryOperatorNode * node)653 libtextclassifier3::Status QueryVisitor::ProcessNumericComparator(
654     const NaryOperatorNode* node) {
655   if (node->children().size() != 2) {
656     return absl_ports::InvalidArgumentError("Expected 2 children.");
657   }
658 
659   // 1. Put in a placeholder PendingValue
660   pending_values_.push(PendingValue());
661 
662   // 2. The first child is the property to restrict by.
663   node->children().at(0)->Accept(this);
664   if (has_pending_error()) {
665     return std::move(pending_error_);
666   }
667   ICING_ASSIGN_OR_RETURN(QueryTerm text_value, PopPendingTextValue());
668 
669   if (text_value.is_prefix_val) {
670     return absl_ports::InvalidArgumentError(
671         "Cannot use prefix operator '*' with a property name!");
672   }
673 
674   // If there is an active property restrict and this property is not present in
675   // in the active restrict set, then it's not satisfiable.
676   if (pending_property_restricts_.has_active_property_restricts() &&
677       pending_property_restricts_.active_property_restricts().find(
678           text_value.term) ==
679           pending_property_restricts_.active_property_restricts().end()) {
680     // The property restrict can't be satisfiable. Pop the placeholder that was
681     // just added and push a FALSE iterator.
682     pending_property_restricts_.PopRestricts();
683     pending_values_.pop();
684     pending_values_.push(
685         PendingValue(std::make_unique<DocHitInfoIteratorNone>()));
686     return libtextclassifier3::Status::OK;
687   }
688 
689   // 3. The second child should be parseable as an integer value.
690   expecting_numeric_arg_ = true;
691   node->children().at(1)->Accept(this);
692   expecting_numeric_arg_ = false;
693   ICING_ASSIGN_OR_RETURN(int64_t int_value, PopPendingIntValue());
694 
695   // 4. Check for the placeholder.
696   if (!pending_values_.top().is_placeholder()) {
697     return absl_ports::InvalidArgumentError(
698         "Error processing arguments for node.");
699   }
700   pending_values_.pop();
701 
702   // 5. Create the iterator and push it onto pending_values_.
703   ICING_ASSIGN_OR_RETURN(Int64Range range,
704                          GetInt64Range(node->operator_text(), int_value));
705   ICING_ASSIGN_OR_RETURN(std::unique_ptr<DocHitInfoIterator> iterator,
706                          numeric_index_.GetIterator(
707                              text_value.term, range.low, range.high,
708                              document_store_, schema_store_, current_time_ms_));
709 
710   features_.insert(kNumericSearchFeature);
711   pending_values_.push(PendingValue(std::move(iterator)));
712   return libtextclassifier3::Status::OK;
713 }
714 
ProcessAndOperator(const NaryOperatorNode * node)715 libtextclassifier3::StatusOr<PendingValue> QueryVisitor::ProcessAndOperator(
716     const NaryOperatorNode* node) {
717   ICING_ASSIGN_OR_RETURN(
718       std::vector<std::unique_ptr<DocHitInfoIterator>> iterators,
719       PopAllPendingIterators());
720   return PendingValue(CreateAndIterator(std::move(iterators)));
721 }
722 
ProcessOrOperator(const NaryOperatorNode * node)723 libtextclassifier3::StatusOr<PendingValue> QueryVisitor::ProcessOrOperator(
724     const NaryOperatorNode* node) {
725   ICING_ASSIGN_OR_RETURN(
726       std::vector<std::unique_ptr<DocHitInfoIterator>> iterators,
727       PopAllPendingIterators());
728   return PendingValue(CreateOrIterator(std::move(iterators)));
729 }
730 
ProcessNegationOperator(const UnaryOperatorNode * node)731 libtextclassifier3::Status QueryVisitor::ProcessNegationOperator(
732     const UnaryOperatorNode* node) {
733   // 1. Put in a placeholder PendingValue
734   pending_values_.push(PendingValue());
735 
736   // 2. Visit child
737   node->child()->Accept(this);
738   if (has_pending_error()) {
739     return std::move(pending_error_);
740   }
741 
742   if (pending_values_.size() < 2) {
743     return absl_ports::InvalidArgumentError(
744         "Visit unary operator child didn't correctly add pending values.");
745   }
746 
747   // 3. We want to preserve the original text of the numeric value, append our
748   // minus to the text. It will be parsed as either an int or a double later.
749   ICING_ASSIGN_OR_RETURN(QueryTerm numeric_text_val, PopPendingTextValue());
750   numeric_text_val.term = absl_ports::StrCat("-", numeric_text_val.term);
751   PendingValue pending_value =
752       PendingValue::CreateTextPendingValue(std::move(numeric_text_val));
753 
754   // We've parsed our numeric value successfully. Pop our placeholder, push it
755   // on to the stack and return successfully.
756   if (!pending_values_.top().is_placeholder()) {
757     return absl_ports::InvalidArgumentError(
758         "Error processing arguments for node.");
759   }
760   pending_values_.pop();
761   pending_values_.push(std::move(pending_value));
762   return libtextclassifier3::Status::OK;
763 }
764 
ProcessNotOperator(const UnaryOperatorNode * node)765 libtextclassifier3::Status QueryVisitor::ProcessNotOperator(
766     const UnaryOperatorNode* node) {
767   // TODO(b/265312785) Consider implementing query optimization when we run into
768   // nested NOTs. This would allow us to simplify a query like "NOT (-foo)" to
769   // just "foo". This would also require more complicate rewrites as we would
770   // need to do things like rewrite "NOT (-a OR b)" as "a AND -b" and
771   // "NOT (price < 5)" as "price >= 5".
772   // 1. Put in a placeholder PendingValue
773   pending_values_.push(PendingValue());
774   // Toggle whatever the current value of 'processing_not_' is before visiting
775   // the children.
776   processing_not_ = !processing_not_;
777 
778   // 2. Visit child
779   node->child()->Accept(this);
780   if (has_pending_error()) {
781     return std::move(pending_error_);
782   }
783 
784   if (pending_values_.size() < 2) {
785     return absl_ports::InvalidArgumentError(
786         "Visit unary operator child didn't correctly add pending values.");
787   }
788 
789   // 3. Retrieve the delegate iterator
790   ICING_ASSIGN_OR_RETURN(std::unique_ptr<DocHitInfoIterator> delegate,
791                          PopPendingIterator());
792 
793   // 4. Check for the placeholder.
794   if (!pending_values_.top().is_placeholder()) {
795     return absl_ports::InvalidArgumentError(
796         "Error processing arguments for node.");
797   }
798   pending_values_.pop();
799 
800   pending_values_.push(PendingValue(std::make_unique<DocHitInfoIteratorNot>(
801       std::move(delegate), document_store_.last_added_document_id())));
802 
803   // Untoggle whatever the current value of 'processing_not_' is now that we've
804   // finished processing this NOT.
805   processing_not_ = !processing_not_;
806   return libtextclassifier3::Status::OK;
807 }
808 
ProcessHasOperator(const NaryOperatorNode * node)809 libtextclassifier3::Status QueryVisitor::ProcessHasOperator(
810     const NaryOperatorNode* node) {
811   if (node->children().size() != 2) {
812     return absl_ports::InvalidArgumentError("Expected 2 children.");
813   }
814 
815   // 1. Put in a placeholder PendingValue
816   pending_values_.push(PendingValue());
817 
818   // 2. Visit the first child - the property.
819   node->children().at(0)->Accept(this);
820   if (has_pending_error()) {
821     return pending_error_;
822   }
823   ICING_ASSIGN_OR_RETURN(QueryTerm text_value, PopPendingTextValue());
824   if (text_value.is_prefix_val) {
825     return absl_ports::InvalidArgumentError(
826         "Cannot use prefix operator '*' with a property name!");
827   }
828   pending_property_restricts_.AddValidRestricts({text_value.term});
829 
830   // Just added a restrict - if there are no active property restricts then that
831   // be because this restrict is unsatisfiable.
832   if (pending_property_restricts_.active_property_restricts().empty()) {
833     // The property restrict can't be satisfiable. Pop the placeholder that was
834     // just added and push a FALSE iterator.
835     pending_property_restricts_.PopRestricts();
836     pending_values_.pop();
837     pending_values_.push(
838         PendingValue(std::make_unique<DocHitInfoIteratorNone>()));
839     return libtextclassifier3::Status::OK;
840   }
841 
842   // 3. Visit the second child - the argument.
843   node->children().at(1)->Accept(this);
844   if (has_pending_error()) {
845     return pending_error_;
846   }
847   ICING_ASSIGN_OR_RETURN(std::unique_ptr<DocHitInfoIterator> delegate,
848                          PopPendingIterator());
849 
850   // 4. Check for the placeholder.
851   if (!pending_values_.top().is_placeholder()) {
852     return absl_ports::InvalidArgumentError(
853         "Error processing arguments for node.");
854   }
855   pending_values_.pop();
856   pending_property_restricts_.PopRestricts();
857 
858   std::set<std::string> property_restricts = {std::move(text_value.term)};
859   pending_values_.push(
860       PendingValue(DocHitInfoIteratorSectionRestrict::ApplyRestrictions(
861           std::move(delegate), &document_store_, &schema_store_,
862           std::move(property_restricts), current_time_ms_)));
863   return libtextclassifier3::Status::OK;
864 }
865 
VisitString(const StringNode * node)866 void QueryVisitor::VisitString(const StringNode* node) {
867   // A STRING node can only be a term. Create the iterator now.
868   auto unescaped_string_or = string_util::UnescapeStringValue(node->value());
869   if (!unescaped_string_or.ok()) {
870     pending_error_ = std::move(unescaped_string_or).status();
871     return;
872   }
873   std::string unescaped_string = std::move(unescaped_string_or).ValueOrDie();
874   QueryTerm val{std::move(unescaped_string), node->raw_value(),
875                 node->is_prefix()};
876   pending_values_.push(PendingValue::CreateStringPendingValue(std::move(val)));
877 }
878 
VisitText(const TextNode * node)879 void QueryVisitor::VisitText(const TextNode* node) {
880   // TEXT nodes could either be a term (and will become DocHitInfoIteratorTerm)
881   // or a property name. As such, we just push the TEXT value into pending
882   // values and determine which it is at a later point.
883   QueryTerm val{std::move(node->value()), node->raw_value(), node->is_prefix()};
884   pending_values_.push(PendingValue::CreateTextPendingValue(std::move(val)));
885 }
886 
VisitMember(const MemberNode * node)887 void QueryVisitor::VisitMember(const MemberNode* node) {
888   if (node->children().empty()) {
889     pending_error_ =
890         absl_ports::InvalidArgumentError("Encountered malformed member node.");
891     return;
892   }
893 
894   // 1. Put in a placeholder PendingValue
895   pending_values_.push(PendingValue());
896 
897   // 2. Visit the children.
898   for (const std::unique_ptr<TextNode>& child : node->children()) {
899     child->Accept(this);
900     if (has_pending_error()) {
901       return;
902     }
903   }
904 
905   // 3. Now process the results of the children and produce a single pending
906   //    value representing this member.
907   PendingValue pending_value;
908   if (node->children().size() == 1) {
909     // 3a. This member only has a single child, then the pending value produced
910     //    by that child is the final value produced by this member.
911     pending_value = std::move(pending_values_.top());
912     pending_values_.pop();
913   } else {
914     // 3b. Retrieve the values of all children and concatenate them into a
915     // single value.
916     libtextclassifier3::StatusOr<QueryTerm> member_or;
917     std::vector<std::string> members;
918     QueryTerm text_val;
919     const char* start = nullptr;
920     const char* end = nullptr;
921     while (!pending_values_.empty() &&
922            !pending_values_.top().is_placeholder()) {
923       member_or = PopPendingTextValue();
924       if (!member_or.ok()) {
925         pending_error_ = std::move(member_or).status();
926         return;
927       }
928       text_val = std::move(member_or).ValueOrDie();
929       if (text_val.is_prefix_val) {
930         pending_error_ = absl_ports::InvalidArgumentError(
931             "Cannot use prefix operator '*' within a property name!");
932         return;
933       }
934       if (start == nullptr) {
935         start = text_val.raw_term.data();
936         end = text_val.raw_term.data() + text_val.raw_term.length();
937       } else {
938         start = std::min(start, text_val.raw_term.data());
939         end = std::max(end,
940                        text_val.raw_term.data() + text_val.raw_term.length());
941       }
942       members.push_back(std::move(text_val.term));
943     }
944     QueryTerm member;
945     member.term = absl_ports::StrJoin(members.rbegin(), members.rend(),
946                                       property_util::kPropertyPathSeparator);
947     member.raw_term = std::string_view(start, end - start);
948     member.is_prefix_val = false;
949     pending_value = PendingValue::CreateTextPendingValue(std::move(member));
950   }
951 
952   // 4. If pending_values_ is empty somehow, then our placeholder disappeared
953   // somehow.
954   if (pending_values_.empty()) {
955     pending_error_ = absl_ports::InvalidArgumentError(
956         "Error processing arguments for member node.");
957     return;
958   }
959   pending_values_.pop();
960 
961   pending_values_.push(std::move(pending_value));
962 }
963 
VisitFunction(const FunctionNode * node)964 void QueryVisitor::VisitFunction(const FunctionNode* node) {
965   // 1. Get the associated function.
966   auto itr = registered_functions_.find(node->function_name());
967   if (itr == registered_functions_.end()) {
968     pending_error_ = absl_ports::InvalidArgumentError(absl_ports::StrCat(
969         "Function ", node->function_name(), " is not supported."));
970     return;
971   }
972   const Function& function = itr->second;
973 
974   // 2. Put in a placeholder PendingValue
975   pending_values_.push(PendingValue());
976 
977   // 3. Visit the children.
978   expecting_numeric_arg_ = true;
979   for (int i = 0; i < node->args().size(); ++i) {
980     const std::unique_ptr<Node>& arg = node->args()[i];
981     libtextclassifier3::StatusOr<DataType> arg_type_or =
982         function.get_param_type(i);
983     bool current_level_expecting_numeric_arg = expecting_numeric_arg_;
984     // If arg_type_or has an error, we should ignore it for now, since
985     // function.Eval should do the type check and return better error messages.
986     if (arg_type_or.ok() && (arg_type_or.ValueOrDie() == DataType::kLong ||
987                              arg_type_or.ValueOrDie() == DataType::kDouble)) {
988       expecting_numeric_arg_ = true;
989     }
990     arg->Accept(this);
991     expecting_numeric_arg_ = current_level_expecting_numeric_arg;
992     if (has_pending_error()) {
993       return;
994     }
995   }
996 
997   // 4. Collect the arguments and evaluate the function.
998   std::vector<PendingValue> args;
999   while (!pending_values_.empty() && !pending_values_.top().is_placeholder()) {
1000     args.push_back(std::move(pending_values_.top()));
1001     pending_values_.pop();
1002   }
1003   std::reverse(args.begin(), args.end());
1004   auto eval_result = function.Eval(std::move(args));
1005   if (!eval_result.ok()) {
1006     pending_error_ = std::move(eval_result).status();
1007     return;
1008   }
1009 
1010   // 5. Pop placeholder in pending_values and add the result of our function.
1011   pending_values_.pop();
1012   pending_values_.push(std::move(eval_result).ValueOrDie());
1013 
1014   // Support for custom functions was added in list filters.
1015   features_.insert(kListFilterQueryLanguageFeature);
1016 }
1017 
1018 // TODO(b/265312785) Clarify handling of the interaction between HAS and NOT.
1019 // Currently, `prop1:(NOT foo bar)` will not match any documents. Likewise,
1020 // `search("NOT foo bar", createList("prop1"))` will not match any documents.
1021 //
1022 // We should either confirm that this is the desired behavior or consider
1023 // rewriting these queries so that they're interpreted as
1024 // `NOT prop1:foo AND prop1:bar` and
1025 // `NOT search("foo", createList("prop1"))
1026 //  AND search("bar", createList("prop1"))`
VisitUnaryOperator(const UnaryOperatorNode * node)1027 void QueryVisitor::VisitUnaryOperator(const UnaryOperatorNode* node) {
1028   bool is_minus = node->operator_text() == "MINUS";
1029   if (node->operator_text() != "NOT" && !is_minus) {
1030     pending_error_ = absl_ports::UnimplementedError(
1031         absl_ports::StrCat("Visiting for unary operator ",
1032                            node->operator_text(), " not implemented yet."));
1033     return;
1034   }
1035 
1036   libtextclassifier3::Status status;
1037   if (expecting_numeric_arg_ && is_minus) {
1038     // If the operator is a MINUS ('-') and we're at the child of a numeric
1039     // comparator, then this must be a negation ('-3')
1040     status = ProcessNegationOperator(node);
1041   } else {
1042     status = ProcessNotOperator(node);
1043   }
1044 
1045   if (!status.ok()) {
1046     pending_error_ = std::move(status);
1047   }
1048 
1049   if (!is_minus ||
1050       pending_property_restricts_.has_active_property_restricts() ||
1051       processing_not_) {
1052     // 'NOT' operator was added in list filters.
1053     // Likewise, mixing property restricts and NOTs were made valid in list
1054     // filters.
1055     features_.insert(kListFilterQueryLanguageFeature);
1056   }
1057 }
1058 
VisitNaryOperator(const NaryOperatorNode * node)1059 void QueryVisitor::VisitNaryOperator(const NaryOperatorNode* node) {
1060   if (!IsSupportedNaryOperator(node->operator_text())) {
1061     pending_error_ = absl_ports::UnimplementedError(
1062         "No support for any non-numeric operators.");
1063     return;
1064   }
1065 
1066   if (pending_property_restricts_.has_active_property_restricts() ||
1067       processing_not_) {
1068     // Likewise, mixing property restricts and NOT with compound statements was
1069     // added in list filters.
1070     features_.insert(kListFilterQueryLanguageFeature);
1071   }
1072 
1073   if (node->operator_text() == ":") {
1074     libtextclassifier3::Status status = ProcessHasOperator(node);
1075     if (!status.ok()) {
1076       pending_error_ = std::move(status);
1077     }
1078     return;
1079   } else if (IsNumericComparator(node->operator_text())) {
1080     libtextclassifier3::Status status = ProcessNumericComparator(node);
1081     if (!status.ok()) {
1082       pending_error_ = std::move(status);
1083     }
1084     return;
1085   }
1086 
1087   // 1. Put in a placeholder PendingValue
1088   pending_values_.push(PendingValue());
1089 
1090   // 2. Visit the children.
1091   for (int i = 0; i < node->children().size(); ++i) {
1092     node->children().at(i)->Accept(this);
1093     if (has_pending_error()) {
1094       return;
1095     }
1096   }
1097 
1098   // 3. Retrieve the pending value for this node.
1099   libtextclassifier3::StatusOr<PendingValue> pending_value_or;
1100   if (node->operator_text() == "AND") {
1101     pending_value_or = ProcessAndOperator(node);
1102   } else if (node->operator_text() == "OR") {
1103     pending_value_or = ProcessOrOperator(node);
1104   }
1105   if (!pending_value_or.ok()) {
1106     pending_error_ = std::move(pending_value_or).status();
1107     return;
1108   }
1109   PendingValue pending_value = std::move(pending_value_or).ValueOrDie();
1110 
1111   // 4. Check for the placeholder.
1112   if (!pending_values_.top().is_placeholder()) {
1113     pending_error_ = absl_ports::InvalidArgumentError(
1114         "Error processing arguments for node.");
1115     return;
1116   }
1117   pending_values_.pop();
1118 
1119   pending_values_.push(std::move(pending_value));
1120 }
1121 
ConsumeResults()1122 libtextclassifier3::StatusOr<QueryResults> QueryVisitor::ConsumeResults() && {
1123   if (has_pending_error()) {
1124     return std::move(pending_error_);
1125   }
1126   if (pending_values_.size() != 1) {
1127     return absl_ports::InvalidArgumentError(
1128         "Visitor does not contain a single root iterator.");
1129   }
1130   auto iterator_or = PopPendingIterator();
1131   if (!iterator_or.ok()) {
1132     return std::move(iterator_or).status();
1133   }
1134 
1135   QueryResults results;
1136   results.root_iterator = std::move(iterator_or).ValueOrDie();
1137   results.query_term_iterators = std::move(query_term_iterators_);
1138   results.query_terms = std::move(property_query_terms_map_);
1139   results.embedding_query_results = std::move(embedding_query_results_);
1140   results.features_in_use = std::move(features_);
1141   return results;
1142 }
1143 
1144 }  // namespace lib
1145 }  // namespace icing
1146