Skip to content

Commit 280db3a

Browse files
committed
Bug 1957309: Handle location signs as one of Yelp modifier (again)
1 parent 8e2ae2a commit 280db3a

File tree

5 files changed

+60
-91
lines changed

5 files changed

+60
-91
lines changed

components/suggest/src/db.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1355,11 +1355,6 @@ impl<'a> SuggestDao<'a> {
13551355
named_params! { ":record_id": record_id.as_str() },
13561356
)?;
13571357
self.scope.err_if_interrupted()?;
1358-
self.conn.execute_cached(
1359-
"DELETE FROM yelp_location_signs WHERE record_id = :record_id",
1360-
named_params! { ":record_id": record_id.as_str() },
1361-
)?;
1362-
self.scope.err_if_interrupted()?;
13631358
self.conn.execute_cached(
13641359
"DELETE FROM yelp_custom_details WHERE record_id = :record_id",
13651360
named_params! { ":record_id": record_id.as_str() },

components/suggest/src/rs.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ use serde::{Deserialize, Deserializer, Serialize};
4141
use serde_json::{Map, Value};
4242

4343
use crate::{error::Error, query::full_keywords_to_fts_content, Result};
44+
use rusqlite::{types::ToSqlOutput, ToSql};
4445

4546
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
4647
pub enum Collection {
@@ -453,12 +454,21 @@ pub(crate) struct DownloadedPocketSuggestion {
453454
pub high_confidence_keywords: Vec<String>,
454455
pub score: f64,
455456
}
456-
/// A location sign for Yelp to ingest from a Yelp Attachment
457+
/// Yelp location sign data type
457458
#[derive(Clone, Debug, Deserialize)]
458-
pub(crate) struct DownloadedYelpLocationSign {
459-
pub keyword: String,
460-
#[serde(rename = "needLocation")]
461-
pub need_location: bool,
459+
#[serde(untagged)]
460+
pub enum DownloadedYelpLocationSign {
461+
V1 { keyword: String },
462+
V2(String),
463+
}
464+
impl ToSql for DownloadedYelpLocationSign {
465+
fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
466+
let keyword = match self {
467+
DownloadedYelpLocationSign::V1 { keyword } => keyword,
468+
DownloadedYelpLocationSign::V2(keyword) => keyword,
469+
};
470+
Ok(ToSqlOutput::from(keyword.as_str()))
471+
}
462472
}
463473
/// A Yelp suggestion to ingest from a Yelp Attachment
464474
#[derive(Clone, Debug, Deserialize)]

components/suggest/src/schema.rs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use sql_support::{
2323
/// `clear_database()` by adding their names to `conditional_tables`, unless
2424
/// they are cleared via a deletion trigger or there's some other good
2525
/// reason not to do so.
26-
pub const VERSION: u32 = 36;
26+
pub const VERSION: u32 = 37;
2727

2828
/// The current Suggest database schema.
2929
pub const SQL: &str = "
@@ -171,12 +171,6 @@ CREATE TABLE yelp_modifiers(
171171
PRIMARY KEY (type, keyword)
172172
) WITHOUT ROWID;
173173
174-
CREATE TABLE yelp_location_signs(
175-
keyword TEXT PRIMARY KEY,
176-
need_location INTEGER NOT NULL,
177-
record_id TEXT NOT NULL
178-
) WITHOUT ROWID;
179-
180174
CREATE TABLE yelp_custom_details(
181175
icon_id TEXT PRIMARY KEY,
182176
score REAL NOT NULL,
@@ -649,6 +643,10 @@ impl ConnectionInitializer for SuggestConnectionInitializer<'_> {
649643
// The commit that added this migration was reverted.
650644
Ok(())
651645
}
646+
36 => {
647+
tx.execute_batch("DROP TABLE IF EXISTS yelp_location_signs;")?;
648+
Ok(())
649+
}
652650
_ => Err(open_database::Error::IncompatibleVersion(version)),
653651
}
654652
}
@@ -666,7 +664,6 @@ pub fn clear_database(db: &Connection) -> rusqlite::Result<()> {
666664
DELETE FROM icons;
667665
DELETE FROM yelp_subjects;
668666
DELETE FROM yelp_modifiers;
669-
DELETE FROM yelp_location_signs;
670667
DELETE FROM yelp_custom_details;
671668
",
672669
)?;
@@ -850,4 +847,26 @@ PRAGMA user_version=16;
850847

851848
Ok(())
852849
}
850+
851+
/// Test that yelp_location_signs table could be removed correctly.
852+
#[test]
853+
fn test_remove_yelp_location_signs_table() -> anyhow::Result<()> {
854+
// Start with the v16 schema.
855+
let db_file =
856+
MigratedDatabaseFile::new(SuggestConnectionInitializer::default(), V16_SCHEMA);
857+
858+
// Upgrade to v36.
859+
db_file.upgrade_to(36);
860+
861+
// Drop the table to simulate old 35 > 36 migration.
862+
let conn = db_file.open();
863+
conn.execute("DROP table yelp_location_signs", ())?;
864+
conn.close().expect("Connection should be closed");
865+
866+
// Finish upgrading to the current version.
867+
db_file.upgrade_to(VERSION);
868+
db_file.assert_schema_matches_new_database();
869+
870+
Ok(())
871+
}
853872
}

