RustでRedisを操作する方法まとめ

2024年3月3日日曜日

Rust

t f B! P L

Rustを使用してRedisとのやり取りを行う方法について詳しく説明します。

Redisのクレートを設定

RustプロジェクトでRedisを使い始めるには、まずredisクレートをプロジェクトの依存関係に追加する必要があります。これを行うには、プロジェクトのCargo.tomlファイルに以下の行を追加します。

[dependencies]
redis = "0.24.0"

この設定により、Redisクレートがプロジェクトに追加され、Redisサーバーとの通信に必要な関数や型が利用可能になります。

スポンサーリンク

Redisサーバーへ接続する

Redisサーバーへの接続を確立するためには、以下の関数を定義します。この関数は、ローカルホスト上の標準Redisポートを指すURLを使用してRedisサーバーへの接続を試みます。

fn get_connection() -> redis::RedisResult<redis::Connection> {
    let client = redis::Client::open("redis://127.0.0.1/")?;
    let con = client.get_connection()?;
    Ok(con) 
}

この関数は、成功した場合にRedisサーバーへの接続を表すredis::Connectionオブジェクトを返します。エラーが発生した場合には、redis::RedisResultを通じてエラー情報が提供されます。

データの設定と取得方法

データの設定(Set)

キーに対する値の設定は、以下の関数を使用して行います。この例では、my_keyというキーに99999という整数値を設定しています。

use redis::{Commands};

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;
    
    //「my_key」に値を設定
    con.set("my_key", 99999)
}

データの取得(Get)

設定されたキーの値を取得するには、以下の関数を使用します。この関数はmy_keyキーの値を取得し、存在しない場合にはデフォルト値9999を使用します。

use redis::{Commands};

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;
    
    //「my_key」キーの値を取得
    let value: Option<i32> = con.get("my_key")?;
    println!("{}", value.unwrap_or(9999));
    
    Ok(())
}

データの削除(Del)

削除操作はdel関数を使用して行います。この関数には削除したいキーの名前を引数として渡します。削除が成功した場合、関数は削除されたキーの数を返します。キーが存在しない場合の返り値は0です。これにより、削除操作が成功したかどうかを簡単に確認できます。

以下の例では、my_keyというキーに紐づくデータを削除しています。

use redis::{Commands};

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;
    
    //「my_key」キーに紐づくデータを削除
    let delete_count: i32 = con.del("my_key")?;
    if delete_count > 0 {
        println!("削除成功: {} 個のキーが削除されました。", delete_count);
    } else {
        println!("削除対象のキーが存在しません。");
    }
    
    Ok(())
}

リスト操作

Redisでは、リスト構造を使用して複数の値を順序付けて格納することができます。リストへの要素の追加、リストからの要素の取得について見ていきましょう。

末尾に要素を追加する(rpush)

単一の要素または複数の要素をリストの末尾に追加するには、rpushメソッドを使用します。

use redis::{Commands};

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;
    
    //「my_list」の末尾に要素を追加
    con.rpush("my_list", "1")
    con.rpush("my_list", &["1", "2", "3"])
}

リストの先頭に要素を挿入(lpush)

単一の要素または複数の要素をリストの先頭に挿入するには、lpushメソッドを使用します。

use redis::{Commands};

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;
    
    //「my_list」の先頭に要素を挿入
    con.lpush("my_list", "0")
    con.lpush("my_list", &["0", "-1", "-2"])
}

指定したインデックスの要素を取得

リストの特定の位置にある要素を取得するには、lindexメソッドを使用します。このメソッドは、指定されたインデックスにある要素を返します。インデックスは0から始まり、リストの先頭の要素が0番目の要素となります。また、負のインデックスを使用すると、リストの末尾からの位置を指定できます(例:-1は最後の要素を指します)。

以下は、my_listという名前のリストから2番目(インデックス1)の要素を取得するRustのサンプルコードです。

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;
        
    //「my_list」の2番目の要素を取得
    let str: Option<String> = con.lindex("my_list", 1)?;
    println!("{}", str.unwrap_or(String::from("not found")));
}

指定したインデックスの要素の値を変更

リスト内の特定の位置にある要素の値を新しい値に変更するには、lsetコマンドを使用します。このコマンドは、指定されたインデックスにある要素を新しい値で上書きします。インデックスは0から始まり、リストの先頭の要素が0番目になります。

以下は、my_listという名前のリストの2番目の要素(インデックスは1)の値を"9"に変更するRustのサンプルコードです。

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;
        
    //「my_list」の2番目の要素の値を書き換え
    con.lset("my_list", 1, "9")?;
}

リストから特定の範囲の要素を取得

リストから特定の範囲の要素を取得するには、lrangeメソッドを使用します。以下の例では、リストの全要素と特定の範囲の要素を取得しています。

