🔎
MeiliSearch使ってみる Rust編
MeiliSearch使ってみる Rails編
前回はRailsからMeiliSearchを使ってみたので、今度はRustから使ってみたいと思います
環境
- OS: Ubuntu 20.04
- MeiliSearch: 0.22.0
- Rust: 1.55
- meilisearch-sdk: 0.10.2
準備
meilisearch
前回までと同じ環境を使うので割愛します
Indexのデータ用の構造体を用意する
データ用の構造体を用意しておきます。
models.rs
use meilisearch_sdk::document::Document;
use serde::{Deserialize, Serialize};
use serde_aux::prelude::*;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct User {
#[serde(deserialize_with = "deserialize_number_from_string")]
pub id: u64,
pub given_name: String,
pub family_name: String,
pub age: Option<u32>,
}
impl Document for User {
type UIDType = u64;
fn get_uid(&self) -> &Self::UIDType {
&self.id
}
}
- serdeでシリアライズ・デシリアライズできるようにしておく
- モデルの構造体には
Document
を実装する- primary-keyになる値と型を定義する
- 前回Railsで作成したデータと同じデータを使いたかったが、Railsから作成したIndexはidが文字列になっているので、同じデータを使う為にRust上ではu64にデシリアライズできるように
serde_aux
を使いました - これであれば、Dieselとかで使っている構造体をそのままMeiliSearchにも使うことが出来そうな気もしますね
その他のライブラリは
Cargo.toml
[package]
name = "rust_sample"
version = "0.1.0"
edition = "2018"
[dependencies]
anyhow = "1.0.44"
meilisearch-sdk = "0.10.2"
serde = { version = "1.0.130", features = ["derive"] }
serde-aux = "2.3.0"
tokio = { version = "1.12.0", features = ["full"] }
こんな感じになっております。
検索してみる
src/bin/sample.rs
use meilisearch_sdk::{client::*, search::*};
use rust_sample::models::User;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = Client::new("http://localhost:7700", "api-key");
let index = client.get_or_create("User").await?;
let users = index
.search()
.with_query("本田")
.with_attributes_to_highlight(Selectors::Some(&["*"]))
.with_matches(true)
.execute::<User>()
.await?;
println!("{:#?}", users);
Ok(())
}
SearchResults {
hits: [
SearchResult {
result: User {
id: 1,
given_name: "圭佑",
family_name: "本田",
age: None,
},
formatted_result: Some(
User {
id: 1,
given_name: "圭佑",
family_name: "<em>本田</em>",
age: None,
},
),
matches_info: Some(
{
"family_name": [
MatchRange {
start: 0,
length: 6,
},
],
},
),
},
SearchResult {
result: User {
id: 6,
given_name: "たろう",
family_name: "田中",
age: Some(
1,
),
},
formatted_result: Some(
User {
id: 6,
given_name: "たろう",
family_name: "<em>田中</em>",
age: Some(
1,
),
},
),
matches_info: Some(
{
"full_name": [
MatchRange {
start: 0,
length: 3,
},
],
"family_name": [
MatchRange {
start: 0,
length: 6,
},
],
},
),
},
SearchResult {
result: User {
id: 8,
given_name: "はなこ",
family_name: "田中",
age: Some(
1,
),
},
formatted_result: Some(
User {
id: 8,
given_name: "はなこ",
family_name: "<em>田中</em>",
age: Some(
1,
),
},
),
matches_info: Some(
{
"family_name": [
MatchRange {
start: 0,
length: 6,
},
],
},
),
},
],
offset: 0,
limit: 20,
nb_hits: 3,
exhaustive_nb_hits: false,
facets_distribution: None,
exhaustive_facets_count: None,
processing_time_ms: 0,
query: "本田",
}
こんな感じになります。
SearchResult
に型があるのでやっぱりわかりやすい
indexにDocumentの登録
add_documents
は add_or_replace
のaliasらしいです。
use meilisearch_sdk::{client::*, search::*};
use rust_sample::models::User;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = Client::new("http://localhost:7700", "api-key");
let index = client.get_or_create("User").await?;
index
.add_documents(
&[User {
id: 1,
given_name: "大五郎".to_owned(),
family_name: "焼酎".to_owned(),
age: None,
}],
Some("id"),
)
.await?;
index.set_filterable_attributes(["age"]).await?;
let users = index
.search()
.with_query("焼酎")
.with_attributes_to_highlight(Selectors::Some(&["*"]))
.with_matches(true)
.execute::<User>()
.await?;
println!("{:#?}", users);
Ok(())
}
- sleep入れてないので、ass_documentsした直後だとまだindexに追加されてなくて、searchしても返ってこない場合があります
-
Document
にget_uid
っていうのがあるのに、primary_keyが指定できるのはなんでだろう?- ※要確認
- add_or_replaceとadd_or_updateの違いがよく分かってない
- ※要確認
まとめ
- 今回は内容的には薄いですが...
- 既存の構造体にDocumentを実装すれば使えそう
- wasmでも使えるみたい
TODO
- Railsから使ってみる
- Rustから使ってみる
- k8sでの運用(冗長化など)
Discussion