diff --git a/.cargo/config.in b/.cargo/config.in index 0de201f42f91c..c4dbe6fdc79a7 100644 --- a/.cargo/config.in +++ b/.cargo/config.in @@ -60,9 +60,9 @@ git = "https://github.com/mozilla-spidermonkey/jsparagus" rev = "61f399c53a641ebd3077c1f39f054f6d396a633c" replace-with = "vendored-sources" -[source."git+https://github.com/mozilla/application-services?rev=41367fda038268843a87c459698691f33f0d9063"] +[source."git+https://github.com/mozilla/application-services?rev=0b241a66c0722290b3a42366378b3f97d8c268d7"] git = "https://github.com/mozilla/application-services" -rev = "41367fda038268843a87c459698691f33f0d9063" +rev = "0b241a66c0722290b3a42366378b3f97d8c268d7" replace-with = "vendored-sources" [source."git+https://github.com/mozilla/audioipc?rev=596bdb7fbb5745ea415726e16bd497e6c850a540"] diff --git a/Cargo.lock b/Cargo.lock index d7294f5c15a64..2756ac7a1ab48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1585,7 +1585,7 @@ dependencies = [ [[package]] name = "error-support" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=41367fda038268843a87c459698691f33f0d9063#41367fda038268843a87c459698691f33f0d9063" +source = "git+https://github.com/mozilla/application-services?rev=0b241a66c0722290b3a42366378b3f97d8c268d7#0b241a66c0722290b3a42366378b3f97d8c268d7" dependencies = [ "error-support-macros", "lazy_static", @@ -1597,7 +1597,7 @@ dependencies = [ [[package]] name = "error-support-macros" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=41367fda038268843a87c459698691f33f0d9063#41367fda038268843a87c459698691f33f0d9063" +source = "git+https://github.com/mozilla/application-services?rev=0b241a66c0722290b3a42366378b3f97d8c268d7#0b241a66c0722290b3a42366378b3f97d8c268d7" dependencies = [ "proc-macro2", "quote", @@ -2855,7 +2855,7 @@ dependencies = [ [[package]] name = "interrupt-support" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=41367fda038268843a87c459698691f33f0d9063#41367fda038268843a87c459698691f33f0d9063" +source = "git+https://github.com/mozilla/application-services?rev=0b241a66c0722290b3a42366378b3f97d8c268d7#0b241a66c0722290b3a42366378b3f97d8c268d7" dependencies = [ "lazy_static", "parking_lot", @@ -4038,7 +4038,7 @@ dependencies = [ [[package]] name = "nss_build_common" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=41367fda038268843a87c459698691f33f0d9063#41367fda038268843a87c459698691f33f0d9063" +source = "git+https://github.com/mozilla/application-services?rev=0b241a66c0722290b3a42366378b3f97d8c268d7#0b241a66c0722290b3a42366378b3f97d8c268d7" [[package]] name = "nsstring" @@ -4708,7 +4708,7 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "remote_settings" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=41367fda038268843a87c459698691f33f0d9063#41367fda038268843a87c459698691f33f0d9063" +source = "git+https://github.com/mozilla/application-services?rev=0b241a66c0722290b3a42366378b3f97d8c268d7#0b241a66c0722290b3a42366378b3f97d8c268d7" dependencies = [ "parking_lot", "serde", @@ -5231,7 +5231,7 @@ dependencies = [ [[package]] name = "sql-support" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=41367fda038268843a87c459698691f33f0d9063#41367fda038268843a87c459698691f33f0d9063" +source = "git+https://github.com/mozilla/application-services?rev=0b241a66c0722290b3a42366378b3f97d8c268d7#0b241a66c0722290b3a42366378b3f97d8c268d7" dependencies = [ "ffi-support", "interrupt-support", @@ -5412,7 +5412,7 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "suggest" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=41367fda038268843a87c459698691f33f0d9063#41367fda038268843a87c459698691f33f0d9063" +source = "git+https://github.com/mozilla/application-services?rev=0b241a66c0722290b3a42366378b3f97d8c268d7#0b241a66c0722290b3a42366378b3f97d8c268d7" dependencies = [ "anyhow", "chrono", @@ -5461,7 +5461,7 @@ dependencies = [ [[package]] name = "sync-guid" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=41367fda038268843a87c459698691f33f0d9063#41367fda038268843a87c459698691f33f0d9063" +source = "git+https://github.com/mozilla/application-services?rev=0b241a66c0722290b3a42366378b3f97d8c268d7#0b241a66c0722290b3a42366378b3f97d8c268d7" dependencies = [ "base64 0.21.3", "rand", @@ -5472,7 +5472,7 @@ dependencies = [ [[package]] name = "sync15" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=41367fda038268843a87c459698691f33f0d9063#41367fda038268843a87c459698691f33f0d9063" +source = "git+https://github.com/mozilla/application-services?rev=0b241a66c0722290b3a42366378b3f97d8c268d7#0b241a66c0722290b3a42366378b3f97d8c268d7" dependencies = [ "anyhow", "error-support", @@ -5504,7 +5504,7 @@ dependencies = [ [[package]] name = "tabs" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=41367fda038268843a87c459698691f33f0d9063#41367fda038268843a87c459698691f33f0d9063" +source = "git+https://github.com/mozilla/application-services?rev=0b241a66c0722290b3a42366378b3f97d8c268d7#0b241a66c0722290b3a42366378b3f97d8c268d7" dependencies = [ "anyhow", "error-support", @@ -6154,7 +6154,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "viaduct" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=41367fda038268843a87c459698691f33f0d9063#41367fda038268843a87c459698691f33f0d9063" +source = "git+https://github.com/mozilla/application-services?rev=0b241a66c0722290b3a42366378b3f97d8c268d7#0b241a66c0722290b3a42366378b3f97d8c268d7" dependencies = [ "ffi-support", "log", @@ -6300,7 +6300,7 @@ dependencies = [ [[package]] name = "webext-storage" version = "0.1.0" -source = "git+https://github.com/mozilla/application-services?rev=41367fda038268843a87c459698691f33f0d9063#41367fda038268843a87c459698691f33f0d9063" +source = "git+https://github.com/mozilla/application-services?rev=0b241a66c0722290b3a42366378b3f97d8c268d7#0b241a66c0722290b3a42366378b3f97d8c268d7" dependencies = [ "anyhow", "error-support", diff --git a/Cargo.toml b/Cargo.toml index 16af5d84843cd..6fc6390e66d71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -206,13 +206,13 @@ warp = { git = "https://github.com/seanmonstar/warp", rev = "9d081461ae1167eb321 malloc_size_of_derive = { path = "xpcom/rust/malloc_size_of_derive" } # application-services overrides to make updating them all simpler. -interrupt-support = { git = "https://github.com/mozilla/application-services", rev = "41367fda038268843a87c459698691f33f0d9063" } -sql-support = { git = "https://github.com/mozilla/application-services", rev = "41367fda038268843a87c459698691f33f0d9063" } -suggest = { git = "https://github.com/mozilla/application-services", rev = "41367fda038268843a87c459698691f33f0d9063" } -sync15 = { git = "https://github.com/mozilla/application-services", rev = "41367fda038268843a87c459698691f33f0d9063" } -tabs = { git = "https://github.com/mozilla/application-services", rev = "41367fda038268843a87c459698691f33f0d9063" } -viaduct = { git = "https://github.com/mozilla/application-services", rev = "41367fda038268843a87c459698691f33f0d9063" } -webext-storage = { git = "https://github.com/mozilla/application-services", rev = "41367fda038268843a87c459698691f33f0d9063" } +interrupt-support = { git = "https://github.com/mozilla/application-services", rev = "0b241a66c0722290b3a42366378b3f97d8c268d7" } +sql-support = { git = "https://github.com/mozilla/application-services", rev = "0b241a66c0722290b3a42366378b3f97d8c268d7" } +suggest = { git = "https://github.com/mozilla/application-services", rev = "0b241a66c0722290b3a42366378b3f97d8c268d7" } +sync15 = { git = "https://github.com/mozilla/application-services", rev = "0b241a66c0722290b3a42366378b3f97d8c268d7" } +tabs = { git = "https://github.com/mozilla/application-services", rev = "0b241a66c0722290b3a42366378b3f97d8c268d7" } +viaduct = { git = "https://github.com/mozilla/application-services", rev = "0b241a66c0722290b3a42366378b3f97d8c268d7" } +webext-storage = { git = "https://github.com/mozilla/application-services", rev = "0b241a66c0722290b3a42366378b3f97d8c268d7" } # Patch mio 0.8.8 to use windows-sys 0.52 (backport https://github.com/tokio-rs/mio/commit/eea9e3e0c469480e5c59c01e6c3c7e5fd88f0848) mio_0_8 = { package = "mio", git = "https://github.com/glandium/mio", rev = "9a2ef335c366044ffe73b1c4acabe50a1daefe05" } diff --git a/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_yelp.js b/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_yelp.js index 44b5f17c19906..83cf5232a7d4f 100644 --- a/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_yelp.js +++ b/browser/components/urlbar/tests/quicksuggest/browser/browser_quicksuggest_yelp.js @@ -14,6 +14,7 @@ const REMOTE_SETTINGS_RECORDS = [ postModifiers: ["delivery"], locationSigns: [{ keyword: "in", needLocation: true }], yelpModifiers: [], + icon: "1234", }, }, ]; diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_yelp.js b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_yelp.js index c75e54c9be486..7f06d0dba7b1b 100644 --- a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_yelp.js +++ b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_yelp.js @@ -15,6 +15,7 @@ const REMOTE_SETTINGS_RECORDS = [ postModifiers: ["delivery"], locationSigns: [{ keyword: "in", needLocation: true }], yelpModifiers: [], + icon: "1234", }, }, ]; @@ -301,6 +302,7 @@ function makeExpectedResult(expected) { url, title: expected.title, displayUrl, + icon: null, }, }; } diff --git a/third_party/rust/remote_settings/.cargo-checksum.json b/third_party/rust/remote_settings/.cargo-checksum.json index e4294a63b9492..8a2b63f314704 100644 --- a/third_party/rust/remote_settings/.cargo-checksum.json +++ b/third_party/rust/remote_settings/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"4fa89b0606fe8ec8ac8c479b8b9adf33d0c936b09fa5af108ded74139ace37fb","build.rs":"4326f03729cf8f1673e4228e6dc111de1ea4d8bcc06351f7ae563efb2613f866","src/client.rs":"3d87162e6913a81cc6f5178a7ca791e262d0d029e7dedf3df4fe2f66e5501185","src/config.rs":"7bb678addfae3b4ed5f2892d32263e5b33cc05e5a12a250f664150e78211f94a","src/error.rs":"192ca42af7c6b882f3129378c23b45dab8a0d2b179e23a8813a335ffd56b21dc","src/lib.rs":"416e99894e152f6cea7418ad2fabfd94bc3d907efd9f33fbd2a83fb99452b2df","src/remote_settings.udl":"2e71491ad3894d17e5bde0663d9490bfea6294d99cdbe9d67a36137faeedc593","uniffi.toml":"f8ec8dc593e0d501c2e9e40368ec93ec33b1edd8608e29495e0a54b63144e880"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"4fa89b0606fe8ec8ac8c479b8b9adf33d0c936b09fa5af108ded74139ace37fb","build.rs":"4326f03729cf8f1673e4228e6dc111de1ea4d8bcc06351f7ae563efb2613f866","src/client.rs":"fb3f2cd47460e5ae07a5e8d61b358d588d14075bd9dd6b6e818e1af74abd5dba","src/config.rs":"7bb678addfae3b4ed5f2892d32263e5b33cc05e5a12a250f664150e78211f94a","src/error.rs":"192ca42af7c6b882f3129378c23b45dab8a0d2b179e23a8813a335ffd56b21dc","src/lib.rs":"416e99894e152f6cea7418ad2fabfd94bc3d907efd9f33fbd2a83fb99452b2df","src/remote_settings.udl":"2e71491ad3894d17e5bde0663d9490bfea6294d99cdbe9d67a36137faeedc593","uniffi.toml":"f8ec8dc593e0d501c2e9e40368ec93ec33b1edd8608e29495e0a54b63144e880"},"package":null} \ No newline at end of file diff --git a/third_party/rust/remote_settings/src/client.rs b/third_party/rust/remote_settings/src/client.rs index 9585a7cc7c57c..0d99de9cc1493 100644 --- a/third_party/rust/remote_settings/src/client.rs +++ b/third_party/rust/remote_settings/src/client.rs @@ -155,7 +155,10 @@ impl Client { if resp.is_success() { Ok(resp) } else { - Err(RemoteSettingsError::ResponseError(resp.text().to_string())) + Err(RemoteSettingsError::ResponseError(format!( + "status code: {}", + resp.status + ))) } } diff --git a/third_party/rust/suggest/.cargo-checksum.json b/third_party/rust/suggest/.cargo-checksum.json index 4827ba5a2b059..e57a0ae95ca05 100644 --- a/third_party/rust/suggest/.cargo-checksum.json +++ b/third_party/rust/suggest/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"4aa81cff67e67b08ba3348c1acddaa5aee887df3c35006754c9cda4273a94458","README.md":"8d7457893194e255b87e5a2667ee25c87bd470f5338d7078506f866a67a3fdbd","build.rs":"78780c5cccfe22c3ff4198624b9e188559c437c3e6fa1c8bb66548eee6aa66bf","src/config.rs":"03630b2219b6674e332a1f96f44db74def17f985c850a800299b815fa72241c2","src/db.rs":"208916c915f29fa2fd8725f635822d63ac86023ea0eb2050a92d4c7fbc9f6697","src/error.rs":"f563210a6c050d98ec85e0f6d9401e7373bfb816e865e8edabbabb23d848ba13","src/keyword.rs":"988d0ab021c0df19cfd3c519df7d37f606bf984cd14d0efca4e5a7aff88344dd","src/lib.rs":"65a035dbfb17e2d2d9f237ad52dc03982ae28c70e3dcf3d96cc9f2d7af79efe3","src/pocket.rs":"c4dda43390d1c39dc795933596b3c1e4e282932cac6c69da53c6e05d39e9ef29","src/provider.rs":"3fe8f90d77586f5ff683374f24df026110bfaaab128fd4503d2c695eed71d4fe","src/rs.rs":"038e954eaeaa6898a2edb4d29728233a6286ac818b42028da1ba5c1b03a3efc0","src/schema.rs":"f7995c1cdd98c642e4497b5f3d0881a3ab6ac4f086172142c195adb0934b93de","src/store.rs":"b868b2853beba800cf43183f2b629a4024e435fa9617dcd751480ee89f8fe12f","src/suggest.udl":"9b82f97afb49d94a82cad3a9d44a25e8cab3b998b018ea9c3467b170b795e65b","src/suggestion.rs":"355f01b8a82a55a506e0a4c7d26dcf6d059802f84df3789309f5e0f42b80d793","src/yelp.rs":"2844e639bdf163fc23ba6e463495f47ec8d9c617de4754b49a58aa16d6ea39f5","uniffi.toml":"f26317442ddb5b3281245bef6e60ffcb78bb95d29fe4a351a56dbb88d4ec8aab"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"4aa81cff67e67b08ba3348c1acddaa5aee887df3c35006754c9cda4273a94458","README.md":"8d7457893194e255b87e5a2667ee25c87bd470f5338d7078506f866a67a3fdbd","build.rs":"78780c5cccfe22c3ff4198624b9e188559c437c3e6fa1c8bb66548eee6aa66bf","src/config.rs":"03630b2219b6674e332a1f96f44db74def17f985c850a800299b815fa72241c2","src/db.rs":"d373ad097edac2bbcc6e1b14f51c21b6e2cab2289d27667332798c9cde4dcbef","src/error.rs":"f563210a6c050d98ec85e0f6d9401e7373bfb816e865e8edabbabb23d848ba13","src/keyword.rs":"988d0ab021c0df19cfd3c519df7d37f606bf984cd14d0efca4e5a7aff88344dd","src/lib.rs":"65a035dbfb17e2d2d9f237ad52dc03982ae28c70e3dcf3d96cc9f2d7af79efe3","src/pocket.rs":"c4dda43390d1c39dc795933596b3c1e4e282932cac6c69da53c6e05d39e9ef29","src/provider.rs":"4fe662587efc5a80d000c217ce124506c6800293c50ff460ef95e9e659c764b9","src/rs.rs":"5dc41f6fac152b9f87d77a6b97b30377403d089649c35f6550460494d79246d0","src/schema.rs":"4c7264cd70050fbc1f711c80727ca65f7103d0e06d9247eacc5cc42b965bcd6f","src/store.rs":"77e1b8580fbd5e725a20d8da721e420306a762331046cb46faa4cee239ebe946","src/suggest.udl":"7dcc48cadcac44f67830d320b1e964e59d389b935107f18a066e5b30275073e7","src/suggestion.rs":"5994710ddefbcb4589e25b7b89854344b58bfb6754e13d0aa0549a8c188a0899","src/yelp.rs":"8f90146f5b32ea04b7506cae217c5025d532337ac9acd977866f110979559101","uniffi.toml":"f26317442ddb5b3281245bef6e60ffcb78bb95d29fe4a351a56dbb88d4ec8aab"},"package":null} \ No newline at end of file diff --git a/third_party/rust/suggest/src/db.rs b/third_party/rust/suggest/src/db.rs index 52f7211679782..07fc3ab4a2645 100644 --- a/third_party/rust/suggest/src/db.rs +++ b/third_party/rust/suggest/src/db.rs @@ -21,12 +21,13 @@ use crate::{ pocket::{split_keyword, KeywordConfidence}, provider::SuggestionProvider, rs::{ - DownloadedAmoSuggestion, DownloadedAmpWikipediaSuggestion, DownloadedMdnSuggestion, - DownloadedPocketSuggestion, DownloadedWeatherData, SuggestRecordId, + DownloadedAmoSuggestion, DownloadedAmpSuggestion, DownloadedAmpWikipediaSuggestion, + DownloadedMdnSuggestion, DownloadedPocketSuggestion, DownloadedWeatherData, + SuggestRecordId, }, schema::{SuggestConnectionInitializer, VERSION}, store::{UnparsableRecord, UnparsableRecords}, - suggestion::{cook_raw_suggestion_url, Suggestion}, + suggestion::{cook_raw_suggestion_url, AmpSuggestionType, Suggestion}, Result, SuggestionQuery, }; @@ -186,7 +187,12 @@ impl<'a> SuggestDao<'a> { .iter() .try_fold(vec![], |mut acc, provider| { let suggestions = match provider { - SuggestionProvider::Amp => self.fetch_amp_suggestions(query), + SuggestionProvider::Amp => { + self.fetch_amp_suggestions(query, AmpSuggestionType::Desktop) + } + SuggestionProvider::AmpMobile => { + self.fetch_amp_suggestions(query, AmpSuggestionType::Mobile) + } SuggestionProvider::Wikipedia => self.fetch_wikipedia_suggestions(query), SuggestionProvider::Amo => self.fetch_amo_suggestions(query), SuggestionProvider::Pocket => self.fetch_pocket_suggestions(query), @@ -207,28 +213,56 @@ impl<'a> SuggestDao<'a> { } /// Fetches Suggestions of type Amp provider that match the given query - pub fn fetch_amp_suggestions(&self, query: &SuggestionQuery) -> Result> { + pub fn fetch_amp_suggestions( + &self, + query: &SuggestionQuery, + suggestion_type: AmpSuggestionType, + ) -> Result> { let keyword_lowercased = &query.keyword.to_lowercase(); + let provider = match suggestion_type { + AmpSuggestionType::Mobile => SuggestionProvider::AmpMobile, + AmpSuggestionType::Desktop => SuggestionProvider::Amp, + }; let suggestions = self.conn.query_rows_and_then_cached( - "SELECT s.id, k.rank, s.title, s.url, s.provider, s.score - FROM suggestions s - JOIN keywords k ON k.suggestion_id = s.id - WHERE s.provider = :provider AND - k.keyword = :keyword", - named_params! { + r#" + SELECT + s.id, + k.rank, + s.title, + s.url, + s.provider, + s.score + FROM + suggestions s + JOIN + keywords k + ON k.suggestion_id = s.id + WHERE + s.provider = :provider + AND k.keyword = :keyword + "#, + named_params! { ":keyword": keyword_lowercased, - ":provider": SuggestionProvider::Amp + ":provider": provider }, - |row| -> Result{ + |row| -> Result { let suggestion_id: i64 = row.get("id")?; let title = row.get("title")?; let raw_url = row.get::<_, String>("url")?; let score = row.get::<_, f64>("score")?; let keywords: Vec = self.conn.query_rows_and_then_cached( - "SELECT keyword FROM keywords - WHERE suggestion_id = :suggestion_id AND rank >= :rank - ORDER BY rank ASC", + r#" + SELECT + keyword + FROM + keywords + WHERE + suggestion_id = :suggestion_id + AND rank >= :rank + ORDER BY + rank ASC + "#, named_params! { ":suggestion_id": suggestion_id, ":rank": row.get::<_, i64>("rank")?, @@ -236,10 +270,19 @@ impl<'a> SuggestDao<'a> { |row| row.get(0), )?; self.conn.query_row_and_then( - "SELECT amp.advertiser, amp.block_id, amp.iab_category, amp.impression_url, amp.click_url, - (SELECT i.data FROM icons i WHERE i.id = amp.icon_id) AS icon - FROM amp_custom_details amp - WHERE amp.suggestion_id = :suggestion_id", + r#" + SELECT + amp.advertiser, + amp.block_id, + amp.iab_category, + amp.impression_url, + amp.click_url, + (SELECT i.data FROM icons i WHERE i.id = amp.icon_id) AS icon + FROM + amp_custom_details amp + WHERE + amp.suggestion_id = :suggestion_id + "#, named_params! { ":suggestion_id": suggestion_id }, @@ -247,6 +290,7 @@ impl<'a> SuggestDao<'a> { let cooked_url = cook_raw_suggestion_url(&raw_url); let raw_click_url = row.get::<_, String>("click_url")?; let cooked_click_url = cook_raw_suggestion_url(&raw_click_url); + Ok(Suggestion::Amp { block_id: row.get("block_id")?, advertiser: row.get("advertiser")?, @@ -261,10 +305,10 @@ impl<'a> SuggestDao<'a> { raw_click_url, score, }) - } + }, ) - } - )?; + }, + )?; Ok(suggestions) } @@ -272,11 +316,21 @@ impl<'a> SuggestDao<'a> { pub fn fetch_wikipedia_suggestions(&self, query: &SuggestionQuery) -> Result> { let keyword_lowercased = &query.keyword.to_lowercase(); let suggestions = self.conn.query_rows_and_then_cached( - "SELECT s.id, k.rank, s.title, s.url - FROM suggestions s - JOIN keywords k ON k.suggestion_id = s.id - WHERE s.provider = :provider AND - k.keyword = :keyword", + r#" + SELECT + s.id, + k.rank, + s.title, + s.url + FROM + suggestions s + JOIN + keywords k + ON k.suggestion_id = s.id + WHERE + s.provider = :provider + AND k.keyword = :keyword + "#, named_params! { ":keyword": keyword_lowercased, ":provider": SuggestionProvider::Wikipedia @@ -322,47 +376,87 @@ impl<'a> SuggestDao<'a> { let keyword_lowercased = &query.keyword.to_lowercase(); let (keyword_prefix, keyword_suffix) = split_keyword(keyword_lowercased); let suggestions_limit = &query.limit.unwrap_or(-1); - let suggestions = self.conn.query_rows_and_then_cached( - "SELECT s.id, k.rank, s.title, s.url, s.provider, s.score, k.confidence, k.keyword_suffix - FROM suggestions s - JOIN prefix_keywords k ON k.suggestion_id = s.id - WHERE k.keyword_prefix = :keyword_prefix AND s.provider = :provider - ORDER by s.score DESC - LIMIT :suggestions_limit", - named_params! { - ":keyword_prefix": keyword_prefix, - ":provider": SuggestionProvider::Amo, - ":suggestions_limit": suggestions_limit, - }, - |row| -> Result>{ - let suggestion_id: i64 = row.get("id")?; - let title = row.get("title")?; - let raw_url = row.get::<_, String>("url")?; - let score = row.get::<_, f64>("score")?; + let suggestions = self + .conn + .query_rows_and_then_cached( + r#" + SELECT + s.id, + MAX(k.rank) AS rank, + s.title, + s.url, + s.provider, + s.score, + k.keyword_suffix + FROM + suggestions s + JOIN + prefix_keywords k + ON k.suggestion_id = s.id + WHERE + k.keyword_prefix = :keyword_prefix + AND (k.keyword_suffix BETWEEN :keyword_suffix AND :keyword_suffix || x'FFFF') + AND s.provider = :provider + GROUP BY + s.id + ORDER BY + s.score DESC, + rank DESC + LIMIT + :suggestions_limit + "#, + named_params! { + ":keyword_prefix": keyword_prefix, + ":keyword_suffix": keyword_suffix, + ":provider": SuggestionProvider::Amo, + ":suggestions_limit": suggestions_limit, + }, + |row| -> Result> { + let suggestion_id: i64 = row.get("id")?; + let title = row.get("title")?; + let raw_url = row.get::<_, String>("url")?; + let score = row.get::<_, f64>("score")?; - let full_suffix = row.get::<_, String>("keyword_suffix")?; - full_suffix.starts_with(keyword_suffix).then(|| - self.conn.query_row_and_then( - "SELECT amo.description, amo.guid, amo.rating, amo.icon_url, amo.number_of_ratings - FROM amo_custom_details amo - WHERE amo.suggestion_id = :suggestion_id", - named_params! { - ":suggestion_id": suggestion_id - }, - |row| { - Ok(Suggestion::Amo { - title, - url: raw_url, - icon_url: row.get("icon_url")?, - description: row.get("description")?, - rating: row.get("rating")?, - number_of_ratings: row.get("number_of_ratings")?, - guid: row.get("guid")?, - score, + let full_suffix = row.get::<_, String>("keyword_suffix")?; + full_suffix + .starts_with(keyword_suffix) + .then(|| { + self.conn.query_row_and_then( + r#" + SELECT + amo.description, + amo.guid, + amo.rating, + amo.icon_url, + amo.number_of_ratings + FROM + amo_custom_details amo + WHERE + amo.suggestion_id = :suggestion_id + "#, + named_params! { + ":suggestion_id": suggestion_id + }, + |row| { + Ok(Suggestion::Amo { + title, + url: raw_url, + icon_url: row.get("icon_url")?, + description: row.get("description")?, + rating: row.get("rating")?, + number_of_ratings: row.get("number_of_ratings")?, + guid: row.get("guid")?, + score, + }) + }, + ) }) - })).transpose() - } - )?.into_iter().flatten().collect(); + .transpose() + }, + )? + .into_iter() + .flatten() + .collect(); Ok(suggestions) } @@ -370,44 +464,71 @@ impl<'a> SuggestDao<'a> { pub fn fetch_pocket_suggestions(&self, query: &SuggestionQuery) -> Result> { let keyword_lowercased = &query.keyword.to_lowercase(); let (keyword_prefix, keyword_suffix) = split_keyword(keyword_lowercased); - let suggestions_limit = &query.limit.unwrap_or(-1); - let suggestions = self.conn.query_rows_and_then_cached( - "SELECT s.id, k.rank, s.title, s.url, s.provider, s.score, k.confidence, k.keyword_suffix - FROM suggestions s - JOIN prefix_keywords k ON k.suggestion_id = s.id - WHERE k.keyword_prefix = :keyword_prefix AND s.provider = :provider - ORDER BY s.score DESC - LIMIT :suggestions_limit", - named_params! { - ":keyword_prefix": keyword_prefix, - ":provider": SuggestionProvider::Pocket, - ":suggestions_limit": suggestions_limit, - - }, - |row| -> Result>{ - let title = row.get("title")?; - let raw_url = row.get::<_, String>("url")?; - let score = row.get::<_, f64>("score")?; - let confidence = row.get("confidence")?; - let full_suffix = row.get::<_, String>("keyword_suffix")?; - let suffixes_match = match confidence { - KeywordConfidence::Low => full_suffix.starts_with(keyword_suffix), - KeywordConfidence::High => full_suffix == keyword_suffix, - }; - if suffixes_match { - Ok(Some(Suggestion::Pocket { - title, - url: raw_url, - score, - is_top_pick: matches!( - confidence, - KeywordConfidence::High) - })) - } else { - Ok(None) - } - } - )?.into_iter().flatten().collect(); + let suggestions = self + .conn + .query_rows_and_then_cached( + r#" + SELECT + s.id, + MAX(k.rank) AS rank, + s.title, + s.url, + s.provider, + s.score, + k.confidence, + k.keyword_suffix + FROM + suggestions s + JOIN + prefix_keywords k + ON k.suggestion_id = s.id + WHERE + k.keyword_prefix = :keyword_prefix + AND (k.keyword_suffix BETWEEN :keyword_suffix AND :keyword_suffix || x'FFFF') + AND s.provider = :provider + GROUP BY + s.id, + k.confidence + ORDER BY + s.score DESC, + rank DESC + "#, + named_params! { + ":keyword_prefix": keyword_prefix, + ":keyword_suffix": keyword_suffix, + ":provider": SuggestionProvider::Pocket, + }, + |row| -> Result> { + let title = row.get("title")?; + let raw_url = row.get::<_, String>("url")?; + let score = row.get::<_, f64>("score")?; + let confidence = row.get("confidence")?; + let full_suffix = row.get::<_, String>("keyword_suffix")?; + let suffixes_match = match confidence { + KeywordConfidence::Low => full_suffix.starts_with(keyword_suffix), + KeywordConfidence::High => full_suffix == keyword_suffix, + }; + if suffixes_match { + Ok(Some(Suggestion::Pocket { + title, + url: raw_url, + score, + is_top_pick: matches!(confidence, KeywordConfidence::High), + })) + } else { + Ok(None) + } + }, + )? + .into_iter() + .flatten() + .take( + query + .limit + .and_then(|limit| usize::try_from(limit).ok()) + .unwrap_or(usize::MAX), + ) + .collect(); Ok(suggestions) } @@ -421,21 +542,33 @@ impl<'a> SuggestDao<'a> { .query_rows_and_then_cached( r#" SELECT - s.id, s.title, s.url, s.provider, s.score, k.keyword_suffix + s.id, + MAX(k.rank) AS rank, + s.title, + s.url, + s.provider, + s.score, + k.keyword_suffix FROM - suggestions s + suggestions s JOIN - prefix_keywords k ON k.suggestion_id = s.id + prefix_keywords k + ON k.suggestion_id = s.id WHERE - k.keyword_prefix = :keyword_prefix - AND - s.provider = :provider + k.keyword_prefix = :keyword_prefix + AND (k.keyword_suffix BETWEEN :keyword_suffix AND :keyword_suffix || x'FFFF') + AND s.provider = :provider + GROUP BY + s.id ORDER BY - s.score DESC - LIMIT :suggestions_limit + s.score DESC, + rank DESC + LIMIT + :suggestions_limit "#, named_params! { ":keyword_prefix": keyword_prefix, + ":keyword_suffix": keyword_suffix, ":provider": SuggestionProvider::Mdn, ":suggestions_limit": suggestions_limit, }, @@ -492,10 +625,18 @@ impl<'a> SuggestDao<'a> { let keyword_lowercased = &query.keyword.trim().to_lowercase(); let suggestions = self.conn.query_rows_and_then_cached( - "SELECT s.score - FROM suggestions s - JOIN keywords k ON k.suggestion_id = s.id - WHERE s.provider = :provider AND (k.keyword BETWEEN :keyword AND :keyword || X'FFFF')", + r#" + SELECT + s.score + FROM + suggestions s + JOIN + keywords k + ON k.suggestion_id = s.id + WHERE + s.provider = :provider + AND (k.keyword BETWEEN :keyword AND :keyword || X'FFFF') + "#, named_params! { ":keyword": keyword_lowercased, ":provider": SuggestionProvider::Weather @@ -610,6 +751,7 @@ impl<'a> SuggestDao<'a> { self.scope.err_if_interrupted()?; let common_details = suggestion.common_details(); let provider = suggestion.provider(); + let suggestion_id: i64 = self.conn.query_row_and_then_cachable( &format!( "INSERT INTO suggestions( @@ -710,6 +852,97 @@ impl<'a> SuggestDao<'a> { Ok(()) } + /// Inserts all suggestions from a downloaded AMP-Mobile attachment into + /// the database. + pub fn insert_amp_mobile_suggestions( + &mut self, + record_id: &SuggestRecordId, + suggestions: &[DownloadedAmpSuggestion], + ) -> Result<()> { + for suggestion in suggestions { + self.scope.err_if_interrupted()?; + let common_details = &suggestion.common_details; + let suggestion_id: i64 = self.conn.query_row_and_then_cachable( + &format!( + "INSERT INTO suggestions( + record_id, + provider, + title, + url, + score + ) + VALUES( + :record_id, + {}, + :title, + :url, + :score + ) + RETURNING id", + SuggestionProvider::AmpMobile as u8 + ), + named_params! { + ":record_id": record_id.as_str(), + ":title": common_details.title, + ":url": common_details.url, + ":score": common_details.score.unwrap_or(DEFAULT_SUGGESTION_SCORE) + }, + |row| row.get(0), + true, + )?; + self.conn.execute( + "INSERT INTO amp_custom_details( + suggestion_id, + advertiser, + block_id, + iab_category, + impression_url, + click_url, + icon_id + ) + VALUES( + :suggestion_id, + :advertiser, + :block_id, + :iab_category, + :impression_url, + :click_url, + :icon_id + )", + named_params! { + ":suggestion_id": suggestion_id, + ":advertiser": suggestion.advertiser, + ":block_id": suggestion.block_id, + ":iab_category": suggestion.iab_category, + ":impression_url": suggestion.impression_url, + ":click_url": suggestion.click_url, + ":icon_id": suggestion.icon_id, + }, + )?; + + for (index, keyword) in common_details.keywords.iter().enumerate() { + self.conn.execute( + "INSERT INTO keywords( + keyword, + suggestion_id, + rank + ) + VALUES( + :keyword, + :suggestion_id, + :rank + )", + named_params! { + ":keyword": keyword, + ":rank": index, + ":suggestion_id": suggestion_id, + }, + )?; + } + } + Ok(()) + } + /// Inserts all suggestions from a downloaded Pocket attachment into /// the database. pub fn insert_pocket_suggestions( @@ -945,6 +1178,10 @@ impl<'a> SuggestDao<'a> { "DELETE FROM yelp_location_signs WHERE record_id = :record_id", named_params! { ":record_id": record_id.as_str() }, )?; + self.conn.execute_cached( + "DELETE FROM yelp_custom_details WHERE record_id = :record_id", + named_params! { ":record_id": record_id.as_str() }, + )?; Ok(()) } diff --git a/third_party/rust/suggest/src/provider.rs b/third_party/rust/suggest/src/provider.rs index cbcc314444a4b..1449c35c8a035 100644 --- a/third_party/rust/suggest/src/provider.rs +++ b/third_party/rust/suggest/src/provider.rs @@ -19,6 +19,7 @@ pub enum SuggestionProvider { Yelp = 5, Mdn = 6, Weather = 7, + AmpMobile = 8, } impl FromSql for SuggestionProvider { diff --git a/third_party/rust/suggest/src/rs.rs b/third_party/rust/suggest/src/rs.rs index a40295e2c76d2..a1cbbdbf9a5fc 100644 --- a/third_party/rust/suggest/src/rs.rs +++ b/third_party/rust/suggest/src/rs.rs @@ -98,6 +98,8 @@ pub(crate) enum SuggestRecord { Weather(DownloadedWeatherData), #[serde(rename = "configuration")] GlobalConfig(DownloadedGlobalConfig), + #[serde(rename = "amp-mobile-suggestions")] + AmpMobile, } /// Represents either a single value, or a list of values. This is used to @@ -294,6 +296,8 @@ pub(crate) struct DownloadedYelpSuggestion { pub location_signs: Vec, #[serde(rename = "yelpModifiers")] pub yelp_modifiers: Vec, + #[serde(rename = "icon")] + pub icon_id: String, } /// An MDN suggestion to ingest from an attachment diff --git a/third_party/rust/suggest/src/schema.rs b/third_party/rust/suggest/src/schema.rs index 4c79c837598c3..86472b77c46d8 100644 --- a/third_party/rust/suggest/src/schema.rs +++ b/third_party/rust/suggest/src/schema.rs @@ -6,8 +6,16 @@ use rusqlite::{Connection, Transaction}; use sql_support::open_database::{self, ConnectionInitializer}; -pub const VERSION: u32 = 12; - +/// The current database schema version. +/// +/// For any changes to the schema [`SQL`], please make sure to: +/// +/// 1. Bump this version. +/// 2. Add a migration from the old version to the new version in +/// [`SuggestConnectionInitializer::upgrade_from`]. +pub const VERSION: u32 = 13; + +/// The current Suggest database schema. pub const SQL: &str = " CREATE TABLE meta( key TEXT PRIMARY KEY, @@ -92,6 +100,11 @@ pub const SQL: &str = " record_id TEXT NOT NULL ) WITHOUT ROWID; + CREATE TABLE yelp_custom_details( + icon_id TEXT PRIMARY KEY, + record_id TEXT NOT NULL + ) WITHOUT ROWID; + CREATE TABLE mdn_custom_details( suggestion_id INTEGER PRIMARY KEY, description TEXT NOT NULL, @@ -127,10 +140,10 @@ impl ConnectionInitializer for SuggestConnectionInitializer { fn upgrade_from(&self, _db: &Transaction<'_>, version: u32) -> open_database::Result<()> { match version { - 1..=11 => { - // These schema versions were used during development, and never - // shipped in any applications. Treat these databases as - // corrupt, so that they'll be replaced. + 1..=12 => { + // Treat databases with these older schema versions as corrupt, + // so that they'll be replaced by a fresh, empty database with + // the current schema. Err(open_database::Error::Corrupt) } _ => Err(open_database::Error::IncompatibleVersion(version)), diff --git a/third_party/rust/suggest/src/store.rs b/third_party/rust/suggest/src/store.rs index 72c959050a28c..e1c412d2f7a2b 100644 --- a/third_party/rust/suggest/src/store.rs +++ b/third_party/rust/suggest/src/store.rs @@ -392,6 +392,11 @@ where dao.insert_amp_wikipedia_suggestions(record_id, suggestions) })?; } + SuggestRecord::AmpMobile => { + self.ingest_attachment(writer, record, |dao, record_id, suggestions| { + dao.insert_amp_mobile_suggestions(record_id, suggestions) + })?; + } SuggestRecord::Icon => { let (Some(icon_id), Some(attachment)) = (record_id.as_icon_id(), record.attachment.as_ref()) @@ -1884,6 +1889,17 @@ mod tests { "hash": "", "size": 0, }, + }, { + "id": "icon-yelp-favicon", + "type": "icon", + "last_modified": 25, + "attachment": { + "filename": "yelp-favicon.svg", + "mimetype": "image/svg+xml", + "location": "yelp-favicon.svg", + "hash": "", + "size": 0, + }, }]))? .with_data( "data-1.json", @@ -1975,7 +1991,7 @@ mod tests { .with_data( "data-4.json", json!({ - "subjects": ["ramen", "spicy ramen", "spicy random ramen", "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789Z"], + "subjects": ["ramen", "spicy ramen", "spicy random ramen", "rats", "raven", "raccoon", "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789Z"], "preModifiers": ["best", "super best", "same_modifier"], "postModifiers": ["delivery", "super delivery", "same_modifier"], "locationSigns": [ @@ -1985,6 +2001,7 @@ mod tests { { "keyword": "near me", "needLocation": false }, ], "yelpModifiers": ["yelp", "yelp keyword"], + "icon": "yelp-favicon" }), )? .with_data( @@ -2000,7 +2017,8 @@ mod tests { ]), )? .with_icon("icon-2.png", "i-am-an-icon".as_bytes().into()) - .with_icon("icon-3.png", "also-an-icon".as_bytes().into()); + .with_icon("icon-3.png", "also-an-icon".as_bytes().into()) + .with_icon("yelp-favicon.svg", "yelp-icon".as_bytes().into()); let store = unique_test_store(SnapshotSettingsClient::with_snapshot(snapshot)); @@ -2578,13 +2596,26 @@ mod tests { limit: None, }, expect![[r#" - [ - Yelp { - url: "https://www.yelp.com/search?find_desc=best+spicy+ramen+delivery&find_loc=tokyo", - title: "best spicy ramen delivery in tokyo", - is_top_pick: true, - }, - ] + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=best+spicy+ramen+delivery&find_loc=tokyo", + title: "best spicy ramen delivery in tokyo", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] "#]], ), ( @@ -2595,13 +2626,26 @@ mod tests { limit: None, }, expect![[r#" - [ - Yelp { - url: "https://www.yelp.com/search?find_desc=BeSt+SpIcY+rAmEn+DeLiVeRy&find_loc=ToKyO", - title: "BeSt SpIcY rAmEn DeLiVeRy In ToKyO", - is_top_pick: true, - }, - ] + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=BeSt+SpIcY+rAmEn+DeLiVeRy&find_loc=ToKyO", + title: "BeSt SpIcY rAmEn DeLiVeRy In ToKyO", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] "#]], ), ( @@ -2612,13 +2656,26 @@ mod tests { limit: None, }, expect![[r#" - [ - Yelp { - url: "https://www.yelp.com/search?find_desc=best+ramen+delivery&find_loc=tokyo", - title: "best ramen delivery in tokyo", - is_top_pick: true, - }, - ] + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=best+ramen+delivery&find_loc=tokyo", + title: "best ramen delivery in tokyo", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] "#]], ), ( @@ -2651,13 +2708,26 @@ mod tests { limit: None, }, expect![[r#" - [ - Yelp { - url: "https://www.yelp.com/search?find_desc=super+best+ramen+delivery&find_loc=tokyo", - title: "super best ramen delivery in tokyo", - is_top_pick: true, - }, - ] + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=super+best+ramen+delivery&find_loc=tokyo", + title: "super best ramen delivery in tokyo", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] "#]], ), ( @@ -2679,13 +2749,26 @@ mod tests { limit: None, }, expect![[r#" - [ - Yelp { - url: "https://www.yelp.com/search?find_desc=ramen+delivery&find_loc=tokyo", - title: "ramen delivery in tokyo", - is_top_pick: true, - }, - ] + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=ramen+delivery&find_loc=tokyo", + title: "ramen delivery in tokyo", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] "#]], ), ( @@ -2696,13 +2779,26 @@ mod tests { limit: None, }, expect![[r#" - [ - Yelp { - url: "https://www.yelp.com/search?find_desc=ramen+super+delivery&find_loc=tokyo", - title: "ramen super delivery in tokyo", - is_top_pick: true, - }, - ] + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=ramen+super+delivery&find_loc=tokyo", + title: "ramen super delivery in tokyo", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] "#]], ), ( @@ -2724,13 +2820,26 @@ mod tests { limit: None, }, expect![[r#" - [ - Yelp { - url: "https://www.yelp.com/search?find_desc=ramen&find_loc=tokyo", - title: "ramen in tokyo", - is_top_pick: true, - }, - ] + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=ramen&find_loc=tokyo", + title: "ramen in tokyo", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] "#]], ), ( @@ -2741,13 +2850,26 @@ mod tests { limit: None, }, expect![[r#" - [ - Yelp { - url: "https://www.yelp.com/search?find_desc=ramen&find_loc=tokyo", - title: "ramen near tokyo", - is_top_pick: true, - }, - ] + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=ramen&find_loc=tokyo", + title: "ramen near tokyo", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] "#]], ), ( @@ -2769,13 +2891,26 @@ mod tests { limit: None, }, expect![[r#" - [ - Yelp { - url: "https://www.yelp.com/search?find_desc=ramen&find_loc=San+Francisco", - title: "ramen in San Francisco", - is_top_pick: true, - }, - ] + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=ramen&find_loc=San+Francisco", + title: "ramen in San Francisco", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] "#]], ), ( @@ -2786,13 +2921,26 @@ mod tests { limit: None, }, expect![[r#" - [ - Yelp { - url: "https://www.yelp.com/search?find_desc=ramen", - title: "ramen in", - is_top_pick: true, - }, - ] + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=ramen", + title: "ramen in", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] "#]], ), ( @@ -2803,13 +2951,26 @@ mod tests { limit: None, }, expect![[r#" - [ - Yelp { - url: "https://www.yelp.com/search?find_desc=ramen+near+by", - title: "ramen near by", - is_top_pick: true, - }, - ] + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=ramen+near+by", + title: "ramen near by", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] "#]], ), ( @@ -2820,13 +2981,26 @@ mod tests { limit: None, }, expect![[r#" - [ - Yelp { - url: "https://www.yelp.com/search?find_desc=ramen+near+me", - title: "ramen near me", - is_top_pick: true, - }, - ] + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=ramen+near+me", + title: "ramen near me", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] "#]], ), ( @@ -2848,13 +3022,26 @@ mod tests { limit: None, }, expect![[r#" - [ - Yelp { - url: "https://www.yelp.com/search?find_desc=ramen", - title: "ramen", - is_top_pick: true, - }, - ] + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=ramen", + title: "ramen", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] "#]], ), ( @@ -2865,16 +3052,29 @@ mod tests { limit: None, }, expect![[r#" - [ - Yelp { - url: "https://www.yelp.com/search?find_desc=012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", - title: "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", - is_top_pick: true, - }, - ] - "#]], - ), - ( + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", + title: "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] + "#]], + ), + ( "keyword = over chars; Yelp only", SuggestionQuery { keyword: "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789Z".into(), @@ -2926,13 +3126,26 @@ mod tests { limit: None, }, expect![[r#" - [ - Yelp { - url: "https://www.yelp.com/search?find_desc=ramen", - title: "yelp ramen", - is_top_pick: true, - }, - ] + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=ramen", + title: "ramen", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] "#]], ), ( @@ -2943,13 +3156,26 @@ mod tests { limit: None, }, expect![[r#" - [ - Yelp { - url: "https://www.yelp.com/search?find_desc=ramen", - title: "yelp keyword ramen", - is_top_pick: true, - }, - ] + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=ramen", + title: "ramen", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] "#]], ), ( @@ -2960,13 +3186,26 @@ mod tests { limit: None, }, expect![[r#" - [ - Yelp { - url: "https://www.yelp.com/search?find_desc=ramen&find_loc=tokyo", - title: "ramen in tokyo yelp", - is_top_pick: true, - }, - ] + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=ramen&find_loc=tokyo", + title: "ramen in tokyo", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] "#]], ), ( @@ -2977,13 +3216,26 @@ mod tests { limit: None, }, expect![[r#" - [ - Yelp { - url: "https://www.yelp.com/search?find_desc=ramen&find_loc=tokyo", - title: "ramen in tokyo yelp keyword", - is_top_pick: true, - }, - ] + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=ramen&find_loc=tokyo", + title: "ramen in tokyo", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] "#]], ), ( @@ -2994,13 +3246,26 @@ mod tests { limit: None, }, expect![[r#" - [ - Yelp { - url: "https://www.yelp.com/search?find_desc=ramen", - title: "yelp ramen yelp", - is_top_pick: true, - }, - ] + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=ramen", + title: "ramen", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] "#]], ), ( @@ -3021,54 +3286,205 @@ mod tests { providers: vec![SuggestionProvider::Yelp], limit: None, }, + expect![[r#" + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=Spicy+Ramen", + title: "Spicy Ramen", + subject_exact_match: false, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] + "#]], + ), + ( + "keyword = `BeSt Ramen`; Yelp only", + SuggestionQuery { + keyword: "BeSt Ramen".into(), + providers: vec![SuggestionProvider::Yelp], + limit: None, + }, + expect![[r#" + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=BeSt+Ramen", + title: "BeSt Ramen", + subject_exact_match: true, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] + "#]], + ), + ( + "keyword = `BeSt Spicy R`; Yelp only", + SuggestionQuery { + keyword: "BeSt Spicy R".into(), + providers: vec![SuggestionProvider::Yelp], + limit: None, + }, + expect![[r#" + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=BeSt+Spicy+Ramen", + title: "BeSt Spicy Ramen", + subject_exact_match: false, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] + "#]], + ), + ( + "keyword = `BeSt R`; Yelp only", + SuggestionQuery { + keyword: "BeSt R".into(), + providers: vec![SuggestionProvider::Yelp], + limit: None, + }, + expect![[r#" + [] + "#]], + ), + ( + "keyword = `r`; Yelp only", + SuggestionQuery { + keyword: "r".into(), + providers: vec![SuggestionProvider::Yelp], + limit: None, + }, + expect![[r#" + [] + "#]], + ), + ( + "keyword = `ra`; Yelp only", + SuggestionQuery { + keyword: "ra".into(), + providers: vec![SuggestionProvider::Yelp], + limit: None, + }, expect![[r#" [ Yelp { - url: "https://www.yelp.com/search?find_desc=Spicy+Ramen", - title: "Spicy Ramen", - is_top_pick: false, + url: "https://www.yelp.com/search?find_desc=rats", + title: "rats", + subject_exact_match: false, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), }, ] "#]], ), ( - "keyword = `BeSt Ramen`; Yelp only", + "keyword = `ram`; Yelp only", SuggestionQuery { - keyword: "BeSt Ramen".into(), + keyword: "ram".into(), providers: vec![SuggestionProvider::Yelp], limit: None, }, expect![[r#" [ Yelp { - url: "https://www.yelp.com/search?find_desc=BeSt+Ramen", - title: "BeSt Ramen", - is_top_pick: true, + url: "https://www.yelp.com/search?find_desc=ramen", + title: "ramen", + subject_exact_match: false, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), }, ] "#]], ), ( - "keyword = `BeSt Spicy R`; Yelp only", + "keyword = `rac`; Yelp only", SuggestionQuery { - keyword: "BeSt Spicy R".into(), + keyword: "rac".into(), providers: vec![SuggestionProvider::Yelp], limit: None, }, expect![[r#" [ Yelp { - url: "https://www.yelp.com/search?find_desc=BeSt+Spicy+Ramen", - title: "BeSt Spicy Ramen", - is_top_pick: false, + url: "https://www.yelp.com/search?find_desc=raccoon", + title: "raccoon", + subject_exact_match: false, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), }, ] "#]], ), ( - "keyword = `BeSt R`; Yelp only", + "keyword = `best r`; Yelp only", SuggestionQuery { - keyword: "BeSt R".into(), + keyword: "best r".into(), providers: vec![SuggestionProvider::Yelp], limit: None, }, @@ -3076,19 +3492,49 @@ mod tests { [] "#]], ), - ]; - for (what, query, expect) in table { - expect.assert_debug_eq( - &store - .query(query) - .with_context(|| format!("Couldn't query store for {}", what))?, - ); - } - - Ok(()) - } - - // Tests querying amp wikipedia + ( + "keyword = `best ra`; Yelp only", + SuggestionQuery { + keyword: "best ra".into(), + providers: vec![SuggestionProvider::Yelp], + limit: None, + }, + expect![[r#" + [ + Yelp { + url: "https://www.yelp.com/search?find_desc=best+rats", + title: "best rats", + subject_exact_match: false, + icon: Some( + [ + 121, + 101, + 108, + 112, + 45, + 105, + 99, + 111, + 110, + ], + ), + }, + ] + "#]], + ), + ]; + for (what, query, expect) in table { + expect.assert_debug_eq( + &store + .query(query) + .with_context(|| format!("Couldn't query store for {}", what))?, + ); + } + + Ok(()) + } + + // Tests querying amp wikipedia #[test] fn query_with_multiple_providers_and_diff_scores() -> anyhow::Result<()> { before_each(); @@ -3411,6 +3857,398 @@ mod tests { Ok(()) } + + // Tests querying multiple suggestions with multiple keywords with same prefix keyword + #[test] + fn query_with_multiple_suggestions_with_same_prefix() -> anyhow::Result<()> { + before_each(); + + let snapshot = Snapshot::with_records(json!([{ + "id": "data-1", + "type": "amo-suggestions", + "last_modified": 15, + "attachment": { + "filename": "data-1.json", + "mimetype": "application/json", + "location": "data-1.json", + "hash": "", + "size": 0, + }, + }, { + "id": "data-2", + "type": "pocket-suggestions", + "last_modified": 15, + "attachment": { + "filename": "data-2.json", + "mimetype": "application/json", + "location": "data-2.json", + "hash": "", + "size": 0, + }, + }, { + "id": "icon-3", + "type": "icon", + "last_modified": 25, + "attachment": { + "filename": "icon-3.png", + "mimetype": "image/png", + "location": "icon-3.png", + "hash": "", + "size": 0, + }, + }]))? + .with_data( + "data-1.json", + json!([ + { + "description": "amo suggestion", + "url": "https://addons.mozilla.org/en-US/firefox/addon/example", + "guid": "{b9db16a4-6edc-47ec-a1f4-b86292ed211d}", + "keywords": ["relay", "spam", "masking email", "masking emails", "masking accounts", "alias" ], + "title": "Firefox Relay", + "icon": "https://addons.mozilla.org/user-media/addon_icons/2633/2633704-64.png?modified=2c11a80b", + "rating": "4.9", + "number_of_ratings": 888, + "score": 0.25 + } + ]), + )? + .with_data( + "data-2.json", + json!([ + { + "description": "pocket suggestion", + "url": "https://getpocket.com/collections/its-not-just-burnout-how-grind-culture-failed-women", + "lowConfidenceKeywords": ["soft life", "soft living", "soft work", "workaholism", "toxic work culture"], + "highConfidenceKeywords": ["burnout women", "grind culture", "women burnout", "soft lives"], + "title": "‘It’s Not Just Burnout:’ How Grind Culture Fails Women", + "score": 0.05 + } + ]), + )? + .with_icon("icon-3.png", "also-an-icon".as_bytes().into()); + + let store = unique_test_store(SnapshotSettingsClient::with_snapshot(snapshot)); + + store.ingest(SuggestIngestionConstraints::default())?; + + let table = [ + ( + "keyword = `soft li`; pocket", + SuggestionQuery { + keyword: "soft li".into(), + providers: vec![SuggestionProvider::Pocket], + limit: None, + }, + expect![[r#" + [ + Pocket { + title: "‘It’s Not Just Burnout:’ How Grind Culture Fails Women", + url: "https://getpocket.com/collections/its-not-just-burnout-how-grind-culture-failed-women", + score: 0.05, + is_top_pick: false, + }, + ] + "#]], + ), + ( + "keyword = `soft lives`; pocket", + SuggestionQuery { + keyword: "soft lives".into(), + providers: vec![SuggestionProvider::Pocket], + limit: None, + }, + expect![[r#" + [ + Pocket { + title: "‘It’s Not Just Burnout:’ How Grind Culture Fails Women", + url: "https://getpocket.com/collections/its-not-just-burnout-how-grind-culture-failed-women", + score: 0.05, + is_top_pick: true, + }, + ] + "#]], + ), + ( + "keyword = `masking `; amo provider", + SuggestionQuery { + keyword: "masking ".into(), + providers: vec![SuggestionProvider::Amo], + limit: None, + }, + expect![[r#" + [ + Amo { + title: "Firefox Relay", + url: "https://addons.mozilla.org/en-US/firefox/addon/example", + icon_url: "https://addons.mozilla.org/user-media/addon_icons/2633/2633704-64.png?modified=2c11a80b", + description: "amo suggestion", + rating: Some( + "4.9", + ), + number_of_ratings: 888, + guid: "{b9db16a4-6edc-47ec-a1f4-b86292ed211d}", + score: 0.25, + }, + ] + "#]], + ), + ]; + for (what, query, expect) in table { + expect.assert_debug_eq( + &store + .query(query) + .with_context(|| format!("Couldn't query store for {}", what))?, + ); + } + + Ok(()) + } + + // Tests querying multiple suggestions with multiple keywords with same prefix keyword + #[test] + fn query_with_amp_mobile_provider() -> anyhow::Result<()> { + before_each(); + + let snapshot = Snapshot::with_records(json!([{ + "id": "data-1", + "type": "amp-mobile-suggestions", + "last_modified": 15, + "attachment": { + "filename": "data-1.json", + "mimetype": "application/json", + "location": "data-1.json", + "hash": "", + "size": 0, + }, + }, { + "id": "data-2", + "type": "data", + "last_modified": 15, + "attachment": { + "filename": "data-2.json", + "mimetype": "application/json", + "location": "data-2.json", + "hash": "", + "size": 0, + }, + }, { + "id": "icon-3", + "type": "icon", + "last_modified": 25, + "attachment": { + "filename": "icon-3.png", + "mimetype": "image/png", + "location": "icon-3.png", + "hash": "", + "size": 0, + }, + }]))? + .with_data( + "data-1.json", + json!([ + { + "id": 0, + "advertiser": "Good Place Eats", + "iab_category": "8 - Food & Drink", + "keywords": ["la", "las", "lasa", "lasagna", "lasagna come out tomorrow"], + "title": "Mobile - Lasagna Come Out Tomorrow", + "url": "https://www.lasagna.restaurant", + "icon": "3", + "impression_url": "https://example.com/impression_url", + "click_url": "https://example.com/click_url", + "score": 0.3 + } + ]), + )? + .with_data( + "data-2.json", + json!([ + { + "id": 0, + "advertiser": "Good Place Eats", + "iab_category": "8 - Food & Drink", + "keywords": ["la", "las", "lasa", "lasagna", "lasagna come out tomorrow"], + "title": "Desktop - Lasagna Come Out Tomorrow", + "url": "https://www.lasagna.restaurant", + "icon": "3", + "impression_url": "https://example.com/impression_url", + "click_url": "https://example.com/click_url", + "score": 0.2 + } + ]), + )? + .with_icon("icon-3.png", "also-an-icon".as_bytes().into()); + + let store = unique_test_store(SnapshotSettingsClient::with_snapshot(snapshot)); + + store.ingest(SuggestIngestionConstraints::default())?; + + let table = [ + ( + "keyword = `las`; Amp Mobile", + SuggestionQuery { + keyword: "las".into(), + providers: vec![SuggestionProvider::AmpMobile], + limit: None, + }, + expect![[r#" + [ + Amp { + title: "Mobile - Lasagna Come Out Tomorrow", + url: "https://www.lasagna.restaurant", + raw_url: "https://www.lasagna.restaurant", + icon: Some( + [ + 97, + 108, + 115, + 111, + 45, + 97, + 110, + 45, + 105, + 99, + 111, + 110, + ], + ), + full_keyword: "lasagna", + block_id: 0, + advertiser: "Good Place Eats", + iab_category: "8 - Food & Drink", + impression_url: "https://example.com/impression_url", + click_url: "https://example.com/click_url", + raw_click_url: "https://example.com/click_url", + score: 0.3, + }, + ] + "#]], + ), + ( + "keyword = `las`; Amp", + SuggestionQuery { + keyword: "las".into(), + providers: vec![SuggestionProvider::Amp], + limit: None, + }, + expect![[r#" + [ + Amp { + title: "Desktop - Lasagna Come Out Tomorrow", + url: "https://www.lasagna.restaurant", + raw_url: "https://www.lasagna.restaurant", + icon: Some( + [ + 97, + 108, + 115, + 111, + 45, + 97, + 110, + 45, + 105, + 99, + 111, + 110, + ], + ), + full_keyword: "lasagna", + block_id: 0, + advertiser: "Good Place Eats", + iab_category: "8 - Food & Drink", + impression_url: "https://example.com/impression_url", + click_url: "https://example.com/click_url", + raw_click_url: "https://example.com/click_url", + score: 0.2, + }, + ] + "#]], + ), + ( + "keyword = `las `; amp and amp mobile", + SuggestionQuery { + keyword: "las".into(), + providers: vec![SuggestionProvider::Amp, SuggestionProvider::AmpMobile], + limit: None, + }, + expect![[r#" + [ + Amp { + title: "Mobile - Lasagna Come Out Tomorrow", + url: "https://www.lasagna.restaurant", + raw_url: "https://www.lasagna.restaurant", + icon: Some( + [ + 97, + 108, + 115, + 111, + 45, + 97, + 110, + 45, + 105, + 99, + 111, + 110, + ], + ), + full_keyword: "lasagna", + block_id: 0, + advertiser: "Good Place Eats", + iab_category: "8 - Food & Drink", + impression_url: "https://example.com/impression_url", + click_url: "https://example.com/click_url", + raw_click_url: "https://example.com/click_url", + score: 0.3, + }, + Amp { + title: "Desktop - Lasagna Come Out Tomorrow", + url: "https://www.lasagna.restaurant", + raw_url: "https://www.lasagna.restaurant", + icon: Some( + [ + 97, + 108, + 115, + 111, + 45, + 97, + 110, + 45, + 105, + 99, + 111, + 110, + ], + ), + full_keyword: "lasagna", + block_id: 0, + advertiser: "Good Place Eats", + iab_category: "8 - Food & Drink", + impression_url: "https://example.com/impression_url", + click_url: "https://example.com/click_url", + raw_click_url: "https://example.com/click_url", + score: 0.2, + }, + ] + "#]], + ), + ]; + for (what, query, expect) in table { + expect.assert_debug_eq( + &store + .query(query) + .with_context(|| format!("Couldn't query store for {}", what))?, + ); + } + + Ok(()) + } + /// Tests ingesting malformed Remote Settings records that we understand, /// but that are missing fields, or aren't in the format we expect. #[test] @@ -3489,10 +4327,10 @@ mod tests { UnparsableRecords( { "clippy-2": UnparsableRecord { - schema_version: 12, + schema_version: 13, }, "fancy-new-suggestions-1": UnparsableRecord { - schema_version: 12, + schema_version: 13, }, }, ), @@ -3558,10 +4396,10 @@ mod tests { UnparsableRecords( { "clippy-2": UnparsableRecord { - schema_version: 12, + schema_version: 13, }, "fancy-new-suggestions-1": UnparsableRecord { - schema_version: 12, + schema_version: 13, }, }, ), @@ -3665,10 +4503,10 @@ mod tests { UnparsableRecords( { "clippy-2": UnparsableRecord { - schema_version: 12, + schema_version: 13, }, "fancy-new-suggestions-1": UnparsableRecord { - schema_version: 12, + schema_version: 13, }, }, ), @@ -3754,7 +4592,7 @@ mod tests { UnparsableRecords( { "invalid-attachment": UnparsableRecord { - schema_version: 12, + schema_version: 13, }, }, ), diff --git a/third_party/rust/suggest/src/suggest.udl b/third_party/rust/suggest/src/suggest.udl index b63645dac3d9b..7f420b67d92a5 100644 --- a/third_party/rust/suggest/src/suggest.udl +++ b/third_party/rust/suggest/src/suggest.udl @@ -30,6 +30,7 @@ enum SuggestionProvider { "Yelp", "Mdn", "Weather", + "AmpMobile", }; [Enum] @@ -73,7 +74,8 @@ interface Suggestion { Yelp( string url, string title, - boolean is_top_pick + boolean subject_exact_match, + sequence? icon ); Mdn( string title, diff --git a/third_party/rust/suggest/src/suggestion.rs b/third_party/rust/suggest/src/suggestion.rs index 68135d65d8967..2bb2c0ed711d8 100644 --- a/third_party/rust/suggest/src/suggestion.rs +++ b/third_party/rust/suggest/src/suggestion.rs @@ -16,6 +16,11 @@ const TIMESTAMP_TEMPLATE: &str = "%YYYYMMDDHH%"; /// 2 bytes shorter than [`TIMESTAMP_TEMPLATE`]. const TIMESTAMP_LENGTH: usize = 10; +/// Suggestion Types for Amp +pub(crate) enum AmpSuggestionType { + Mobile, + Desktop, +} /// A suggestion from the database to show in the address bar. #[derive(Clone, Debug, PartialEq)] pub enum Suggestion { @@ -58,7 +63,8 @@ pub enum Suggestion { Yelp { url: String, title: String, - is_top_pick: bool, + subject_exact_match: bool, + icon: Option>, }, Mdn { title: String, diff --git a/third_party/rust/suggest/src/yelp.rs b/third_party/rust/suggest/src/yelp.rs index f3c385250c29f..24e95c2d879db 100644 --- a/third_party/rust/suggest/src/yelp.rs +++ b/third_party/rust/suggest/src/yelp.rs @@ -47,8 +47,8 @@ const MAX_QUERY_LENGTH: usize = 150; /// "keyword=:modifier" (please see is_modifier()), define this how many words we should check. const MAX_MODIFIER_WORDS_NUMBER: usize = 2; -/// The threshold that enables prefix-match. -const PREFIX_MATCH_THRESHOLD: usize = 6; +/// At least this many characters must be typed for a subject to be matched. +const SUBJECT_PREFIX_MATCH_THRESHOLD: usize = 2; impl<'a> SuggestDao<'a> { /// Inserts the suggestions for Yelp attachment into the database. @@ -116,6 +116,15 @@ impl<'a> SuggestDao<'a> { )?; } + self.scope.err_if_interrupted()?; + self.conn.execute_cached( + "INSERT INTO yelp_custom_details(record_id, icon_id) VALUES(:record_id, :icon_id)", + named_params! { + ":record_id": record_id.as_str(), + ":icon_id": suggestion.icon_id + }, + )?; + Ok(()) } @@ -134,6 +143,7 @@ impl<'a> SuggestDao<'a> { let Some((subject, subject_exact_match)) = self.find_subject(query_string)? else { return Ok(vec![]); }; + let icon = self.fetch_icon()?; let builder = SuggestionBuilder { subject: &subject, subject_exact_match, @@ -142,14 +152,13 @@ impl<'a> SuggestDao<'a> { location_sign: None, location: None, need_location: false, - pre_yelp_modifier: None, - post_yelp_modifier: None, + icon, }; return Ok(vec![builder.into()]); } // Find the yelp keyword modifier and remove them from the query. - let (query_without_yelp_modifiers, pre_yelp_modifier, post_yelp_modifier) = + let (query_without_yelp_modifiers, _, _) = self.find_modifiers(query_string, Modifier::Yelp, Modifier::Yelp)?; // Find the location sign and the location. @@ -173,6 +182,8 @@ impl<'a> SuggestDao<'a> { let Some((subject, subject_exact_match)) = self.find_subject(&subject_candidate)? else { return Ok(vec![]); }; + + let icon = self.fetch_icon()?; let builder = SuggestionBuilder { subject: &subject, subject_exact_match, @@ -181,12 +192,36 @@ impl<'a> SuggestDao<'a> { location_sign, location, need_location, - pre_yelp_modifier, - post_yelp_modifier, + icon, }; Ok(vec![builder.into()]) } + /// Fetch the icon for Yelp suggestions. + /// + /// Note that there should be only one record in `yelp_custom_details` + /// as all the Yelp assets are stored in the attachment of a single record + /// on Remote Settings. The following query will perform a table scan against + /// `yelp_custom_details` followed by an index search against `icons`, which + /// should be fine since there is only one record in the first table. + fn fetch_icon(&self) -> Result>> { + Ok(self.conn.try_query_one( + r#" + SELECT + i.data + FROM + yelp_custom_details y + JOIN + icons i + ON y.icon_id = i.id + LIMIT + 1 + "#, + (), + true, + )?) + } + /// Find the location information from the given query string. /// It returns the location tuple as follows: /// ( @@ -312,9 +347,9 @@ impl<'a> SuggestDao<'a> { return Ok(None); } - // If the length of subject candidate is less than PREFIX_MATCH_THRESHOLD, - // should exact match. - if candidate.len() < PREFIX_MATCH_THRESHOLD { + // If the length of subject candidate is less than + // SUBJECT_PREFIX_MATCH_THRESHOLD, should exact match. + if candidate.len() < SUBJECT_PREFIX_MATCH_THRESHOLD { return Ok(if self.is_subject(candidate)? { Some((candidate.to_string(), true)) } else { @@ -325,7 +360,11 @@ impl<'a> SuggestDao<'a> { // Otherwise, apply prefix-match. Ok( match self.conn.query_row_and_then_cachable( - "SELECT keyword FROM yelp_subjects WHERE keyword BETWEEN :candidate AND :candidate || x'FFFF' ORDER BY LENGTH(keyword) ASC LIMIT 1", + "SELECT keyword + FROM yelp_subjects + WHERE keyword BETWEEN :candidate AND :candidate || x'FFFF' + ORDER BY LENGTH(keyword) ASC, keyword ASC + LIMIT 1", named_params! { ":candidate": candidate.to_lowercase(), }, @@ -334,10 +373,13 @@ impl<'a> SuggestDao<'a> { ) { Ok(keyword) => { debug_assert!(candidate.len() <= keyword.len()); - Some((format!("{}{}", candidate, &keyword[candidate.len()..]), candidate.len() == keyword.len())) - }, - Err(_) => None - } + Some(( + format!("{}{}", candidate, &keyword[candidate.len()..]), + candidate.len() == keyword.len(), + )) + } + Err(_) => None, + }, ) } @@ -385,8 +427,7 @@ struct SuggestionBuilder<'a> { location_sign: Option, location: Option, need_location: bool, - pre_yelp_modifier: Option, - post_yelp_modifier: Option, + icon: Option>, } impl<'a> From> for Suggestion { @@ -419,13 +460,11 @@ impl<'a> From> for Suggestion { url.push_str(¶meters.finish()); let title = [ - builder.pre_yelp_modifier.as_deref(), builder.pre_modifier.as_deref(), Some(builder.subject), builder.post_modifier.as_deref(), builder.location_sign.as_deref(), builder.location.as_deref(), - builder.post_yelp_modifier.as_deref(), ] .iter() .flatten() @@ -436,7 +475,8 @@ impl<'a> From> for Suggestion { Suggestion::Yelp { url, title, - is_top_pick: builder.subject_exact_match, + subject_exact_match: builder.subject_exact_match, + icon: builder.icon, } } } diff --git a/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustSuggest.sys.mjs b/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustSuggest.sys.mjs index 25c056f6036e1..23ba7e1a96754 100644 --- a/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustSuggest.sys.mjs +++ b/toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustSuggest.sys.mjs @@ -1252,12 +1252,14 @@ Suggestion.Yelp = class extends Suggestion{ constructor( url, title, - isTopPick + subjectExactMatch, + icon ) { super(); this.url = url; this.title = title; - this.isTopPick = isTopPick; + this.subjectExactMatch = subjectExactMatch; + this.icon = icon; } } Suggestion.Mdn = class extends Suggestion{ @@ -1331,7 +1333,8 @@ export class FfiConverterTypeSuggestion extends FfiConverterArrayBuffer { return new Suggestion.Yelp( FfiConverterString.read(dataStream), FfiConverterString.read(dataStream), - FfiConverterBool.read(dataStream) + FfiConverterBool.read(dataStream), + FfiConverterOptionalSequenceu8.read(dataStream) ); case 6: return new Suggestion.Mdn( @@ -1398,7 +1401,8 @@ export class FfiConverterTypeSuggestion extends FfiConverterArrayBuffer { dataStream.writeInt32(5); FfiConverterString.write(dataStream, value.url); FfiConverterString.write(dataStream, value.title); - FfiConverterBool.write(dataStream, value.isTopPick); + FfiConverterBool.write(dataStream, value.subjectExactMatch); + FfiConverterOptionalSequenceu8.write(dataStream, value.icon); return; } if (value instanceof Suggestion.Mdn) { @@ -1463,7 +1467,8 @@ export class FfiConverterTypeSuggestion extends FfiConverterArrayBuffer { if (value instanceof Suggestion.Yelp) { totalSize += FfiConverterString.computeSize(value.url); totalSize += FfiConverterString.computeSize(value.title); - totalSize += FfiConverterBool.computeSize(value.isTopPick); + totalSize += FfiConverterBool.computeSize(value.subjectExactMatch); + totalSize += FfiConverterOptionalSequenceu8.computeSize(value.icon); return totalSize; } if (value instanceof Suggestion.Mdn) { @@ -1497,6 +1502,7 @@ export const SuggestionProvider = { YELP: 5, MDN: 6, WEATHER: 7, + AMP_MOBILE: 8, }; Object.freeze(SuggestionProvider); @@ -1518,6 +1524,8 @@ export class FfiConverterTypeSuggestionProvider extends FfiConverterArrayBuffer return SuggestionProvider.MDN case 7: return SuggestionProvider.WEATHER + case 8: + return SuggestionProvider.AMP_MOBILE default: return new Error("Unknown SuggestionProvider variant"); } @@ -1552,6 +1560,10 @@ export class FfiConverterTypeSuggestionProvider extends FfiConverterArrayBuffer dataStream.writeInt32(7); return; } + if (value === SuggestionProvider.AMP_MOBILE) { + dataStream.writeInt32(8); + return; + } return new Error("Unknown SuggestionProvider variant"); } @@ -1560,7 +1572,7 @@ export class FfiConverterTypeSuggestionProvider extends FfiConverterArrayBuffer } static checkType(value) { - if (!Number.isInteger(value) || value < 1 || value > 7) { + if (!Number.isInteger(value) || value < 1 || value > 8) { throw new UniFFITypeError(`${value} is not a valid value for SuggestionProvider`); } }