aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrace Zhao <gracezrx@google.com>2023-07-07 15:31:32 -0700
committerGrace Zhao <gracezrx@google.com>2023-07-07 15:31:32 -0700
commit3094e6cc63609d1fff7b57f6d8548853b4840924 (patch)
treec37f32245d3655235cfc55bbb42ec66b94adc4c0
parent4ac48569941d4f48b1a9bffdaf4abb2a3e8e9269 (diff)
downloadicing-3094e6cc63609d1fff7b57f6d8548853b4840924.tar.gz
Update Icing from upstream.
Descriptions: ======================================================================== Fix MainIndex::TransferAndAddHits by adding a safety check ======================================================================== [Indexable_nested_properties_list 4.5/5] Modify sectionId assignment so that all property paths in the indexable_nested_properties_list consume sectionIds. ======================================================================== Bug: 288934779 Bug: 289150947 NO_IFTTT="Path is only valid in G3." Change-Id: Ibad4a09d2026ad038f6b64391497e4248fb891ed
-rw-r--r--icing/icing-search-engine_schema_test.cc278
-rw-r--r--icing/index/main/main-index.cc7
-rw-r--r--icing/schema/schema-property-iterator.cc36
-rw-r--r--icing/schema/schema-property-iterator.h18
-rw-r--r--icing/schema/schema-property-iterator_test.cc708
-rw-r--r--icing/schema/schema-type-manager.cc29
-rw-r--r--icing/schema/section-manager-builder_test.cc4
-rw-r--r--icing/schema/section-manager.cc33
-rw-r--r--icing/schema/section-manager_test.cc244
-rw-r--r--synced_AOSP_CL_number.txt2
10 files changed, 1296 insertions, 63 deletions
diff --git a/icing/icing-search-engine_schema_test.cc b/icing/icing-search-engine_schema_test.cc
index 1987afe..af9d0e2 100644
--- a/icing/icing-search-engine_schema_test.cc
+++ b/icing/icing-search-engine_schema_test.cc
@@ -18,7 +18,6 @@
#include <string>
#include <utility>
-#include "icing/text_classifier/lib3/utils/base/status.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "icing/document-builder.h"
@@ -27,14 +26,12 @@
#include "icing/icing-search-engine.h"
#include "icing/jni/jni-cache.h"
#include "icing/join/join-processor.h"
-#include "icing/portable/endian.h"
#include "icing/portable/equals-proto.h"
#include "icing/portable/platform.h"
#include "icing/proto/debug.pb.h"
#include "icing/proto/document.pb.h"
#include "icing/proto/document_wrapper.pb.h"
#include "icing/proto/initialize.pb.h"
-#include "icing/proto/logging.pb.h"
#include "icing/proto/optimize.pb.h"
#include "icing/proto/persist.pb.h"
#include "icing/proto/reset.pb.h"
@@ -1081,6 +1078,281 @@ TEST_F(IcingSearchEngineSchemaTest,
EqualsSearchResultIgnoreStatsAndScores(empty_result));
}
+TEST_F(
+ IcingSearchEngineSchemaTest,
+ SetSchemaChangeNestedPropertiesListTriggersIndexRestorationAndReturnsOk) {
+ IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
+ ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
+
+ SchemaTypeConfigProto person_proto =
+ SchemaTypeConfigBuilder()
+ .SetType("Person")
+ .AddProperty(
+ PropertyConfigBuilder()
+ .SetName("name")
+ .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_OPTIONAL))
+ .AddProperty(
+ PropertyConfigBuilder()
+ .SetName("lastName")
+ .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_OPTIONAL))
+ .AddProperty(
+ PropertyConfigBuilder()
+ .SetName("address")
+ .SetDataTypeString(TERM_MATCH_UNKNOWN, TOKENIZER_NONE)
+ .SetCardinality(CARDINALITY_OPTIONAL))
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("age")
+ .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
+ .SetCardinality(CARDINALITY_OPTIONAL))
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("birthday")
+ .SetDataTypeInt64(NUMERIC_MATCH_UNKNOWN)
+ .SetCardinality(CARDINALITY_OPTIONAL))
+ .Build();
+ // Create a schema with nested properties:
+ // - "sender.address": string type, (nested) non-indexable. Section id = 0.
+ // - "sender.age": int64 type, (nested) indexed. Section id = 1.
+ // - "sender.birthday": int64 type, (nested) non-indexable. Section id = 2.
+ // - "sender.lastName": int64 type, (nested) indexed. Section id = 3.
+ // - "sender.name": string type, (nested) indexed. Section id = 4.
+ // - "subject": string type, indexed. Section id = 5.
+ // - "timestamp": int64 type, indexed. Section id = 6.
+ // - "sender.foo": unknown type, (nested) non-indexable. Section id = 7.
+ //
+ // "sender.address" and "sender.birthday" are assigned a section id because
+ // they are listed in the indexable_nested_properties_list for 'Email.sender'.
+ // They are assigned a sectionId but are not indexed since their indexing
+ // configs are non-indexable.
+ //
+ // "sender.foo" is also assigned a section id, but is also not undefined by
+ // the schema definition. Trying to index a document with this nested property
+ // should fail.
+ SchemaProto nested_schema =
+ SchemaBuilder()
+ .AddType(person_proto)
+ .AddType(
+ SchemaTypeConfigBuilder()
+ .SetType("Email")
+ .AddProperty(
+ PropertyConfigBuilder()
+ .SetName("sender")
+ .SetDataTypeDocument(
+ "Person", /*indexable_nested_properties_list=*/
+ {"age", "lastName", "address", "name", "birthday",
+ "foo"})
+ .SetCardinality(CARDINALITY_OPTIONAL))
+ .AddProperty(
+ PropertyConfigBuilder()
+ .SetName("subject")
+ .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_OPTIONAL))
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("timestamp")
+ .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
+ .SetCardinality(CARDINALITY_OPTIONAL)))
+ .Build();
+
+ SetSchemaResultProto set_schema_result = icing.SetSchema(nested_schema);
+ // Ignore latency numbers. They're covered elsewhere.
+ set_schema_result.clear_latency_ms();
+ SetSchemaResultProto expected_set_schema_result;
+ expected_set_schema_result.mutable_status()->set_code(StatusProto::OK);
+ expected_set_schema_result.mutable_new_schema_types()->Add("Email");
+ expected_set_schema_result.mutable_new_schema_types()->Add("Person");
+ EXPECT_THAT(set_schema_result, EqualsProto(expected_set_schema_result));
+
+ DocumentProto document =
+ DocumentBuilder()
+ .SetKey("namespace1", "uri1")
+ .SetSchema("Email")
+ .SetCreationTimestampMs(1000)
+ .AddStringProperty("subject",
+ "Did you get the memo about TPS reports?")
+ .AddDocumentProperty(
+ "sender",
+ DocumentBuilder()
+ .SetKey("namespace1", "uri1")
+ .SetSchema("Person")
+ .AddStringProperty("name", "Bill")
+ .AddStringProperty("lastName", "Lundbergh")
+ .AddStringProperty("address", "1600 Amphitheatre Pkwy")
+ .AddInt64Property("age", 20)
+ .AddInt64Property("birthday", 20)
+ .Build())
+ .AddInt64Property("timestamp", 1234)
+ .Build();
+
+ // Indexing this doc should fail, since the 'sender.foo' property is not found
+ DocumentProto invalid_document =
+ DocumentBuilder()
+ .SetKey("namespace2", "uri1")
+ .SetSchema("Email")
+ .SetCreationTimestampMs(1000)
+ .AddStringProperty("subject",
+ "Did you get the memo about TPS reports?")
+ .AddDocumentProperty(
+ "sender",
+ DocumentBuilder()
+ .SetKey("namespace1", "uri1")
+ .SetSchema("Person")
+ .AddStringProperty("name", "Bill")
+ .AddStringProperty("lastName", "Lundbergh")
+ .AddStringProperty("address", "1600 Amphitheatre Pkwy")
+ .AddInt64Property("age", 20)
+ .AddInt64Property("birthday", 20)
+ .AddBytesProperty("foo", "bar bytes")
+ .Build())
+ .AddInt64Property("timestamp", 1234)
+ .Build();
+
+ EXPECT_THAT(icing.Put(document).status(), ProtoIsOk());
+ EXPECT_THAT(icing.Put(invalid_document).status(),
+ ProtoStatusIs(StatusProto::NOT_FOUND));
+
+ SearchResultProto expected_search_result_proto;
+ expected_search_result_proto.mutable_status()->set_code(StatusProto::OK);
+ *expected_search_result_proto.mutable_results()->Add()->mutable_document() =
+ document;
+
+ SearchResultProto empty_result;
+ empty_result.mutable_status()->set_code(StatusProto::OK);
+
+ // Verify term search
+ // document should match a query for 'Bill' in 'sender.name', but not in
+ // 'sender.lastName'
+ SearchSpecProto search_spec1;
+ search_spec1.set_query("sender.name:Bill");
+ search_spec1.set_term_match_type(TermMatchType::EXACT_ONLY);
+
+ SearchResultProto actual_results =
+ icing.Search(search_spec1, GetDefaultScoringSpec(),
+ ResultSpecProto::default_instance());
+ EXPECT_THAT(actual_results, EqualsSearchResultIgnoreStatsAndScores(
+ expected_search_result_proto));
+
+ search_spec1.set_query("sender.lastName:Bill");
+ actual_results = icing.Search(search_spec1, GetDefaultScoringSpec(),
+ ResultSpecProto::default_instance());
+ EXPECT_THAT(actual_results,
+ EqualsSearchResultIgnoreStatsAndScores(empty_result));
+
+ // document should match a query for 'Lundber' in 'sender.lastName', but not
+ // in 'sender.name'.
+ SearchSpecProto search_spec2;
+ search_spec2.set_query("sender.lastName:Lundber");
+ search_spec2.set_term_match_type(TermMatchType::PREFIX);
+
+ actual_results = icing.Search(search_spec2, GetDefaultScoringSpec(),
+ ResultSpecProto::default_instance());
+ EXPECT_THAT(actual_results, EqualsSearchResultIgnoreStatsAndScores(
+ expected_search_result_proto));
+
+ search_spec2.set_query("sender.name:Lundber");
+ actual_results = icing.Search(search_spec2, GetDefaultScoringSpec(),
+ ResultSpecProto::default_instance());
+ EXPECT_THAT(actual_results,
+ EqualsSearchResultIgnoreStatsAndScores(empty_result));
+
+ // document should not match a query for 'Amphitheatre' because the
+ // 'sender.address' field is not indexed.
+ search_spec2.set_query("Amphitheatre");
+ search_spec2.set_term_match_type(TermMatchType::PREFIX);
+
+ actual_results = icing.Search(search_spec2, GetDefaultScoringSpec(),
+ ResultSpecProto::default_instance());
+ EXPECT_THAT(actual_results,
+ EqualsSearchResultIgnoreStatsAndScores(empty_result));
+
+ // Verify numeric (integer) search
+ // document should match a query for 20 in 'sender.age', but not in
+ // 'timestamp' or 'sender.birthday'
+ SearchSpecProto search_spec3;
+ search_spec3.set_query("sender.age == 20");
+ search_spec3.set_search_type(
+ SearchSpecProto::SearchType::EXPERIMENTAL_ICING_ADVANCED_QUERY);
+ search_spec3.add_enabled_features(std::string(kNumericSearchFeature));
+
+ actual_results = icing.Search(search_spec3, GetDefaultScoringSpec(),
+ ResultSpecProto::default_instance());
+ EXPECT_THAT(actual_results, EqualsSearchResultIgnoreStatsAndScores(
+ expected_search_result_proto));
+
+ search_spec3.set_query("timestamp == 20");
+ actual_results = icing.Search(search_spec3, GetDefaultScoringSpec(),
+ ResultSpecProto::default_instance());
+ EXPECT_THAT(actual_results,
+ EqualsSearchResultIgnoreStatsAndScores(empty_result));
+
+ search_spec3.set_query("birthday == 20");
+ actual_results = icing.Search(search_spec3, GetDefaultScoringSpec(),
+ ResultSpecProto::default_instance());
+ EXPECT_THAT(actual_results,
+ EqualsSearchResultIgnoreStatsAndScores(empty_result));
+
+ // Now update the schema and don't index "sender.name", "sender.birthday" and
+ // "sender.foo".
+ // This should reassign section ids, lead to an index rebuild and ensure that
+ // nothing match a query for "Bill".
+ //
+ // Section id assignment:
+ // - "sender.address": string type, (nested) non-indexable. Section id = 0.
+ // - "sender.age": int64 type, (nested) indexed. Section id = 1.
+ // - "sender.birthday": int64 type, (nested) unindexed. No section id.
+ // - "sender.lastName": int64 type, (nested) indexed. Section id = 2.
+ // - "sender.name": string type, (nested) unindexed. No section id.
+ // - "subject": string type, indexed. Section id = 3.
+ // - "timestamp": int64 type, indexed. Section id = 4.
+ // - "sender.foo": unknown type, invalid. No section id.
+ SchemaProto nested_schema_with_less_props =
+ SchemaBuilder()
+ .AddType(person_proto)
+ .AddType(SchemaTypeConfigBuilder()
+ .SetType("Email")
+ .AddProperty(
+ PropertyConfigBuilder()
+ .SetName("sender")
+ .SetDataTypeDocument(
+ "Person", /*indexable_nested_properties=*/
+ {"age", "lastName", "address"})
+ .SetCardinality(CARDINALITY_OPTIONAL))
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("subject")
+ .SetDataTypeString(TERM_MATCH_PREFIX,
+ TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_OPTIONAL))
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("timestamp")
+ .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
+ .SetCardinality(CARDINALITY_OPTIONAL)))
+ .Build();
+
+ set_schema_result = icing.SetSchema(nested_schema_with_less_props);
+ // Ignore latency numbers. They're covered elsewhere.
+ set_schema_result.clear_latency_ms();
+ expected_set_schema_result = SetSchemaResultProto();
+ expected_set_schema_result.mutable_status()->set_code(StatusProto::OK);
+ expected_set_schema_result.mutable_index_incompatible_changed_schema_types()
+ ->Add("Email");
+ EXPECT_THAT(set_schema_result, EqualsProto(expected_set_schema_result));
+
+ // Verify term search
+ // document shouldn't match a query for 'Bill' in either 'sender.name' or
+ // 'subject'
+ search_spec1.set_query("sender.name:Bill");
+ actual_results = icing.Search(search_spec1, GetDefaultScoringSpec(),
+ ResultSpecProto::default_instance());
+ EXPECT_THAT(actual_results,
+ EqualsSearchResultIgnoreStatsAndScores(empty_result));
+
+ search_spec1.set_query("subject:Bill");
+ actual_results = icing.Search(search_spec1, GetDefaultScoringSpec(),
+ ResultSpecProto::default_instance());
+ EXPECT_THAT(actual_results,
+ EqualsSearchResultIgnoreStatsAndScores(empty_result));
+}
+
TEST_F(IcingSearchEngineSchemaTest,
SetSchemaNewJoinablePropertyTriggersIndexRestorationAndReturnsOk) {
IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
diff --git a/icing/index/main/main-index.cc b/icing/index/main/main-index.cc
index d5e9d57..aae60c6 100644
--- a/icing/index/main/main-index.cc
+++ b/icing/index/main/main-index.cc
@@ -751,6 +751,13 @@ libtextclassifier3::StatusOr<DocumentId> MainIndex::TransferAndAddHits(
old_pl_accessor.GetNextHitsBatch());
while (!tmp.empty()) {
for (const Hit& hit : tmp) {
+ // A safety check to add robustness to the codebase, so to make sure that
+ // we never access invalid memory, in case that hit from the posting list
+ // is corrupted.
+ if (hit.document_id() < 0 ||
+ hit.document_id() >= document_id_old_to_new.size()) {
+ continue;
+ }
DocumentId new_document_id = document_id_old_to_new[hit.document_id()];
// Transfer the document id of the hit, if the document is not deleted
// or outdated.
diff --git a/icing/schema/schema-property-iterator.cc b/icing/schema/schema-property-iterator.cc
index 2d1ca7e..8fc245c 100644
--- a/icing/schema/schema-property-iterator.cc
+++ b/icing/schema/schema-property-iterator.cc
@@ -55,8 +55,12 @@ libtextclassifier3::Status SchemaPropertyIterator::Advance() {
// If an element in sorted_top_level_indexable_nested_properties_ < the
// current property path, it means that we've already iterated past the
// possible position for it without seeing it.
- // It's not a valid property path in our schema definition. Ignore it and
- // advance current_top_level_indexable_nested_properties_idx_.
+ // It's not a valid property path in our schema definition. Add it to
+ // unknown_indexable_nested_properties_ and advance
+ // current_top_level_indexable_nested_properties_idx_.
+ unknown_indexable_nested_property_paths_.push_back(
+ sorted_top_level_indexable_nested_properties_.at(
+ current_top_level_indexable_nested_properties_idx_));
++current_top_level_indexable_nested_properties_idx_;
}
@@ -75,13 +79,16 @@ libtextclassifier3::Status SchemaPropertyIterator::Advance() {
: nullptr;
if (current_indexable_nested_prop == nullptr ||
*current_indexable_nested_prop > curr_property_path) {
- // Current property is not in the indexable list. Set its indexable
- // config according to the current level's indexable config.
- levels_.back().SetCurrentPropertyIndexable(
- levels_.back().GetLevelNestedIndexable());
+ // Current property is not in the indexable list. Set it as indexable if
+ // its schema level is indexable AND it is an indexable property.
+ bool is_property_indexable =
+ levels_.back().GetLevelNestedIndexable() &&
+ SchemaUtil::IsIndexedProperty(curr_property_config);
+ levels_.back().SetCurrentPropertyIndexable(is_property_indexable);
} else if (*current_indexable_nested_prop == curr_property_path) {
// Current property is in the indexable list. Set its indexable config
- // to true.
+ // to true. This property will consume a sectionId regardless of whether
+ // or not it is actually indexable.
levels_.back().SetCurrentPropertyIndexable(true);
++current_top_level_indexable_nested_properties_idx_;
}
@@ -134,8 +141,14 @@ libtextclassifier3::Status SchemaPropertyIterator::Advance() {
property));
}
current_top_level_indexable_nested_properties_idx_ = 0;
+ // Sort elements and dedupe
std::sort(sorted_top_level_indexable_nested_properties_.begin(),
sorted_top_level_indexable_nested_properties_.end());
+ auto last =
+ std::unique(sorted_top_level_indexable_nested_properties_.begin(),
+ sorted_top_level_indexable_nested_properties_.end());
+ sorted_top_level_indexable_nested_properties_.erase(
+ last, sorted_top_level_indexable_nested_properties_.end());
}
bool is_cycle =
@@ -169,6 +182,15 @@ libtextclassifier3::Status SchemaPropertyIterator::Advance() {
all_nested_properties_indexable));
parent_type_config_names_.insert(nested_type_config.schema_type());
}
+
+ // Before returning, move all remaining uniterated properties from
+ // sorted_top_level_indexable_nested_properties_ into
+ // unknown_indexable_nested_properties_.
+ std::move(sorted_top_level_indexable_nested_properties_.begin() +
+ current_top_level_indexable_nested_properties_idx_,
+ sorted_top_level_indexable_nested_properties_.end(),
+ std::back_inserter(unknown_indexable_nested_property_paths_));
+
return absl_ports::OutOfRangeError("End of iterator");
}
diff --git a/icing/schema/schema-property-iterator.h b/icing/schema/schema-property-iterator.h
index 3babf9e..66b8f32 100644
--- a/icing/schema/schema-property-iterator.h
+++ b/icing/schema/schema-property-iterator.h
@@ -83,6 +83,15 @@ class SchemaPropertyIterator {
return levels_.back().GetLevelNestedIndexable();
}
+ // The set of indexable nested properties that are defined in the
+ // indexable_nested_properties_list but are not found in the schema
+ // definition. These properties still consume sectionIds, but will not be
+ // indexed.
+ const std::vector<std::string>& unknown_indexable_nested_property_paths()
+ const {
+ return unknown_indexable_nested_property_paths_;
+ }
+
// Advances to the next leaf property.
//
// Returns:
@@ -196,6 +205,15 @@ class SchemaPropertyIterator {
// Current iteration index in the sorted_top_level_indexable_nested_properties
// list.
int current_top_level_indexable_nested_properties_idx_ = 0;
+
+ // Vector of indexable nested properties defined in the
+ // indexable_nested_properties_list, but not found in the schema definition.
+ // These properties still consume sectionIds, but will not be indexed.
+ // Properties are inserted into this vector in sorted order.
+ //
+ // TODO(b/289152024): Implement support for indexing these properties if they
+ // are in the child types of polymorphic nested properties.
+ std::vector<std::string> unknown_indexable_nested_property_paths_;
};
} // namespace lib
diff --git a/icing/schema/schema-property-iterator_test.cc b/icing/schema/schema-property-iterator_test.cc
index ed71134..2b0226d 100644
--- a/icing/schema/schema-property-iterator_test.cc
+++ b/icing/schema/schema-property-iterator_test.cc
@@ -31,7 +31,9 @@ namespace lib {
namespace {
using portable_equals_proto::EqualsProto;
+using ::testing::ElementsAre;
using ::testing::Eq;
+using ::testing::IsEmpty;
using ::testing::IsFalse;
using ::testing::IsTrue;
@@ -42,13 +44,14 @@ TEST(SchemaPropertyIteratorTest,
SchemaTypeConfigProto schema_type_config =
SchemaTypeConfigBuilder()
.SetType(schema_type_name)
- .AddProperty(PropertyConfigBuilder().SetName("Google").SetDataType(
- TYPE_STRING))
+ .AddProperty(
+ PropertyConfigBuilder().SetName("Google").SetDataTypeString(
+ TERM_MATCH_EXACT, TOKENIZER_PLAIN))
.AddProperty(PropertyConfigBuilder().SetName("Youtube").SetDataType(
TYPE_BYTES))
.AddProperty(PropertyConfigBuilder()
.SetName("Alphabet")
- .SetDataType(TYPE_INT64))
+ .SetDataTypeInt64(NUMERIC_MATCH_UNKNOWN))
.Build();
SchemaUtil::TypeConfigMap type_config_map = {
{schema_type_name, schema_type_config}};
@@ -58,7 +61,7 @@ TEST(SchemaPropertyIteratorTest,
EXPECT_THAT(iterator.GetCurrentPropertyPath(), Eq("Alphabet"));
EXPECT_THAT(iterator.GetCurrentPropertyConfig(),
EqualsProto(schema_type_config.properties(2)));
- EXPECT_THAT(iterator.GetCurrentPropertyIndexable(), IsTrue());
+ EXPECT_THAT(iterator.GetCurrentPropertyIndexable(), IsFalse());
EXPECT_THAT(iterator.Advance(), IsOk());
EXPECT_THAT(iterator.GetCurrentPropertyPath(), Eq("Google"));
@@ -70,10 +73,12 @@ TEST(SchemaPropertyIteratorTest,
EXPECT_THAT(iterator.GetCurrentPropertyPath(), Eq("Youtube"));
EXPECT_THAT(iterator.GetCurrentPropertyConfig(),
EqualsProto(schema_type_config.properties(1)));
- EXPECT_THAT(iterator.GetCurrentPropertyIndexable(), IsTrue());
+ EXPECT_THAT(iterator.GetCurrentPropertyIndexable(), IsFalse());
EXPECT_THAT(iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(iterator.unknown_indexable_nested_property_paths(), IsEmpty());
}
TEST(SchemaPropertyIteratorTest,
@@ -85,19 +90,20 @@ TEST(SchemaPropertyIteratorTest,
SchemaTypeConfigProto schema_type_config1 =
SchemaTypeConfigBuilder()
.SetType(schema_type_name1)
- .AddProperty(PropertyConfigBuilder().SetName("Google").SetDataType(
- TYPE_STRING))
+ .AddProperty(
+ PropertyConfigBuilder().SetName("Google").SetDataTypeString(
+ TERM_MATCH_EXACT, TOKENIZER_PLAIN))
.AddProperty(PropertyConfigBuilder().SetName("Youtube").SetDataType(
TYPE_BYTES))
.AddProperty(PropertyConfigBuilder()
.SetName("Alphabet")
- .SetDataType(TYPE_INT64))
+ .SetDataTypeInt64(NUMERIC_MATCH_RANGE))
.Build();
SchemaTypeConfigProto schema_type_config2 =
SchemaTypeConfigBuilder()
.SetType(schema_type_name2)
- .AddProperty(
- PropertyConfigBuilder().SetName("Foo").SetDataType(TYPE_STRING))
+ .AddProperty(PropertyConfigBuilder().SetName("Foo").SetDataTypeString(
+ TERM_MATCH_UNKNOWN, TOKENIZER_NONE))
.AddProperty(
PropertyConfigBuilder().SetName("Bar").SetDataTypeDocument(
schema_type_name1, /*index_nested_properties=*/true))
@@ -106,7 +112,8 @@ TEST(SchemaPropertyIteratorTest,
SchemaTypeConfigBuilder()
.SetType(schema_type_name3)
.AddProperty(
- PropertyConfigBuilder().SetName("Hello").SetDataType(TYPE_STRING))
+ PropertyConfigBuilder().SetName("Hello").SetDataTypeString(
+ TERM_MATCH_EXACT, TOKENIZER_PLAIN))
.AddProperty(
PropertyConfigBuilder().SetName("World").SetDataTypeDocument(
schema_type_name1, /*index_nested_properties=*/true))
@@ -158,13 +165,13 @@ TEST(SchemaPropertyIteratorTest,
EXPECT_THAT(iterator.GetCurrentPropertyPath(), Eq("Icing.Bar.Youtube"));
EXPECT_THAT(iterator.GetCurrentPropertyConfig(),
EqualsProto(schema_type_config1.properties(1)));
- EXPECT_THAT(iterator.GetCurrentPropertyIndexable(), IsTrue());
+ EXPECT_THAT(iterator.GetCurrentPropertyIndexable(), IsFalse());
EXPECT_THAT(iterator.Advance(), IsOk());
EXPECT_THAT(iterator.GetCurrentPropertyPath(), Eq("Icing.Foo"));
EXPECT_THAT(iterator.GetCurrentPropertyConfig(),
EqualsProto(schema_type_config2.properties(0)));
- EXPECT_THAT(iterator.GetCurrentPropertyIndexable(), IsTrue());
+ EXPECT_THAT(iterator.GetCurrentPropertyIndexable(), IsFalse());
EXPECT_THAT(iterator.Advance(), IsOk());
EXPECT_THAT(iterator.GetCurrentPropertyPath(), Eq("World.Alphabet"));
@@ -182,10 +189,12 @@ TEST(SchemaPropertyIteratorTest,
EXPECT_THAT(iterator.GetCurrentPropertyPath(), Eq("World.Youtube"));
EXPECT_THAT(iterator.GetCurrentPropertyConfig(),
EqualsProto(schema_type_config1.properties(1)));
- EXPECT_THAT(iterator.GetCurrentPropertyIndexable(), IsTrue());
+ EXPECT_THAT(iterator.GetCurrentPropertyIndexable(), IsFalse());
EXPECT_THAT(iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(iterator.unknown_indexable_nested_property_paths(), IsEmpty());
}
TEST(SchemaPropertyIteratorTest,
@@ -235,6 +244,7 @@ TEST(SchemaPropertyIteratorTest,
SchemaPropertyIterator iterator(schema_type_config, type_config_map);
EXPECT_THAT(iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(iterator.unknown_indexable_nested_property_paths(), IsEmpty());
}
TEST(SchemaPropertyIteratorTest, NestedIndexable) {
@@ -413,6 +423,8 @@ TEST(SchemaPropertyIteratorTest, NestedIndexable) {
EXPECT_THAT(iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(iterator.unknown_indexable_nested_property_paths(), IsEmpty());
}
TEST(SchemaPropertyIteratorTest,
@@ -426,7 +438,7 @@ TEST(SchemaPropertyIteratorTest,
.AddProperty(
PropertyConfigBuilder()
.SetName("schema1prop1")
- .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN))
+ .SetDataTypeString(TERM_MATCH_UNKNOWN, TOKENIZER_NONE))
.AddProperty(
PropertyConfigBuilder()
.SetName("schema1prop2")
@@ -435,6 +447,12 @@ TEST(SchemaPropertyIteratorTest,
PropertyConfigBuilder()
.SetName("schema1prop3")
.SetDataTypeString(TERM_MATCH_UNKNOWN, TOKENIZER_NONE))
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("schema1prop4")
+ .SetDataTypeInt64(NUMERIC_MATCH_RANGE))
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("schema1prop5")
+ .SetDataType(TYPE_BOOLEAN))
.Build();
SchemaTypeConfigProto schema_type_config2 =
SchemaTypeConfigBuilder()
@@ -445,15 +463,15 @@ TEST(SchemaPropertyIteratorTest,
.SetDataTypeDocument(
schema_type_name1,
/*indexable_nested_properties_list=*/{"schema1prop2",
- "schema1prop3"}))
+ "schema1prop3",
+ "schema1prop5"}))
.AddProperty(
PropertyConfigBuilder()
.SetName("schema2prop2")
.SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN))
- .AddProperty(
- PropertyConfigBuilder()
- .SetName("schema2prop3")
- .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN))
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("schema2prop3")
+ .SetDataTypeInt64(NUMERIC_MATCH_UNKNOWN))
.Build();
SchemaUtil::TypeConfigMap type_config_map = {
{schema_type_name1, schema_type_config1},
@@ -461,11 +479,22 @@ TEST(SchemaPropertyIteratorTest,
// Order of iteration for Schema2:
// {"schema2prop1.schema1prop1", "schema2prop1.schema1prop2",
- // "schema2prop1.schema1prop3", "schema2prop2", "schema2prop3"}
+ // "schema2prop1.schema1prop3", "schema2prop1.schema1prop4",
+ // "schema2prop1.schema1prop5", "schema2prop2", "schema2prop3"}
//
// Indexable properties:
- // {"schema2prop1.schema1prop2", "schema2prop1.schema1prop3", "schema2prop2",
- // "schema2prop3"}
+ // {"schema2prop1.schema1prop2", "schema2prop1.schema1prop3",
+ // "schema2prop1.schema1prop5", "schema2prop2"}.
+ //
+ // "schema2prop1.schema1prop4" is indexable by its indexing-config, but is not
+ // considered indexable for Schema2 because Schema2 sets its
+ // index_nested_properties config to false, and "schema1prop4" is not
+ // in the indexable_nested_properties_list for schema2prop1.
+ //
+ // "schema2prop1.schema1prop1", "schema2prop1.schema1prop3" and
+ // "schema2prop1.schema1prop5" are non-indexable by its indexing-config.
+ // However "schema2prop1.schema1prop3" and "schema2prop1.schema1prop5" are
+ // indexed as it appears in the indexable_list.
SchemaPropertyIterator schema2_iterator(schema_type_config2, type_config_map);
EXPECT_THAT(schema2_iterator.Advance(), IsOk());
@@ -490,6 +519,20 @@ TEST(SchemaPropertyIteratorTest,
EXPECT_THAT(schema2_iterator.GetCurrentPropertyIndexable(), IsTrue());
EXPECT_THAT(schema2_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyPath(),
+ Eq("schema2prop1.schema1prop4"));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(3)));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyIndexable(), IsFalse());
+
+ EXPECT_THAT(schema2_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyPath(),
+ Eq("schema2prop1.schema1prop5"));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(4)));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyIndexable(), IsTrue());
+
+ EXPECT_THAT(schema2_iterator.Advance(), IsOk());
EXPECT_THAT(schema2_iterator.GetCurrentPropertyPath(), Eq("schema2prop2"));
EXPECT_THAT(schema2_iterator.GetCurrentPropertyConfig(),
EqualsProto(schema_type_config2.properties(1)));
@@ -499,10 +542,54 @@ TEST(SchemaPropertyIteratorTest,
EXPECT_THAT(schema2_iterator.GetCurrentPropertyPath(), Eq("schema2prop3"));
EXPECT_THAT(schema2_iterator.GetCurrentPropertyConfig(),
EqualsProto(schema_type_config2.properties(2)));
- EXPECT_THAT(schema2_iterator.GetCurrentPropertyIndexable(), IsTrue());
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyIndexable(), IsFalse());
EXPECT_THAT(schema2_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(schema2_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
+ // Iterate through schema1 properties. Schema1 only has non-document type leaf
+ // properties, so its properties will be assigned indexable or not according
+ // to their indexing configs.
+ SchemaPropertyIterator schema1_iterator(schema_type_config1, type_config_map);
+
+ EXPECT_THAT(schema1_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema1_iterator.GetCurrentPropertyPath(), Eq("schema1prop1"));
+ EXPECT_THAT(schema1_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(0)));
+ EXPECT_THAT(schema1_iterator.GetCurrentPropertyIndexable(), IsFalse());
+
+ EXPECT_THAT(schema1_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema1_iterator.GetCurrentPropertyPath(), Eq("schema1prop2"));
+ EXPECT_THAT(schema1_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(1)));
+ EXPECT_THAT(schema1_iterator.GetCurrentPropertyIndexable(), IsTrue());
+
+ EXPECT_THAT(schema1_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema1_iterator.GetCurrentPropertyPath(), Eq("schema1prop3"));
+ EXPECT_THAT(schema1_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(2)));
+ EXPECT_THAT(schema1_iterator.GetCurrentPropertyIndexable(), IsFalse());
+
+ EXPECT_THAT(schema1_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema1_iterator.GetCurrentPropertyPath(), Eq("schema1prop4"));
+ EXPECT_THAT(schema1_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(3)));
+ EXPECT_THAT(schema1_iterator.GetCurrentPropertyIndexable(), IsTrue());
+
+ EXPECT_THAT(schema1_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema1_iterator.GetCurrentPropertyPath(), Eq("schema1prop5"));
+ EXPECT_THAT(schema1_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(4)));
+ EXPECT_THAT(schema1_iterator.GetCurrentPropertyIndexable(), IsFalse());
+
+ EXPECT_THAT(schema1_iterator.Advance(),
+ StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(schema1_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
}
TEST(SchemaPropertyIteratorTest,
@@ -654,6 +741,9 @@ TEST(SchemaPropertyIteratorTest,
EXPECT_THAT(schema3_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema3_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration for Schema2:
// {"schema2prop1.schema1prop1", "schema2prop1.schema1prop2",
// "schema2prop1.schema1prop3", "schema2prop2", "schema2prop3"}
@@ -702,6 +792,9 @@ TEST(SchemaPropertyIteratorTest,
EXPECT_THAT(schema2_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(schema2_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
}
TEST(SchemaPropertyIteratorTest,
@@ -774,6 +867,9 @@ TEST(SchemaPropertyIteratorTest,
EXPECT_THAT(schema3_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema3_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration for Schema2:
// {"schema2prop1.schema1prop1", "schema2prop1.schema1prop2"}
//
@@ -798,6 +894,9 @@ TEST(SchemaPropertyIteratorTest,
EXPECT_THAT(schema2_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(schema2_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
}
TEST(SchemaPropertyIteratorTest,
@@ -951,6 +1050,9 @@ TEST(SchemaPropertyIteratorTest,
EXPECT_THAT(schema3_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema3_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration for Schema2:
// {"schema2prop1.schema1prop1", "schema2prop1.schema1prop2",
// "schema2prop1.schema1prop3", "schema2prop2", "schema2prop3"}
@@ -997,6 +1099,9 @@ TEST(SchemaPropertyIteratorTest,
EXPECT_THAT(schema2_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(schema2_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
}
TEST(
@@ -1084,6 +1189,9 @@ TEST(
EXPECT_THAT(schema4_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema4_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration for Schema3:
// {"schema3prop1.schema2prop1.schema1prop1",
// "schema3prop1.schema2prop1.schema1prop2"}.
@@ -1108,6 +1216,9 @@ TEST(
EXPECT_THAT(schema3_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema3_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration for Schema2:
// {"schema2prop1.schema1prop1", "schema2prop1.schema1prop2"}
//
@@ -1133,6 +1244,443 @@ TEST(
EXPECT_THAT(schema2_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(schema2_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+}
+
+TEST(SchemaPropertyIteratorTest,
+ IndexableNestedPropertiesList_unknownPropPaths) {
+ std::string schema_type_name1 = "SchemaOne";
+ std::string schema_type_name2 = "SchemaTwo";
+ std::string schema_type_name3 = "SchemaThree";
+ std::string schema_type_name4 = "SchemaFour";
+
+ SchemaTypeConfigProto schema_type_config1 =
+ SchemaTypeConfigBuilder()
+ .SetType(schema_type_name1)
+ .AddProperty(
+ PropertyConfigBuilder()
+ .SetName("schema1prop1")
+ .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN))
+ .AddProperty(
+ PropertyConfigBuilder()
+ .SetName("schema1prop2")
+ .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN))
+ .Build();
+ SchemaTypeConfigProto schema_type_config2 =
+ SchemaTypeConfigBuilder()
+ .SetType(schema_type_name2)
+ .AddProperty(
+ PropertyConfigBuilder()
+ .SetName("schema2prop1")
+ .SetDataTypeDocument(schema_type_name1,
+ /*indexable_nested_properties_list=*/
+ {"schema1prop2", "schema1prop2.foo",
+ "foo.bar", "zzz", "aaa.zzz"}))
+ .AddProperty(
+ PropertyConfigBuilder()
+ .SetName("schema2prop2")
+ .SetDataTypeDocument(
+ schema_type_name1,
+ /*indexable_nested_properties_list=*/
+ {"schema1prop1", "schema1prop2", "unknown.path"}))
+ .Build();
+ SchemaTypeConfigProto schema_type_config3 =
+ SchemaTypeConfigBuilder()
+ .SetType(schema_type_name3)
+ .AddProperty(
+ PropertyConfigBuilder()
+ .SetName("schema3prop1")
+ .SetDataTypeDocument(
+ schema_type_name2,
+ /*indexable_nested_properties_list=*/
+ {"schema3prop1", "schema2prop1", "schema1prop2",
+ "schema2prop1.schema1prop2", "schema2prop1.zzz", "zzz"}))
+ .Build();
+ SchemaTypeConfigProto schema_type_config4 =
+ SchemaTypeConfigBuilder()
+ .SetType(schema_type_name4)
+ .AddProperty(
+ PropertyConfigBuilder()
+ .SetName("schema4prop1")
+ .SetDataTypeDocument(schema_type_name3,
+ /*index_nested_properties=*/true))
+ .Build();
+ SchemaUtil::TypeConfigMap type_config_map = {
+ {schema_type_name1, schema_type_config1},
+ {schema_type_name2, schema_type_config2},
+ {schema_type_name3, schema_type_config3},
+ {schema_type_name4, schema_type_config4}};
+
+ // Order of iteration for Schema4:
+ // "schema4prop1.schema3prop1.schema2prop1.schema1prop1",
+ // "schema4prop1.schema3prop1.schema2prop1.schema1prop2" (indexable),
+ // "schema4prop1.schema3prop1.schema2prop2.schema1prop1",
+ // "schema4prop1.schema3prop1.schema2prop2.schema1prop2"
+ //
+ // Unknown property paths from schema3 will also be included for schema4,
+ // since schema4 sets index_nested_properties=true.
+ // This includes everything in schema3prop1's list except
+ // "schema2prop1.schema1prop2".
+ SchemaPropertyIterator schema4_iterator(schema_type_config4, type_config_map);
+
+ EXPECT_THAT(schema4_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyPath(),
+ Eq("schema4prop1.schema3prop1.schema2prop1.schema1prop1"));
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(0)));
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyIndexable(), IsFalse());
+
+ EXPECT_THAT(schema4_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyPath(),
+ Eq("schema4prop1.schema3prop1.schema2prop1.schema1prop2"));
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(1)));
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyIndexable(), IsTrue());
+
+ EXPECT_THAT(schema4_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyPath(),
+ Eq("schema4prop1.schema3prop1.schema2prop2.schema1prop1"));
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(0)));
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyIndexable(), IsFalse());
+
+ EXPECT_THAT(schema4_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyPath(),
+ Eq("schema4prop1.schema3prop1.schema2prop2.schema1prop2"));
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(1)));
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyIndexable(), IsFalse());
+
+ EXPECT_THAT(schema4_iterator.Advance(),
+ StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(schema4_iterator.unknown_indexable_nested_property_paths(),
+ testing::ElementsAre("schema4prop1.schema3prop1.schema1prop2",
+ "schema4prop1.schema3prop1.schema2prop1",
+ "schema4prop1.schema3prop1.schema2prop1.zzz",
+ "schema4prop1.schema3prop1.schema3prop1",
+ "schema4prop1.schema3prop1.zzz"));
+
+ // Order of iteration for Schema3:
+ // "schema3prop1.schema2prop1.schema1prop1",
+ // "schema3prop1.schema2prop1.schema1prop2" (indexable),
+ // "schema3prop1.schema2prop2.schema1prop1",
+ // "schema3prop1.schema2prop2.schema1prop2"
+ //
+ // Unknown properties (in order):
+ // "schema3prop1.schema1prop2", "schema3prop1.schema2prop1" (not a leaf prop),
+ // "schema3prop1.schema2prop1.zzz", "schema3prop1.schema3prop1",
+ // "schema3prop1.zzz"
+ SchemaPropertyIterator schema3_iterator(schema_type_config3, type_config_map);
+
+ EXPECT_THAT(schema3_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyPath(),
+ Eq("schema3prop1.schema2prop1.schema1prop1"));
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(0)));
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyIndexable(), IsFalse());
+
+ EXPECT_THAT(schema3_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyPath(),
+ Eq("schema3prop1.schema2prop1.schema1prop2"));
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(1)));
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyIndexable(), IsTrue());
+
+ EXPECT_THAT(schema3_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyPath(),
+ Eq("schema3prop1.schema2prop2.schema1prop1"));
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(0)));
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyIndexable(), IsFalse());
+
+ EXPECT_THAT(schema3_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyPath(),
+ Eq("schema3prop1.schema2prop2.schema1prop2"));
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(1)));
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyIndexable(), IsFalse());
+
+ EXPECT_THAT(schema3_iterator.Advance(),
+ StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(schema3_iterator.unknown_indexable_nested_property_paths(),
+ testing::ElementsAre(
+ "schema3prop1.schema1prop2", "schema3prop1.schema2prop1",
+ "schema3prop1.schema2prop1.zzz", "schema3prop1.schema3prop1",
+ "schema3prop1.zzz"));
+
+ // Order of iteration for Schema2:
+ // "schema2prop1.schema1prop1",
+ // "schema2prop1.schema1prop2" (indexable),
+ // "schema2prop2.schema1prop1" (indexable),
+ // "schema2prop2.schema1prop2" (indexable)
+ //
+ // Unknown properties (in order):
+ // "schema2prop1.aaa.zzz", "schema2prop1.foo.bar",
+ // "schema2prop1.schema1prop2.foo", "schema2prop1.zzz",
+ // "schema2prop2.unknown.path"
+ SchemaPropertyIterator schema2_iterator(schema_type_config2, type_config_map);
+
+ EXPECT_THAT(schema2_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyPath(),
+ Eq("schema2prop1.schema1prop1"));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(0)));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyIndexable(), IsFalse());
+
+ EXPECT_THAT(schema2_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyPath(),
+ Eq("schema2prop1.schema1prop2"));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(1)));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyIndexable(), IsTrue());
+
+ EXPECT_THAT(schema2_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyPath(),
+ Eq("schema2prop2.schema1prop1"));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(0)));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyIndexable(), IsTrue());
+
+ EXPECT_THAT(schema2_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyPath(),
+ Eq("schema2prop2.schema1prop2"));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(1)));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyIndexable(), IsTrue());
+
+ EXPECT_THAT(schema2_iterator.Advance(),
+ StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(
+ schema2_iterator.unknown_indexable_nested_property_paths(),
+ testing::ElementsAre("schema2prop1.aaa.zzz", "schema2prop1.foo.bar",
+ "schema2prop1.schema1prop2.foo", "schema2prop1.zzz",
+ "schema2prop2.unknown.path"));
+}
+
+TEST(SchemaPropertyIteratorTest,
+ IndexableNestedPropertiesListDuplicateElements) {
+ std::string schema_type_name1 = "SchemaOne";
+ std::string schema_type_name2 = "SchemaTwo";
+ std::string schema_type_name3 = "SchemaThree";
+ std::string schema_type_name4 = "SchemaFour";
+
+ SchemaTypeConfigProto schema_type_config1 =
+ SchemaTypeConfigBuilder()
+ .SetType(schema_type_name1)
+ .AddProperty(
+ PropertyConfigBuilder()
+ .SetName("schema1prop1")
+ .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN))
+ .AddProperty(
+ PropertyConfigBuilder()
+ .SetName("schema1prop2")
+ .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN))
+ .Build();
+ SchemaTypeConfigProto schema_type_config2 =
+ SchemaTypeConfigBuilder()
+ .SetType(schema_type_name2)
+ .AddProperty(
+ PropertyConfigBuilder()
+ .SetName("schema2prop1")
+ .SetDataTypeDocument(
+ schema_type_name1,
+ /*indexable_nested_properties_list=*/
+ {"schema1prop2", "schema1prop2", "schema1prop2.foo",
+ "schema1prop2.foo", "foo.bar", "foo.bar", "foo.bar",
+ "zzz", "zzz", "aaa.zzz", "schema1prop2"}))
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("schema2prop2")
+ .SetDataTypeDocument(
+ schema_type_name1,
+ /*indexable_nested_properties_list=*/
+ {"schema1prop1", "schema1prop2", "unknown.path",
+ "unknown.path", "unknown.path", "unknown.path",
+ "schema1prop1"}))
+ .Build();
+ SchemaTypeConfigProto schema_type_config3 =
+ SchemaTypeConfigBuilder()
+ .SetType(schema_type_name3)
+ .AddProperty(
+ PropertyConfigBuilder()
+ .SetName("schema3prop1")
+ .SetDataTypeDocument(
+ schema_type_name2,
+ /*indexable_nested_properties_list=*/
+ {"schema3prop1", "schema3prop1", "schema2prop1",
+ "schema2prop1", "schema1prop2", "schema1prop2",
+ "schema2prop1.schema1prop2", "schema2prop1.schema1prop2",
+ "schema2prop1.zzz", "zzz", "zzz"}))
+ .Build();
+ SchemaTypeConfigProto schema_type_config4 =
+ SchemaTypeConfigBuilder()
+ .SetType(schema_type_name4)
+ .AddProperty(
+ PropertyConfigBuilder()
+ .SetName("schema4prop1")
+ .SetDataTypeDocument(schema_type_name3,
+ /*index_nested_properties=*/true))
+ .Build();
+ SchemaUtil::TypeConfigMap type_config_map = {
+ {schema_type_name1, schema_type_config1},
+ {schema_type_name2, schema_type_config2},
+ {schema_type_name3, schema_type_config3},
+ {schema_type_name4, schema_type_config4}};
+
+ // The results of this test case is the same as the previous test case. This
+ // is to test that the indexable-list is deduped correctly.
+
+ // Order of iteration for Schema4:
+ // "schema4prop1.schema3prop1.schema2prop1.schema1prop1",
+ // "schema4prop1.schema3prop1.schema2prop1.schema1prop2" (indexable),
+ // "schema4prop1.schema3prop1.schema2prop2.schema1prop1",
+ // "schema4prop1.schema3prop1.schema2prop2.schema1prop2"
+ //
+ // Unknown property paths from schema3 will also be included for schema4,
+ // since schema4 sets index_nested_properties=true.
+ // This includes everything in schema3prop1's list except
+ // "schema2prop1.schema1prop2".
+ SchemaPropertyIterator schema4_iterator(schema_type_config4, type_config_map);
+
+ EXPECT_THAT(schema4_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyPath(),
+ Eq("schema4prop1.schema3prop1.schema2prop1.schema1prop1"));
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(0)));
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyIndexable(), IsFalse());
+
+ EXPECT_THAT(schema4_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyPath(),
+ Eq("schema4prop1.schema3prop1.schema2prop1.schema1prop2"));
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(1)));
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyIndexable(), IsTrue());
+
+ EXPECT_THAT(schema4_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyPath(),
+ Eq("schema4prop1.schema3prop1.schema2prop2.schema1prop1"));
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(0)));
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyIndexable(), IsFalse());
+
+ EXPECT_THAT(schema4_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyPath(),
+ Eq("schema4prop1.schema3prop1.schema2prop2.schema1prop2"));
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(1)));
+ EXPECT_THAT(schema4_iterator.GetCurrentPropertyIndexable(), IsFalse());
+
+ EXPECT_THAT(schema4_iterator.Advance(),
+ StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(schema4_iterator.unknown_indexable_nested_property_paths(),
+ testing::ElementsAre("schema4prop1.schema3prop1.schema1prop2",
+ "schema4prop1.schema3prop1.schema2prop1",
+ "schema4prop1.schema3prop1.schema2prop1.zzz",
+ "schema4prop1.schema3prop1.schema3prop1",
+ "schema4prop1.schema3prop1.zzz"));
+
+ // Order of iteration for Schema3:
+ // "schema3prop1.schema2prop1.schema1prop1",
+ // "schema3prop1.schema2prop1.schema1prop2" (indexable),
+ // "schema3prop1.schema2prop2.schema1prop1",
+ // "schema3prop1.schema2prop2.schema1prop2"
+ //
+ // Unknown properties (in order):
+ // "schema2prop1.aaa.zzz", "schema2prop1.foo.bar",
+ // "schema2prop1.schema1prop2.foo", "schema2prop1.zzz",
+ // "schema2prop2.unknown.path"
+ SchemaPropertyIterator schema3_iterator(schema_type_config3, type_config_map);
+
+ EXPECT_THAT(schema3_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyPath(),
+ Eq("schema3prop1.schema2prop1.schema1prop1"));
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(0)));
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyIndexable(), IsFalse());
+
+ EXPECT_THAT(schema3_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyPath(),
+ Eq("schema3prop1.schema2prop1.schema1prop2"));
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(1)));
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyIndexable(), IsTrue());
+
+ EXPECT_THAT(schema3_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyPath(),
+ Eq("schema3prop1.schema2prop2.schema1prop1"));
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(0)));
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyIndexable(), IsFalse());
+
+ EXPECT_THAT(schema3_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyPath(),
+ Eq("schema3prop1.schema2prop2.schema1prop2"));
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(1)));
+ EXPECT_THAT(schema3_iterator.GetCurrentPropertyIndexable(), IsFalse());
+
+ EXPECT_THAT(schema3_iterator.Advance(),
+ StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(schema3_iterator.unknown_indexable_nested_property_paths(),
+ testing::ElementsAre(
+ "schema3prop1.schema1prop2", "schema3prop1.schema2prop1",
+ "schema3prop1.schema2prop1.zzz", "schema3prop1.schema3prop1",
+ "schema3prop1.zzz"));
+
+ // Order of iteration for Schema2:
+ // "schema2prop1.schema1prop1",
+ // "schema2prop1.schema1prop2" (indexable),
+ // "schema2prop2.schema1prop1" (indexable),
+ // "schema2prop2.schema1prop2" (indexable)
+ //
+ // Unknown properties (in order):
+ // "schema2prop1.aaa.zzz", "schema2prop1.foo.bar",
+ // "schema2prop1.schema1prop2.foo", "schema2prop1.zzz",
+ // "schema2prop2.unknown.path"
+ SchemaPropertyIterator schema2_iterator(schema_type_config2, type_config_map);
+
+ EXPECT_THAT(schema2_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyPath(),
+ Eq("schema2prop1.schema1prop1"));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(0)));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyIndexable(), IsFalse());
+
+ EXPECT_THAT(schema2_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyPath(),
+ Eq("schema2prop1.schema1prop2"));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(1)));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyIndexable(), IsTrue());
+
+ EXPECT_THAT(schema2_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyPath(),
+ Eq("schema2prop2.schema1prop1"));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(0)));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyIndexable(), IsTrue());
+
+ EXPECT_THAT(schema2_iterator.Advance(), IsOk());
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyPath(),
+ Eq("schema2prop2.schema1prop2"));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyConfig(),
+ EqualsProto(schema_type_config1.properties(1)));
+ EXPECT_THAT(schema2_iterator.GetCurrentPropertyIndexable(), IsTrue());
+
+ EXPECT_THAT(schema2_iterator.Advance(),
+ StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(
+ schema2_iterator.unknown_indexable_nested_property_paths(),
+ testing::ElementsAre("schema2prop1.aaa.zzz", "schema2prop1.foo.bar",
+ "schema2prop1.schema1prop2.foo", "schema2prop1.zzz",
+ "schema2prop2.unknown.path"));
}
TEST(SchemaPropertyIteratorTest,
@@ -1288,6 +1836,9 @@ TEST(SchemaPropertyIteratorTest,
EXPECT_THAT(schema3_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema3_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration for Schema2:
// {"prop1.prop1", "prop1.prop2",
// "prop1.prop3", "prop2", "prop3"}
@@ -1331,6 +1882,9 @@ TEST(SchemaPropertyIteratorTest,
EXPECT_THAT(schema2_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(schema2_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
}
TEST(SchemaPropertyIteratorTest, SingleLevelCycle) {
std::string schema_a = "A";
@@ -1386,6 +1940,9 @@ TEST(SchemaPropertyIteratorTest, SingleLevelCycle) {
EXPECT_THAT(schema_a_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema_a_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration for schema B:
// {"schemaBprop2"}, indexable.
SchemaPropertyIterator schema_b_iterator(schema_type_config_b,
@@ -1399,6 +1956,9 @@ TEST(SchemaPropertyIteratorTest, SingleLevelCycle) {
EXPECT_THAT(schema_b_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(schema_b_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
}
TEST(SchemaPropertyIteratorTest, MultipleLevelCycle) {
@@ -1478,6 +2038,9 @@ TEST(SchemaPropertyIteratorTest, MultipleLevelCycle) {
EXPECT_THAT(schema_a_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema_a_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration for schema B:
// {"schemaBprop1.schemaCprop1.schemaAprop2", "schemaBprop1.schemaCprop2",
// "schemaBprop2"}
@@ -1509,6 +2072,9 @@ TEST(SchemaPropertyIteratorTest, MultipleLevelCycle) {
EXPECT_THAT(schema_b_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema_b_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration for schema C:
// {"schemaCprop1.schemaAprop1.schemaBprop2", "schemaCprop1.schemaAprop2",
// "schemaCprop2"}
@@ -1539,6 +2105,9 @@ TEST(SchemaPropertyIteratorTest, MultipleLevelCycle) {
EXPECT_THAT(schema_c_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(schema_c_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
}
TEST(SchemaPropertyIteratorTest, SingleLevelCycleWithIndexableList) {
@@ -1661,6 +2230,9 @@ TEST(SchemaPropertyIteratorTest, SingleLevelCycleWithIndexableList) {
EXPECT_THAT(schema_a_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema_a_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration for schema B:
// {"schemaBprop1" (true),
// "schemaBprop2.schemaBprop1" (true),
@@ -1729,6 +2301,9 @@ TEST(SchemaPropertyIteratorTest, SingleLevelCycleWithIndexableList) {
EXPECT_THAT(schema_b_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(schema_b_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
}
TEST(SchemaPropertyIteratorTest, MultipleCycles) {
@@ -1838,6 +2413,9 @@ TEST(SchemaPropertyIteratorTest, MultipleCycles) {
EXPECT_THAT(schema_a_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema_a_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration for schema B:
// {"schemaBprop1.schemaCprop1.schemaAprop2",
// "schemaBprop1.schemaCprop1.schemaAprop3.schemaDprop2",
@@ -1877,6 +2455,9 @@ TEST(SchemaPropertyIteratorTest, MultipleCycles) {
EXPECT_THAT(schema_b_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema_b_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration for schema C:
// {"schemaCprop1.schemaAprop1.schemaBprop2", "schemaCprop1.schemaAprop2",
// "schemaCprop1.schemaAprop3.schemaDprop2", "schemaCprop2"}
@@ -1915,6 +2496,9 @@ TEST(SchemaPropertyIteratorTest, MultipleCycles) {
EXPECT_THAT(schema_c_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema_c_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration for schema D:
// {"schemaDprop1.schemaAprop1.schemaBprop1.schemaCprop2",
// "schemaDprop1.schemaAprop1.schemaBprop2", "schemaDprop1.schemaAprop2",
@@ -1953,6 +2537,9 @@ TEST(SchemaPropertyIteratorTest, MultipleCycles) {
EXPECT_THAT(schema_d_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(schema_d_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
}
TEST(SchemaPropertyIteratorTest, MultipleCyclesWithIndexableList) {
@@ -2143,6 +2730,9 @@ TEST(SchemaPropertyIteratorTest, MultipleCyclesWithIndexableList) {
EXPECT_THAT(schema_a_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema_a_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration and whether each property is indexable for schema B:
// "schemaBprop1.schemaCprop1.schemaAprop2" (false),
// "schemaBprop1.schemaCprop1.schemaAprop3.schemaDprop2" (false),
@@ -2181,6 +2771,9 @@ TEST(SchemaPropertyIteratorTest, MultipleCyclesWithIndexableList) {
EXPECT_THAT(schema_b_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema_b_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration for schema C:
// "schemaCprop1.schemaAprop1.schemaBprop2" (false),
// "schemaCprop1.schemaAprop2" (false),
@@ -2219,6 +2812,9 @@ TEST(SchemaPropertyIteratorTest, MultipleCyclesWithIndexableList) {
EXPECT_THAT(schema_c_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema_c_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration for schema D:
// "schemaDprop1.schemaAprop1.schemaBprop1.schemaCprop2" (false),
// "schemaDprop1.schemaAprop1.schemaBprop2" (false),
@@ -2256,6 +2852,9 @@ TEST(SchemaPropertyIteratorTest, MultipleCyclesWithIndexableList) {
EXPECT_THAT(schema_d_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(schema_d_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
}
TEST(SchemaPropertyIteratorTest, MultipleCyclesWithIndexableList_allIndexTrue) {
@@ -2446,6 +3045,9 @@ TEST(SchemaPropertyIteratorTest, MultipleCyclesWithIndexableList_allIndexTrue) {
EXPECT_THAT(schema_a_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema_a_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration and whether each property is indexable for schema B:
// "schemaBprop1.schemaCprop1.schemaAprop1.schemaBprop1.schemaCprop1.schemaAprop1.schemaBprop2"
// (true),
@@ -2579,6 +3181,9 @@ TEST(SchemaPropertyIteratorTest, MultipleCyclesWithIndexableList_allIndexTrue) {
EXPECT_THAT(schema_b_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema_b_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration and whether each property is indexable for schema C:
// "schemaCprop1.schemaAprop1.schemaBprop1.schemaCprop1.schemaAprop1.schemaBprop2"
// (true), "schemaCprop1.schemaAprop1.schemaBprop1.schemaCprop1.schemaAprop2"
@@ -2700,6 +3305,9 @@ TEST(SchemaPropertyIteratorTest, MultipleCyclesWithIndexableList_allIndexTrue) {
EXPECT_THAT(schema_c_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema_c_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration and whether each property is indexable for schema D:
// "schemaDprop1.schemaAprop1.schemaBprop1.schemaCprop1.schemaAprop1.schemaBprop2"
// (true), "schemaDprop1.schemaAprop1.schemaBprop1.schemaCprop1.schemaAprop2"
@@ -2818,10 +3426,13 @@ TEST(SchemaPropertyIteratorTest, MultipleCyclesWithIndexableList_allIndexTrue) {
EXPECT_THAT(schema_d_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(schema_d_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
}
TEST(SchemaPropertyIteratorTest,
- MultipleCyclesWithIndexableList_nonExistentPropPaths) {
+ MultipleCyclesWithIndexableList_unknownPropPaths) {
std::string schema_a = "A";
std::string schema_b = "B";
std::string schema_c = "C";
@@ -2910,7 +3521,7 @@ TEST(SchemaPropertyIteratorTest,
{schema_d, schema_type_config_d}};
// Order of iteration and whether each property is indexable for schema A:
- // {"schemaAprop1.schemaBprop1.schemaCprop1.schemaAprop1.schemaBprop2" (true),
+ // "schemaAprop1.schemaBprop1.schemaCprop1.schemaAprop1.schemaBprop2" (true),
// "schemaAprop1.schemaBprop1.schemaCprop1.schemaAprop2" (true),
// "schemaAprop1.schemaBprop1.schemaCprop1.schemaAprop3.schemaDprop1.schemaAprop2"
// (true), "schemaAprop1.schemaBprop1.schemaCprop1.schemaAprop3.schemaDprop2"
@@ -2924,14 +3535,20 @@ TEST(SchemaPropertyIteratorTest,
// "schemaAprop3.schemaDprop2" (true)
//
// The following properties listed in the indexable_list are not defined
- // in the schema and should not be seen during iteration:
- // - From schemaAprop1's list:
- // "schemaBprop1.schemaCprop1", "schemaBprop1.schemaCprop1.schemaAprop3",
- // "schemaAprop2", "schemaBprop2.schemaCprop2", "schemaBprop1.foo.bar",
- // "foo", "bar"
- // - From schemaAprop3's list:
- // "schemaBprop2", "bar", "schemaDprop2.foo", "schemaDprop1",
- // "schemaAprop3.schemaDprop2"
+ // in the schema and should not be seen during iteration. These should appear
+ // in the unknown_indexable_nested_properties_ set.
+ // "schemaAprop1.bar",
+ // "schemaAprop1.foo",
+ // "schemaAprop1.schemaAprop2",
+ // "schemaAprop1.schemaBprop1.foo.bar",
+ // "schemaAprop1.schemaBprop1.schemaCprop1",
+ // "schemaAprop1.schemaBprop1.schemaCprop1.schemaAprop3",
+ // "schemaAprop1.schemaBprop2.schemaCprop2",
+ // "schemaAprop3.bar",
+ // "schemaAprop3.schemaAprop3.schemaDprop2",
+ // "schemaAprop3.schemaBprop2",
+ // "schemaAprop3.schemaDprop1",
+ // "schemaAprop3.schemaDprop2.foo"
SchemaPropertyIterator schema_a_iterator(schema_type_config_a,
type_config_map);
@@ -3025,6 +3642,17 @@ TEST(SchemaPropertyIteratorTest,
EXPECT_THAT(schema_a_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(
+ schema_a_iterator.unknown_indexable_nested_property_paths(),
+ ElementsAre(
+ "schemaAprop1.bar", "schemaAprop1.foo", "schemaAprop1.schemaAprop2",
+ "schemaAprop1.schemaBprop1.foo.bar",
+ "schemaAprop1.schemaBprop1.schemaCprop1",
+ "schemaAprop1.schemaBprop1.schemaCprop1.schemaAprop3",
+ "schemaAprop1.schemaBprop2.schemaCprop2", "schemaAprop3.bar",
+ "schemaAprop3.schemaAprop3.schemaDprop2", "schemaAprop3.schemaBprop2",
+ "schemaAprop3.schemaDprop1", "schemaAprop3.schemaDprop2.foo"));
+
// Order of iteration and whether each property is indexable for schema B:
// "schemaBprop1.schemaCprop1.schemaAprop2" (false),
// "schemaBprop1.schemaCprop1.schemaAprop3.schemaDprop2" (false),
@@ -3063,6 +3691,9 @@ TEST(SchemaPropertyIteratorTest,
EXPECT_THAT(schema_b_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema_b_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration for schema C:
// "schemaCprop1.schemaAprop1.schemaBprop2" (false),
// "schemaCprop1.schemaAprop2" (false),
@@ -3101,6 +3732,9 @@ TEST(SchemaPropertyIteratorTest,
EXPECT_THAT(schema_c_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+ EXPECT_THAT(schema_c_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
+
// Order of iteration for schema D:
// "schemaDprop1.schemaAprop1.schemaBprop1.schemaCprop2" (false),
// "schemaDprop1.schemaAprop1.schemaBprop2" (false),
@@ -3138,6 +3772,9 @@ TEST(SchemaPropertyIteratorTest,
EXPECT_THAT(schema_d_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(schema_d_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
}
TEST(SchemaPropertyIteratorTest, TopLevelCycleWithMultipleIndexableLists) {
@@ -3257,6 +3894,9 @@ TEST(SchemaPropertyIteratorTest, TopLevelCycleWithMultipleIndexableLists) {
EXPECT_THAT(schema_a_iterator.Advance(),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
+
+ EXPECT_THAT(schema_a_iterator.unknown_indexable_nested_property_paths(),
+ IsEmpty());
}
} // namespace
diff --git a/icing/schema/schema-type-manager.cc b/icing/schema/schema-type-manager.cc
index ca2f45c..4a6b7f2 100644
--- a/icing/schema/schema-type-manager.cc
+++ b/icing/schema/schema-type-manager.cc
@@ -20,6 +20,7 @@
#include "icing/text_classifier/lib3/utils/base/statusor.h"
#include "icing/absl_ports/canonical_errors.h"
#include "icing/schema/joinable-property-manager.h"
+#include "icing/schema/property-util.h"
#include "icing/schema/schema-property-iterator.h"
#include "icing/schema/schema-util.h"
#include "icing/schema/section-manager.h"
@@ -68,6 +69,34 @@ SchemaTypeManager::Create(const SchemaUtil::TypeConfigMap& type_config_map,
schema_type_id, iterator.GetCurrentPropertyConfig(),
iterator.GetCurrentPropertyPath()));
}
+
+ // Process unknown property paths in the indexable_nested_properties_list.
+ // These property paths should consume sectionIds but are currently
+ // not indexed.
+ //
+ // SectionId assignment order:
+ // - We assign section ids to known (existing) properties first in alphabet
+ // order.
+ // - After handling all known properties, we assign section ids to all
+ // unknown (non-existent) properties that are specified in the
+ // indexable_nested_properties_list.
+ // - As a result, assignment of the entire section set is not done
+ // alphabetically, but assignment is still deterministic and alphabetical
+ // order is preserved inside the known properties and unknown properties
+ // sets individually.
+ for (const auto& property_path :
+ iterator.unknown_indexable_nested_property_paths()) {
+ PropertyConfigProto unknown_property_config;
+ unknown_property_config.set_property_name(std::string(
+ property_util::SplitPropertyPathExpr(property_path).back()));
+ unknown_property_config.set_data_type(
+ PropertyConfigProto::DataType::UNKNOWN);
+
+ ICING_RETURN_IF_ERROR(
+ section_manager_builder.ProcessSchemaTypePropertyConfig(
+ schema_type_id, unknown_property_config,
+ std::string(property_path)));
+ }
}
return std::unique_ptr<SchemaTypeManager>(new SchemaTypeManager(
diff --git a/icing/schema/section-manager-builder_test.cc b/icing/schema/section-manager-builder_test.cc
index 60dd507..1d452d5 100644
--- a/icing/schema/section-manager-builder_test.cc
+++ b/icing/schema/section-manager-builder_test.cc
@@ -270,9 +270,11 @@ TEST_P(NonIndexableSectionManagerBuilderTest, Build) {
ICING_ASSERT_OK(builder.ProcessSchemaTypePropertyConfig(
/*schema_type_id=*/0, property_config, std::string(kPropertyPath)));
+ // NonIndexable sections will still consume a sectionId.
std::unique_ptr<SectionManager> section_manager = std::move(builder).Build();
EXPECT_THAT(section_manager->GetMetadataList(std::string(kSchemaType)),
- IsOkAndHolds(Pointee(IsEmpty())));
+ IsOkAndHolds(Pointee(ElementsAre(EqualsSectionMetadata(
+ /*expected_id=*/0, kPropertyPath, property_config)))));
}
// The following types are considered non-indexable:
diff --git a/icing/schema/section-manager.cc b/icing/schema/section-manager.cc
index 38042d0..3d540d6 100644
--- a/icing/schema/section-manager.cc
+++ b/icing/schema/section-manager.cc
@@ -15,15 +15,9 @@
#include "icing/schema/section-manager.h"
#include <algorithm>
-#include <cinttypes>
-#include <cstddef>
#include <cstdint>
-#include <iterator>
-#include <memory>
#include <string>
#include <string_view>
-#include <unordered_map>
-#include <unordered_set>
#include <utility>
#include <vector>
@@ -35,7 +29,6 @@
#include "icing/proto/schema.pb.h"
#include "icing/proto/term.pb.h"
#include "icing/schema/property-util.h"
-#include "icing/schema/schema-util.h"
#include "icing/schema/section.h"
#include "icing/store/document-filter-data.h"
#include "icing/store/key-mapper.h"
@@ -99,12 +92,14 @@ SectionManager::Builder::ProcessSchemaTypePropertyConfig(
return absl_ports::InvalidArgumentError("Invalid schema type id");
}
- if (SchemaUtil::IsIndexedProperty(property_config)) {
- ICING_RETURN_IF_ERROR(
- AppendNewSectionMetadata(&section_metadata_cache_[schema_type_id],
- std::move(property_path), property_config));
- }
-
+ // We don't need to check if the property is indexable. This method will
+ // only be called properties that should consume sectionIds, even if the
+ // property's indexing configuration itself is not indexable.
+ // This would be the case for unknown and non-indexable property paths that
+ // are defined in the indexable_nested_properties_list.
+ ICING_RETURN_IF_ERROR(
+ AppendNewSectionMetadata(&section_metadata_cache_[schema_type_id],
+ std::move(property_path), property_config));
return libtextclassifier3::Status::OK;
}
@@ -141,6 +136,13 @@ libtextclassifier3::StatusOr<SectionGroup> SectionManager::ExtractSections(
for (const SectionMetadata& section_metadata : *metadata_list) {
switch (section_metadata.data_type) {
case PropertyConfigProto::DataType::STRING: {
+ if (section_metadata.term_match_type == TermMatchType::UNKNOWN ||
+ section_metadata.tokenizer ==
+ StringIndexingConfig::TokenizerType::NONE) {
+ // Skip if term-match type is UNKNOWN, or if the tokenizer-type is
+ // NONE.
+ break;
+ }
AppendSection(
section_metadata,
property_util::ExtractPropertyValuesFromDocument<std::string_view>(
@@ -149,6 +151,11 @@ libtextclassifier3::StatusOr<SectionGroup> SectionManager::ExtractSections(
break;
}
case PropertyConfigProto::DataType::INT64: {
+ if (section_metadata.numeric_match_type ==
+ IntegerIndexingConfig::NumericMatchType::UNKNOWN) {
+ // Skip if numeric-match type is UNKNOWN.
+ break;
+ }
AppendSection(section_metadata,
property_util::ExtractPropertyValuesFromDocument<int64_t>(
document, section_metadata.path),
diff --git a/icing/schema/section-manager_test.cc b/icing/schema/section-manager_test.cc
index db2be6b..eee78e9 100644
--- a/icing/schema/section-manager_test.cc
+++ b/icing/schema/section-manager_test.cc
@@ -14,7 +14,6 @@
#include "icing/schema/section-manager.h"
-#include <limits>
#include <memory>
#include <string>
#include <string_view>
@@ -25,7 +24,6 @@
#include "icing/file/filesystem.h"
#include "icing/proto/document.pb.h"
#include "icing/proto/schema.pb.h"
-#include "icing/proto/term.pb.h"
#include "icing/schema-builder.h"
#include "icing/schema/schema-type-manager.h"
#include "icing/schema/schema-util.h"
@@ -63,6 +61,28 @@ static constexpr std::string_view kTypeConversation = "Conversation";
static constexpr std::string_view kPropertyEmails = "emails";
static constexpr std::string_view kPropertyName = "name";
+// type and property names of Group
+static constexpr std::string_view kTypeGroup = "Group";
+// indexable
+static constexpr std::string_view kPropertyConversation = "conversation";
+static constexpr std::string_view kPropertyGroupName = "groupName";
+// nested indexable
+static constexpr std::string_view kPropertyNestedConversationName = "name";
+static constexpr std::string_view kPropertyNestedConversationEmailRecipientIds =
+ "emails.recipientIds";
+static constexpr std::string_view kPropertyNestedConversationEmailRecipient =
+ "emails.recipients";
+static constexpr std::string_view kPropertyNestedConversationEmailSubject =
+ "emails.subject";
+// nested non-indexable
+static constexpr std::string_view kPropertyNestedConversationEmailAttachment =
+ "emails.attachment";
+// non-existent property path
+static constexpr std::string_view kPropertyNestedNonExistent =
+ "emails.nonExistentNestedProperty";
+static constexpr std::string_view kPropertyNestedNonExistent2 =
+ "emails.nonExistentNestedProperty2";
+
constexpr int64_t kDefaultTimestamp = 1663274901;
PropertyConfigProto CreateRecipientIdsPropertyConfig() {
@@ -105,6 +125,22 @@ PropertyConfigProto CreateNamePropertyConfig() {
.Build();
}
+PropertyConfigProto CreateAttachmentPropertyConfig() {
+ return PropertyConfigBuilder()
+ .SetName(kPropertyAttachment)
+ .SetDataType(TYPE_BYTES)
+ .SetCardinality(CARDINALITY_OPTIONAL)
+ .Build();
+}
+
+PropertyConfigProto CreateGroupNamePropertyConfig() {
+ return PropertyConfigBuilder()
+ .SetName(kPropertyGroupName)
+ .SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_OPTIONAL)
+ .Build();
+}
+
SchemaTypeConfigProto CreateEmailTypeConfig() {
return SchemaTypeConfigBuilder()
.SetType(kTypeEmail)
@@ -139,6 +175,28 @@ SchemaTypeConfigProto CreateConversationTypeConfig() {
.Build();
}
+SchemaTypeConfigProto CreateGroupTypeConfig() {
+ return SchemaTypeConfigBuilder()
+ .SetType(kTypeGroup)
+ .AddProperty(CreateGroupNamePropertyConfig())
+ .AddProperty(
+ PropertyConfigBuilder()
+ .SetName(kPropertyConversation)
+ .SetDataTypeDocument(
+ kTypeConversation,
+ /*indexable_nested_properties_list=*/
+ {std::string(kPropertyNestedConversationName),
+ std::string(kPropertyNestedConversationEmailRecipientIds),
+ std::string(kPropertyNestedConversationEmailSubject),
+ std::string(kPropertyNestedConversationEmailRecipient),
+ std::string(kPropertyNestedConversationEmailAttachment),
+ std::string(kPropertyNestedNonExistent2),
+ std::string(kPropertyNestedNonExistent),
+ std::string(kPropertyNestedNonExistent)})
+ .SetCardinality(CARDINALITY_REPEATED))
+ .Build();
+}
+
class SectionManagerTest : public ::testing::Test {
protected:
void SetUp() override {
@@ -146,9 +204,11 @@ class SectionManagerTest : public ::testing::Test {
auto email_type = CreateEmailTypeConfig();
auto conversation_type = CreateConversationTypeConfig();
+ auto group_type = CreateGroupTypeConfig();
type_config_map_.emplace(email_type.schema_type(), email_type);
type_config_map_.emplace(conversation_type.schema_type(),
conversation_type);
+ type_config_map_.emplace(group_type.schema_type(), group_type);
// DynamicTrieKeyMapper uses 3 internal arrays for bookkeeping. Give each
// one 128KiB so the total DynamicTrieKeyMapper should get 384KiB
@@ -158,6 +218,7 @@ class SectionManagerTest : public ::testing::Test {
filesystem_, test_dir_, key_mapper_size));
ICING_ASSERT_OK(schema_type_mapper_->Put(kTypeEmail, 0));
ICING_ASSERT_OK(schema_type_mapper_->Put(kTypeConversation, 1));
+ ICING_ASSERT_OK(schema_type_mapper_->Put(kTypeGroup, 2));
email_document_ =
DocumentBuilder()
@@ -183,6 +244,15 @@ class SectionManagerTest : public ::testing::Test {
DocumentProto(email_document_),
DocumentProto(email_document_))
.Build();
+
+ group_document_ =
+ DocumentBuilder()
+ .SetKey("icing", "group/1")
+ .SetSchema(std::string(kTypeGroup))
+ .AddDocumentProperty(std::string(kPropertyConversation),
+ DocumentProto(conversation_document_))
+ .AddStringProperty(std::string(kPropertyGroupName), "group_name_1")
+ .Build();
}
void TearDown() override {
@@ -197,6 +267,7 @@ class SectionManagerTest : public ::testing::Test {
DocumentProto email_document_;
DocumentProto conversation_document_;
+ DocumentProto group_document_;
};
TEST_F(SectionManagerTest, ExtractSections) {
@@ -295,6 +366,91 @@ TEST_F(SectionManagerTest, ExtractSectionsNested) {
ElementsAre(kDefaultTimestamp, kDefaultTimestamp));
}
+TEST_F(SectionManagerTest, ExtractSectionsIndexableNestedPropertiesList) {
+ // Use SchemaTypeManager factory method to instantiate SectionManager.
+ ICING_ASSERT_OK_AND_ASSIGN(
+ std::unique_ptr<SchemaTypeManager> schema_type_manager,
+ SchemaTypeManager::Create(type_config_map_, schema_type_mapper_.get()));
+
+ // Extracts all sections from 'Group' document
+ ICING_ASSERT_OK_AND_ASSIGN(
+ SectionGroup section_group,
+ schema_type_manager->section_manager().ExtractSections(group_document_));
+
+ // SectionId assignments:
+ // 0 -> conversation.emails.attachment (bytes, non-indexable)
+ // 1 -> conversation.emails.recipientIds (int64)
+ // 2 -> conversation.emails.recipients (string)
+ // 3 -> conversation.emails.subject (string)
+ // 4 -> conversation.name
+ // (string, but no entry for this in conversation_document_)
+ // 5 -> groupName (string)
+ // 6 -> conversation.emails.nonExistentNestedProperty
+ // (unknown, non-indexable)
+ // 7 -> conversation.emails.nonExistentNestedProperty2
+ // (unknown, non-indexable)
+ //
+ // SectionId assignment order:
+ // - We assign section ids to known (existing) properties first in alphabet
+ // order.
+ // - After handling all known properties, we assign section ids to all unknown
+ // (non-existent) properties that are specified in the
+ // indexable_nested_properties_list.
+ // - As a result, assignment of the entire section set is not done
+ // alphabetically, but assignment is still deterministic and alphabetical
+ // order is preserved inside the known properties and unknown properties
+ // sets individually.
+ //
+ // 'conversation.emails.attachment',
+ // 'conversation.emails.nonExistentNestedProperty' and
+ // 'conversation.emails.nonExistentNestedProperty2' are assigned sectionIds
+ // even though they are non-indexable because they appear in 'Group' schema
+ // type's indexable_nested_props_list.
+ // However 'conversation.emails.attachment' does not exist in section_group
+ // (even though the property exists and has a sectionId assignment) as
+ // SectionManager::ExtractSections only extracts indexable string and integer
+ // section data from a document.
+
+ // String sections
+ EXPECT_THAT(section_group.string_sections, SizeIs(3));
+
+ EXPECT_THAT(section_group.string_sections[0].metadata,
+ EqualsSectionMetadata(
+ /*expected_id=*/2,
+ /*expected_property_path=*/"conversation.emails.recipients",
+ CreateRecipientsPropertyConfig()));
+ EXPECT_THAT(section_group.string_sections[0].content,
+ ElementsAre("recipient1", "recipient2", "recipient3",
+ "recipient1", "recipient2", "recipient3"));
+
+ EXPECT_THAT(section_group.string_sections[1].metadata,
+ EqualsSectionMetadata(
+ /*expected_id=*/3,
+ /*expected_property_path=*/"conversation.emails.subject",
+ CreateSubjectPropertyConfig()));
+ EXPECT_THAT(section_group.string_sections[1].content,
+ ElementsAre("the subject", "the subject"));
+
+ EXPECT_THAT(section_group.string_sections[2].metadata,
+ EqualsSectionMetadata(
+ /*expected_id=*/5,
+ /*expected_property_path=*/"groupName",
+ CreateGroupNamePropertyConfig()));
+ EXPECT_THAT(section_group.string_sections[2].content,
+ ElementsAre("group_name_1"));
+
+ // Integer sections
+ EXPECT_THAT(section_group.integer_sections, SizeIs(1));
+
+ EXPECT_THAT(section_group.integer_sections[0].metadata,
+ EqualsSectionMetadata(
+ /*expected_id=*/1,
+ /*expected_property_path=*/"conversation.emails.recipientIds",
+ CreateRecipientIdsPropertyConfig()));
+ EXPECT_THAT(section_group.integer_sections[0].content,
+ ElementsAre(1, 2, 3, 1, 2, 3));
+}
+
TEST_F(SectionManagerTest, GetSectionMetadata) {
// Use SchemaTypeManager factory method to instantiate SectionManager.
ICING_ASSERT_OK_AND_ASSIGN(
@@ -352,6 +508,86 @@ TEST_F(SectionManagerTest, GetSectionMetadata) {
IsOkAndHolds(Pointee(EqualsSectionMetadata(
/*expected_id=*/4, /*expected_property_path=*/"name",
CreateNamePropertyConfig()))));
+
+ // Group (section id -> section property path):
+ // 0 -> conversation.emails.attachment (non-indexable)
+ // 1 -> conversation.emails.recipientIds
+ // 2 -> conversation.emails.recipients
+ // 3 -> conversation.emails.subject
+ // 4 -> conversation.name
+ // 5 -> groupName
+ // 6 -> conversation.emails.nonExistentNestedProperty (non-indexable)
+ // 7 -> conversation.emails.nonExistentNestedProperty2 (non-indexable)
+ //
+ // SectionId assignment order:
+ // - We assign section ids to known (existing) properties first in alphabet
+ // order.
+ // - After handling all known properties, we assign section ids to all unknown
+ // (non-existent) properties that are specified in the
+ // indexable_nested_properties_list.
+ // - As a result, assignment of the entire section set is not done
+ // alphabetically, but assignment is still deterministic and alphabetical
+ // order is preserved inside the known properties and unknown properties
+ // sets individually.
+ EXPECT_THAT(schema_type_manager->section_manager().GetSectionMetadata(
+ /*schema_type_id=*/2, /*section_id=*/0),
+ IsOkAndHolds(Pointee(EqualsSectionMetadata(
+ /*expected_id=*/0,
+ /*expected_property_path=*/"conversation.emails.attachment",
+ CreateAttachmentPropertyConfig()))));
+ EXPECT_THAT(schema_type_manager->section_manager().GetSectionMetadata(
+ /*schema_type_id=*/2, /*section_id=*/1),
+ IsOkAndHolds(Pointee(EqualsSectionMetadata(
+ /*expected_id=*/1,
+ /*expected_property_path=*/"conversation.emails.recipientIds",
+ CreateRecipientIdsPropertyConfig()))));
+ EXPECT_THAT(schema_type_manager->section_manager().GetSectionMetadata(
+ /*schema_type_id=*/2, /*section_id=*/2),
+ IsOkAndHolds(Pointee(EqualsSectionMetadata(
+ /*expected_id=*/2,
+ /*expected_property_path=*/"conversation.emails.recipients",
+ CreateRecipientsPropertyConfig()))));
+ EXPECT_THAT(schema_type_manager->section_manager().GetSectionMetadata(
+ /*schema_type_id=*/2, /*section_id=*/3),
+ IsOkAndHolds(Pointee(EqualsSectionMetadata(
+ /*expected_id=*/3,
+ /*expected_property_path=*/"conversation.emails.subject",
+ CreateSubjectPropertyConfig()))));
+ EXPECT_THAT(
+ schema_type_manager->section_manager().GetSectionMetadata(
+ /*schema_type_id=*/2, /*section_id=*/4),
+ IsOkAndHolds(Pointee(EqualsSectionMetadata(
+ /*expected_id=*/4, /*expected_property_path=*/"conversation.name",
+ CreateNamePropertyConfig()))));
+ EXPECT_THAT(schema_type_manager->section_manager().GetSectionMetadata(
+ /*schema_type_id=*/2, /*section_id=*/5),
+ IsOkAndHolds(Pointee(EqualsSectionMetadata(
+ /*expected_id=*/5, /*expected_property_path=*/"groupName",
+ CreateGroupNamePropertyConfig()))));
+ EXPECT_THAT(schema_type_manager->section_manager().GetSectionMetadata(
+ /*schema_type_id=*/2, /*section_id=*/6),
+ IsOkAndHolds(Pointee(EqualsSectionMetadata(
+ /*expected_id=*/6,
+ /*expected_property_path=*/
+ "conversation.emails.nonExistentNestedProperty",
+ PropertyConfigBuilder()
+ .SetName("nonExistentNestedProperty")
+ .SetDataType(TYPE_UNKNOWN)
+ .Build()))));
+ EXPECT_THAT(schema_type_manager->section_manager().GetSectionMetadata(
+ /*schema_type_id=*/2, /*section_id=*/7),
+ IsOkAndHolds(Pointee(EqualsSectionMetadata(
+ /*expected_id=*/7,
+ /*expected_property_path=*/
+ "conversation.emails.nonExistentNestedProperty2",
+ PropertyConfigBuilder()
+ .SetName("nonExistentNestedProperty2")
+ .SetDataType(TYPE_UNKNOWN)
+ .Build()))));
+ // Check that no more properties are indexed
+ EXPECT_THAT(schema_type_manager->section_manager().GetSectionMetadata(
+ /*schema_type_id=*/2, /*section_id=*/8),
+ StatusIs(libtextclassifier3::StatusCode::INVALID_ARGUMENT));
}
TEST_F(SectionManagerTest, GetSectionMetadataInvalidSchemaTypeId) {
@@ -359,13 +595,13 @@ TEST_F(SectionManagerTest, GetSectionMetadataInvalidSchemaTypeId) {
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<SchemaTypeManager> schema_type_manager,
SchemaTypeManager::Create(type_config_map_, schema_type_mapper_.get()));
- ASSERT_THAT(type_config_map_, SizeIs(2));
+ ASSERT_THAT(type_config_map_, SizeIs(3));
EXPECT_THAT(schema_type_manager->section_manager().GetSectionMetadata(
/*schema_type_id=*/-1, /*section_id=*/0),
StatusIs(libtextclassifier3::StatusCode::INVALID_ARGUMENT));
EXPECT_THAT(schema_type_manager->section_manager().GetSectionMetadata(
- /*schema_type_id=*/2, /*section_id=*/0),
+ /*schema_type_id=*/3, /*section_id=*/0),
StatusIs(libtextclassifier3::StatusCode::INVALID_ARGUMENT));
}
diff --git a/synced_AOSP_CL_number.txt b/synced_AOSP_CL_number.txt
index 9a7bd49..09c1edd 100644
--- a/synced_AOSP_CL_number.txt
+++ b/synced_AOSP_CL_number.txt
@@ -1 +1 @@
-set(synced_AOSP_CL_number=546080947)
+set(synced_AOSP_CL_number=546391919)