components/suggest/src/testing/data.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,10 +297,12 @@ pub fn ramen_yelp() -> JsonValue {
297297
"preModifiers": ["best", "super best", "same_modifier"],
298298
"postModifiers": ["delivery", "super delivery", "same_modifier"],
299299
"locationSigns": [
300+
// V1 format also can be used as location sign.
300301
{ "keyword": "in", "needLocation": true },
301-
{ "keyword": "near", "needLocation": true },
302302
{ "keyword": "near by", "needLocation": false },
303-
{ "keyword": "near me", "needLocation": false },
303+
// V2 format.
304+
"near",
305+
"near me",
304306
],
305307
"yelpModifiers": ["yelp", "yelp keyword"],
306308
"icon": "yelp-favicon",

components/suggest/src/yelp.rs

Lines changed: 14 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ enum Modifier {
2222
Pre = 0,
2323
Post = 1,
2424
Yelp = 2,
25+
LocationSign = 3,
2526
}
2627

2728
impl ToSql for Modifier {
@@ -53,11 +54,6 @@ const MAX_QUERY_LENGTH: usize = 150;
5354
/// "keyword=:modifier" (please see is_modifier()), define this how many words we should check.
5455
const MAX_MODIFIER_WORDS_NUMBER: usize = 2;
5556

56-
/// The max number of words consisting the location sign. To improve the SQL performance by matching
57-
/// with "keyword=:modifier" (please see is_location_sign()), define this how many words we should
58-
/// check.
59-
const MAX_LOCATION_SIGN_WORDS_NUMBER: usize = 2;
60-
6157
/// At least this many characters must be typed for a subject to be matched.
6258
const SUBJECT_PREFIX_MATCH_THRESHOLD: usize = 2;
6359

@@ -115,14 +111,14 @@ impl SuggestDao<'_> {
115111
)?;
116112
}
117113

118-
for sign in &suggestion.location_signs {
114+
for keyword in &suggestion.location_signs {
119115
self.scope.err_if_interrupted()?;
120116
self.conn.execute_cached(
121-
"INSERT INTO yelp_location_signs(record_id, keyword, need_location) VALUES(:record_id, :keyword, :need_location)",
117+
"INSERT INTO yelp_modifiers(record_id, type, keyword) VALUES(:record_id, :type, :keyword)",
122118
named_params! {
123119
":record_id": record_id.as_str(),
124-
":keyword": sign.keyword,
125-
":need_location": sign.need_location,
120+
":type": Modifier::LocationSign,
121+
":keyword": keyword,
126122
},
127123
)?;
128124
}
@@ -178,7 +174,8 @@ impl SuggestDao<'_> {
178174
query_words = rest;
179175
}
180176

181-
let location_sign_tuple = self.find_location_sign(query_words)?;
177+
let location_sign_tuple =
178+
self.find_modifier(query_words, Modifier::LocationSign, FindFrom::First)?;
182179
if let Some((_, rest)) = location_sign_tuple {
183180
query_words = rest;
184181
}
@@ -201,7 +198,6 @@ impl SuggestDao<'_> {
201198
subject_exact_match: subject_tuple.1,
202199
pre_modifier: pre_modifier_tuple.map(|(words, _)| words.to_string()),
203200
post_modifier: post_modifier_tuple.map(|(words, _)| words.to_string()),
204-
need_location: location_sign_tuple.is_some() || location.is_some(),
205201
location_sign: location_sign_tuple.map(|(words, _)| words.to_string()),
206202
location,
207203
icon,
@@ -343,55 +339,6 @@ impl SuggestDao<'_> {
343339
Ok(None)
344340
}
345341

