aboutsummaryrefslogtreecommitdiff
path: root/icing/icing-search-engine_optimize_test.cc
diff options
context:
space:
mode:
authorTim Barron <tjbarron@google.com>2023-03-13 17:00:29 -0700
committerTim Barron <tjbarron@google.com>2023-03-14 08:41:33 -0700
commitd5c81ae0c41ae9c1aefb3601f3836570b9f686c7 (patch)
tree75040182238304328ac85e570c0baae90b7fe53e /icing/icing-search-engine_optimize_test.cc
parent3fe6aa4251989fb27863fdbf51e18d8c1f9e42dd (diff)
downloadicing-d5c81ae0c41ae9c1aefb3601f3836570b9f686c7.tar.gz
Update Icing from upstream.
Descriptions: ======================================================================== Cache an instance of UBreakIterator to reduce unnecessary creations. ======================================================================== Cap number of individual IntegerIndexStorages that IntegerIndex creates. ======================================================================== Change error in trimRightMostNode from Unimplemented to InvalidArgument. ======================================================================== Add detection for new language features of List Filters Query Language. ======================================================================== Add option to control threshold to rebuild index during optimize by flag ======================================================================== Add option to control use of namespace id to build urimapper by flag. ======================================================================== Enforce schema validation for joinable config. ======================================================================== Adopt bucket splitting for IntegerIndexStorage. ======================================================================== Implement bucket splitting function. ======================================================================== Add Icing initialization unit tests for QualifiedIdTypeJoinableIndex. ======================================================================== Add Icing schema change unit tests for QualifiedIdTypeJoinableIndex. ======================================================================== Add Icing optimization unit tests for QualifiedIdTypeJoinableIndex. ======================================================================== Integrate QualifiedIdTypeJoinableIndex into IcingSearchEngine. ======================================================================== Implement QualifiedIdJoinablePropertyIndexingHandler. ======================================================================== Change QualifiedIdTypeJoinableIndex to store raw qualified id string. ======================================================================== Pass info about unnormalized query terms through lexer/parser/visitor. ======================================================================== Bug: 208654892 Bug: 263890397 Bug: 259743562 Bug: 272145329 Bug: 227356108 Change-Id: I438a390ddda5673cf2b5781af502f2b7cfeaee74
Diffstat (limited to 'icing/icing-search-engine_optimize_test.cc')
-rw-r--r--icing/icing-search-engine_optimize_test.cc886
1 files changed, 785 insertions, 101 deletions
diff --git a/icing/icing-search-engine_optimize_test.cc b/icing/icing-search-engine_optimize_test.cc
index b2c7a62..0c5cb7a 100644
--- a/icing/icing-search-engine_optimize_test.cc
+++ b/icing/icing-search-engine_optimize_test.cc
@@ -28,6 +28,7 @@
#include "icing/file/mock-filesystem.h"
#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"
@@ -123,46 +124,46 @@ IcingSearchEngineOptions GetDefaultIcingOptions() {
return icing_options;
}
-DocumentProto CreateMessageDocument(std::string name_space, std::string uri) {
- return DocumentBuilder()
- .SetKey(std::move(name_space), std::move(uri))
- .SetSchema("Message")
- .AddStringProperty("body", "message body")
- .AddInt64Property("indexableInteger", 123)
- .SetCreationTimestampMs(kDefaultCreationTimestampMs)
- .Build();
-}
-
-SchemaProto CreateMessageSchema() {
- return SchemaBuilder()
- .AddType(SchemaTypeConfigBuilder()
- .SetType("Message")
- .AddProperty(PropertyConfigBuilder()
- .SetName("body")
- .SetDataTypeString(TERM_MATCH_PREFIX,
- TOKENIZER_PLAIN)
- .SetCardinality(CARDINALITY_REQUIRED))
- .AddProperty(PropertyConfigBuilder()
- .SetName("indexableInteger")
- .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
- .SetCardinality(CARDINALITY_REQUIRED)))
- .Build();
-}
-
ScoringSpecProto GetDefaultScoringSpec() {
ScoringSpecProto scoring_spec;
scoring_spec.set_rank_by(ScoringSpecProto::RankingStrategy::DOCUMENT_SCORE);
return scoring_spec;
}
+// TODO(b/272145329): create SearchSpecBuilder, JoinSpecBuilder,
+// SearchResultProtoBuilder and ResultProtoBuilder for unit tests and build all
+// instances by them.
+
TEST_F(IcingSearchEngineOptimizeTest,
AllPageTokensShouldBeInvalidatedAfterOptimization) {
+ SchemaProto schema =
+ SchemaBuilder()
+ .AddType(SchemaTypeConfigBuilder().SetType("Message").AddProperty(
+ PropertyConfigBuilder()
+ .SetName("body")
+ .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_REQUIRED)))
+ .Build();
+
+ DocumentProto document1 =
+ DocumentBuilder()
+ .SetKey("namespace", "uri1")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body one")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+ DocumentProto document2 =
+ DocumentBuilder()
+ .SetKey("namespace", "uri2")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body two")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+
IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
- ASSERT_THAT(icing.SetSchema(CreateMessageSchema()).status(), ProtoIsOk());
+ ASSERT_THAT(icing.SetSchema(schema).status(), ProtoIsOk());
- DocumentProto document1 = CreateMessageDocument("namespace", "uri1");
- DocumentProto document2 = CreateMessageDocument("namespace", "uri2");
ASSERT_THAT(icing.Put(document1).status(), ProtoIsOk());
ASSERT_THAT(icing.Put(document2).status(), ProtoIsOk());
@@ -205,9 +206,24 @@ TEST_F(IcingSearchEngineOptimizeTest,
}
TEST_F(IcingSearchEngineOptimizeTest, OptimizationShouldRemoveDeletedDocs) {
- IcingSearchEngineOptions icing_options = GetDefaultIcingOptions();
+ SchemaProto schema =
+ SchemaBuilder()
+ .AddType(SchemaTypeConfigBuilder().SetType("Message").AddProperty(
+ PropertyConfigBuilder()
+ .SetName("body")
+ .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_REQUIRED)))
+ .Build();
+
+ DocumentProto document1 =
+ DocumentBuilder()
+ .SetKey("namespace", "uri1")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body one")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
- DocumentProto document1 = CreateMessageDocument("namespace", "uri1");
+ IcingSearchEngineOptions icing_options = GetDefaultIcingOptions();
GetResultProto expected_get_result_proto;
expected_get_result_proto.mutable_status()->set_code(StatusProto::NOT_FOUND);
@@ -216,7 +232,7 @@ TEST_F(IcingSearchEngineOptimizeTest, OptimizationShouldRemoveDeletedDocs) {
{
IcingSearchEngine icing(icing_options, GetTestJniCache());
ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
- ASSERT_THAT(icing.SetSchema(CreateMessageSchema()).status(), ProtoIsOk());
+ ASSERT_THAT(icing.SetSchema(schema).status(), ProtoIsOk());
ASSERT_THAT(icing.Put(document1).status(), ProtoIsOk());
// Deletes document1
@@ -247,10 +263,19 @@ TEST_F(IcingSearchEngineOptimizeTest, OptimizationShouldRemoveDeletedDocs) {
TEST_F(IcingSearchEngineOptimizeTest,
OptimizationShouldDeleteTemporaryDirectory) {
+ SchemaProto schema =
+ SchemaBuilder()
+ .AddType(SchemaTypeConfigBuilder().SetType("Message").AddProperty(
+ PropertyConfigBuilder()
+ .SetName("body")
+ .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_REQUIRED)))
+ .Build();
+
IcingSearchEngineOptions icing_options = GetDefaultIcingOptions();
IcingSearchEngine icing(icing_options, GetTestJniCache());
ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
- ASSERT_THAT(icing.SetSchema(CreateMessageSchema()).status(), ProtoIsOk());
+ ASSERT_THAT(icing.SetSchema(schema).status(), ProtoIsOk());
// Create a tmp dir that will be used in Optimize() to swap files,
// this validates that any tmp dirs will be deleted before using.
@@ -271,12 +296,26 @@ TEST_F(IcingSearchEngineOptimizeTest,
}
TEST_F(IcingSearchEngineOptimizeTest, GetOptimizeInfoHasCorrectStats) {
- DocumentProto document1 = CreateMessageDocument("namespace", "uri1");
+ SchemaProto schema =
+ SchemaBuilder()
+ .AddType(SchemaTypeConfigBuilder().SetType("Message").AddProperty(
+ PropertyConfigBuilder()
+ .SetName("body")
+ .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_REQUIRED)))
+ .Build();
+
+ DocumentProto document1 =
+ DocumentBuilder()
+ .SetKey("namespace", "uri1")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body one")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
DocumentProto document2 = DocumentBuilder()
.SetKey("namespace", "uri2")
.SetSchema("Message")
- .AddStringProperty("body", "message body")
- .AddInt64Property("indexableInteger", 456)
+ .AddStringProperty("body", "message body two")
.SetCreationTimestampMs(100)
.SetTtlMs(500)
.Build();
@@ -298,7 +337,7 @@ TEST_F(IcingSearchEngineOptimizeTest, GetOptimizeInfoHasCorrectStats) {
EXPECT_THAT(optimize_info.estimated_optimizable_bytes(), Eq(0));
EXPECT_THAT(optimize_info.time_since_last_optimize_ms(), Eq(0));
- ASSERT_THAT(icing.SetSchema(CreateMessageSchema()).status(), ProtoIsOk());
+ ASSERT_THAT(icing.SetSchema(schema).status(), ProtoIsOk());
ASSERT_THAT(icing.Put(document1).status(), ProtoIsOk());
// Only have active documents, nothing is optimizable yet.
@@ -356,11 +395,50 @@ TEST_F(IcingSearchEngineOptimizeTest, GetOptimizeInfoHasCorrectStats) {
}
TEST_F(IcingSearchEngineOptimizeTest, GetAndPutShouldWorkAfterOptimization) {
- DocumentProto document1 = CreateMessageDocument("namespace", "uri1");
- DocumentProto document2 = CreateMessageDocument("namespace", "uri2");
- DocumentProto document3 = CreateMessageDocument("namespace", "uri3");
- DocumentProto document4 = CreateMessageDocument("namespace", "uri4");
- DocumentProto document5 = CreateMessageDocument("namespace", "uri5");
+ SchemaProto schema =
+ SchemaBuilder()
+ .AddType(SchemaTypeConfigBuilder().SetType("Message").AddProperty(
+ PropertyConfigBuilder()
+ .SetName("body")
+ .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_REQUIRED)))
+ .Build();
+
+ DocumentProto document1 =
+ DocumentBuilder()
+ .SetKey("namespace", "uri1")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body one")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+ DocumentProto document2 =
+ DocumentBuilder()
+ .SetKey("namespace", "uri2")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body two")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+ DocumentProto document3 =
+ DocumentBuilder()
+ .SetKey("namespace", "uri3")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body three")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+ DocumentProto document4 =
+ DocumentBuilder()
+ .SetKey("namespace", "uri4")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body four")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+ DocumentProto document5 =
+ DocumentBuilder()
+ .SetKey("namespace", "uri5")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body five")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
GetResultProto expected_get_result_proto;
expected_get_result_proto.mutable_status()->set_code(StatusProto::OK);
@@ -368,7 +446,7 @@ TEST_F(IcingSearchEngineOptimizeTest, GetAndPutShouldWorkAfterOptimization) {
{
IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
- ASSERT_THAT(icing.SetSchema(CreateMessageSchema()).status(), ProtoIsOk());
+ ASSERT_THAT(icing.SetSchema(schema).status(), ProtoIsOk());
ASSERT_THAT(icing.Put(document1).status(), ProtoIsOk());
ASSERT_THAT(icing.Put(document2).status(), ProtoIsOk());
@@ -474,12 +552,34 @@ TEST_F(IcingSearchEngineOptimizeTest,
}
TEST_F(IcingSearchEngineOptimizeTest, DeleteShouldWorkAfterOptimization) {
- DocumentProto document1 = CreateMessageDocument("namespace", "uri1");
- DocumentProto document2 = CreateMessageDocument("namespace", "uri2");
+ SchemaProto schema =
+ SchemaBuilder()
+ .AddType(SchemaTypeConfigBuilder().SetType("Message").AddProperty(
+ PropertyConfigBuilder()
+ .SetName("body")
+ .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_REQUIRED)))
+ .Build();
+
+ DocumentProto document1 =
+ DocumentBuilder()
+ .SetKey("namespace", "uri1")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body one")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+ DocumentProto document2 =
+ DocumentBuilder()
+ .SetKey("namespace", "uri2")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body two")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+
{
IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
- ASSERT_THAT(icing.SetSchema(CreateMessageSchema()).status(), ProtoIsOk());
+ ASSERT_THAT(icing.SetSchema(schema).status(), ProtoIsOk());
ASSERT_THAT(icing.Put(document1).status(), ProtoIsOk());
ASSERT_THAT(icing.Put(document2).status(), ProtoIsOk());
ASSERT_THAT(icing.Optimize().status(), ProtoIsOk());
@@ -557,13 +657,14 @@ TEST_F(IcingSearchEngineOptimizeTest, OptimizationFailureUninitializesIcing) {
ASSERT_THAT(icing.Optimize().status(), ProtoStatusIs(StatusProto::INTERNAL));
// Ordinary operations should fail safely.
- SchemaProto simple_schema;
- auto type = simple_schema.add_types();
- type->set_schema_type("type0");
- auto property = type->add_properties();
- property->set_property_name("prop0");
- property->set_data_type(PropertyConfigProto::DataType::STRING);
- property->set_cardinality(PropertyConfigProto::Cardinality::OPTIONAL);
+ SchemaProto simple_schema =
+ SchemaBuilder()
+ .AddType(SchemaTypeConfigBuilder().SetType("type0").AddProperty(
+ PropertyConfigBuilder()
+ .SetName("prop0")
+ .SetDataType(TYPE_STRING)
+ .SetCardinality(CARDINALITY_OPTIONAL)))
+ .Build();
DocumentProto simple_doc = DocumentBuilder()
.SetKey("namespace0", "uri0")
@@ -606,27 +707,30 @@ TEST_F(IcingSearchEngineOptimizeTest, OptimizationFailureUninitializesIcing) {
TEST_F(IcingSearchEngineOptimizeTest, SetSchemaShouldWorkAfterOptimization) {
// Creates 3 test schemas
- SchemaProto schema1 = SchemaProto(CreateMessageSchema());
+ SchemaProto schema1 =
+ SchemaBuilder()
+ .AddType(SchemaTypeConfigBuilder().SetType("Message").AddProperty(
+ PropertyConfigBuilder()
+ .SetName("body")
+ .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_REQUIRED)))
+ .Build();
SchemaProto schema2 = SchemaProto(schema1);
- auto new_property2 = schema2.mutable_types(0)->add_properties();
- new_property2->set_property_name("property2");
- new_property2->set_data_type(PropertyConfigProto::DataType::STRING);
- new_property2->set_cardinality(PropertyConfigProto::Cardinality::OPTIONAL);
- new_property2->mutable_string_indexing_config()->set_term_match_type(
- TermMatchType::PREFIX);
- new_property2->mutable_string_indexing_config()->set_tokenizer_type(
- StringIndexingConfig::TokenizerType::PLAIN);
+ *schema2.mutable_types(0)->add_properties() =
+ PropertyConfigBuilder()
+ .SetName("property2")
+ .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_OPTIONAL)
+ .Build();
SchemaProto schema3 = SchemaProto(schema2);
- auto new_property3 = schema3.mutable_types(0)->add_properties();
- new_property3->set_property_name("property3");
- new_property3->set_data_type(PropertyConfigProto::DataType::STRING);
- new_property3->set_cardinality(PropertyConfigProto::Cardinality::OPTIONAL);
- new_property3->mutable_string_indexing_config()->set_term_match_type(
- TermMatchType::PREFIX);
- new_property3->mutable_string_indexing_config()->set_tokenizer_type(
- StringIndexingConfig::TokenizerType::PLAIN);
+ *schema3.mutable_types(0)->add_properties() =
+ PropertyConfigBuilder()
+ .SetName("property3")
+ .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_OPTIONAL)
+ .Build();
{
IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
@@ -644,7 +748,29 @@ TEST_F(IcingSearchEngineOptimizeTest, SetSchemaShouldWorkAfterOptimization) {
}
TEST_F(IcingSearchEngineOptimizeTest, SearchShouldWorkAfterOptimization) {
- DocumentProto document = CreateMessageDocument("namespace", "uri");
+ SchemaProto schema =
+ SchemaBuilder()
+ .AddType(SchemaTypeConfigBuilder()
+ .SetType("Message")
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("body")
+ .SetDataTypeString(TERM_MATCH_PREFIX,
+ TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_REQUIRED))
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("indexableInteger")
+ .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
+ .SetCardinality(CARDINALITY_REQUIRED)))
+ .Build();
+
+ DocumentProto document =
+ DocumentBuilder()
+ .SetKey("namespace", "uri")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body")
+ .AddInt64Property("indexableInteger", 123)
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
SearchSpecProto search_spec1;
search_spec1.set_term_match_type(TermMatchType::PREFIX);
@@ -664,7 +790,7 @@ TEST_F(IcingSearchEngineOptimizeTest, SearchShouldWorkAfterOptimization) {
{
IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
- ASSERT_THAT(icing.SetSchema(CreateMessageSchema()).status(), ProtoIsOk());
+ ASSERT_THAT(icing.SetSchema(schema).status(), ProtoIsOk());
ASSERT_THAT(icing.Put(document).status(), ProtoIsOk());
ASSERT_THAT(icing.Optimize().status(), ProtoIsOk());
@@ -703,14 +829,308 @@ TEST_F(IcingSearchEngineOptimizeTest, SearchShouldWorkAfterOptimization) {
}
TEST_F(IcingSearchEngineOptimizeTest,
+ JoinShouldWorkAfterOptimizationDeleteParent) {
+ SchemaProto schema =
+ SchemaBuilder()
+ .AddType(SchemaTypeConfigBuilder().SetType("Person").AddProperty(
+ PropertyConfigBuilder()
+ .SetName("name")
+ .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_REQUIRED)))
+ .AddType(SchemaTypeConfigBuilder()
+ .SetType("Message")
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("body")
+ .SetDataTypeString(TERM_MATCH_PREFIX,
+ TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_REQUIRED))
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("senderQualifiedId")
+ .SetDataTypeJoinableString(
+ JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+ .SetCardinality(CARDINALITY_REQUIRED)))
+ .Build();
+
+ DocumentProto person1 =
+ DocumentBuilder()
+ .SetKey("namespace", "person1")
+ .SetSchema("Person")
+ .AddStringProperty("name", "person one")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+ DocumentProto person2 =
+ DocumentBuilder()
+ .SetKey("namespace", "person2")
+ .SetSchema("Person")
+ .AddStringProperty("name", "person two")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+
+ DocumentProto message1 =
+ DocumentBuilder()
+ .SetKey("namespace", "message1")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body one")
+ .AddStringProperty("senderQualifiedId", "namespace#person1")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+ DocumentProto message2 =
+ DocumentBuilder()
+ .SetKey("namespace", "message2")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body two")
+ .AddStringProperty("senderQualifiedId", "namespace#person1")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+ DocumentProto message3 =
+ DocumentBuilder()
+ .SetKey("namespace", "message3")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body three")
+ .AddStringProperty("senderQualifiedId", "namespace#person2")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+
+ // Prepare join search spec to join a query for `name:person` with a child
+ // query for `body:message` based on the child's `senderQualifiedId` field.
+ SearchSpecProto search_spec;
+ search_spec.set_term_match_type(TermMatchType::EXACT_ONLY);
+ search_spec.set_query("name:person");
+ JoinSpecProto* join_spec = search_spec.mutable_join_spec();
+ join_spec->set_max_joined_child_count(100);
+ join_spec->set_parent_property_expression(
+ std::string(JoinProcessor::kQualifiedIdExpr));
+ join_spec->set_child_property_expression("senderQualifiedId");
+ join_spec->set_aggregation_scoring_strategy(
+ JoinSpecProto::AggregationScoringStrategy::COUNT);
+ JoinSpecProto::NestedSpecProto* nested_spec =
+ join_spec->mutable_nested_spec();
+ SearchSpecProto* nested_search_spec = nested_spec->mutable_search_spec();
+ nested_search_spec->set_term_match_type(TermMatchType::EXACT_ONLY);
+ nested_search_spec->set_query("body:message");
+ *nested_spec->mutable_scoring_spec() = GetDefaultScoringSpec();
+ *nested_spec->mutable_result_spec() = ResultSpecProto::default_instance();
+
+ // Person1 is going to be deleted below. Only person2 which is joined with
+ // message3 should match the query.
+ SearchResultProto expected_search_result_proto;
+ expected_search_result_proto.mutable_status()->set_code(StatusProto::OK);
+ SearchResultProto::ResultProto* result_proto =
+ expected_search_result_proto.mutable_results()->Add();
+ *result_proto->mutable_document() = person2;
+ *result_proto->mutable_joined_results()->Add()->mutable_document() = message3;
+
+ {
+ IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
+ ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
+ ASSERT_THAT(icing.SetSchema(schema).status(), ProtoIsOk());
+ ASSERT_THAT(icing.Put(person1).status(), ProtoIsOk());
+ ASSERT_THAT(icing.Put(person2).status(), ProtoIsOk());
+ ASSERT_THAT(icing.Put(message1).status(), ProtoIsOk());
+ ASSERT_THAT(icing.Put(message2).status(), ProtoIsOk());
+ ASSERT_THAT(icing.Put(message3).status(), ProtoIsOk());
+ // Delete parent document: person1
+ ASSERT_THAT(icing.Delete("namespace", "person1").status(), ProtoIsOk());
+ ASSERT_THAT(icing.Optimize().status(), ProtoIsOk());
+
+ // Validates that join search query works right after Optimize()
+ SearchResultProto search_result_proto =
+ icing.Search(search_spec, GetDefaultScoringSpec(),
+ ResultSpecProto::default_instance());
+ EXPECT_THAT(search_result_proto, EqualsSearchResultIgnoreStatsAndScores(
+ expected_search_result_proto));
+ } // Destroys IcingSearchEngine to make sure nothing is cached.
+
+ IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
+ EXPECT_THAT(icing.Initialize().status(), ProtoIsOk());
+
+ SearchResultProto search_result_proto =
+ icing.Search(search_spec, GetDefaultScoringSpec(),
+ ResultSpecProto::default_instance());
+ EXPECT_THAT(search_result_proto, EqualsSearchResultIgnoreStatsAndScores(
+ expected_search_result_proto));
+}
+
+TEST_F(IcingSearchEngineOptimizeTest,
+ JoinShouldWorkAfterOptimizationDeleteChild) {
+ SchemaProto schema =
+ SchemaBuilder()
+ .AddType(SchemaTypeConfigBuilder().SetType("Person").AddProperty(
+ PropertyConfigBuilder()
+ .SetName("name")
+ .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_REQUIRED)))
+ .AddType(SchemaTypeConfigBuilder()
+ .SetType("Message")
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("body")
+ .SetDataTypeString(TERM_MATCH_PREFIX,
+ TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_REQUIRED))
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("senderQualifiedId")
+ .SetDataTypeJoinableString(
+ JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+ .SetCardinality(CARDINALITY_REQUIRED)))
+ .Build();
+
+ DocumentProto person1 =
+ DocumentBuilder()
+ .SetKey("namespace", "person1")
+ .SetSchema("Person")
+ .AddStringProperty("name", "person one")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+ DocumentProto person2 =
+ DocumentBuilder()
+ .SetKey("namespace", "person2")
+ .SetSchema("Person")
+ .AddStringProperty("name", "person two")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+
+ DocumentProto message1 =
+ DocumentBuilder()
+ .SetKey("namespace", "message1")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body one")
+ .AddStringProperty("senderQualifiedId", "namespace#person1")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+ DocumentProto message2 =
+ DocumentBuilder()
+ .SetKey("namespace", "message2")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body two")
+ .AddStringProperty("senderQualifiedId", "namespace#person1")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+ DocumentProto message3 =
+ DocumentBuilder()
+ .SetKey("namespace", "message3")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body three")
+ .AddStringProperty("senderQualifiedId", "namespace#person2")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+
+ // Prepare join search spec to join a query for `name:person` with a child
+ // query for `body:message` based on the child's `senderQualifiedId` field.
+ SearchSpecProto search_spec;
+ search_spec.set_term_match_type(TermMatchType::EXACT_ONLY);
+ search_spec.set_query("name:person");
+ JoinSpecProto* join_spec = search_spec.mutable_join_spec();
+ join_spec->set_max_joined_child_count(100);
+ join_spec->set_parent_property_expression(
+ std::string(JoinProcessor::kQualifiedIdExpr));
+ join_spec->set_child_property_expression("senderQualifiedId");
+ join_spec->set_aggregation_scoring_strategy(
+ JoinSpecProto::AggregationScoringStrategy::COUNT);
+ JoinSpecProto::NestedSpecProto* nested_spec =
+ join_spec->mutable_nested_spec();
+ SearchSpecProto* nested_search_spec = nested_spec->mutable_search_spec();
+ nested_search_spec->set_term_match_type(TermMatchType::EXACT_ONLY);
+ nested_search_spec->set_query("body:message");
+ *nested_spec->mutable_scoring_spec() = GetDefaultScoringSpec();
+ *nested_spec->mutable_result_spec() = ResultSpecProto::default_instance();
+
+ // Message1 and message3 are going to be deleted below. Both person1 and
+ // person2 should be included even though person2 has no child (since we're
+ // doing left join).
+ SearchResultProto expected_search_result_proto;
+ expected_search_result_proto.mutable_status()->set_code(StatusProto::OK);
+ SearchResultProto::ResultProto* result_proto1 =
+ expected_search_result_proto.mutable_results()->Add();
+ *result_proto1->mutable_document() = person1;
+ *result_proto1->mutable_joined_results()->Add()->mutable_document() =
+ message2;
+ SearchResultProto::ResultProto* result_google::protobuf =
+ expected_search_result_proto.mutable_results()->Add();
+ *result_google::protobuf->mutable_document() = person2;
+
+ {
+ IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
+ ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
+ ASSERT_THAT(icing.SetSchema(schema).status(), ProtoIsOk());
+ ASSERT_THAT(icing.Put(person1).status(), ProtoIsOk());
+ ASSERT_THAT(icing.Put(person2).status(), ProtoIsOk());
+ ASSERT_THAT(icing.Put(message1).status(), ProtoIsOk());
+ ASSERT_THAT(icing.Put(message2).status(), ProtoIsOk());
+ ASSERT_THAT(icing.Put(message3).status(), ProtoIsOk());
+ // Delete child documents: message1 and message3
+ ASSERT_THAT(icing.Delete("namespace", "message1").status(), ProtoIsOk());
+ ASSERT_THAT(icing.Delete("namespace", "message3").status(), ProtoIsOk());
+ ASSERT_THAT(icing.Optimize().status(), ProtoIsOk());
+
+ // Validates that join search query works right after Optimize()
+ SearchResultProto search_result_proto =
+ icing.Search(search_spec, GetDefaultScoringSpec(),
+ ResultSpecProto::default_instance());
+ EXPECT_THAT(search_result_proto, EqualsSearchResultIgnoreStatsAndScores(
+ expected_search_result_proto));
+ } // Destroys IcingSearchEngine to make sure nothing is cached.
+
+ IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
+ EXPECT_THAT(icing.Initialize().status(), ProtoIsOk());
+
+ SearchResultProto search_result_proto =
+ icing.Search(search_spec, GetDefaultScoringSpec(),
+ ResultSpecProto::default_instance());
+ EXPECT_THAT(search_result_proto, EqualsSearchResultIgnoreStatsAndScores(
+ expected_search_result_proto));
+}
+
+TEST_F(IcingSearchEngineOptimizeTest,
IcingShouldWorkFineIfOptimizationIsAborted) {
- DocumentProto document1 = CreateMessageDocument("namespace", "uri1");
+ SchemaProto schema =
+ SchemaBuilder()
+ .AddType(SchemaTypeConfigBuilder().SetType("Person").AddProperty(
+ PropertyConfigBuilder()
+ .SetName("name")
+ .SetDataTypeString(TERM_MATCH_PREFIX, TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_REQUIRED)))
+ .AddType(SchemaTypeConfigBuilder()
+ .SetType("Message")
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("body")
+ .SetDataTypeString(TERM_MATCH_PREFIX,
+ TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_REQUIRED))
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("indexableInteger")
+ .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
+ .SetCardinality(CARDINALITY_REQUIRED))
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("senderQualifiedId")
+ .SetDataTypeJoinableString(
+ JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+ .SetCardinality(CARDINALITY_REQUIRED)))
+ .Build();
+
+ DocumentProto person =
+ DocumentBuilder()
+ .SetKey("namespace", "person")
+ .SetSchema("Person")
+ .AddStringProperty("name", "person")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+
+ DocumentProto message1 =
+ DocumentBuilder()
+ .SetKey("namespace", "message1")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body one")
+ .AddInt64Property("indexableInteger", 123)
+ .AddStringProperty("senderQualifiedId", "namespace#person")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
{
// Initializes a normal icing to create files needed
IcingSearchEngine icing(GetDefaultIcingOptions(), GetTestJniCache());
ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
- ASSERT_THAT(icing.SetSchema(CreateMessageSchema()).status(), ProtoIsOk());
- ASSERT_THAT(icing.Put(document1).status(), ProtoIsOk());
+ ASSERT_THAT(icing.SetSchema(schema).status(), ProtoIsOk());
+ ASSERT_THAT(icing.Put(person).status(), ProtoIsOk());
+ ASSERT_THAT(icing.Put(message1).status(), ProtoIsOk());
}
// Creates a mock filesystem in which DeleteDirectoryRecursively() always
@@ -733,25 +1153,33 @@ TEST_F(IcingSearchEngineOptimizeTest,
GetResultProto expected_get_result_proto;
expected_get_result_proto.mutable_status()->set_code(StatusProto::OK);
- *expected_get_result_proto.mutable_document() = document1;
- EXPECT_THAT(
- icing.Get("namespace", "uri1", GetResultSpecProto::default_instance()),
- EqualsProto(expected_get_result_proto));
+ *expected_get_result_proto.mutable_document() = message1;
+ EXPECT_THAT(icing.Get("namespace", "message1",
+ GetResultSpecProto::default_instance()),
+ EqualsProto(expected_get_result_proto));
- DocumentProto document2 = CreateMessageDocument("namespace", "uri2");
+ DocumentProto message2 =
+ DocumentBuilder()
+ .SetKey("namespace", "message2")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body two")
+ .AddInt64Property("indexableInteger", 123)
+ .AddStringProperty("senderQualifiedId", "namespace#person")
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
- EXPECT_THAT(icing.Put(document2).status(), ProtoIsOk());
+ EXPECT_THAT(icing.Put(message2).status(), ProtoIsOk());
SearchResultProto expected_search_result_proto;
expected_search_result_proto.mutable_status()->set_code(StatusProto::OK);
*expected_search_result_proto.mutable_results()->Add()->mutable_document() =
- document2;
+ message2;
*expected_search_result_proto.mutable_results()->Add()->mutable_document() =
- document1;
+ message1;
// Verify term search
SearchSpecProto search_spec1;
- search_spec1.set_query("m");
+ search_spec1.set_query("body:m");
search_spec1.set_term_match_type(TermMatchType::PREFIX);
SearchResultProto search_result_proto1 =
@@ -772,10 +1200,68 @@ TEST_F(IcingSearchEngineOptimizeTest,
ResultSpecProto::default_instance());
EXPECT_THAT(search_result_google::protobuf, EqualsSearchResultIgnoreStatsAndScores(
expected_search_result_proto));
+
+ // Verify join search: join a query for `name:person` with a child query for
+ // `body:message` based on the child's `senderQualifiedId` field.
+ SearchSpecProto search_spec3;
+ search_spec3.set_term_match_type(TermMatchType::EXACT_ONLY);
+ search_spec3.set_query("name:person");
+ JoinSpecProto* join_spec = search_spec3.mutable_join_spec();
+ join_spec->set_max_joined_child_count(100);
+ join_spec->set_parent_property_expression(
+ std::string(JoinProcessor::kQualifiedIdExpr));
+ join_spec->set_child_property_expression("senderQualifiedId");
+ join_spec->set_aggregation_scoring_strategy(
+ JoinSpecProto::AggregationScoringStrategy::COUNT);
+ JoinSpecProto::NestedSpecProto* nested_spec =
+ join_spec->mutable_nested_spec();
+ SearchSpecProto* nested_search_spec = nested_spec->mutable_search_spec();
+ nested_search_spec->set_term_match_type(TermMatchType::EXACT_ONLY);
+ nested_search_spec->set_query("body:message");
+ *nested_spec->mutable_scoring_spec() = GetDefaultScoringSpec();
+ *nested_spec->mutable_result_spec() = ResultSpecProto::default_instance();
+
+ SearchResultProto expected_join_search_result_proto;
+ expected_join_search_result_proto.mutable_status()->set_code(StatusProto::OK);
+ SearchResultProto::ResultProto* result_proto =
+ expected_join_search_result_proto.mutable_results()->Add();
+ *result_proto->mutable_document() = person;
+ *result_proto->mutable_joined_results()->Add()->mutable_document() = message2;
+ *result_proto->mutable_joined_results()->Add()->mutable_document() = message1;
+
+ SearchResultProto search_result_proto3 =
+ icing.Search(search_spec3, GetDefaultScoringSpec(),
+ ResultSpecProto::default_instance());
+ EXPECT_THAT(search_result_proto3, EqualsSearchResultIgnoreStatsAndScores(
+ expected_join_search_result_proto));
}
TEST_F(IcingSearchEngineOptimizeTest,
OptimizationShouldRecoverIfFileDirectoriesAreMissing) {
+ SchemaProto schema =
+ SchemaBuilder()
+ .AddType(SchemaTypeConfigBuilder()
+ .SetType("Message")
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("body")
+ .SetDataTypeString(TERM_MATCH_PREFIX,
+ TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_REQUIRED))
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("indexableInteger")
+ .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
+ .SetCardinality(CARDINALITY_REQUIRED)))
+ .Build();
+
+ DocumentProto document =
+ DocumentBuilder()
+ .SetKey("namespace", "uri1")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body")
+ .AddInt64Property("indexableInteger", 123)
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+
// Creates a mock filesystem in which SwapFiles() always fails and deletes the
// directories. This will fail IcingSearchEngine::OptimizeDocumentStore().
auto mock_filesystem = std::make_unique<MockFilesystem>();
@@ -793,9 +1279,8 @@ TEST_F(IcingSearchEngineOptimizeTest,
std::make_unique<FakeClock>(), GetTestJniCache());
ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
- ASSERT_THAT(icing.SetSchema(CreateMessageSchema()).status(), ProtoIsOk());
- ASSERT_THAT(icing.Put(CreateMessageDocument("namespace", "uri")).status(),
- ProtoIsOk());
+ ASSERT_THAT(icing.SetSchema(schema).status(), ProtoIsOk());
+ ASSERT_THAT(icing.Put(document).status(), ProtoIsOk());
// Optimize() fails due to filesystem error
OptimizeResultProto result = icing.Optimize();
@@ -873,6 +1358,30 @@ TEST_F(IcingSearchEngineOptimizeTest,
TEST_F(IcingSearchEngineOptimizeTest,
OptimizationShouldRecoverIfDataFilesAreMissing) {
+ SchemaProto schema =
+ SchemaBuilder()
+ .AddType(SchemaTypeConfigBuilder()
+ .SetType("Message")
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("body")
+ .SetDataTypeString(TERM_MATCH_PREFIX,
+ TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_REQUIRED))
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("indexableInteger")
+ .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
+ .SetCardinality(CARDINALITY_REQUIRED)))
+ .Build();
+
+ DocumentProto document =
+ DocumentBuilder()
+ .SetKey("namespace", "uri1")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body")
+ .AddInt64Property("indexableInteger", 123)
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+
// Creates a mock filesystem in which SwapFiles() always fails and empties the
// directories. This will fail IcingSearchEngine::OptimizeDocumentStore().
auto mock_filesystem = std::make_unique<MockFilesystem>();
@@ -892,9 +1401,8 @@ TEST_F(IcingSearchEngineOptimizeTest,
std::make_unique<FakeClock>(), GetTestJniCache());
ASSERT_THAT(icing.Initialize().status(), ProtoIsOk());
- ASSERT_THAT(icing.SetSchema(CreateMessageSchema()).status(), ProtoIsOk());
- ASSERT_THAT(icing.Put(CreateMessageDocument("namespace", "uri")).status(),
- ProtoIsOk());
+ ASSERT_THAT(icing.SetSchema(schema).status(), ProtoIsOk());
+ ASSERT_THAT(icing.Put(document).status(), ProtoIsOk());
// Optimize() fails due to filesystem error
OptimizeResultProto result = icing.Optimize();
@@ -969,23 +1477,61 @@ TEST_F(IcingSearchEngineOptimizeTest,
expected_search_result_proto));
}
-TEST_F(IcingSearchEngineOptimizeTest, OptimizeStatsProtoTest) {
+TEST_F(IcingSearchEngineOptimizeTest, OptimizeThresholdTest) {
+ SchemaProto schema =
+ SchemaBuilder()
+ .AddType(SchemaTypeConfigBuilder()
+ .SetType("Message")
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("body")
+ .SetDataTypeString(TERM_MATCH_PREFIX,
+ TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_REQUIRED))
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("indexableInteger")
+ .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
+ .SetCardinality(CARDINALITY_REQUIRED)))
+ .Build();
+
+ DocumentProto document1 =
+ DocumentBuilder()
+ .SetKey("namespace", "uri1")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body one")
+ .AddInt64Property("indexableInteger", 1)
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+ DocumentProto document2 = DocumentBuilder()
+ .SetKey("namespace", "uri2")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body two")
+ .AddInt64Property("indexableInteger", 2)
+ .SetCreationTimestampMs(9000)
+ .SetTtlMs(500)
+ .Build();
+ DocumentProto document3 =
+ DocumentBuilder()
+ .SetKey("namespace", "uri3")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body three")
+ .AddInt64Property("indexableInteger", 3)
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+
auto fake_clock = std::make_unique<FakeClock>();
fake_clock->SetTimerElapsedMilliseconds(5);
fake_clock->SetSystemTimeMilliseconds(10000);
+ IcingSearchEngineOptions options = GetDefaultIcingOptions();
+ // Set the threshold to 0.9 to test that the threshold works.
+ options.set_optimize_rebuild_index_threshold(0.9);
auto icing = std::make_unique<TestIcingSearchEngine>(
- GetDefaultIcingOptions(), std::make_unique<Filesystem>(),
+ options, std::make_unique<Filesystem>(),
std::make_unique<IcingFilesystem>(), std::move(fake_clock),
GetTestJniCache());
ASSERT_THAT(icing->Initialize().status(), ProtoIsOk());
- ASSERT_THAT(icing->SetSchema(CreateMessageSchema()).status(), ProtoIsOk());
-
- // Create three documents.
- DocumentProto document1 = CreateMessageDocument("namespace", "uri1");
- DocumentProto document2 = CreateMessageDocument("namespace", "uri2");
- document2.set_creation_timestamp_ms(9000);
- document2.set_ttl_ms(500);
- DocumentProto document3 = CreateMessageDocument("namespace", "uri3");
+ ASSERT_THAT(icing->SetSchema(schema).status(), ProtoIsOk());
+
+ // Add three documents.
ASSERT_THAT(icing->Put(document1).status(), ProtoIsOk());
ASSERT_THAT(icing->Put(document2).status(), ProtoIsOk());
ASSERT_THAT(icing->Put(document3).status(), ProtoIsOk());
@@ -1022,7 +1568,7 @@ TEST_F(IcingSearchEngineOptimizeTest, OptimizeStatsProtoTest) {
fake_clock->SetTimerElapsedMilliseconds(5);
fake_clock->SetSystemTimeMilliseconds(20000);
icing = std::make_unique<TestIcingSearchEngine>(
- GetDefaultIcingOptions(), std::make_unique<Filesystem>(),
+ options, std::make_unique<Filesystem>(),
std::make_unique<IcingFilesystem>(), std::move(fake_clock),
GetTestJniCache());
ASSERT_THAT(icing->Initialize().status(), ProtoIsOk());
@@ -1069,6 +1615,144 @@ TEST_F(IcingSearchEngineOptimizeTest, OptimizeStatsProtoTest) {
EXPECT_THAT(result.optimize_stats(), EqualsProto(expected));
}
+TEST_F(IcingSearchEngineOptimizeTest, OptimizeStatsProtoTest) {
+ SchemaProto schema =
+ SchemaBuilder()
+ .AddType(SchemaTypeConfigBuilder()
+ .SetType("Message")
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("body")
+ .SetDataTypeString(TERM_MATCH_PREFIX,
+ TOKENIZER_PLAIN)
+ .SetCardinality(CARDINALITY_REQUIRED))
+ .AddProperty(PropertyConfigBuilder()
+ .SetName("indexableInteger")
+ .SetDataTypeInt64(NUMERIC_MATCH_RANGE)
+ .SetCardinality(CARDINALITY_REQUIRED)))
+ .Build();
+
+ DocumentProto document1 =
+ DocumentBuilder()
+ .SetKey("namespace", "uri1")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body one")
+ .AddInt64Property("indexableInteger", 1)
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+ DocumentProto document2 = DocumentBuilder()
+ .SetKey("namespace", "uri2")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body two")
+ .AddInt64Property("indexableInteger", 2)
+ .SetCreationTimestampMs(9000)
+ .SetTtlMs(500)
+ .Build();
+ DocumentProto document3 =
+ DocumentBuilder()
+ .SetKey("namespace", "uri3")
+ .SetSchema("Message")
+ .AddStringProperty("body", "message body three")
+ .AddInt64Property("indexableInteger", 3)
+ .SetCreationTimestampMs(kDefaultCreationTimestampMs)
+ .Build();
+
+ auto fake_clock = std::make_unique<FakeClock>();
+ fake_clock->SetTimerElapsedMilliseconds(5);
+ fake_clock->SetSystemTimeMilliseconds(10000);
+ // Use the default Icing options, so that a change to the default value will
+ // require updating this test.
+ auto icing = std::make_unique<TestIcingSearchEngine>(
+ GetDefaultIcingOptions(), std::make_unique<Filesystem>(),
+ std::make_unique<IcingFilesystem>(), std::move(fake_clock),
+ GetTestJniCache());
+ ASSERT_THAT(icing->Initialize().status(), ProtoIsOk());
+ ASSERT_THAT(icing->SetSchema(schema).status(), ProtoIsOk());
+
+ // Add three documents.
+ ASSERT_THAT(icing->Put(document1).status(), ProtoIsOk());
+ ASSERT_THAT(icing->Put(document2).status(), ProtoIsOk());
+ ASSERT_THAT(icing->Put(document3).status(), ProtoIsOk());
+
+ // Delete the first document.
+ ASSERT_THAT(icing->Delete(document1.namespace_(), document1.uri()).status(),
+ ProtoIsOk());
+ ASSERT_THAT(icing->PersistToDisk(PersistType::FULL).status(), ProtoIsOk());
+
+ OptimizeStatsProto expected;
+ expected.set_latency_ms(5);
+ expected.set_document_store_optimize_latency_ms(5);
+ expected.set_index_restoration_latency_ms(5);
+ expected.set_num_original_documents(3);
+ expected.set_num_deleted_documents(1);
+ expected.set_num_expired_documents(1);
+ expected.set_index_restoration_mode(OptimizeStatsProto::FULL_INDEX_REBUILD);
+
+ // Run Optimize
+ OptimizeResultProto result = icing->Optimize();
+ // Depending on how many blocks the documents end up spread across, it's
+ // possible that Optimize can remove documents without shrinking storage. The
+ // first Optimize call will also write the OptimizeStatusProto for the first
+ // time which will take up 1 block. So make sure that before_size is no less
+ // than after_size - 1 block.
+ uint32_t page_size = getpagesize();
+ EXPECT_THAT(result.optimize_stats().storage_size_before(),
+ Ge(result.optimize_stats().storage_size_after() - page_size));
+ result.mutable_optimize_stats()->clear_storage_size_before();
+ result.mutable_optimize_stats()->clear_storage_size_after();
+ EXPECT_THAT(result.optimize_stats(), EqualsProto(expected));
+
+ fake_clock = std::make_unique<FakeClock>();
+ fake_clock->SetTimerElapsedMilliseconds(5);
+ fake_clock->SetSystemTimeMilliseconds(20000);
+ // Use the default Icing options, so that a change to the default value will
+ // require updating this test.
+ icing = std::make_unique<TestIcingSearchEngine>(
+ GetDefaultIcingOptions(), std::make_unique<Filesystem>(),
+ std::make_unique<IcingFilesystem>(), std::move(fake_clock),
+ GetTestJniCache());
+ ASSERT_THAT(icing->Initialize().status(), ProtoIsOk());
+
+ expected = OptimizeStatsProto();
+ expected.set_latency_ms(5);
+ expected.set_document_store_optimize_latency_ms(5);
+ expected.set_index_restoration_latency_ms(5);
+ expected.set_num_original_documents(1);
+ expected.set_num_deleted_documents(0);
+ expected.set_num_expired_documents(0);
+ expected.set_time_since_last_optimize_ms(10000);
+ expected.set_index_restoration_mode(OptimizeStatsProto::FULL_INDEX_REBUILD);
+
+ // Run Optimize
+ result = icing->Optimize();
+ EXPECT_THAT(result.optimize_stats().storage_size_before(),
+ Eq(result.optimize_stats().storage_size_after()));
+ result.mutable_optimize_stats()->clear_storage_size_before();
+ result.mutable_optimize_stats()->clear_storage_size_after();
+ EXPECT_THAT(result.optimize_stats(), EqualsProto(expected));
+
+ // Delete the last document.
+ ASSERT_THAT(icing->Delete(document3.namespace_(), document3.uri()).status(),
+ ProtoIsOk());
+
+ expected = OptimizeStatsProto();
+ expected.set_latency_ms(5);
+ expected.set_document_store_optimize_latency_ms(5);
+ expected.set_index_restoration_latency_ms(5);
+ expected.set_num_original_documents(1);
+ expected.set_num_deleted_documents(1);
+ expected.set_num_expired_documents(0);
+ expected.set_time_since_last_optimize_ms(0);
+ expected.set_index_restoration_mode(OptimizeStatsProto::FULL_INDEX_REBUILD);
+
+ // Run Optimize
+ result = icing->Optimize();
+ EXPECT_THAT(result.optimize_stats().storage_size_before(),
+ Ge(result.optimize_stats().storage_size_after()));
+ result.mutable_optimize_stats()->clear_storage_size_before();
+ result.mutable_optimize_stats()->clear_storage_size_after();
+ EXPECT_THAT(result.optimize_stats(), EqualsProto(expected));
+}
+
} // namespace
} // namespace lib
} // namespace icing