• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef ICING_SCORING_SCORED_DOCUMENT_HIT_H_
16 #define ICING_SCORING_SCORED_DOCUMENT_HIT_H_
17 
18 #include <type_traits>
19 
20 #include "icing/legacy/core/icing-packed-pod.h"
21 #include "icing/schema/section.h"
22 #include "icing/store/document-id.h"
23 
24 namespace icing {
25 namespace lib {
26 
27 class JoinedScoredDocumentHit;
28 
29 // A data class containing information about the document, hit sections, and a
30 // score. The score is calculated against both the document and the hit
31 // sections.
32 class ScoredDocumentHit {
33  public:
34   class Converter {
35    public:
36     JoinedScoredDocumentHit operator()(
37         ScoredDocumentHit&& scored_doc_hit) const;
38   };
39 
ScoredDocumentHit(DocumentId document_id,SectionIdMask hit_section_id_mask,double score)40   ScoredDocumentHit(DocumentId document_id, SectionIdMask hit_section_id_mask,
41                     double score)
42       : document_id_(document_id),
43         hit_section_id_mask_(hit_section_id_mask),
44         score_(score) {}
45 
46   bool operator<(const ScoredDocumentHit& other) const {
47     if (score() < other.score()) return true;
48     if (score() > other.score()) return false;
49     return document_id() < other.document_id();
50   }
51 
document_id()52   DocumentId document_id() const { return document_id_; }
53 
hit_section_id_mask()54   SectionIdMask hit_section_id_mask() const { return hit_section_id_mask_; }
55 
score()56   double score() const { return score_; }
57 
58  private:
59   DocumentId document_id_;
60   SectionIdMask hit_section_id_mask_;
61   double score_;
62 } __attribute__((packed));
63 
64 static_assert(sizeof(ScoredDocumentHit) == 20,
65               "Size of ScoredDocHit should be 20");
66 static_assert(icing_is_packed_pod<ScoredDocumentHit>::value, "go/icing-ubsan");
67 
68 // A custom comparator for ScoredDocumentHit that determines which
69 // ScoredDocumentHit is better (should come first) based off of
70 // ScoredDocumentHit itself and the order of its score.
71 //
72 // Returns true if left is better than right according to score and order.
73 // Comparison is based off of score with ties broken by
74 // ScoredDocumentHit.document_id().
75 class ScoredDocumentHitComparator {
76  public:
77   explicit ScoredDocumentHitComparator(bool is_descending = true)
is_descending_(is_descending)78       : is_descending_(is_descending) {}
79 
operator()80   bool operator()(const ScoredDocumentHit& lhs,
81                   const ScoredDocumentHit& rhs) const {
82     // STL comparator requirement: equal MUST return false.
83     // If writing `return is_descending_ == !(lhs < rhs)`:
84     // - When lhs == rhs, !(lhs < rhs) is true
85     // - If is_descending_ is true, then we return true for equal case!
86     if (is_descending_) {
87       return rhs < lhs;
88     }
89     return lhs < rhs;
90   }
91 
92  private:
93   bool is_descending_;
94 };
95 
96 // A data class containing information about a composite document after joining,
97 // including final score, parent ScoredDocumentHit, and a vector of all child
98 // ScoredDocumentHits. The final score is calculated by the strategy specified
99 // in join spec/rank strategy. It could be aggregated score, raw parent doc
100 // score, or anything else.
101 //
102 // ScoredDocumentHitsRanker may store ScoredDocumentHit or
103 // JoinedScoredDocumentHit.
104 // - We could've created a virtual class for them and ScoredDocumentHitsRanker
105 //   uses the abstract type.
106 // - However, Icing lib caches ScoredDocumentHitsRanker (which contains a list
107 //   of (Joined)ScoredDocumentHits) in ResultState. Inheriting the virtual class
108 //   makes both classes have additional 8 bytes for vtable, which increases 40%
109 //   and 15% memory usage respectively.
110 // - Also since JoinedScoredDocumentHit is a super-set of ScoredDocumentHit,
111 //   let's avoid the common virtual class and instead implement a convert
112 //   function (original type -> JoinedScoredDocumentHit) for each class, so
113 //   ScoredDocumentHitsRanker::PopNext can return a common type (i.e.
114 //   JoinedScoredDocumentHit).
115 class JoinedScoredDocumentHit {
116  public:
117   class Converter {
118    public:
operator()119     JoinedScoredDocumentHit operator()(
120         JoinedScoredDocumentHit&& scored_doc_hit) const {
121       return scored_doc_hit;
122     }
123   };
124 
JoinedScoredDocumentHit(double final_score,ScoredDocumentHit parent_scored_document_hit,std::vector<ScoredDocumentHit> child_scored_document_hits)125   explicit JoinedScoredDocumentHit(
126       double final_score, ScoredDocumentHit parent_scored_document_hit,
127       std::vector<ScoredDocumentHit> child_scored_document_hits)
128       : final_score_(final_score),
129         parent_scored_document_hit_(std::move(parent_scored_document_hit)),
130         child_scored_document_hits_(std::move(child_scored_document_hits)) {}
131 
132   bool operator<(const JoinedScoredDocumentHit& other) const {
133     if (final_score_ != other.final_score_) {
134       return final_score_ < other.final_score_;
135     }
136     return parent_scored_document_hit_ < other.parent_scored_document_hit_;
137   }
138 
final_score()139   double final_score() const { return final_score_; }
140 
parent_scored_document_hit()141   const ScoredDocumentHit& parent_scored_document_hit() const {
142     return parent_scored_document_hit_;
143   }
144 
child_scored_document_hits()145   const std::vector<ScoredDocumentHit>& child_scored_document_hits() const {
146     return child_scored_document_hits_;
147   }
148 
149  private:
150   double final_score_;
151   ScoredDocumentHit parent_scored_document_hit_;
152   std::vector<ScoredDocumentHit> child_scored_document_hits_;
153 } __attribute__((packed));
154 
155 }  // namespace lib
156 }  // namespace icing
157 
158 #endif  // ICING_SCORING_SCORED_DOCUMENT_HIT_H_
159