@@ -125,11 +125,11 @@ impl SuggestDao<'_> {
125125 Ok ( all_tokens)
126126 } ) ?
127127 . into_iter ( )
128- // Step 3: Map each token path to a tuple that represents a matched
129- // city, region, and keyword (each optional). Since paths are vecs,
130- // they're ordered, so we may end up with duplicate tuples after
131- // this step. e.g., the paths `[<Waterloo IA>, <IA>]` and `[<IA>,
132- // <Waterloo IA>]` map to the same match .
128+ // Step 3: Map each token path to a city-region-keyword tuple (each
129+ // optional). Paths are vecs, so they're ordered, so we may end up
130+ // with duplicate tuples after this step. e.g., the paths
131+ // `[<Waterloo IA>, <IA>]` and `[<IA>, <Waterloo IA>]` map to the
132+ // same `( <Waterloo IA>, <IA>, None)` tuple .
133133 . map ( |path| {
134134 path. into_iter ( )
135135 . fold ( ( None , None , None ) , |mut match_tuple, token| {
@@ -147,9 +147,9 @@ impl SuggestDao<'_> {
147147 match_tuple
148148 } )
149149 } )
150- // Step 4: Discard matches that don't have the right combination of
150+ // Step 4: Discard tuples that don't have the right combination of
151151 // tokens or that are otherwise invalid. Along with step 2, this is
152- // the core of the matching logic. In general, allow a match if it
152+ // the core of the matching logic. In general, allow a tuple if it
153153 // has (a) a city name typed in full or (b) a weather keyword at
154154 // least as long as the config's min keyword length, since that
155155 // indicates a weather intent.
@@ -168,34 +168,37 @@ impl SuggestDao<'_> {
168168 }
169169 }
170170 } )
171- // Step 5: Map the match objects to their underlying values.
172- . map ( |( city, region, kw) | {
173- ( city. map ( |c| c. geoname ) , region. map ( |r| r. geoname ) , kw. map ( |k| k. keyword ) )
171+ // Step 5: Map each tuple to a city-region tuple: Convert geoname
172+ // matches to their `Geoname` values and discard keywords.
173+ // Discarding keywords is important because we'll collect the tuples
174+ // in a set in the next step in order to dedupe city-regions.
175+ . map ( |( city, region, _) | {
176+ ( city. map ( |c| c. geoname ) , region. map ( |r| r. geoname ) )
174177 } )
175- // Step 6: Dedupe the values by collecting them into a set.
178+ // Step 6: Dedupe the city-regions by collecting them in a set.
176179 . collect :: < HashSet < _ > > ( )
177180 . into_iter ( )
178181 . collect :: < Vec < _ > > ( ) ;
179182
180183 // Sort the matches so cities with larger populations are first.
181184 matches. sort_by (
182- |( city1, region1, kw1 ) , ( city2, region2, kw2 ) | match ( & city1, & city2) {
185+ |( city1, region1) , ( city2, region2) | match ( & city1, & city2) {
183186 ( Some ( _) , None ) => Ordering :: Less ,
184187 ( None , Some ( _) ) => Ordering :: Greater ,
185188 ( Some ( c1) , Some ( c2) ) => c2. population . cmp ( & c1. population ) ,
186189 ( None , None ) => match ( & region1, & region2) {
187190 ( Some ( _) , None ) => Ordering :: Less ,
188191 ( None , Some ( _) ) => Ordering :: Greater ,
189192 ( Some ( r1) , Some ( r2) ) => r2. population . cmp ( & r1. population ) ,
190- ( None , None ) => kw1 . cmp ( kw2 ) ,
193+ ( None , None ) => Ordering :: Equal ,
191194 } ,
192195 } ,
193196 ) ;
194197
195198 // Finally, map matches to suggestions.
196199 Ok ( matches
197200 . iter ( )
198- . map ( |( city, _, _ ) | Suggestion :: Weather {
201+ . map ( |( city, _) | Suggestion :: Weather {
199202 city : city. as_ref ( ) . map ( |c| c. name . clone ( ) ) ,
200203 region : city. as_ref ( ) . map ( |c| c. admin1_code . clone ( ) ) ,
201204 country : city. as_ref ( ) . map ( |c| c. country_code . clone ( ) ) ,
@@ -646,7 +649,11 @@ mod tests {
646649 "weather" ,
647650 "weather-1" ,
648651 json ! ( {
649- "keywords" : [ "ab" , "xyz" , "weather" ] ,
652+ // Include a keyword that's a prefix of another keyword --
653+ // "weather" and "weather near me" -- so that when a test
654+ // matches both we can verify only one suggestion is returned,
655+ // not two.
656+ "keywords" : [ "ab" , "xyz" , "weather" , "weather near me" ] ,
650657 "min_keyword_length" : 5 ,
651658 "max_keyword_length" : "weather" . len( ) ,
652659 "max_keyword_word_count" : 1 ,
0 commit comments