maaash.jp

what I create

RedisのネストしたSETにSADDやSMEMBERSしやすくするluaスクリプトを書いた

Redis v2.6.0からluaスクリプトのロード実行ができるようになっていて、 luaの方に興味があって「いつか…」と思っていた。

ところで、RedisのSETをネストした構造のようなものを使う時の話。 user has many entries. entry has many tags. 以下のような構造のとき、以下のようになってしまう。

# input:
# my $users = {
#     # user.id
#     1 => {
#         # entry.id
#         10 => [
#             # tag.id
#             100,
#             101
#         ],
#         11 => [
#             110
#         ],
#     },
#     2 => {
#         20 => [
#             200
#         ],
#     }
# };
#
# output (1) - get all tags for user[1]'s entry[10]
# [ 100, 101 ]
#
# output (2) - get all tags for user[1]'s all entries
# [ 100, 101, 110 ]
#
# output (3) - get all tags
# [ 100, 101, 110, 200 ]
#
# output (4) - get all entry ids
# [ 10, 11, 20 ]

subtest 'normal' => sub {
    $redis->flushdb;

    $redis->multi;
    $redis->sadd( "users"      => "1" );
    $redis->sadd( "users.1"    => "10" );
    $redis->sadd( "users.1.10" => "100" );
    $redis->sadd( "users.1.10" => "101" );
    $redis->sadd( "users.1"    => "11" );
    $redis->sadd( "users.1.11" => "110" );
    $redis->sadd( "users"      => "2" );
    $redis->sadd( "users.2"    => "20" );
    $redis->sadd( "users.2.20" => "200" );
    $redis->exec;

    is_deeply( [ $redis->smembers( 'users.1.10' ) ],
               [ 100, 101 ] );

    is_deeply( [ $redis->sunion(
                     map {
                         "users.1.$_"
                     } $redis->smembers( 'users.1' )
                 ) ],
               [ 100, 101, 110 ] );

    is_deeply( [ $redis->sunion(
                     map {
                         my $user_id   = $_->[ 0 ];
                         my $entry_ids = $_->[ 1 ];
                         map { "users.$user_id.$_" } @$entry_ids;
                     } map {
                         [ $_ => [ $redis->smembers("users.$_") ] ]
                     } $redis->smembers( 'users' )
                 ) ],
               [ 100, 101, 110, 200 ] );

    is_deeply( [ $redis->sunion(
                     map {
                         "users.$_"
                     } $redis->smembers( 'users' )
                 ) ],
               [ 10, 11, 20 ] );
};

ネストしたSET構造に直接SADDしたりネストした構造のある深さのSMEMBERSをとろうとするとめんどくさいので、 luaを使えばその辺うまくやれるカスタムコマンドをつくれるんじゃないかと思って書いてみた。

MULTIの中でもEVALSHAできるようだし、 もしかしたら使えるかもしれない。

luaを書いてみる理由ができてよかった。

Comments