use redis::{Commands};

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;

    con.lpush("my_list", &["0", "1", "2", "3"])?;
    
    //「my_list」のすべての要素を取得
    let list : Vec<String> = con.lrange("my_list", 0, -1)?;
    println!("{:?}", list);  // ["0", "1", "2", "3"]
    
    //「my_list」の1〜2要素目を取得
    let list : Vec<String> = con.lrange("my_list", 1, 2)?;
    println!("{:?}", list);  // ["1", "2"]
    
    Ok(())
}

末尾の要素を削除

リストの末尾から要素を削除するには、rpopメソッドを使用します。以下の関数例では、my_listという名前のリストから末尾の要素を1つ削除し、削除された要素を表示します。

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;

    //「my_list」の末尾から1つ要素を削除
    let value : Vec<String> = con.rpop("my_list", std::num::NonZeroUsize::new(1))?;
    println!("{:?}", value);

    Ok(())
}

このコードは、rpopメソッドを呼び出すことにより、指定されたリスト(my_list)から末尾の要素を削除します。std::num::NonZeroUsize::new(1)は、削除する要素の数を指定します。この場合は1つです。操作が成功すると、削除された要素が戻り値として返されます。

先頭の要素を削除

リストの先頭から要素を削除するには、lpopメソッドを使用します。以下の関数例では、my_listという名前のリストから先頭の要素を1つ削除し、削除された要素を表示します。

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;

    //「my_list」の先頭から1つ要素を削除
    let value : Vec<String> = con.lpop("my_list", std::num::NonZeroUsize::new(1))?;
    println!("{:?}", value);

    Ok(())
}

指定した範囲の要素を削除する

Redisでは、LTRIMコマンドを使用してリストの範囲を指定し、その範囲外の要素を削除することができます。このコマンドはリストを指定した範囲の要素のみを保持し、その範囲外の要素を削除します。

use redis::{Commands};

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;
    
    // 「my_list」の指定した範囲以外の要素を削除
    // ここでは、インデックス1から3の要素のみを保持し、それ以外を削除します
    con.ltrim("my_list", 1, 3)?;

    Ok(())
}

この関数は、my_listという名前のリストに対してLTRIM操作を適用し、インデックスが1から3の要素のみを保持します。これにより、指定された範囲以外の要素がリストから削除されます。ltrimメソッドは、リストの名前、保持したい範囲の開始インデックス、終了インデックスを引数に取ります。操作が成功すると、指定された範囲の要素のみがリストに残り、それ以外の要素は削除されます。

処理イメージ

上のコードで、インデックスが1から3の要素のみを保持した場合。

インデックス 処理前の値 処理後の値
0 A B
1 B C
2 C D
3 D -
4 E -

スポンサーリンク

Sets(セット型)で一意な集合を扱う

Redisのセット型は、ユニークな文字列の集合を扱う際に非常に便利なデータ構造です。重複を許さず、順序も持たないため、メンバーの一意性が自動的に保証されます。この特性は、例えばユーザーの一意のIDリストやイベントの一意の参加者リストを管理する際に非常に有用です。Rustを使用してRedisのセット操作を行う方法について、基本的な操作を見ていきましょう。

要素の追加

セットに新しい要素を追加するには、saddメソッドを使用します。このメソッドは、指定したセットに一つ以上の要素を追加し、追加された要素の数を返します(既にセットに存在する要素は追加されず、カウントに含まれません)。

以下の例では、my_setという名前のセットに単一の要素"A"を追加しています。

use redis::{Commands};

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;
    
    // 「my_set」に要素追加
    con.sadd("my_set", "A")?;

    Ok(())
}

複数の要素を一度に追加する場合は、以下のように配列を使用します。

con.sadd("my_set", &["A", "B", "C"])?;

登録された要素を取得

セットに登録されているすべての要素を取得するには、smembersメソッドを使用します。このメソッドは、セットに含まれるすべてのメンバーのリストを返します。

use std::collections::HashSet;
use redis::{Commands};

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;
    
    // 「my_set」に要素追加
    con.sadd("my_set", &["A", "B", "B", "C"])?;    

    // 「my_set」の要素を取得
    let mems : HashSet<String> = con.smembers("my_set")?;
    println!("{:?}", mems);  // {"A", "C", "B"}

    Ok(())
}

このコードは、my_setセットに"A", "B", "B", "C"の要素を追加した後、セット内のすべてのメンバーを取得しています。取得されたメンバーの一意性が保証されます。

要素の削除

セットから特定の要素を削除するには、sremメソッドを使用します。このメソッドは、指定したセットから一つ以上の要素を削除し、実際に削除された要素の数を返します。

use redis::{Commands};

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;

    // 「my_set」の要素を削除
    con.srem("my_set", "A")?;

    Ok(())
}

要素の存在チェック

Redisのセット型において、特定の要素がセットに含まれているかどうかを確認するには、sismemberメソッドを使用します。このメソッドは、指定した要素がセット内に存在する場合にtrueを、存在しない場合にfalseを返します。

以下は、my_setセット内に"A"という要素が存在するかどうかをチェックするRustのサンプルコードです。

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;

    // 「my_set」に指定さえたキーの要素が存在するか確認
    let is_member: bool = con.sismember("my_set", "A")?;
    println!("{:?}", is_member);

    Ok(())
}

