» Rust:使用Rocket构建REST API » 2. 开发 » 2.11 搜索

搜索

在 RESTful API 中实现搜索功能,你需要先定义一个处理搜索参数的路由,然后在底层数据库中使用前缀或全文索引进行搜索,再返回搜索结果。

在 MySQL 中搜索

添加 keyword 参数到 BookManager.get_booksdomain/gateway/book_manager.rs:

@@ -7,5 +7,5 @@ pub trait BookManager: Send + Sync {
     fn update_book(&self, id: u32, b: &model::Book) -> Result<(), Box<dyn Error>>;
     fn delete_book(&self, id: u32) -> Result<(), Box<dyn Error>>;
     fn get_book(&self, id: u32) -> Result<Option<model::Book>, Box<dyn Error>>;
-    fn get_books(&self, offset: u32) -> Result<Vec<model::Book>, Box<dyn Error>>;
+    fn get_books(&self, offset: u32, keyword: &str) -> Result<Vec<model::Book>, Box<dyn Error>>;
 }

添加搜索功能,infrastructure/database/mysql.rs:

@@ -94,10 +94,20 @@ impl BookManager for MySQLPersistence {
         Ok(books.first().cloned())
     }
 
-    fn get_books(&self, offset: u32) -> Result<Vec<model::Book>, Box<dyn Error>> {
+    fn get_books(&self, offset: u32, keyword: &str) -> Result<Vec<model::Book>, Box<dyn Error>> {
         let mut conn = self.pool.get_conn()?;
+        let mut query = format!("SELECT * FROM books LIMIT {}, {}", offset, self.page_size);
+        if !keyword.is_empty() {
+            // Warning: don't do SQL concatenation in production code. It leads to SQL Injection Vulnerabilities.
+            // Here is for tutorial brevity.
+            let term = format!("%{}%", keyword.replace("'", ""));
+            query = format!(
+                "SELECT * FROM books WHERE title LIKE '{}' OR author LIKE '{}' LIMIT {}, {}",
+                term, term, offset, self.page_size
+            );
+        }
         let books = conn.query_map(
-            format!("SELECT * FROM books LIMIT {}, {}", offset, self.page_size),
+            query,
             |(
                 id,
                 title,

注意

在生产数据库中不要使用 LIKE %keyword%。与其他搜索方法相比,这种方法效率较低,特别是在处理大型数据集时。 LIKE %keyword% 语法强制数据库执行全表扫描或索引扫描,这可能会很慢,特别是在大型表上。它不能有效地利用索引,导致查询性能较慢。
如果你的用例允许,考虑使用前缀搜索(例如,LIKE keyword%)。与子字符串搜索相比,这可以更有效地利用索引。

调整 application/executor/book_operator.rs:

@@ -30,14 +30,23 @@ impl BookOperator {
         self.book_manager.get_book(id)
     }
 
-    pub fn get_books(&self, offset: u32) -> Result<Vec<model::Book>, Box<dyn std::error::Error>> {
+    pub fn get_books(
+        &self,
+        offset: u32,
+        query: &str,
+    ) -> Result<Vec<model::Book>, Box<dyn std::error::Error>> {
+        // Search results, don't cache it
+        if !query.is_empty() {
+            return self.book_manager.get_books(offset, query);
+        }
+        // Normal list of results
         let k = format!("{}-{}", BOOKS_KEY, offset);
         let raw_value = self.cache_helper.load(&k)?;
         if let Some(v) = raw_value {
             let cached_books = serde_json::from_str(&v)?;
             Ok(cached_books)
         } else {
-            let fetched_books = self.book_manager.get_books(offset)?;
+            let fetched_books = self.book_manager.get_books(offset, "")?;
             let v = serde_json::to_string(&fetched_books)?;
             self.cache_helper.save(&k, &v)?;
             Ok(fetched_books)

传入 query 参数,adapter/router.rs:

@@ -24,12 +24,16 @@ pub fn health_check() -> content::RawJson<&'static str> {
     content::RawJson("{\"status\":\"ok\"}")
 }
 
-#[get("/books?<o>")]
+#[get("/books?<o>&<q>")]
 pub fn get_books(
     rest_handler: &rocket::State<RestHandler>,
     o: Option<u32>,
+    q: Option<&str>,
 ) -> Result<Json<Vec<model::Book>>, status::Custom<Json<ErrorResponse>>> {
-    match rest_handler.book_operator.get_books(o.unwrap_or(0)) {
+    match rest_handler
+        .book_operator
+        .get_books(o.unwrap_or(0), q.unwrap_or(""))
+    {
         Ok(books) => Ok(Json(books)),
         Err(err) => Err(status::Custom(
             Status::InternalServerError,

保存修改,重启 api 服务器。

使用 curl 搜索图书

搜索图书

curl -X GET "http://localhost:8000/books?q=and"

Result:

[
  {
    "id": 7,
    "title": "Pride and Prejudice",
    "author": "Jane Austen",
    "published_at": "1813-01-28",
    "description": "A classic novel exploring the themes of love, reputation, and social class in Georgian England.",
    "isbn": "9780486284736",
    "total_pages": 279,
    "created_at": "2024-03-02 05:48:25",
    "updated_at": "2024-03-02 05:48:25"
  },
  {
    "id": 13,
    "title": "War and Peace",
    "author": "Leo Tolstoy",
    "published_at": "1869-01-01",
    "description": "A novel depicting the Napoleonic era in Russia, exploring themes of love, war, and historical determinism.",
    "isbn": "9781400079988",
    "total_pages": 1392,
    "created_at": "2024-03-02 05:48:25",
    "updated_at": "2024-03-02 05:48:25"
  },
  {
    "id": 14,
    "title": "Alice’s Adventures in Wonderland",
    "author": "Lewis Carroll",
    "published_at": "1865-11-26",
    "description": "A children’s novel featuring a young girl named Alice who falls into a fantastical world populated by peculiar creatures.",
    "isbn": "9780141439761",
    "total_pages": 192,
    "created_at": "2024-03-02 05:48:25",
    "updated_at": "2024-03-02 05:48:25"
  }
]

搜索图书并带一个偏移

curl -X GET "http://localhost:8000/books?q=the&o=4"

Result:

[
  {
    "id": 12,
    "title": "The Adventures of Huckleberry Finn",
    "author": "Mark Twain",
    "published_at": "1884-12-10",
    "description": "A novel depicting the journey of a young boy and an escaped slave along the Mississippi River.",
    "isbn": "9780486280615",
    "total_pages": 366,
    "created_at": "2024-03-02 05:48:25",
    "updated_at": "2024-03-02 05:48:25"
  },
  {
    "id": 15,
    "title": "The Odyssey",
    "author": "Homer",
    "published_at": "8th Century BC",
    "description": "An ancient Greek epic poem attributed to Homer, detailing the journey of Odysseus after the Trojan War.",
    "isbn": "9780140268867",
    "total_pages": 541,
    "created_at": "2024-03-02 05:48:25",
    "updated_at": "2024-03-02 05:48:25"
  }
]

在 mongoDB 中搜索

添加 keyword 参数到 ReviewManager.get_reviews_of_bookdomain/gateway/review_manager.rs:

@@ -7,5 +7,9 @@ pub trait ReviewManager: Send + Sync {
     fn update_review(&self, id: &str, b: &model::Review) -> Result<(), Box<dyn Error>>;
     fn delete_review(&self, id: &str) -> Result<(), Box<dyn Error>>;
     fn get_review(&self, id: &str) -> Result<Option<model::Review>, Box<dyn Error>>;
-    fn get_reviews_of_book(&self, book_id: u32) -> Result<Vec<model::Review>, Box<dyn Error>>;
+    fn get_reviews_of_book(
+        &self,
+        book_id: u32,
+        keyword: &str,
+    ) -> Result<Vec<model::Review>, Box<dyn Error>>;
 }

添加搜索功能,infrastructure/database/mongo.rs:

@@ -1,7 +1,7 @@
 use std::error::Error;
 
 use mongodb::{
-    bson::{doc, oid::ObjectId, DateTime},
+    bson::{doc, oid::ObjectId, DateTime, Regex},
     error::Error as MongoError,
     sync::{Client, Collection},
 };
@@ -68,8 +68,25 @@ impl ReviewManager for MongoPersistence {
         }
     }
 
-    fn get_reviews_of_book(&self, book_id: u32) -> Result<Vec<Review>, Box<dyn Error>> {
-        let filter = doc! { "book_id": book_id };
+    fn get_reviews_of_book(
+        &self,
+        book_id: u32,
+        keyword: &str,
+    ) -> Result<Vec<Review>, Box<dyn Error>> {
+        let mut filter = doc! { "book_id": book_id };
+        if !keyword.is_empty() {
+            filter = doc! {
+                "$and": [
+                    {
+                        "$or": [
+                            {"title": Regex{pattern: keyword.to_string(), options: String::from("i")}},
+                            {"content": Regex{pattern: keyword.to_string(), options: String::from("i")}},
+                        ]
+                    },
+                    {"book_id": book_id}
+                ]
+            }
+        }
         let cursor = self.coll.find(filter, None)?;
         let mut reviews = Vec::new();
         for result in cursor {

注意

正则表达式查询无法充分利用索引。虽然某些操作可能仍然从索引中受益,但与精确匹配前缀搜索相比,使用正则表达式可能效率较低。
如果你的集合包含大量文档,并且被搜索的字段没有建立索引,可能会导致查询性能较慢。

微调 application/executor/review_operator.rs:

@@ -43,8 +43,9 @@ impl ReviewOperator {
     pub fn get_reviews_of_book(
         &self,
         book_id: u32,
+        query: &str,
     ) -> Result<Vec<model::Review>, Box<dyn std::error::Error>> {
-        self.review_manager.get_reviews_of_book(book_id)
+        self.review_manager.get_reviews_of_book(book_id, query)
     }
 
     pub fn update_review(

传入 query 参数,adapter/router.rs:

@@ -120,12 +120,16 @@ pub fn delete_book(
     }
 }
 
-#[get("/books/<id>/reviews")]
+#[get("/books/<id>/reviews?<q>")]
 pub fn get_reviews_of_book(
     rest_handler: &rocket::State<RestHandler>,
     id: u32,
+    q: Option<&str>,
 ) -> Result<Json<Vec<model::Review>>, status::Custom<Json<ErrorResponse>>> {
-    match rest_handler.review_operator.get_reviews_of_book(id) {
+    match rest_handler
+        .review_operator
+        .get_reviews_of_book(id, q.unwrap_or(""))
+    {
         Ok(reviews) => Ok(Json(reviews)),
         Err(err) => Err(status::Custom(
             Status::InternalServerError,

利用 mongoDB 实现搜索功能所需的更改已经全部加入。让我们测试一波。

使用 curl 尝试搜索书评

先加入一些假书评数据:

curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "Alice Johnson", "title": "A Timeless Classic", "content": "The Great Gatsby is a timeless classic that delves deep into the complexities of the human psyche and the pursuit of the American Dream."}' http://localhost:8000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "Michael Adams", "title": "Brilliantly Written", "content": "Fitzgerald’s prose in The Great Gatsby is simply brilliant. Each sentence is crafted with care, drawing the reader into a world of opulence and tragedy."}' http://localhost:8000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "Emily Parker", "title": "Mesmerizing", "content": "I was completely mesmerized by The Great Gatsby. Fitzgerald’s ability to create such vivid characters and evoke a sense of longing is unmatched."}' http://localhost:8000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "Daniel White", "title": "Enthralling Plot", "content": "The plot of The Great Gatsby is enthralling from start to finish. It’s a story of love, loss, and the emptiness of the American Dream."}' http://localhost:8000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "Sophia Lee", "title": "A Literary Gem", "content": "The Great Gatsby is a literary gem that shines with its insightful commentary on society and human nature. A must-read for all."}' http://localhost:8000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "Ethan Miller", "title": "Timeless Masterpiece", "content": "The Great Gatsby remains a timeless masterpiece that continues to resonate with readers across generations. Fitzgerald’s prose is as relevant today as it was in the 1920s."}' http://localhost:8000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "Olivia Wilson", "title": "Immersive Experience", "content": "Reading The Great Gatsby is an immersive experience like no other. Fitzgerald’s vivid descriptions transport you to the roaring twenties, making you feel like you’re part of the story."}' http://localhost:8000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "Jacob Thompson", "title": "Intriguing Characters", "content": "What sets The Great Gatsby apart are its intriguing characters. Each one is flawed yet fascinating, contributing to the richness of the narrative."}' http://localhost:8000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "Ava Martinez", "title": "Gripping Narrative", "content": "The Great Gatsby grips you from the first page and doesn’t let go until the very end. It’s a story that lingers in your mind long after you’ve finished reading."}' http://localhost:8000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "Noah Taylor", "title": "Profound Themes", "content": "Beneath its glitzy surface, The Great Gatsby explores profound themes of love, betrayal, and the corrupting influence of wealth. A true masterpiece."}' http://localhost:8000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "Isabella Hernandez", "title": "Hauntingly Beautiful", "content": "The Great Gatsby is hauntingly beautiful, painting a picture of an era filled with glamour and excess, yet tinged with sadness and longing."}' http://localhost:8000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "Lucas Moore", "title": "Compelling Narrative", "content": "Fitzgerald weaves a compelling narrative in The Great Gatsby, drawing readers into the world of Jay Gatsby and Daisy Buchanan with finesse and skill."}' http://localhost:8000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "Mia Clark", "title": "Unforgettable Characters", "content": "The characters in The Great Gatsby are unforgettable, each with their own desires and flaws that drive the story forward with intensity and emotion."}' http://localhost:8000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "Elijah Garcia", "title": "Eloquent Prose", "content": "Fitzgerald’s eloquent prose in The Great Gatsby elevates the novel to literary greatness. It’s a book that demands to be savored and appreciated."}' http://localhost:8000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "Charlotte King", "title": "Riveting Plot", "content": "The plot of The Great Gatsby is riveting, filled with twists and turns that keep you on the edge of your seat until the very end. An absolute page-turner."}' http://localhost:8000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "William Brown", "title": "Emotionally Resonant", "content": "The Great Gatsby is emotionally resonant, exploring themes of love and longing in a way that stays with you long after you’ve turned the final page."}' http://localhost:8000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "Chloe Rodriguez", "title": "Sensory Delight", "content": "Reading The Great Gatsby is a sensory delight, with Fitzgerald’s vivid descriptions bringing the sights, sounds, and smells of the 1920s to life."}' http://localhost:8000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "James Lee", "title": "Thought-Provoking", "content": "The Great Gatsby is thought-provoking, challenging readers to reflect on the nature of wealth, ambition, and the pursuit of happiness."}' http://localhost:8000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "Emma Taylor", "title": "Rich Symbolism", "content": "The Great Gatsby is rich in symbolism, with every detail serving a purpose in conveying deeper themes and messages about society and human nature."}' http://localhost:8000/reviews
curl -X POST -H "Content-Type: application/json" -d '{"book_id": 8, "author": "Ryan Martinez", "title": "A Literary Masterpiece", "content": "The Great Gatsby is undeniably a literary masterpiece, with its lyrical prose and timeless themes making it a must-read for all lovers of literature."}' http://localhost:8000/reviews

根据 title 和 content 搜索书评,关键字:masterpiece

curl -X GET "http://localhost:8000/books/8/reviews?q=masterpiece"

结果:

[
  {
    "id": "",
    "book_id": 8,
    "author": "Ethan Miller",
    "title": "Timeless Masterpiece",
    "content": "The Great Gatsby remains a timeless masterpiece that continues to resonate with readers across generations. Fitzgerald’s prose is as relevant today as it was in the 1920s.",
    "created_at": "2024-03-19T10:52:22.665957Z",
    "updated_at": "2024-03-19T10:52:22.665957Z"
  },
  {
    "id": "",
    "book_id": 8,
    "author": "Noah Taylor",
    "title": "Profound Themes",
    "content": "Beneath its glitzy surface, The Great Gatsby explores profound themes of love, betrayal, and the corrupting influence of wealth. A true masterpiece.",
    "created_at": "2024-03-19T10:52:22.733401Z",
    "updated_at": "2024-03-19T10:52:22.733401Z"
  },
  {
    "id": "",
    "book_id": 8,
    "author": "Ryan Martinez",
    "title": "A Literary Masterpiece",
    "content": "The Great Gatsby is undeniably a literary masterpiece, with its lyrical prose and timeless themes making it a must-read for all lovers of literature.",
    "created_at": "2024-03-19T10:52:22.902011Z",
    "updated_at": "2024-03-19T10:52:22.902011Z"
  }
]

根据 title 和 content 搜索书评,关键字:Fitzgerald

curl -X GET "http://localhost:8000/books/8/reviews?q=Fitzgerald"

结果:

[
  {
    "id": "",
    "book_id": 8,
    "author": "Michael Adams",
    "title": "Brilliantly Written",
    "content": "Fitzgerald’s prose in The Great Gatsby is simply brilliant. Each sentence is crafted with care, drawing the reader into a world of opulence and tragedy.",
    "created_at": "2024-03-19T10:52:22.593688Z",
    "updated_at": "2024-03-19T10:52:22.593688Z"
  },
  {
    "id": "",
    "book_id": 8,
    "author": "Emily Parker",
    "title": "Mesmerizing",
    "content": "I was completely mesmerized by The Great Gatsby. Fitzgerald’s ability to create such vivid characters and evoke a sense of longing is unmatched.",
    "created_at": "2024-03-19T10:52:22.615830Z",
    "updated_at": "2024-03-19T10:52:22.615830Z"
  },
  {
    "id": "",
    "book_id": 8,
    "author": "Ethan Miller",
    "title": "Timeless Masterpiece",
    "content": "The Great Gatsby remains a timeless masterpiece that continues to resonate with readers across generations. Fitzgerald’s prose is as relevant today as it was in the 1920s.",
    "created_at": "2024-03-19T10:52:22.665957Z",
    "updated_at": "2024-03-19T10:52:22.665957Z"
  },
  {
    "id": "",
    "book_id": 8,
    "author": "Olivia Wilson",
    "title": "Immersive Experience",
    "content": "Reading The Great Gatsby is an immersive experience like no other. Fitzgerald’s vivid descriptions transport you to the roaring twenties, making you feel like you’re part of the story.",
    "created_at": "2024-03-19T10:52:22.682927Z",
    "updated_at": "2024-03-19T10:52:22.682927Z"
  },
  {
    "id": "",
    "book_id": 8,
    "author": "Lucas Moore",
    "title": "Compelling Narrative",
    "content": "Fitzgerald weaves a compelling narrative in The Great Gatsby, drawing readers into the world of Jay Gatsby and Daisy Buchanan with finesse and skill.",
    "created_at": "2024-03-19T10:52:22.766302Z",
    "updated_at": "2024-03-19T10:52:22.766302Z"
  },
  {
    "id": "",
    "book_id": 8,
    "author": "Elijah Garcia",
    "title": "Eloquent Prose",
    "content": "Fitzgerald’s eloquent prose in The Great Gatsby elevates the novel to literary greatness. It’s a book that demands to be savored and appreciated.",
    "created_at": "2024-03-19T10:52:22.801144Z",
    "updated_at": "2024-03-19T10:52:22.801144Z"
  },
  {
    "id": "",
    "book_id": 8,
    "author": "Chloe Rodriguez",
    "title": "Sensory Delight",
    "content": "Reading The Great Gatsby is a sensory delight, with Fitzgerald’s vivid descriptions bringing the sights, sounds, and smells of the 1920s to life.",
    "created_at": "2024-03-19T10:52:22.852290Z",
    "updated_at": "2024-03-19T10:52:22.852290Z"
  }
]

不错!基于 MySQL 和 mongoDB 的搜索功能已完成!

高级全文搜索

如果你想要拥有高级全文搜索支持,请尝试 ElasticsearchSolr。 这些解决方案针对文本搜索操作进行了优化,并提供诸如索引、相关性评分和语言特定的词干处理等功能。