346-
/// Find the location sign for given query.
347-
/// It returns Option<tuple> as follows:
348-
/// (
349-
/// String: The keyword in DB (but the case is inherited by query).
350-
/// &[&str]: Words after removed matching location sign.
351-
/// )
352-
fn find_location_sign<'a>(
353-
&self,
354-
query_words: &'a [&'a str],
355-
) -> Result<Option<(String, &'a [&'a str])>> {
356-
if query_words.is_empty() {
357-
return Ok(None);
358-
}
359-
360-
for n in (1..=std::cmp::min(MAX_LOCATION_SIGN_WORDS_NUMBER, query_words.len())).rev() {
361-
let Some((candidate_chunk, rest)) = query_words.split_at_checked(n) else {
362-
continue;
363-
};
364-
365-
let mut candidate = candidate_chunk.join(" ");
366-
367-
if let Some(keyword_lowercase) = self.conn.try_query_one::<String, _>(
368-
if n == query_words.len() {
369-
"
370-
SELECT keyword FROM yelp_location_signs
371-
WHERE keyword BETWEEN :word AND :word || x'FFFF'
372-
LIMIT 1
373-
"
374-
} else {
375-
"
376-
SELECT keyword FROM yelp_location_signs
377-
WHERE keyword = :word
378-
LIMIT 1
379-
"
380-
},
381-
named_params! {
382-
":word": candidate.to_lowercase(),
383-
},
384-
true,
385-
)? {
386-
// Preserve the query as the user typed it including its case.
387-
candidate.push_str(keyword_lowercase.get(candidate.len()..).unwrap_or_default());
388-
return Ok(Some((candidate, rest)));
389-
}
390-
}
391-
392-
Ok(None)
393-
}
394-
395342
/// Fetch the custom details for Yelp suggestions.
396343
/// It returns the location tuple as follows:
397344
/// (
@@ -440,25 +387,17 @@ struct SuggestionBuilder<'a> {
440387
post_modifier: Option<String>,
441388
location_sign: Option<String>,
442389
location: Option<String>,
443-
need_location: bool,
444390
icon: Option<Vec<u8>>,
445391
icon_mimetype: Option<String>,
446392
score: f64,
447393
}
448394

449395
impl<'a> From<SuggestionBuilder<'a>> for Suggestion {
450396
fn from(builder: SuggestionBuilder<'a>) -> Suggestion {
451-
// This location sign such the 'near by' needs to add as a description parameter.
452-
let location_modifier = if !builder.need_location {
453-
builder.location_sign.as_deref()
454-
} else {
455-
None
456-
};
457397
let description = [
458398
builder.pre_modifier.as_deref(),
459399
Some(builder.subject),
460400
builder.post_modifier.as_deref(),
461-
location_modifier,
462401
]
463402
.iter()
464403
.flatten()
@@ -470,7 +409,7 @@ impl<'a> From<SuggestionBuilder<'a>> for Suggestion {
470409
let mut url = String::from("https://www.yelp.com/search?");
471410
let mut parameters = form_urlencoded::Serializer::new(String::new());
472411
parameters.append_pair("find_desc", &description);
473-
if let (Some(location), true) = (&builder.location, builder.need_location) {
412+
if let Some(location) = &builder.location {
474413
parameters.append_pair("find_loc", location);
475414
}
476415
url.push_str(&parameters.finish());
@@ -494,7 +433,7 @@ impl<'a> From<SuggestionBuilder<'a>> for Suggestion {
494433
icon: builder.icon,
495434
icon_mimetype: builder.icon_mimetype,
496435
score: builder.score,
497-
has_location_sign: location_modifier.is_none() && builder.location_sign.is_some(),
436+
has_location_sign: builder.location_sign.is_some(),
498437
subject_exact_match: builder.subject_exact_match,
499438
location_param: "find_loc".to_string(),
500439
}
@@ -756,7 +695,11 @@ mod tests {
756695
];
757696
for (query, expected) in find_location_sign_tests {
758697
assert_eq!(
759-
dao.find_location_sign(&query.split_whitespace().collect::<Vec<_>>())?,
698+
dao.find_modifier(
699+
&query.split_whitespace().collect::<Vec<_>>(),
700+
Modifier::LocationSign,
701+
FindFrom::First
702+
)?,
760703
*expected
761704
);
762705
}

0 commit comments

Comments
 (0)