blob: 28b179786fa7acadbd40aeb72cf43b09a82597b1 [file] [edit]
// Copyright (C) 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "icing/scoring/section-weights.h"
#include <cfloat>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "icing/proto/schema.pb.h"
#include "icing/proto/scoring.pb.h"
#include "icing/proto/term.pb.h"
#include "icing/schema-builder.h"
#include "icing/testing/common-matchers.h"
#include "icing/testing/fake-clock.h"
#include "icing/testing/tmp-directory.h"
namespace icing {
namespace lib {
namespace {
using ::testing::Eq;
class SectionWeightsTest : public testing::Test {
protected:
SectionWeightsTest()
: test_dir_(GetTestTempDir() + "/icing"),
schema_store_dir_(test_dir_ + "/schema_store") {}
void SetUp() override {
// Creates file directories
filesystem_.DeleteDirectoryRecursively(test_dir_.c_str());
filesystem_.CreateDirectoryRecursively(schema_store_dir_.c_str());
ICING_ASSERT_OK_AND_ASSIGN(
schema_store_,
SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
SchemaTypeConfigProto sender_schema =
SchemaTypeConfigBuilder()
.SetType("sender")
.AddProperty(
PropertyConfigBuilder()
.SetName("name")
.SetDataTypeString(
TermMatchType::PREFIX,
StringIndexingConfig::TokenizerType::PLAIN)
.SetCardinality(PropertyConfigProto::Cardinality::OPTIONAL))
.Build();
SchemaTypeConfigProto email_schema =
SchemaTypeConfigBuilder()
.SetType("email")
.AddProperty(
PropertyConfigBuilder()
.SetName("subject")
.SetDataTypeString(
TermMatchType::PREFIX,
StringIndexingConfig::TokenizerType::PLAIN)
.SetDataType(PropertyConfigProto::DataType::STRING)
.SetCardinality(PropertyConfigProto::Cardinality::OPTIONAL))
.AddProperty(
PropertyConfigBuilder()
.SetName("body")
.SetDataTypeString(
TermMatchType::PREFIX,
StringIndexingConfig::TokenizerType::PLAIN)
.SetDataType(PropertyConfigProto::DataType::STRING)
.SetCardinality(PropertyConfigProto::Cardinality::OPTIONAL))
.AddProperty(
PropertyConfigBuilder()
.SetName("sender")
.SetDataTypeDocument("sender",
/*index_nested_properties=*/true)
.SetCardinality(PropertyConfigProto::Cardinality::OPTIONAL))
.Build();
SchemaProto schema =
SchemaBuilder().AddType(sender_schema).AddType(email_schema).Build();
ICING_ASSERT_OK(schema_store_->SetSchema(
schema, /*ignore_errors_and_delete_documents=*/false,
/*allow_circular_schema_definitions=*/false));
}
void TearDown() override {
schema_store_.reset();
filesystem_.DeleteDirectoryRecursively(test_dir_.c_str());
}
SchemaStore *schema_store() { return schema_store_.get(); }
private:
const std::string test_dir_;
const std::string schema_store_dir_;
Filesystem filesystem_;
FakeClock fake_clock_;
std::unique_ptr<SchemaStore> schema_store_;
};
TEST_F(SectionWeightsTest, ShouldNormalizeSinglePropertyWeight) {
ScoringSpecProto spec_proto;
TypePropertyWeights *type_property_weights =
spec_proto.add_type_property_weights();
type_property_weights->set_schema_type("sender");
PropertyWeight *property_weight =
type_property_weights->add_property_weights();
property_weight->set_weight(5.0);
property_weight->set_path("name");
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<SectionWeights> section_weights,
SectionWeights::Create(schema_store(), spec_proto));
ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId sender_schema_type_id,
schema_store()->GetSchemaTypeId("sender"));
// section_id 0 corresponds to property "name".
// We expect 1.0 as there is only one property in the "sender" schema type
// so it should take the max normalized weight of 1.0.
EXPECT_THAT(section_weights->GetNormalizedSectionWeight(sender_schema_type_id,
/*section_id=*/0),
Eq(1.0));
}
TEST_F(SectionWeightsTest, ShouldAcceptMaxWeightValue) {
ScoringSpecProto spec_proto;
TypePropertyWeights *type_property_weights =
spec_proto.add_type_property_weights();
type_property_weights->set_schema_type("sender");
PropertyWeight *property_weight =
type_property_weights->add_property_weights();
property_weight->set_weight(DBL_MAX);
property_weight->set_path("name");
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<SectionWeights> section_weights,
SectionWeights::Create(schema_store(), spec_proto));
ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId sender_schema_type_id,
schema_store()->GetSchemaTypeId("sender"));
// section_id 0 corresponds to property "name".
EXPECT_THAT(section_weights->GetNormalizedSectionWeight(sender_schema_type_id,
/*section_id=*/0),
Eq(1.0));
}
TEST_F(SectionWeightsTest, ShouldFailWithNegativeWeights) {
ScoringSpecProto spec_proto;
TypePropertyWeights *type_property_weights =
spec_proto.add_type_property_weights();
type_property_weights->set_schema_type("email");
PropertyWeight *body_propery_weight =
type_property_weights->add_property_weights();
body_propery_weight->set_weight(-100.0);
body_propery_weight->set_path("body");
EXPECT_THAT(SectionWeights::Create(schema_store(), spec_proto).status(),
StatusIs(libtextclassifier3::StatusCode::INVALID_ARGUMENT));
}
TEST_F(SectionWeightsTest, ShouldAcceptZeroWeight) {
ScoringSpecProto spec_proto;
TypePropertyWeights *type_property_weights =
spec_proto.add_type_property_weights();
type_property_weights->set_schema_type("email");
PropertyWeight *body_property_weight =
type_property_weights->add_property_weights();
body_property_weight->set_weight(2.0);
body_property_weight->set_path("body");
PropertyWeight *subject_property_weight =
type_property_weights->add_property_weights();
subject_property_weight->set_weight(0.0);
subject_property_weight->set_path("subject");
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<SectionWeights> section_weights,
SectionWeights::Create(schema_store(), spec_proto));
ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId email_schema_type_id,
schema_store()->GetSchemaTypeId("email"));
// Normalized weight for "body" property.
EXPECT_THAT(section_weights->GetNormalizedSectionWeight(email_schema_type_id,
/*section_id=*/0),
Eq(1.0));
// Normalized weight for "subject" property.
EXPECT_THAT(section_weights->GetNormalizedSectionWeight(email_schema_type_id,
/*section_id=*/2),
Eq(0.0));
}
TEST_F(SectionWeightsTest, ShouldNormalizeToZeroWhenAllWeightsZero) {
ScoringSpecProto spec_proto;
TypePropertyWeights *type_property_weights =
spec_proto.add_type_property_weights();
type_property_weights->set_schema_type("email");
PropertyWeight *body_property_weight =
type_property_weights->add_property_weights();
body_property_weight->set_weight(0.0);
body_property_weight->set_path("body");
PropertyWeight *sender_property_weight =
type_property_weights->add_property_weights();
sender_property_weight->set_weight(0.0);
sender_property_weight->set_path("sender.name");
PropertyWeight *subject_property_weight =
type_property_weights->add_property_weights();
subject_property_weight->set_weight(0.0);
subject_property_weight->set_path("subject");
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<SectionWeights> section_weights,
SectionWeights::Create(schema_store(), spec_proto));
ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId email_schema_type_id,
schema_store()->GetSchemaTypeId("email"));
// Normalized weight for "body" property.
EXPECT_THAT(section_weights->GetNormalizedSectionWeight(email_schema_type_id,
/*section_id=*/0),
Eq(0.0));
// Normalized weight for "sender.name" property (the nested property).
EXPECT_THAT(section_weights->GetNormalizedSectionWeight(email_schema_type_id,
/*section_id=*/1),
Eq(0.0));
// Normalized weight for "subject" property.
EXPECT_THAT(section_weights->GetNormalizedSectionWeight(email_schema_type_id,
/*section_id=*/2),
Eq(0.0));
}
TEST_F(SectionWeightsTest, ShouldReturnDefaultIfTypePropertyWeightsNotSet) {
ScoringSpecProto spec_proto;
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<SectionWeights> section_weights,
SectionWeights::Create(schema_store(), spec_proto));
ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId email_schema_type_id,
schema_store()->GetSchemaTypeId("email"));
EXPECT_THAT(section_weights->GetNormalizedSectionWeight(email_schema_type_id,
/*section_id=*/0),
Eq(kDefaultSectionWeight));
}
TEST_F(SectionWeightsTest, ShouldSetNestedPropertyWeights) {
ScoringSpecProto spec_proto;
TypePropertyWeights *type_property_weights =
spec_proto.add_type_property_weights();
type_property_weights->set_schema_type("email");
PropertyWeight *body_property_weight =
type_property_weights->add_property_weights();
body_property_weight->set_weight(1.0);
body_property_weight->set_path("body");
PropertyWeight *subject_property_weight =
type_property_weights->add_property_weights();
subject_property_weight->set_weight(100.0);
subject_property_weight->set_path("subject");
PropertyWeight *nested_property_weight =
type_property_weights->add_property_weights();
nested_property_weight->set_weight(50.0);
nested_property_weight->set_path("sender.name");
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<SectionWeights> section_weights,
SectionWeights::Create(schema_store(), spec_proto));
ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId email_schema_type_id,
schema_store()->GetSchemaTypeId("email"));
// Normalized weight for "body" property.
EXPECT_THAT(section_weights->GetNormalizedSectionWeight(email_schema_type_id,
/*section_id=*/0),
Eq(0.01));
// Normalized weight for "sender.name" property (the nested property).
EXPECT_THAT(section_weights->GetNormalizedSectionWeight(email_schema_type_id,
/*section_id=*/1),
Eq(0.5));
// Normalized weight for "subject" property.
EXPECT_THAT(section_weights->GetNormalizedSectionWeight(email_schema_type_id,
/*section_id=*/2),
Eq(1.0));
}
TEST_F(SectionWeightsTest, ShouldNormalizeIfAllWeightsBelowOne) {
ScoringSpecProto spec_proto;
TypePropertyWeights *type_property_weights =
spec_proto.add_type_property_weights();
type_property_weights->set_schema_type("email");
PropertyWeight *body_property_weight =
type_property_weights->add_property_weights();
body_property_weight->set_weight(0.1);
body_property_weight->set_path("body");
PropertyWeight *sender_name_weight =
type_property_weights->add_property_weights();
sender_name_weight->set_weight(0.2);
sender_name_weight->set_path("sender.name");
PropertyWeight *subject_property_weight =
type_property_weights->add_property_weights();
subject_property_weight->set_weight(0.4);
subject_property_weight->set_path("subject");
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<SectionWeights> section_weights,
SectionWeights::Create(schema_store(), spec_proto));
ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId email_schema_type_id,
schema_store()->GetSchemaTypeId("email"));
// Normalized weight for "body" property.
EXPECT_THAT(section_weights->GetNormalizedSectionWeight(email_schema_type_id,
/*section_id=*/0),
Eq(1.0 / 4.0));
// Normalized weight for "sender.name" property (the nested property).
EXPECT_THAT(section_weights->GetNormalizedSectionWeight(email_schema_type_id,
/*section_id=*/1),
Eq(2.0 / 4.0));
// Normalized weight for "subject" property.
EXPECT_THAT(section_weights->GetNormalizedSectionWeight(email_schema_type_id,
/*section_id=*/2),
Eq(1.0));
}
TEST_F(SectionWeightsTest, ShouldSetNestedPropertyWeightSeparatelyForTypes) {
ScoringSpecProto spec_proto;
TypePropertyWeights *email_type_property_weights =
spec_proto.add_type_property_weights();
email_type_property_weights->set_schema_type("email");
PropertyWeight *body_property_weight =
email_type_property_weights->add_property_weights();
body_property_weight->set_weight(1.0);
body_property_weight->set_path("body");
PropertyWeight *subject_property_weight =
email_type_property_weights->add_property_weights();
subject_property_weight->set_weight(100.0);
subject_property_weight->set_path("subject");
PropertyWeight *sender_name_property_weight =
email_type_property_weights->add_property_weights();
sender_name_property_weight->set_weight(50.0);
sender_name_property_weight->set_path("sender.name");
TypePropertyWeights *sender_type_property_weights =
spec_proto.add_type_property_weights();
sender_type_property_weights->set_schema_type("sender");
PropertyWeight *sender_property_weight =
sender_type_property_weights->add_property_weights();
sender_property_weight->set_weight(25.0);
sender_property_weight->set_path("sender");
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<SectionWeights> section_weights,
SectionWeights::Create(schema_store(), spec_proto));
ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId email_schema_type_id,
schema_store()->GetSchemaTypeId("email"));
ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId sender_schema_type_id,
schema_store()->GetSchemaTypeId("sender"));
// Normalized weight for "sender.name" property (the nested property)
EXPECT_THAT(section_weights->GetNormalizedSectionWeight(email_schema_type_id,
/*section_id=*/1),
Eq(0.5));
// Normalized weight for "name" property for "sender" schema type. As it is
// the only property of the type, it should take the max normalized weight of
// 1.0.
EXPECT_THAT(section_weights->GetNormalizedSectionWeight(sender_schema_type_id,
/*section_id=*/2),
Eq(1.0));
}
TEST_F(SectionWeightsTest, ShouldSkipNonExistentPathWhenSettingWeights) {
ScoringSpecProto spec_proto;
TypePropertyWeights *type_property_weights =
spec_proto.add_type_property_weights();
type_property_weights->set_schema_type("email");
// If this property weight isn't skipped, then the max property weight would
// be set to 100.0 and all weights would be normalized against the max.
PropertyWeight *non_valid_property_weight =
type_property_weights->add_property_weights();
non_valid_property_weight->set_weight(100.0);
non_valid_property_weight->set_path("sender.organization");
PropertyWeight *subject_property_weight =
type_property_weights->add_property_weights();
subject_property_weight->set_weight(10.0);
subject_property_weight->set_path("subject");
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<SectionWeights> section_weights,
SectionWeights::Create(schema_store(), spec_proto));
ICING_ASSERT_OK_AND_ASSIGN(SchemaTypeId email_schema_type_id,
schema_store()->GetSchemaTypeId("email"));
// Normalized weight for "body" property. Because the weight is not explicitly
// set, it is set to the default of 1.0 before being normalized.
EXPECT_THAT(section_weights->GetNormalizedSectionWeight(email_schema_type_id,
/*section_id=*/0),
Eq(0.1));
// Normalized weight for "sender.name" property (the nested property). Because
// the weight is not explicitly set, it is set to the default of 1.0 before
// being normalized.
EXPECT_THAT(section_weights->GetNormalizedSectionWeight(email_schema_type_id,
/*section_id=*/1),
Eq(0.1));
// Normalized weight for "subject" property. Because the invalid property path
// is skipped when assigning weights, subject takes the max normalized weight
// of 1.0 instead.
EXPECT_THAT(section_weights->GetNormalizedSectionWeight(email_schema_type_id,
/*section_id=*/2),
Eq(1.0));
}
} // namespace
} // namespace lib
} // namespace icing