blob: 044e0f2ad2bdc86f1ce3b3f59093c55a3d43dc54 [file] [log] [blame]
// Copyright (C) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "icing/result/result-retriever.h"
#include <limits>
#include <memory>
#include <string_view>
#include <unordered_map>
#include "gtest/gtest.h"
#include "icing/document-builder.h"
#include "icing/file/mock-filesystem.h"
#include "icing/portable/equals-proto.h"
#include "icing/portable/platform.h"
#include "icing/proto/document.pb.h"
#include "icing/proto/schema.pb.h"
#include "icing/proto/search.pb.h"
#include "icing/proto/term.pb.h"
#include "icing/result/projection-tree.h"
#include "icing/schema-builder.h"
#include "icing/schema/schema-store.h"
#include "icing/schema/section.h"
#include "icing/store/document-id.h"
#include "icing/testing/common-matchers.h"
#include "icing/testing/fake-clock.h"
#include "icing/testing/icu-data-file-helper.h"
#include "icing/testing/test-data.h"
#include "icing/testing/tmp-directory.h"
#include "icing/tokenization/language-segmenter-factory.h"
#include "icing/transform/normalizer-factory.h"
#include "icing/transform/normalizer.h"
#include "icing/util/snippet-helpers.h"
#include "unicode/uloc.h"
namespace icing {
namespace lib {
namespace {
using ::icing::lib::portable_equals_proto::EqualsProto;
using ::testing::ElementsAre;
using ::testing::Eq;
using ::testing::IsEmpty;
using ::testing::Return;
using ::testing::SizeIs;
class ResultRetrieverTest : public testing::Test {
protected:
ResultRetrieverTest() : test_dir_(GetTestTempDir() + "/icing") {
filesystem_.CreateDirectoryRecursively(test_dir_.c_str());
}
void SetUp() override {
if (!IsCfStringTokenization() && !IsReverseJniTokenization()) {
ICING_ASSERT_OK(
// File generated via icu_data_file rule in //icing/BUILD.
icu_data_file_helper::SetUpICUDataFile(
GetTestFilePath("icing/icu.dat")));
}
language_segmenter_factory::SegmenterOptions options(ULOC_US);
ICING_ASSERT_OK_AND_ASSIGN(
language_segmenter_,
language_segmenter_factory::Create(std::move(options)));
ICING_ASSERT_OK_AND_ASSIGN(
schema_store_,
SchemaStore::Create(&filesystem_, test_dir_, &fake_clock_));
ICING_ASSERT_OK_AND_ASSIGN(normalizer_, normalizer_factory::Create(
/*max_term_byte_size=*/10000));
SchemaProto schema =
SchemaBuilder()
.AddType(SchemaTypeConfigBuilder()
.SetType("Email")
.AddProperty(PropertyConfigBuilder()
.SetName("name")
.SetDataTypeString(TERM_MATCH_PREFIX,
TOKENIZER_PLAIN)
.SetCardinality(CARDINALITY_OPTIONAL))
.AddProperty(PropertyConfigBuilder()
.SetName("body")
.SetDataTypeString(TERM_MATCH_EXACT,
TOKENIZER_PLAIN)
.SetCardinality(CARDINALITY_OPTIONAL))
.AddProperty(
PropertyConfigBuilder()
.SetName("sender")
.SetDataTypeDocument(
"Person", /*index_nested_properties=*/true)
.SetCardinality(CARDINALITY_OPTIONAL)))
.AddType(
SchemaTypeConfigBuilder()
.SetType("Person")
.AddProperty(PropertyConfigBuilder()
.SetName("name")
.SetDataTypeString(TERM_MATCH_PREFIX,
TOKENIZER_PLAIN)
.SetCardinality(CARDINALITY_OPTIONAL))
.AddProperty(PropertyConfigBuilder()
.SetName("emailAddress")
.SetDataTypeString(TERM_MATCH_PREFIX,
TOKENIZER_PLAIN)
.SetCardinality(CARDINALITY_OPTIONAL)))
.Build();
ASSERT_THAT(schema_store_->SetSchema(schema), IsOk());
}
void TearDown() override {
filesystem_.DeleteDirectoryRecursively(test_dir_.c_str());
}
SectionId GetSectionId(const std::string& type, const std::string& property) {
auto type_id_or = schema_store_->GetSchemaTypeId(type);
if (!type_id_or.ok()) {
return kInvalidSectionId;
}
SchemaTypeId type_id = type_id_or.ValueOrDie();
for (SectionId section_id = 0; section_id <= kMaxSectionId; ++section_id) {
auto metadata_or = schema_store_->GetSectionMetadata(type_id, section_id);
if (!metadata_or.ok()) {
break;
}
const SectionMetadata* metadata = metadata_or.ValueOrDie();
if (metadata->path == property) {
return metadata->id;
}
}
return kInvalidSectionId;
}
const Filesystem filesystem_;
const std::string test_dir_;
std::unique_ptr<LanguageSegmenter> language_segmenter_;
std::unique_ptr<SchemaStore> schema_store_;
std::unique_ptr<Normalizer> normalizer_;
FakeClock fake_clock_;
};
ResultSpecProto::SnippetSpecProto CreateSnippetSpec() {
ResultSpecProto::SnippetSpecProto snippet_spec;
snippet_spec.set_num_to_snippet(std::numeric_limits<int>::max());
snippet_spec.set_num_matches_per_property(std::numeric_limits<int>::max());
snippet_spec.set_max_window_utf32_length(1024);
return snippet_spec;
}
DocumentProto CreateDocument(int id) {
return DocumentBuilder()
.SetKey("icing", "Email/" + std::to_string(id))
.SetSchema("Email")
.AddStringProperty("name", "subject foo " + std::to_string(id))
.AddStringProperty("body", "body bar " + std::to_string(id))
.SetCreationTimestampMs(1574365086666 + id)
.Build();
}
SectionIdMask CreateSectionIdMask(const std::vector<SectionId>& section_ids) {
SectionIdMask mask = 0;
for (SectionId section_id : section_ids) {
mask |= (UINT64_C(1) << section_id);
}
return mask;
}
TEST_F(ResultRetrieverTest, CreationWithNullPointerShouldFail) {
EXPECT_THAT(
ResultRetriever::Create(/*doc_store=*/nullptr, schema_store_.get(),
language_segmenter_.get(), normalizer_.get()),
StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
EXPECT_THAT(
ResultRetriever::Create(doc_store.get(), /*schema_store=*/nullptr,
language_segmenter_.get(), normalizer_.get()),
StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
EXPECT_THAT(ResultRetriever::Create(doc_store.get(), schema_store_.get(),
/*language_segmenter=*/nullptr,
normalizer_.get()),
StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
EXPECT_THAT(ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(),
/*normalizer=*/nullptr),
StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
}
TEST_F(ResultRetrieverTest, ShouldRetrieveSimpleResults) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(CreateDocument(/*id=*/1)));
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(CreateDocument(/*id=*/2)));
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id3,
doc_store->Put(CreateDocument(/*id=*/3)));
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/19},
{document_id2, hit_section_id_mask, /*score=*/5},
{document_id3, hit_section_id_mask, /*score=*/1}};
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get()));
SearchResultProto::ResultProto result1;
*result1.mutable_document() = CreateDocument(/*id=*/1);
result1.set_score(19);
SearchResultProto::ResultProto result2;
*result2.mutable_document() = CreateDocument(/*id=*/2);
result2.set_score(5);
SearchResultProto::ResultProto result3;
*result3.mutable_document() = CreateDocument(/*id=*/3);
result3.set_score(1);
SnippetContext snippet_context(
/*query_terms_in=*/{},
ResultSpecProto::SnippetSpecProto::default_instance(),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context),
std::unordered_map<std::string, ProjectionTree>(),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/3);
EXPECT_THAT(
result_retriever->RetrieveResults(page_result_state),
IsOkAndHolds(ElementsAre(EqualsProto(result1), EqualsProto(result2),
EqualsProto(result3))));
}
TEST_F(ResultRetrieverTest, IgnoreErrors) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(CreateDocument(/*id=*/1)));
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(CreateDocument(/*id=*/2)));
DocumentId invalid_document_id = -1;
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/12},
{document_id2, hit_section_id_mask, /*score=*/4},
{invalid_document_id, hit_section_id_mask, /*score=*/0}};
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get(),
/*ignore_bad_document_ids=*/true));
SearchResultProto::ResultProto result1;
*result1.mutable_document() = CreateDocument(/*id=*/1);
result1.set_score(12);
SearchResultProto::ResultProto result2;
*result2.mutable_document() = CreateDocument(/*id=*/2);
result2.set_score(4);
SnippetContext snippet_context(
/*query_terms_in=*/{},
ResultSpecProto::SnippetSpecProto::default_instance(),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context),
std::unordered_map<std::string, ProjectionTree>(),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/3);
EXPECT_THAT(
result_retriever->RetrieveResults(page_result_state),
IsOkAndHolds(ElementsAre(EqualsProto(result1), EqualsProto(result2))));
}
TEST_F(ResultRetrieverTest, NotIgnoreErrors) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(CreateDocument(/*id=*/1)));
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(CreateDocument(/*id=*/2)));
DocumentId invalid_document_id = -1;
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0},
{invalid_document_id, hit_section_id_mask, /*score=*/0}};
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get(),
/*ignore_bad_document_ids=*/false));
SnippetContext snippet_context(
/*query_terms_in=*/{},
ResultSpecProto::SnippetSpecProto::default_instance(),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context),
std::unordered_map<std::string, ProjectionTree>(),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/3);
EXPECT_THAT(result_retriever->RetrieveResults(page_result_state),
StatusIs(libtextclassifier3::StatusCode::INVALID_ARGUMENT));
DocumentId non_existing_document_id = 4;
page_result_state.scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0},
{non_existing_document_id, hit_section_id_mask, /*score=*/0}};
EXPECT_THAT(result_retriever->RetrieveResults(page_result_state),
StatusIs(libtextclassifier3::StatusCode::NOT_FOUND));
}
TEST_F(ResultRetrieverTest, IOErrorShouldReturnInternalError) {
MockFilesystem mock_filesystem;
ON_CALL(mock_filesystem, PRead(A<int>(), A<void*>(), A<size_t>(), A<off_t>()))
.WillByDefault(Return(false));
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&mock_filesystem, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(CreateDocument(/*id=*/1)));
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(CreateDocument(/*id=*/2)));
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0}};
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get(),
/*ignore_bad_document_ids=*/true));
SnippetContext snippet_context(
/*query_terms_in=*/{},
ResultSpecProto::SnippetSpecProto::default_instance(),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context),
std::unordered_map<std::string, ProjectionTree>(),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/2);
EXPECT_THAT(result_retriever->RetrieveResults(page_result_state),
StatusIs(libtextclassifier3::StatusCode::INTERNAL));
}
TEST_F(ResultRetrieverTest, DefaultSnippetSpecShouldDisableSnippeting) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(CreateDocument(/*id=*/1)));
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(CreateDocument(/*id=*/2)));
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id3,
doc_store->Put(CreateDocument(/*id=*/3)));
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0},
{document_id3, hit_section_id_mask, /*score=*/0}};
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get()));
SnippetContext snippet_context(
/*query_terms_in=*/{},
ResultSpecProto::SnippetSpecProto::default_instance(),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context),
std::unordered_map<std::string, ProjectionTree>(),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/3);
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<SearchResultProto::ResultProto> results,
result_retriever->RetrieveResults(page_result_state));
ASSERT_THAT(results, SizeIs(3));
EXPECT_THAT(results.at(0).snippet(),
EqualsProto(SnippetProto::default_instance()));
EXPECT_THAT(results.at(1).snippet(),
EqualsProto(SnippetProto::default_instance()));
EXPECT_THAT(results.at(2).snippet(),
EqualsProto(SnippetProto::default_instance()));
}
TEST_F(ResultRetrieverTest, SimpleSnippeted) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(CreateDocument(/*id=*/1)));
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(CreateDocument(/*id=*/2)));
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id3,
doc_store->Put(CreateDocument(/*id=*/3)));
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0},
{document_id3, hit_section_id_mask, /*score=*/0}};
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get()));
SnippetContext snippet_context(
/*query_terms_in=*/{{"", {"foo", "bar"}}}, CreateSnippetSpec(),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context),
std::unordered_map<std::string, ProjectionTree>(),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/3);
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<SearchResultProto::ResultProto> result,
result_retriever->RetrieveResults(page_result_state));
EXPECT_THAT(result, SizeIs(3));
const DocumentProto& result_document_one = result.at(0).document();
const SnippetProto& result_snippet_one = result.at(0).snippet();
EXPECT_THAT(result_document_one, EqualsProto(CreateDocument(/*id=*/1)));
EXPECT_THAT(result_snippet_one.entries(), SizeIs(2));
EXPECT_THAT(result_snippet_one.entries(0).property_name(), Eq("body"));
std::string_view content = GetString(
&result_document_one, result_snippet_one.entries(0).property_name());
EXPECT_THAT(GetWindows(content, result_snippet_one.entries(0)),
ElementsAre("body bar 1"));
EXPECT_THAT(GetMatches(content, result_snippet_one.entries(0)),
ElementsAre("bar"));
EXPECT_THAT(result_snippet_one.entries(1).property_name(), Eq("name"));
content = GetString(&result_document_one,
result_snippet_one.entries(1).property_name());
EXPECT_THAT(GetWindows(content, result_snippet_one.entries(1)),
ElementsAre("subject foo 1"));
EXPECT_THAT(GetMatches(content, result_snippet_one.entries(1)),
ElementsAre("foo"));
const DocumentProto& result_document_two = result.at(1).document();
const SnippetProto& result_snippet_two = result.at(1).snippet();
EXPECT_THAT(result_document_two, EqualsProto(CreateDocument(/*id=*/2)));
EXPECT_THAT(result_snippet_two.entries(), SizeIs(2));
EXPECT_THAT(result_snippet_two.entries(0).property_name(), Eq("body"));
content = GetString(&result_document_two,
result_snippet_two.entries(0).property_name());
EXPECT_THAT(GetWindows(content, result_snippet_two.entries(0)),
ElementsAre("body bar 2"));
EXPECT_THAT(GetMatches(content, result_snippet_two.entries(0)),
ElementsAre("bar"));
EXPECT_THAT(result_snippet_two.entries(1).property_name(), Eq("name"));
content = GetString(&result_document_two,
result_snippet_two.entries(1).property_name());
EXPECT_THAT(GetWindows(content, result_snippet_two.entries(1)),
ElementsAre("subject foo 2"));
EXPECT_THAT(GetMatches(content, result_snippet_two.entries(1)),
ElementsAre("foo"));
const DocumentProto& result_document_three = result.at(2).document();
const SnippetProto& result_snippet_three = result.at(2).snippet();
EXPECT_THAT(result_document_three, EqualsProto(CreateDocument(/*id=*/3)));
EXPECT_THAT(result_snippet_three.entries(), SizeIs(2));
EXPECT_THAT(result_snippet_three.entries(0).property_name(), Eq("body"));
content = GetString(&result_document_three,
result_snippet_three.entries(0).property_name());
EXPECT_THAT(GetWindows(content, result_snippet_three.entries(0)),
ElementsAre("body bar 3"));
EXPECT_THAT(GetMatches(content, result_snippet_three.entries(0)),
ElementsAre("bar"));
EXPECT_THAT(result_snippet_three.entries(1).property_name(), Eq("name"));
content = GetString(&result_document_three,
result_snippet_three.entries(1).property_name());
EXPECT_THAT(GetWindows(content, result_snippet_three.entries(1)),
ElementsAre("subject foo 3"));
EXPECT_THAT(GetMatches(content, result_snippet_three.entries(1)),
ElementsAre("foo"));
}
TEST_F(ResultRetrieverTest, OnlyOneDocumentSnippeted) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(CreateDocument(/*id=*/1)));
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(CreateDocument(/*id=*/2)));
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id3,
doc_store->Put(CreateDocument(/*id=*/3)));
ResultSpecProto::SnippetSpecProto snippet_spec = CreateSnippetSpec();
snippet_spec.set_num_to_snippet(1);
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0},
{document_id3, hit_section_id_mask, /*score=*/0}};
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get()));
SnippetContext snippet_context(/*query_terms_in=*/{{"", {"foo", "bar"}}},
snippet_spec, TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context),
std::unordered_map<std::string, ProjectionTree>(),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/3);
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<SearchResultProto::ResultProto> result,
result_retriever->RetrieveResults(page_result_state));
EXPECT_THAT(result, SizeIs(3));
const DocumentProto& result_document = result.at(0).document();
const SnippetProto& result_snippet = result.at(0).snippet();
EXPECT_THAT(result_document, EqualsProto(CreateDocument(/*id=*/1)));
EXPECT_THAT(result_snippet.entries(), SizeIs(2));
EXPECT_THAT(result_snippet.entries(0).property_name(), Eq("body"));
std::string_view content =
GetString(&result_document, result_snippet.entries(0).property_name());
EXPECT_THAT(GetWindows(content, result_snippet.entries(0)),
ElementsAre("body bar 1"));
EXPECT_THAT(GetMatches(content, result_snippet.entries(0)),
ElementsAre("bar"));
EXPECT_THAT(result_snippet.entries(1).property_name(), Eq("name"));
content =
GetString(&result_document, result_snippet.entries(1).property_name());
EXPECT_THAT(GetWindows(content, result_snippet.entries(1)),
ElementsAre("subject foo 1"));
EXPECT_THAT(GetMatches(content, result_snippet.entries(1)),
ElementsAre("foo"));
EXPECT_THAT(result[1].document(), EqualsProto(CreateDocument(/*id=*/2)));
EXPECT_THAT(result[1].snippet(),
EqualsProto(SnippetProto::default_instance()));
EXPECT_THAT(result[2].document(), EqualsProto(CreateDocument(/*id=*/3)));
EXPECT_THAT(result[2].snippet(),
EqualsProto(SnippetProto::default_instance()));
}
TEST_F(ResultRetrieverTest, ShouldSnippetAllResults) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(CreateDocument(/*id=*/1)));
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(CreateDocument(/*id=*/2)));
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id3,
doc_store->Put(CreateDocument(/*id=*/3)));
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0},
{document_id3, hit_section_id_mask, /*score=*/0}};
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get()));
ResultSpecProto::SnippetSpecProto snippet_spec = CreateSnippetSpec();
snippet_spec.set_num_to_snippet(5);
SnippetContext snippet_context(
/*query_terms_in=*/{{"", {"foo", "bar"}}}, std::move(snippet_spec),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context),
std::unordered_map<std::string, ProjectionTree>(),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/3);
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<SearchResultProto::ResultProto> result,
result_retriever->RetrieveResults(page_result_state));
// num_to_snippet = 5, num_previously_returned_in = 0,
// We can return 5 - 0 = 5 snippets at most. We're able to return all 3
// snippets here.
ASSERT_THAT(result, SizeIs(3));
EXPECT_THAT(result[0].snippet().entries(), Not(IsEmpty()));
EXPECT_THAT(result[1].snippet().entries(), Not(IsEmpty()));
EXPECT_THAT(result[2].snippet().entries(), Not(IsEmpty()));
}
TEST_F(ResultRetrieverTest, ShouldSnippetSomeResults) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(CreateDocument(/*id=*/1)));
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(CreateDocument(/*id=*/2)));
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id3,
doc_store->Put(CreateDocument(/*id=*/3)));
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0},
{document_id3, hit_section_id_mask, /*score=*/0}};
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get()));
ResultSpecProto::SnippetSpecProto snippet_spec = CreateSnippetSpec();
snippet_spec.set_num_to_snippet(5);
SnippetContext snippet_context(
/*query_terms_in=*/{{"", {"foo", "bar"}}}, std::move(snippet_spec),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context),
std::unordered_map<std::string, ProjectionTree>(),
/*num_previously_returned_in=*/3,
/*num_per_page_in=*/3);
// num_to_snippet = 5, num_previously_returned_in = 3,
// We can return 5 - 3 = 2 snippets.
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<SearchResultProto::ResultProto> result,
result_retriever->RetrieveResults(page_result_state));
ASSERT_THAT(result, SizeIs(3));
EXPECT_THAT(result[0].snippet().entries(), Not(IsEmpty()));
EXPECT_THAT(result[1].snippet().entries(), Not(IsEmpty()));
EXPECT_THAT(result[2].snippet().entries(), IsEmpty());
}
TEST_F(ResultRetrieverTest, ShouldNotSnippetAnyResults) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(CreateDocument(/*id=*/1)));
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(CreateDocument(/*id=*/2)));
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id3,
doc_store->Put(CreateDocument(/*id=*/3)));
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0},
{document_id3, hit_section_id_mask, /*score=*/0}};
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get()));
ResultSpecProto::SnippetSpecProto snippet_spec = CreateSnippetSpec();
snippet_spec.set_num_to_snippet(5);
SnippetContext snippet_context(
/*query_terms_in=*/{{"", {"foo", "bar"}}}, std::move(snippet_spec),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context),
std::unordered_map<std::string, ProjectionTree>(),
/*num_previously_returned_in=*/6,
/*num_per_page_in=*/3);
// num_to_snippet = 5, num_previously_returned_in = 6,
// We can't return any snippets for this page.
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<SearchResultProto::ResultProto> result,
result_retriever->RetrieveResults(page_result_state));
ASSERT_THAT(result, SizeIs(3));
EXPECT_THAT(result[0].snippet().entries(), IsEmpty());
EXPECT_THAT(result[1].snippet().entries(), IsEmpty());
EXPECT_THAT(result[2].snippet().entries(), IsEmpty());
}
TEST_F(ResultRetrieverTest, ProjectionTopLevelLeadNodeFieldPath) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
// 1. Add two Email documents
DocumentProto document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty("name", "Hello World!")
.AddStringProperty(
"body", "Oh what a beautiful morning! Oh what a beautiful day!")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(document_one));
DocumentProto document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty("name", "Goodnight Moon!")
.AddStringProperty("body",
"Count all the sheep and tell them 'Hello'.")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(document_two));
// 2. Setup the scored results.
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0}};
TypePropertyMask type_property_mask;
type_property_mask.set_schema_type("Email");
type_property_mask.add_paths("name");
std::unordered_map<std::string, ProjectionTree> type_projection_tree_map;
type_projection_tree_map.insert(
{"Email", ProjectionTree(type_property_mask)});
SnippetContext snippet_context(
/*query_terms_in=*/{},
ResultSpecProto::SnippetSpecProto::default_instance(),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context), std::move(type_projection_tree_map),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/2);
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get()));
// 3. Verify that the returned results only contain the 'name' property.
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<SearchResultProto::ResultProto> result,
result_retriever->RetrieveResults(page_result_state));
ASSERT_THAT(result, SizeIs(2));
DocumentProto projected_document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty("name", "Hello World!")
.Build();
EXPECT_THAT(result[0].document(), EqualsProto(projected_document_one));
DocumentProto projected_document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty("name", "Goodnight Moon!")
.Build();
EXPECT_THAT(result[1].document(), EqualsProto(projected_document_two));
}
TEST_F(ResultRetrieverTest, ProjectionNestedLeafNodeFieldPath) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
// 1. Add two Email documents
DocumentProto document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddDocumentProperty(
"sender",
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetSchema("Person")
.AddStringProperty("name", "Meg Ryan")
.AddStringProperty("emailAddress", "shopgirl@aol.com")
.Build())
.AddStringProperty("name", "Hello World!")
.AddStringProperty(
"body", "Oh what a beautiful morning! Oh what a beautiful day!")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(document_one));
DocumentProto document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddDocumentProperty(
"sender", DocumentBuilder()
.SetKey("namespace", "uri2")
.SetSchema("Person")
.AddStringProperty("name", "Tom Hanks")
.AddStringProperty("emailAddress", "ny152@aol.com")
.Build())
.AddStringProperty("name", "Goodnight Moon!")
.AddStringProperty("body",
"Count all the sheep and tell them 'Hello'.")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(document_two));
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0}};
TypePropertyMask type_property_mask;
type_property_mask.set_schema_type("Email");
type_property_mask.add_paths("sender.name");
std::unordered_map<std::string, ProjectionTree> type_projection_tree_map;
type_projection_tree_map.insert(
{"Email", ProjectionTree(type_property_mask)});
SnippetContext snippet_context(
/*query_terms_in=*/{},
ResultSpecProto::SnippetSpecProto::default_instance(),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context), std::move(type_projection_tree_map),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/2);
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get()));
// 3. Verify that the returned results only contain the 'sender.name'
// property.
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<SearchResultProto::ResultProto> result,
result_retriever->RetrieveResults(page_result_state));
ASSERT_THAT(result, SizeIs(2));
DocumentProto projected_document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddDocumentProperty("sender",
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetSchema("Person")
.AddStringProperty("name", "Meg Ryan")
.Build())
.Build();
EXPECT_THAT(result[0].document(), EqualsProto(projected_document_one));
DocumentProto projected_document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddDocumentProperty("sender",
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetSchema("Person")
.AddStringProperty("name", "Tom Hanks")
.Build())
.Build();
EXPECT_THAT(result[1].document(), EqualsProto(projected_document_two));
}
TEST_F(ResultRetrieverTest, ProjectionIntermediateNodeFieldPath) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
// 1. Add two Email documents
DocumentProto document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddDocumentProperty(
"sender",
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetSchema("Person")
.AddStringProperty("name", "Meg Ryan")
.AddStringProperty("emailAddress", "shopgirl@aol.com")
.Build())
.AddStringProperty("name", "Hello World!")
.AddStringProperty(
"body", "Oh what a beautiful morning! Oh what a beautiful day!")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(document_one));
DocumentProto document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddDocumentProperty(
"sender", DocumentBuilder()
.SetKey("namespace", "uri2")
.SetSchema("Person")
.AddStringProperty("name", "Tom Hanks")
.AddStringProperty("emailAddress", "ny152@aol.com")
.Build())
.AddStringProperty("name", "Goodnight Moon!")
.AddStringProperty("body",
"Count all the sheep and tell them 'Hello'.")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(document_two));
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0}};
TypePropertyMask type_property_mask;
type_property_mask.set_schema_type("Email");
type_property_mask.add_paths("sender");
std::unordered_map<std::string, ProjectionTree> type_projection_tree_map;
type_projection_tree_map.insert(
{"Email", ProjectionTree(type_property_mask)});
SnippetContext snippet_context(
/*query_terms_in=*/{},
ResultSpecProto::SnippetSpecProto::default_instance(),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context), std::move(type_projection_tree_map),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/2);
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get()));
// 3. Verify that the returned results only contain the 'sender'
// property and all of the subproperties of 'sender'.
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<SearchResultProto::ResultProto> result,
result_retriever->RetrieveResults(page_result_state));
ASSERT_THAT(result, SizeIs(2));
DocumentProto projected_document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddDocumentProperty(
"sender",
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetSchema("Person")
.AddStringProperty("name", "Meg Ryan")
.AddStringProperty("emailAddress", "shopgirl@aol.com")
.Build())
.Build();
EXPECT_THAT(result[0].document(), EqualsProto(projected_document_one));
DocumentProto projected_document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddDocumentProperty(
"sender", DocumentBuilder()
.SetKey("namespace", "uri2")
.SetSchema("Person")
.AddStringProperty("name", "Tom Hanks")
.AddStringProperty("emailAddress", "ny152@aol.com")
.Build())
.Build();
EXPECT_THAT(result[1].document(), EqualsProto(projected_document_two));
}
TEST_F(ResultRetrieverTest, ProjectionMultipleNestedFieldPaths) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
// 1. Add two Email documents
DocumentProto document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddDocumentProperty(
"sender",
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetSchema("Person")
.AddStringProperty("name", "Meg Ryan")
.AddStringProperty("emailAddress", "shopgirl@aol.com")
.Build())
.AddStringProperty("name", "Hello World!")
.AddStringProperty(
"body", "Oh what a beautiful morning! Oh what a beautiful day!")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(document_one));
DocumentProto document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddDocumentProperty(
"sender", DocumentBuilder()
.SetKey("namespace", "uri2")
.SetSchema("Person")
.AddStringProperty("name", "Tom Hanks")
.AddStringProperty("emailAddress", "ny152@aol.com")
.Build())
.AddStringProperty("name", "Goodnight Moon!")
.AddStringProperty("body",
"Count all the sheep and tell them 'Hello'.")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(document_two));
// 2. Setup the scored results.
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0}};
TypePropertyMask type_property_mask;
type_property_mask.set_schema_type("Email");
type_property_mask.add_paths("sender.name");
type_property_mask.add_paths("sender.emailAddress");
std::unordered_map<std::string, ProjectionTree> type_projection_tree_map;
type_projection_tree_map.insert(
{"Email", ProjectionTree(type_property_mask)});
SnippetContext snippet_context(
/*query_terms_in=*/{},
ResultSpecProto::SnippetSpecProto::default_instance(),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context), std::move(type_projection_tree_map),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/2);
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get()));
// 3. Verify that the returned results only contain the 'sender.name' and
// 'sender.address' properties.
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<SearchResultProto::ResultProto> result,
result_retriever->RetrieveResults(page_result_state));
ASSERT_THAT(result, SizeIs(2));
DocumentProto projected_document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddDocumentProperty(
"sender",
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetSchema("Person")
.AddStringProperty("name", "Meg Ryan")
.AddStringProperty("emailAddress", "shopgirl@aol.com")
.Build())
.Build();
EXPECT_THAT(result[0].document(), EqualsProto(projected_document_one));
DocumentProto projected_document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddDocumentProperty(
"sender", DocumentBuilder()
.SetKey("namespace", "uri2")
.SetSchema("Person")
.AddStringProperty("name", "Tom Hanks")
.AddStringProperty("emailAddress", "ny152@aol.com")
.Build())
.Build();
EXPECT_THAT(result[1].document(), EqualsProto(projected_document_two));
}
TEST_F(ResultRetrieverTest, ProjectionEmptyFieldPath) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
// 1. Add two Email documents
DocumentProto document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty("name", "Hello World!")
.AddStringProperty(
"body", "Oh what a beautiful morning! Oh what a beautiful day!")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(document_one));
DocumentProto document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty("name", "Goodnight Moon!")
.AddStringProperty("body",
"Count all the sheep and tell them 'Hello'.")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(document_two));
// 2. Setup the scored results.
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0}};
TypePropertyMask type_property_mask;
type_property_mask.set_schema_type("Email");
std::unordered_map<std::string, ProjectionTree> type_projection_tree_map;
type_projection_tree_map.insert(
{"Email", ProjectionTree(type_property_mask)});
SnippetContext snippet_context(
/*query_terms_in=*/{},
ResultSpecProto::SnippetSpecProto::default_instance(),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context), std::move(type_projection_tree_map),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/2);
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get()));
// 3. Verify that the returned results contain *no* properties.
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<SearchResultProto::ResultProto> result,
result_retriever->RetrieveResults(page_result_state));
ASSERT_THAT(result, SizeIs(2));
DocumentProto projected_document_one = DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.Build();
EXPECT_THAT(result[0].document(), EqualsProto(projected_document_one));
DocumentProto projected_document_two = DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.Build();
EXPECT_THAT(result[1].document(), EqualsProto(projected_document_two));
}
TEST_F(ResultRetrieverTest, ProjectionInvalidFieldPath) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
// 1. Add two Email documents
DocumentProto document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty("name", "Hello World!")
.AddStringProperty(
"body", "Oh what a beautiful morning! Oh what a beautiful day!")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(document_one));
DocumentProto document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty("name", "Goodnight Moon!")
.AddStringProperty("body",
"Count all the sheep and tell them 'Hello'.")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(document_two));
// 2. Setup the scored results.
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0}};
TypePropertyMask type_property_mask;
type_property_mask.set_schema_type("Email");
type_property_mask.add_paths("nonExistentProperty");
std::unordered_map<std::string, ProjectionTree> type_projection_tree_map;
type_projection_tree_map.insert(
{"Email", ProjectionTree(type_property_mask)});
SnippetContext snippet_context(
/*query_terms_in=*/{},
ResultSpecProto::SnippetSpecProto::default_instance(),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context), std::move(type_projection_tree_map),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/2);
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get()));
// 3. Verify that the returned results contain *no* properties.
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<SearchResultProto::ResultProto> result,
result_retriever->RetrieveResults(page_result_state));
ASSERT_THAT(result, SizeIs(2));
DocumentProto projected_document_one = DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.Build();
EXPECT_THAT(result[0].document(), EqualsProto(projected_document_one));
DocumentProto projected_document_two = DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.Build();
EXPECT_THAT(result[1].document(), EqualsProto(projected_document_two));
}
TEST_F(ResultRetrieverTest, ProjectionValidAndInvalidFieldPath) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
// 1. Add two Email documents
DocumentProto document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty("name", "Hello World!")
.AddStringProperty(
"body", "Oh what a beautiful morning! Oh what a beautiful day!")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(document_one));
DocumentProto document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty("name", "Goodnight Moon!")
.AddStringProperty("body",
"Count all the sheep and tell them 'Hello'.")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(document_two));
// 2. Setup the scored results.
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0}};
TypePropertyMask type_property_mask;
type_property_mask.set_schema_type("Email");
type_property_mask.add_paths("name");
type_property_mask.add_paths("nonExistentProperty");
std::unordered_map<std::string, ProjectionTree> type_projection_tree_map;
type_projection_tree_map.insert(
{"Email", ProjectionTree(type_property_mask)});
SnippetContext snippet_context(
/*query_terms_in=*/{},
ResultSpecProto::SnippetSpecProto::default_instance(),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context), std::move(type_projection_tree_map),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/2);
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get()));
// 3. Verify that the returned results only contain the 'name' property.
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<SearchResultProto::ResultProto> result,
result_retriever->RetrieveResults(page_result_state));
ASSERT_THAT(result, SizeIs(2));
DocumentProto projected_document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty("name", "Hello World!")
.Build();
EXPECT_THAT(result[0].document(), EqualsProto(projected_document_one));
DocumentProto projected_document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty("name", "Goodnight Moon!")
.Build();
EXPECT_THAT(result[1].document(), EqualsProto(projected_document_two));
}
TEST_F(ResultRetrieverTest, ProjectionMultipleTypesNoWildcards) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
// 1. Add two documents
DocumentProto document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty("name", "Hello World!")
.AddStringProperty(
"body", "Oh what a beautiful morning! Oh what a beautiful day!")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(document_one));
DocumentProto document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Person")
.AddStringProperty("name", "Joe Fox")
.AddStringProperty("emailAddress", "ny152@aol.com")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(document_two));
// 2. Setup the scored results.
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0}};
TypePropertyMask type_property_mask;
type_property_mask.set_schema_type("Email");
type_property_mask.add_paths("name");
std::unordered_map<std::string, ProjectionTree> type_projection_tree_map;
type_projection_tree_map.insert(
{"Email", ProjectionTree(type_property_mask)});
SnippetContext snippet_context(
/*query_terms_in=*/{},
ResultSpecProto::SnippetSpecProto::default_instance(),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context), std::move(type_projection_tree_map),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/2);
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get()));
// 3. Verify that the returned Email results only contain the 'name'
// property and the returned Person results have all of their properties.
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<SearchResultProto::ResultProto> result,
result_retriever->RetrieveResults(page_result_state));
ASSERT_THAT(result, SizeIs(2));
DocumentProto projected_document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty("name", "Hello World!")
.Build();
EXPECT_THAT(result[0].document(), EqualsProto(projected_document_one));
DocumentProto projected_document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Person")
.AddStringProperty("name", "Joe Fox")
.AddStringProperty("emailAddress", "ny152@aol.com")
.Build();
EXPECT_THAT(result[1].document(), EqualsProto(projected_document_two));
}
TEST_F(ResultRetrieverTest, ProjectionMultipleTypesWildcard) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
// 1. Add two documents
DocumentProto document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty("name", "Hello World!")
.AddStringProperty(
"body", "Oh what a beautiful morning! Oh what a beautiful day!")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(document_one));
DocumentProto document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Person")
.AddStringProperty("name", "Joe Fox")
.AddStringProperty("emailAddress", "ny152@aol.com")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(document_two));
// 2. Setup the scored results.
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0}};
TypePropertyMask wildcard_type_property_mask;
wildcard_type_property_mask.set_schema_type(
std::string(ProjectionTree::kSchemaTypeWildcard));
wildcard_type_property_mask.add_paths("name");
std::unordered_map<std::string, ProjectionTree> type_projection_tree_map;
type_projection_tree_map.insert(
{std::string(ProjectionTree::kSchemaTypeWildcard),
ProjectionTree(wildcard_type_property_mask)});
SnippetContext snippet_context(
/*query_terms_in=*/{},
ResultSpecProto::SnippetSpecProto::default_instance(),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context), std::move(type_projection_tree_map),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/2);
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get()));
// 3. Verify that the returned Email results only contain the 'name'
// property and the returned Person results only contain the 'name' property.
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<SearchResultProto::ResultProto> result,
result_retriever->RetrieveResults(page_result_state));
ASSERT_THAT(result, SizeIs(2));
DocumentProto projected_document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty("name", "Hello World!")
.Build();
EXPECT_THAT(result[0].document(), EqualsProto(projected_document_one));
DocumentProto projected_document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Person")
.AddStringProperty("name", "Joe Fox")
.Build();
EXPECT_THAT(result[1].document(), EqualsProto(projected_document_two));
}
TEST_F(ResultRetrieverTest, ProjectionMultipleTypesWildcardWithOneOverride) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
// 1. Add two documents
DocumentProto document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty("name", "Hello World!")
.AddStringProperty(
"body", "Oh what a beautiful morning! Oh what a beautiful day!")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(document_one));
DocumentProto document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Person")
.AddStringProperty("name", "Joe Fox")
.AddStringProperty("emailAddress", "ny152@aol.com")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(document_two));
// 2. Setup the scored results.
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0}};
TypePropertyMask email_type_property_mask;
email_type_property_mask.set_schema_type("Email");
email_type_property_mask.add_paths("body");
TypePropertyMask wildcard_type_property_mask;
wildcard_type_property_mask.set_schema_type(
std::string(ProjectionTree::kSchemaTypeWildcard));
wildcard_type_property_mask.add_paths("name");
std::unordered_map<std::string, ProjectionTree> type_projection_tree_map;
type_projection_tree_map.insert(
{"Email", ProjectionTree(email_type_property_mask)});
type_projection_tree_map.insert(
{std::string(ProjectionTree::kSchemaTypeWildcard),
ProjectionTree(wildcard_type_property_mask)});
SnippetContext snippet_context(
/*query_terms_in=*/{},
ResultSpecProto::SnippetSpecProto::default_instance(),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context), std::move(type_projection_tree_map),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/2);
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get()));
// 3. Verify that the returned Email results only contain the 'body'
// property and the returned Person results only contain the 'name' property.
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<SearchResultProto::ResultProto> result,
result_retriever->RetrieveResults(page_result_state));
ASSERT_THAT(result, SizeIs(2));
DocumentProto projected_document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty(
"body", "Oh what a beautiful morning! Oh what a beautiful day!")
.Build();
EXPECT_THAT(result[0].document(), EqualsProto(projected_document_one));
DocumentProto projected_document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Person")
.AddStringProperty("name", "Joe Fox")
.Build();
EXPECT_THAT(result[1].document(), EqualsProto(projected_document_two));
}
TEST_F(ResultRetrieverTest, ProjectionSingleTypesWildcardAndOverride) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
// 1. Add two documents
DocumentProto document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty("name", "Hello World!")
.AddStringProperty(
"body", "Oh what a beautiful morning! Oh what a beautiful day!")
.AddDocumentProperty(
"sender",
DocumentBuilder()
.SetKey("namespace", "uri")
.SetSchema("Person")
.AddStringProperty("name", "Mr. Body")
.AddStringProperty("emailAddress", "mr.body123@gmail.com")
.Build())
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(document_one));
DocumentProto document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Person")
.AddStringProperty("name", "Joe Fox")
.AddStringProperty("emailAddress", "ny152@aol.com")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(document_two));
// 2. Setup the scored results.
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0}};
TypePropertyMask email_type_property_mask;
email_type_property_mask.set_schema_type("Email");
email_type_property_mask.add_paths("sender.name");
TypePropertyMask wildcard_type_property_mask;
wildcard_type_property_mask.set_schema_type(
std::string(ProjectionTree::kSchemaTypeWildcard));
wildcard_type_property_mask.add_paths("name");
std::unordered_map<std::string, ProjectionTree> type_projection_tree_map;
type_projection_tree_map.insert(
{"Email", ProjectionTree(email_type_property_mask)});
type_projection_tree_map.insert(
{std::string(ProjectionTree::kSchemaTypeWildcard),
ProjectionTree(wildcard_type_property_mask)});
SnippetContext snippet_context(
/*query_terms_in=*/{},
ResultSpecProto::SnippetSpecProto::default_instance(),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context), std::move(type_projection_tree_map),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/2);
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get()));
// 3. Verify that the returned Email results only contain the 'sender.name'
// property and the returned Person results only contain the 'name' property.
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<SearchResultProto::ResultProto> result,
result_retriever->RetrieveResults(page_result_state));
ASSERT_THAT(result, SizeIs(2));
DocumentProto projected_document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddDocumentProperty("sender",
DocumentBuilder()
.SetKey("namespace", "uri")
.SetSchema("Person")
.AddStringProperty("name", "Mr. Body")
.Build())
.Build();
EXPECT_THAT(result[0].document(), EqualsProto(projected_document_one));
DocumentProto projected_document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Person")
.AddStringProperty("name", "Joe Fox")
.Build();
EXPECT_THAT(result[1].document(), EqualsProto(projected_document_two));
}
TEST_F(ResultRetrieverTest,
ProjectionSingleTypesWildcardAndOverrideNestedProperty) {
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
DocumentStore::Create(&filesystem_, test_dir_, &fake_clock_,
schema_store_.get()));
std::unique_ptr<DocumentStore> doc_store =
std::move(create_result.document_store);
// 1. Add two documents
DocumentProto document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddStringProperty("name", "Hello World!")
.AddStringProperty(
"body", "Oh what a beautiful morning! Oh what a beautiful day!")
.AddDocumentProperty(
"sender",
DocumentBuilder()
.SetKey("namespace", "uri")
.SetSchema("Person")
.AddStringProperty("name", "Mr. Body")
.AddStringProperty("emailAddress", "mr.body123@gmail.com")
.Build())
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id1,
doc_store->Put(document_one));
DocumentProto document_two =
DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Person")
.AddStringProperty("name", "Joe Fox")
.AddStringProperty("emailAddress", "ny152@aol.com")
.Build();
ICING_ASSERT_OK_AND_ASSIGN(DocumentId document_id2,
doc_store->Put(document_two));
// 2. Setup the scored results.
std::vector<SectionId> hit_section_ids = {GetSectionId("Email", "name"),
GetSectionId("Email", "body")};
SectionIdMask hit_section_id_mask = CreateSectionIdMask(hit_section_ids);
std::vector<ScoredDocumentHit> scored_document_hits = {
{document_id1, hit_section_id_mask, /*score=*/0},
{document_id2, hit_section_id_mask, /*score=*/0}};
TypePropertyMask email_type_property_mask;
email_type_property_mask.set_schema_type("Email");
email_type_property_mask.add_paths("sender.name");
TypePropertyMask wildcard_type_property_mask;
wildcard_type_property_mask.set_schema_type(
std::string(ProjectionTree::kSchemaTypeWildcard));
wildcard_type_property_mask.add_paths("sender");
std::unordered_map<std::string, ProjectionTree> type_projection_tree_map;
type_projection_tree_map.insert(
{"Email", ProjectionTree(email_type_property_mask)});
type_projection_tree_map.insert(
{std::string(ProjectionTree::kSchemaTypeWildcard),
ProjectionTree(wildcard_type_property_mask)});
SnippetContext snippet_context(
/*query_terms_in=*/{},
ResultSpecProto::SnippetSpecProto::default_instance(),
TermMatchType::EXACT_ONLY);
PageResultState page_result_state(
std::move(scored_document_hits), /*next_page_token_in=*/1,
std::move(snippet_context), std::move(type_projection_tree_map),
/*num_previously_returned_in=*/0,
/*num_per_page_in=*/2);
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<ResultRetriever> result_retriever,
ResultRetriever::Create(doc_store.get(), schema_store_.get(),
language_segmenter_.get(), normalizer_.get()));
// 3. Verify that the returned Email results only contain the 'sender.name'
// property and the returned Person results contain no properties.
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<SearchResultProto::ResultProto> result,
result_retriever->RetrieveResults(page_result_state));
ASSERT_THAT(result, SizeIs(2));
DocumentProto projected_document_one =
DocumentBuilder()
.SetKey("namespace", "uri1")
.SetCreationTimestampMs(1000)
.SetSchema("Email")
.AddDocumentProperty("sender",
DocumentBuilder()
.SetKey("namespace", "uri")
.SetSchema("Person")
.AddStringProperty("name", "Mr. Body")
.Build())
.Build();
EXPECT_THAT(result[0].document(), EqualsProto(projected_document_one));
DocumentProto projected_document_two = DocumentBuilder()
.SetKey("namespace", "uri2")
.SetCreationTimestampMs(1000)
.SetSchema("Person")
.Build();
EXPECT_THAT(result[1].document(), EqualsProto(projected_document_two));
}
} // namespace
} // namespace lib
} // namespace icing