特定のセットから他のセットには存在しない要素を抽出

特定のセットから他のセットには存在しない要素を抽出することで、ユニークなデータセットを作成したり、特定の条件を満たす要素だけを選択したりすることができます。Rustを使用してRedisのセット間の差分を取得する方法について解説します。

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;

    // 「my_set1」「my_set2」「my_set3」に要素を設定
    con.sadd("my_set1", &["A", "B", "C", "D", "E"])?;   
    con.sadd("my_set2", &["B", "C", "D"])?; 
    con.sadd("my_set3", &["B", "F"])?; 

    // 「my_set1」と「my_set2」「my_set3」を比較し「my_set1」にしかないキーの差分を取得
    let mems : HashSet<String> = con.sdiff(&["my_set1", "my_set2", "my_set3"])?;
    println!("{:?}", mems);  //{"A", "E"}

    Ok(())
}

このコードは、sdiffメソッドを使用してmy_set1からmy_set2my_set3には存在しない要素を抽出し、その結果をコンソールに出力します。この例では、my_set1にのみ存在する"A""E"が差分として抽出されます。

Hashes(ハッシュ型)でキーと値を組み合わせを扱う

Redisのハッシュ型は、フィールドと値のペアを格納するためのデータ構造であり、複雑なデータセットを効率的に管理することができます。このセクションでは、Rustを使用してRedisのハッシュ型での基本操作—要素の追加、キーの一覧取得、指定したキーの要素の削除、キーの存在チェック—を行う方法について解説します。

ハッシュ型に要素(キーと値)を追加

ハッシュに新しいフィールド(キーと値のペア)を追加するには、hsetコマンドを使用します。このコマンドは、指定されたハッシュに新しいフィールドを追加し、フィールドが既に存在していた場合は値を更新します。

use redis::{Commands};

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;
    
    // ハッシュ「my_hash」に新しい要素を追加
    con.hset("my_hash", "key1", "value1")?;
    Ok(())
}

ハッシュ型からキーを指定して値を取得

ハッシュから特定のフィールドの値を取得するには、hgetコマンドを使用します。このコマンドは、指定されたキー(フィールド名)に対応する値を返します。キーが存在しない場合は、Noneが返されます。

use redis::{Commands};

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;
    
    // ハッシュ「my_hash」からキー「key1」に対応する値を取得
    let value: Option<String> = con.hget("my_hash", "key1")?;
    println!("{}", value.unwrap_or_else(|| "Value not found".to_string()));
    
    Ok(())
}

ハッシュ型のキーの一覧を取得

ハッシュ内のすべてのキー(フィールド名)を取得するには、hkeysコマンドを使用します。このコマンドは、指定されたハッシュのすべてのキーをリストとして返します。

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;
    
    // ハッシュ「my_hash」のすべてのキーを取得
    let keys: Vec<String> = con.hkeys("my_hash")?;
    println!("{:?}", keys);
    Ok(())
}

ハッシュ型から指定したキーの要素を削除

ハッシュから特定のフィールドを削除するには、hdelコマンドを使用します。このコマンドは、指定されたフィールドをハッシュから削除します。

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;
    
    // ハッシュ「my_hash」から特定のキー「key1」を削除
    con.hdel("my_hash", "key1")?;
    Ok(())
}

ハッシュ型のキー存在をチェック

ハッシュ内に特定のフィールド(キー)が存在するかどうかを確認するには、hexistsコマンドを使用します。このコマンドは、指定されたフィールドが存在する場合にtrueを、存在しない場合にfalseを返します。

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;
    
    // ハッシュ「my_hash」内に「key1」が存在するかチェック
    let exists: bool = con.hexists("my_hash", "key1")?;
    println!("{}", exists);
    Ok(())
}

Redisに登録されている全てのキーを表示

Redisではkeysコマンドを使用して、パターンにマッチする全てのキーのリストを取得することができます。
以下のサンプルコードは、Redisに保存されている全てのキーを取得するコードです。ここでは、"*"パターンを使用することで、すべてのキーを取得しています。

use redis::{Commands};

fn do_something() -> redis::RedisResult<()> {
    let mut con = get_connection()?;
    
    // Redisに登録されている全てのキーを取得
    let keys: Vec<String> = con.keys("*")?;
    
    println!("Redisに登録されているキー:");
    for key in keys {
        println!("{}", key);
    }
    
    Ok(())
}

まとめ

RustでRedisを操作する方法を紹介してきました。
今回、紹介した内容は、Redisを用いたアプリケーション開発において非常に一般的であり、データの迅速な読み書きが求められる場面で特に有効です。

スポンサーリンク
スポンサーリンク

このブログを検索

Profile

自分の写真
Webアプリエンジニア。 日々新しい技術を追い求めてブログでアウトプットしています。
プロフィール画像は、猫村ゆゆこ様に書いてもらいました。

仕事募集もしていたり、していなかったり。

QooQ