Skip to content

Commit 8e7aa17

Browse files
committed
Bug 1931718 - Suggest can return duplicate weather suggestions for the same city
1 parent ab36d7a commit 8e7aa17

File tree

1 file changed

+22
-15
lines changed

1 file changed

+22
-15
lines changed

components/suggest/src/weather.rs

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)