Skip to content

Instantly share code, notes, and snippets.

@mash
Created March 23, 2013 16:30
Show Gist options
  • Select an option

  • Save mash/5228322 to your computer and use it in GitHub Desktop.

Select an option

Save mash/5228322 to your computer and use it in GitHub Desktop.
--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])
-- 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]) )
#!/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