Created
March 23, 2013 16:30
-
-
Save mash/5228322 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --thanks to http://d.aoikujira.com/blog/index.php?2009%252F04%252F16%252FLua%E3%81%AEstring%E3%81%ABsplit%E3%81%A8join%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%99%E3%82%8B | |
| --[[* split string *]]-- | |
| -- return iterator | |
| string.split_it = function(str, sep) | |
| if str == nil then return nil end | |
| assert(type(str) == "string", "str must be a string") | |
| assert(type(sep) == "string", "sep must be a string") | |
| return string.gmatch(str, "[^\\" .. sep .. "]+") | |
| end | |
| -- return table | |
| string.split = function(str, sep) | |
| local ret = {} | |
| for seg in string.split_it(str, sep) do | |
| table.insert( ret, seg ) | |
| end | |
| return ret | |
| end | |
| -- saddnested( 'a.b.c', 'd' ) | |
| -- -> sadd('a', 'b') | |
| -- sadd('a.b', 'c') | |
| -- sadd('a.b.c','d') | |
| -- return 1 on success | |
| local saddnested = function(str, val) | |
| assert( str ~= nil, 'provide str argument' ) | |
| assert( val ~= nil, 'provide val argument' ) | |
| local parts = str:split('.') | |
| for i,part in ipairs(parts) do | |
| if i == #parts then | |
| redis.call( 'sadd', str, val ) | |
| else | |
| redis.call( 'sadd', table.concat( parts, '.', 1, i ), parts[ i + 1 ] ) | |
| end | |
| end | |
| return 1 | |
| end | |
| return saddnested(KEYS[1],ARGV[1]) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- smembersnested( 'a', 2 ) | |
| -- 1. fetches smembers( 'a' ) | |
| -- 2. calls smembers( 'a.' .. smember ) for each smember, recursively | |
| -- 3. join the results | |
| -- return table | |
| local function smembersnested( str, depth ) | |
| assert( depth ~= nil, "provide depth argument" ) | |
| local members = redis.call( 'smembers', str ) | |
| if depth == 0 then | |
| return members | |
| elseif #members == 0 then | |
| return members | |
| end | |
| depth = depth - 1 | |
| local ret = {} | |
| for i,member in ipairs(members) do | |
| local deep_members = smembersnested( str .. '.' .. member, depth ) | |
| for j,m in ipairs(deep_members) do | |
| table.insert( ret, m ) | |
| end | |
| end | |
| return ret | |
| end | |
| return smembersnested( KEYS[1], tonumber(ARGV[1]) ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env perl | |
| use strict; | |
| use warnings; | |
| use Test::More; | |
| use Data::Dumper; | |
| use Path::Class; | |
| use Redis; | |
| use Test::RedisServer; | |
| my $redis_server = Test::RedisServer->new; | |
| my $redis = Redis->new( $redis_server->connect_info ); | |
| setup_nested(); | |
| # 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 entries | |
| # [ 100, 101 ] | |
| # | |
| # output (2) - get all tags for user[1] | |
| # [ 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 ] ); | |
| }; | |
| subtest 'scripting' => sub { | |
| $redis->flushdb; | |
| $redis->saddnested( 'users.1.10' => '100' ); | |
| $redis->saddnested( 'users.1.10' => '101' ); | |
| $redis->saddnested( 'users.1.11' => '110' ); | |
| $redis->saddnested( 'users.2.20' => '200' ); | |
| is_deeply( [ $redis->smembersnested( 'users.1.10', 0 ) ], | |
| [ 100, 101 ] ); | |
| is_deeply( [ $redis->smembersnested( 'users.1', 1 ) ], | |
| [ 100, 101, 110 ] ); | |
| is_deeply( [ $redis->smembersnested( 'users', 2 ) ], | |
| [ 100, 101, 110, 200 ] ); | |
| is_deeply( [ $redis->smembersnested( 'users', 1 ) ], | |
| [ 10, 11, 20 ] ); | |
| }; | |
| done_testing; | |
| sub setup_nested { | |
| my ($sha1_saddnested) = $redis->script_load( scalar file('saddnested.lua')->slurp ); | |
| my ($sha1_smembersnested) = $redis->script_load( scalar file('smembersnested.lua')->slurp ); | |
| { | |
| no warnings 'once'; | |
| *Redis::saddnested = sub { | |
| my ($self, $key, $val) = @_; | |
| $self->evalsha( $sha1_saddnested, 1, $key, $val ); | |
| }; | |
| *Redis::smembersnested = sub { | |
| my ($self, $key, $depth) = @_; | |
| $self->evalsha( $sha1_smembersnested, 1, $key, $depth ); | |
| }; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment