github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/m3ninx/search/searcher/conjunction.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package searcher 22 23 import ( 24 "sort" 25 26 "github.com/m3db/m3/src/m3ninx/index" 27 "github.com/m3db/m3/src/m3ninx/postings" 28 "github.com/m3db/m3/src/m3ninx/search" 29 ) 30 31 type conjunctionSearcher struct { 32 searchers search.Searchers 33 negations search.Searchers 34 } 35 36 // NewConjunctionSearcher returns a new Searcher which matches documents which match each 37 // of the given searchers and none of the negations. 38 func NewConjunctionSearcher(searchers, negations search.Searchers) (search.Searcher, error) { 39 if len(searchers) == 0 { 40 return nil, errEmptySearchers 41 } 42 43 return &conjunctionSearcher{ 44 searchers: searchers, 45 negations: negations, 46 }, nil 47 } 48 49 func (s *conjunctionSearcher) Search(r index.Reader) (postings.List, error) { 50 var ( 51 pl postings.List 52 plNeedsClone = true 53 ) 54 55 listCount := len(s.searchers) 56 if listCount < len(s.negations) { 57 listCount = len(s.negations) 58 } 59 lists := make([]postingsListWithLength, 0, listCount) 60 61 for _, sr := range s.searchers { 62 curr, err := sr.Search(r) 63 if err != nil { 64 return nil, err 65 } 66 lists = append(lists, postingsListWithLength{ 67 list: curr, 68 length: curr.Len(), 69 }) 70 } 71 72 sort.Sort(byLengthAscending(lists)) 73 for _, curr := range lists { 74 if pl == nil { 75 pl = curr.list 76 } else { 77 var err error 78 pl, err = pl.Intersect(curr.list) 79 if err != nil { 80 return nil, err 81 } 82 plNeedsClone = false 83 } 84 85 // We can break early if the intersected postings list is ever empty. 86 if pl.IsEmpty() { 87 break 88 } 89 } 90 91 lists = lists[:0] 92 for _, sr := range s.negations { 93 curr, err := sr.Search(r) 94 if err != nil { 95 return nil, err 96 } 97 lists = append(lists, postingsListWithLength{ 98 list: curr, 99 length: curr.Len(), 100 }) 101 } 102 103 sort.Sort(byLengthDescending(lists)) 104 for _, curr := range lists { 105 // We can break early if the resulting postings list is ever empty. 106 if pl.IsEmpty() { 107 break 108 } 109 110 var err error 111 pl, err = pl.Difference(curr.list) 112 if err != nil { 113 return nil, err 114 } 115 plNeedsClone = false 116 } 117 118 if pl != nil && plNeedsClone { 119 // There was no new instance created indirectly (by Intersect/Difference), so need to clone. 120 pl = pl.CloneAsMutable() 121 } 122 123 return pl, nil 124 } 125 126 type postingsListWithLength struct { 127 list postings.List 128 length int 129 } 130 131 type byLengthAscending []postingsListWithLength 132 133 func (l byLengthAscending) Len() int { 134 return len(l) 135 } 136 137 func (l byLengthAscending) Less(i, j int) bool { 138 return l[i].length < l[j].length 139 } 140 141 func (l byLengthAscending) Swap(i, j int) { 142 l[i], l[j] = l[j], l[i] 143 } 144 145 type byLengthDescending []postingsListWithLength 146 147 func (l byLengthDescending) Len() int { 148 return len(l) 149 } 150 151 func (l byLengthDescending) Less(i, j int) bool { 152 return l[i].length > l[j].length 153 } 154 155 func (l byLengthDescending) Swap(i, j int) { 156 l[i], l[j] = l[j], l[i] 157 }