Moved string matching to sqlite
Probably (definitely more efficient)
This commit is contained in:
@@ -9,6 +9,7 @@ edition = "2021"
|
|||||||
chrono = { version = "0.4.39", features = ["serde"] }
|
chrono = { version = "0.4.39", features = ["serde"] }
|
||||||
clap = { version = "4.5.42", features = ["derive"] }
|
clap = { version = "4.5.42", features = ["derive"] }
|
||||||
closestmatch = "0.1.2"
|
closestmatch = "0.1.2"
|
||||||
|
deunicode = "1.6.2"
|
||||||
dir_spec = "0.2.0"
|
dir_spec = "0.2.0"
|
||||||
rusqlite = "0.37.0"
|
rusqlite = "0.37.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
use deunicode::deunicode;
|
||||||
use rusqlite;
|
use rusqlite;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::SplitWhitespace;
|
|
||||||
|
|
||||||
use super::deser::ScryfallCard;
|
use super::deser::ScryfallCard;
|
||||||
use super::utils::get_local_cache_folder;
|
use super::utils::get_local_cache_folder;
|
||||||
@@ -46,13 +46,14 @@ pub fn update_db_with_file(file: PathBuf) -> bool {
|
|||||||
let tx = conn.transaction().unwrap();
|
let tx = conn.transaction().unwrap();
|
||||||
for card in ac {
|
for card in ac {
|
||||||
for word in card.name.split_whitespace() {
|
for word in card.name.split_whitespace() {
|
||||||
|
let word = deunicode(word);
|
||||||
let res = tx.execute(
|
let res = tx.execute(
|
||||||
"INSERT INTO magic_words (word) VALUES (?1)
|
"INSERT INTO magic_words (word) VALUES (?1)
|
||||||
ON CONFLICT (word) DO NOTHING;",
|
ON CONFLICT (word) DO NOTHING;",
|
||||||
[word.replace(",", "")],
|
[word.replace(",", "")],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let lowercase_name = card.name.to_lowercase();
|
let lowercase_name = deunicode(&card.name.to_lowercase());
|
||||||
let power_toughness = match card.power {
|
let power_toughness = match card.power {
|
||||||
Some(p) => Some(format!("{}/{}", p, card.toughness.unwrap())),
|
Some(p) => Some(format!("{}/{}", p, card.toughness.unwrap())),
|
||||||
None => None,
|
None => None,
|
||||||
@@ -107,6 +108,7 @@ impl fmt::Display for DbCard {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DbCard {
|
pub struct DbCard {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub lowercase_name: String,
|
||||||
pub type_line: String,
|
pub type_line: String,
|
||||||
pub oracle_text: Option<String>,
|
pub oracle_text: Option<String>,
|
||||||
pub power_toughness: Option<String>,
|
pub power_toughness: Option<String>,
|
||||||
@@ -125,11 +127,11 @@ pub fn get_card_by_name(name: &str, name_type: GetNameType) -> Option<DbCard> {
|
|||||||
let conn = rusqlite::Connection::open(sqlite_file).unwrap();
|
let conn = rusqlite::Connection::open(sqlite_file).unwrap();
|
||||||
let sql = match name_type {
|
let sql = match name_type {
|
||||||
GetNameType::Name => {
|
GetNameType::Name => {
|
||||||
"SELECT name, type_line, oracle_text, power_toughness, loyalty, mana_cost, scryfall_uri
|
"SELECT name, lowercase_name, type_line, oracle_text, power_toughness, loyalty, mana_cost, scryfall_uri
|
||||||
FROM cards WHERE name = (?1)"
|
FROM cards WHERE name = (?1)"
|
||||||
}
|
}
|
||||||
GetNameType::LowercaseName => {
|
GetNameType::LowercaseName => {
|
||||||
"SELECT name, type_line, oracle_text, power_toughness, loyalty, mana_cost, scryfall_uri
|
"SELECT name, lowercase_name, type_line, oracle_text, power_toughness, loyalty, mana_cost, scryfall_uri
|
||||||
FROM cards WHERE lowercase_name = (?1)"
|
FROM cards WHERE lowercase_name = (?1)"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -138,17 +140,48 @@ pub fn get_card_by_name(name: &str, name_type: GetNameType) -> Option<DbCard> {
|
|||||||
match rows.next().unwrap() {
|
match rows.next().unwrap() {
|
||||||
Some(row) => Some(DbCard {
|
Some(row) => Some(DbCard {
|
||||||
name: row.get(0).unwrap(),
|
name: row.get(0).unwrap(),
|
||||||
type_line: row.get(1).unwrap(),
|
lowercase_name: row.get(1).unwrap(),
|
||||||
oracle_text: row.get(2).unwrap(),
|
type_line: row.get(2).unwrap(),
|
||||||
power_toughness: row.get(3).unwrap(),
|
oracle_text: row.get(3).unwrap(),
|
||||||
loyalty: row.get(4).unwrap(),
|
power_toughness: row.get(4).unwrap(),
|
||||||
mana_cost: row.get(5).unwrap(),
|
loyalty: row.get(5).unwrap(),
|
||||||
scryfall_uri: row.get(6).unwrap(),
|
mana_cost: row.get(6).unwrap(),
|
||||||
|
scryfall_uri: row.get(7).unwrap(),
|
||||||
}),
|
}),
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn find_matching_cards(name: &str) -> Vec<DbCard> {
|
||||||
|
let sqlite_file = get_local_data_sqlite_file();
|
||||||
|
let conn = rusqlite::Connection::open(sqlite_file).unwrap();
|
||||||
|
// There must be something better than this - although I don't think it's possible with a str
|
||||||
|
let mut name = name.to_string();
|
||||||
|
name.push('%');
|
||||||
|
name.insert(0, '%');
|
||||||
|
let mut stmt = conn
|
||||||
|
.prepare(
|
||||||
|
"SELECT name, lowercase_name, type_line, oracle_text, power_toughness, loyalty, mana_cost, scryfall_uri
|
||||||
|
FROM cards WHERE lowercase_name LIKE (?1)",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
stmt.query_map([name], |row| {
|
||||||
|
Ok(DbCard {
|
||||||
|
name: row.get(0).unwrap(),
|
||||||
|
lowercase_name: row.get(1).unwrap(),
|
||||||
|
type_line: row.get(2).unwrap(),
|
||||||
|
oracle_text: row.get(3).unwrap(),
|
||||||
|
power_toughness: row.get(4).unwrap(),
|
||||||
|
loyalty: row.get(5).unwrap(),
|
||||||
|
mana_cost: row.get(6).unwrap(),
|
||||||
|
scryfall_uri: row.get(7).unwrap(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
.filter_map(|res| res.ok())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
const CREATE_CARDS_TABLE_SQL: &str = "
|
const CREATE_CARDS_TABLE_SQL: &str = "
|
||||||
CREATE TABLE cards (
|
CREATE TABLE cards (
|
||||||
name TEXT NOT NULL UNIQUE,
|
name TEXT NOT NULL UNIQUE,
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ pub use crate::deser::ScryfallCard;
|
|||||||
|
|
||||||
mod db;
|
mod db;
|
||||||
pub use db::{
|
pub use db::{
|
||||||
get_all_card_names, get_all_lowercase_card_names, get_card_by_name, init_db,
|
find_matching_cards, get_all_card_names, get_all_lowercase_card_names, get_card_by_name,
|
||||||
update_db_with_file, GetNameType,
|
init_db, update_db_with_file, GetNameType,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use scryfall_deser::find_matching_cards;
|
||||||
use scryfall_deser::get_all_lowercase_card_names;
|
use scryfall_deser::get_all_lowercase_card_names;
|
||||||
use scryfall_deser::get_card_by_name;
|
use scryfall_deser::get_card_by_name;
|
||||||
use scryfall_deser::get_local_cache_folder;
|
use scryfall_deser::get_local_cache_folder;
|
||||||
@@ -55,26 +56,19 @@ fn main() {
|
|||||||
}
|
}
|
||||||
search_string.pop();
|
search_string.pop();
|
||||||
|
|
||||||
// This section should be replaced with a SQL command
|
let matching_cards = find_matching_cards(&search_string);
|
||||||
let cards = get_all_lowercase_card_names();
|
|
||||||
|
|
||||||
let mut matching_cards = Vec::new();
|
|
||||||
for card in cards {
|
|
||||||
if card.contains(&search_string) {
|
|
||||||
matching_cards.push(card.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dbg!(&matching_cards);
|
dbg!(&matching_cards);
|
||||||
|
|
||||||
if matching_cards.is_empty() {
|
if matching_cards.is_empty() {
|
||||||
// Do some distance checking stuff
|
// Do some distance checking stuff
|
||||||
} else if matching_cards.len() == 1 {
|
} else if matching_cards.len() == 1 {
|
||||||
let card = get_card_by_name(&matching_cards[0], GetNameType::LowercaseName);
|
let card = get_card_by_name(&matching_cards[0].name, GetNameType::LowercaseName);
|
||||||
dbg!(card);
|
dbg!(card);
|
||||||
} else {
|
} else {
|
||||||
for card in matching_cards {
|
for card in matching_cards {
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
get_card_by_name(&card, GetNameType::LowercaseName)
|
get_card_by_name(&card.lowercase_name, GetNameType::LowercaseName)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.name
|
.name
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user