Skip to content

Instantly share code, notes, and snippets.

@reddyonrails
Forked from Loupi/nested-redis.rb
Created November 6, 2019 17:59
Show Gist options
  • Save reddyonrails/e7decf3ca2104fb0062d8a8d69a39c23 to your computer and use it in GitHub Desktop.
Save reddyonrails/e7decf3ca2104fb0062d8a8d69a39c23 to your computer and use it in GitHub Desktop.

Revisions

  1. @Loupi Loupi revised this gist May 27, 2012. 1 changed file with 9 additions and 7 deletions.
    16 changes: 9 additions & 7 deletions nested-redis.rb
    Original file line number Diff line number Diff line change
    @@ -43,8 +43,10 @@ def leaf(id)
    end

    def delete(comment, parentId, itemId, isMainThread = true)
    inner_delete comment, parentId, isMainThread
    @redis.del json_key(itemId)
    @redis.multi { |multi|
    inner_delete comment, parentId, isMainThread, multi
    multi.del json_key(itemId)
    }
    end

    private
    @@ -67,16 +69,16 @@ def process_comments(comments)
    }
    end

    def inner_delete(comment, parentId, isMainThread)
    def inner_delete(comment, parentId, isMainThread, multi)
    if comment.kind_of?(Array)
    comment.each {|c| inner_delete c, parentId, isMainThread}
    comment.each {|c| inner_delete c, parentId, isMainThread, multi}
    else
    id = comment['id']
    comments = comment['comments']
    k = isMainThread ? comments_key(parentId) : sub_comments_key(parentId)
    @redis.del comment_key(id)
    @redis.zrem k, id
    inner_delete comments, id, false if comments
    multi.del comment_key(id)
    multi.zrem k, id
    inner_delete comments, id, false, multi if comments
    end
    end

  2. @Loupi Loupi revised this gist May 27, 2012. 1 changed file with 114 additions and 0 deletions.
    114 changes: 114 additions & 0 deletions redis-cmd.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,114 @@
    require 'optparse'
    require './nested-redis'

    options = {}
    COMMANDS = {save: 1, delete: 2, print: 3, leaf: 4, remove: 5, dummy: 6}
    FORMATS = {text: 1, json: 2, pretty_json:3}

    optparse = OptionParser.new { |opts|
    opts.banner = "Usage: redis-cmd.rb command [options]"

    opts.on('-s', '--save item, parent, comment', Array,
    'Save a comment on an item.') { |itemId, parentId, comment|
    options[:command] = COMMANDS[:save]
    options[:itemId] = itemId
    options[:parentId] = parentId if parentId != '0'
    options[:comment] = comment
    }

    opts.on('-p', '--print itemId',
    'Print the comment hierarchy of an item.') { |itemId|
    options[:command] = COMMANDS[:print]
    options[:itemId] = itemId
    options[:format] = FORMATS[:text]
    }

    opts.on('-l', '--leaf leafId',
    'Print the hierarchy of a leaf.') { |leafId|
    options[:command] = COMMANDS[:leaf]
    options[:itemId] = leafId
    options[:format] = FORMATS[:text]
    }

    opts.on('-d', '--delete itemId',
    'Delete all the comments of an item.') { |itemId|
    options[:command] = COMMANDS[:delete]
    options[:itemId] = itemId
    }

    opts.on('-r', '--remove item, leaf, parent', Array,
    'Delete a leaf and it\'s hierarchy from a comment.') { |itemId, leafId, parentId|
    options[:command] = COMMANDS[:remove]
    options[:itemId] = itemId
    options[:leafId] = leafId
    options[:parentId] = parentId
    }

    opts.on('-u', '--dummy itemId',
    'Save a dummy comment hierarchy on an item.') { |itemId|
    options[:command] = COMMANDS[:dummy]
    options[:itemId] = itemId
    }

    opts.on('-j', '--json [pretty]', 'Set the output format to JSON.') { |pretty|
    options[:format] = pretty ? FORMATS[:pretty_json] : FORMATS[:json]
    }

    opts.on( '-h', '--help', 'Display this screen' ) {
    puts opts
    exit
    }
    }

    def print_comments(comments, tabs = 0)
    comments.each { |comment|
    tabs.times { putc "\t" }
    puts "#{comment['comment']}"
    if comment['comments']
    print_comments comment['comments'], tabs + 1
    end
    }
    end

    def save_dummy(cr, id)
    tree = cr.save(id, { comment:'Tree', test:1 })
    root = cr.save(id, { comment:'Root', kind:'ginger', age:50 }, tree)
    trunk = cr.save(id, { comment:'Trunk', kmh_to_mph:0.621 }, tree)
    branch = cr.save(id, { comment:'Branch' }, trunk)
    leaf = cr.save(id, { comment:'Leaf' }, branch)
    top = cr.save(id, { comment:'Top' }, tree)
    end

    def print_result comments, options
    case options[:format]
    when FORMATS[:text]
    print_comments comments
    when FORMATS[:json]
    puts comments.to_json
    when FORMATS[:pretty_json]
    puts JSON.pretty_generate(comments)
    end
    end

    optparse.parse!
    id = options[:itemId]
    cr = CommentsRepository.new

    case options[:command]
    when COMMANDS[:save]
    comment = { comment: options[:comment] }
    id = cr.save(id, comment, options[:parentId])
    puts "New comment saved with id #{id}."
    when COMMANDS[:print]
    print_result cr.get(id), options
    when COMMANDS[:leaf]
    print_result [cr.leaf(id)], options
    when COMMANDS[:delete]
    cr.delete cr.get(id), id, id
    when COMMANDS[:remove]
    cr.delete cr.leaf(options[:leafId]), options[:parentId], id, false
    when COMMANDS[:dummy]
    save_dummy cr, id
    else
    puts optparse
    end
  3. @Loupi Loupi revised this gist May 27, 2012. 1 changed file with 0 additions and 38 deletions.
    38 changes: 0 additions & 38 deletions nested-redis.rb
    Original file line number Diff line number Diff line change
    @@ -97,41 +97,3 @@ def sub_comments_key(id)
    end

    end

    def print_comments(comments, tabs = 0)
    comments.each { |comment|
    tabs.times { putc "\t" }
    puts "#{comment['comment']}"
    if comment['comments']
    print_comments comment['comments'], tabs + 1
    end
    }
    end

    def save_dummy(cr, id)
    tree = cr.save(id, { comment:'Tree', test:1 })
    root = cr.save(id, { comment:'Root', kind:'ginger', age:50 }, tree)
    trunk = cr.save(id, { comment:'Trunk', kmh_to_mph:0.621 }, tree)
    branch = cr.save(id, { comment:'Branch' }, trunk)
    leaf = cr.save(id, { comment:'Leaf' }, branch)
    top = cr.save(id, { comment:'Top' }, tree)
    end

    command = ARGV[0]
    id = ARGV[1]
    cr = CommentsRepository.new

    case command
    when 's'
    save_dummy cr, id
    when 'p'
    print_comments cr.get(id)
    when 'l'
    print_comments [cr.leaf(id)]
    when 'd'
    cr.delete cr.get(id), id, id
    when 'r'
    cr.delete cr.leaf(ARGV[2]), ARGV[3], id, false
    when 'j'
    puts JSON.pretty_generate(cr.get(id))
    end
  4. @Loupi Loupi revised this gist May 26, 2012. 1 changed file with 5 additions and 5 deletions.
    10 changes: 5 additions & 5 deletions nested-redis.rb
    Original file line number Diff line number Diff line change
    @@ -13,11 +13,11 @@ def save(itemId, comment, parentId = nil)
    comment = {id: id, time: now}.merge(comment)
    k = parentId ? sub_comments_key(parentId) : comments_key(itemId)

    @redis.multi do |multi|
    @redis.multi { |multi|
    multi.del json_key(itemId)
    multi.hmset comment_key(id), comment.map { |k,v| [k,v] }.flatten(1)
    multi.zadd k, now, id
    end
    }

    id
    end
    @@ -109,9 +109,9 @@ def print_comments(comments, tabs = 0)
    end

    def save_dummy(cr, id)
    tree = cr.save(id, { comment:'Tree',test:1 })
    root = cr.save(id, { comment:'Root',kind:'ginger', age:50 }, tree)
    trunk = cr.save(id, { comment:'Trunk',kmh_to_mph:0.621 }, tree)
    tree = cr.save(id, { comment:'Tree', test:1 })
    root = cr.save(id, { comment:'Root', kind:'ginger', age:50 }, tree)
    trunk = cr.save(id, { comment:'Trunk', kmh_to_mph:0.621 }, tree)
    branch = cr.save(id, { comment:'Branch' }, trunk)
    leaf = cr.save(id, { comment:'Leaf' }, branch)
    top = cr.save(id, { comment:'Top' }, tree)
  5. @Loupi Loupi revised this gist May 26, 2012. 1 changed file with 64 additions and 78 deletions.
    142 changes: 64 additions & 78 deletions nested-redis.rb
    Original file line number Diff line number Diff line change
    @@ -1,151 +1,137 @@
    require 'redis'
    require 'securerandom'
    require 'json'


    class CommentsRepository

    def initialize
    @redis = Redis.new
    end


    def save itemId, comment, parentId = nil
    def save(itemId, comment, parentId = nil)
    now = Time.now.utc.to_i
    id = @redis.incr 1
    comment = {id: id, comment: comment, time: now}
    childsOfKey = parentId ? subCommentsKey(parentId) : commentsKey(itemId)
    id = @redis.incr(1)
    comment = {id: id, time: now}.merge(comment)
    k = parentId ? sub_comments_key(parentId) : comments_key(itemId)

    @redis.multi do |multi|
    @redis.del jsonKey itemId
    @redis.hmset commentKey(id), comment.map{|k,v| [k,v]}.flatten(1)
    @redis.zadd childsOfKey, now, id
    multi.del json_key(itemId)
    multi.hmset comment_key(id), comment.map { |k,v| [k,v] }.flatten(1)
    multi.zadd k, now, id
    end

    id
    end


    def get id
    jsonId = jsonKey id
    jsonComments = @redis.get jsonId
    def get(id)
    jsonId = json_key(id)
    jsonComments = @redis.get(jsonId)
    if jsonComments
    JSON.parse jsonComments
    comments = JSON.parse(jsonComments)
    else
    comments = multiFetch commentsKey id
    comments = multi_fetch(comments_key(id))
    @redis.set jsonId, comments.to_json
    comments
    end

    comments
    end


    def leaf id
    comment = @redis.hgetall commentKey id
    comment['comments'] = multiFetch subCommentsKey id
    def leaf(id)
    comment = @redis.hgetall(comment_key(id))
    comment['comments'] = multi_fetch(sub_comments_key(id))

    comment
    end


    def delete comment, parentId, itemId, isMainThread = true
    innerDelete comment, parentId, isMainThread
    @redis.del jsonKey itemId
    def delete(comment, parentId, itemId, isMainThread = true)
    inner_delete comment, parentId, isMainThread
    @redis.del json_key(itemId)
    end


    private


    def multiFetch key
    commentIds = @redis.zrange key, 0, -1
    comments = @redis.multi do |multi|
    commentIds.map { |id| multi.hgetall(commentKey id)}
    end
    processComments comments
    def multi_fetch(key)
    commentIds = @redis.zrange(key, 0, -1)
    comments = @redis.multi { |multi|
    commentIds.map { |id| multi.hgetall(comment_key(id)) }
    }

    process_comments(comments)
    end


    def processComments comments
    comments.each do |comment|
    subCommentsId = subCommentsKey comment['id']
    def process_comments(comments)
    comments.each { |comment|
    subCommentsId = sub_comments_key(comment['id'])
    if @redis.zcard(subCommentsId) > 0
    comment['comments'] = multiFetch subCommentsId
    comment['comments'] = multi_fetch(subCommentsId)
    end
    end
    }
    end


    def innerDelete comment, parentId, isMainThread
    if comment.kind_of? Array
    comment.each {|c| innerDelete c, parentId, isMainThread}
    def inner_delete(comment, parentId, isMainThread)
    if comment.kind_of?(Array)
    comment.each {|c| inner_delete c, parentId, isMainThread}
    else
    id = comment['id']
    comments = comment['comments']
    childsKey = isMainThread ? commentsKey(parentId) : subCommentsKey(parentId)
    @redis.del commentKey id
    @redis.zrem childsKey, id
    innerDelete comments, id, false if comments
    k = isMainThread ? comments_key(parentId) : sub_comments_key(parentId)
    @redis.del comment_key(id)
    @redis.zrem k, id
    inner_delete comments, id, false if comments
    end
    end


    def jsonKey id
    def json_key(id)
    "comment:#{id}:json"
    end


    def commentKey id
    def comment_key(id)
    "comment:#{id}"
    end


    def commentsKey id
    def comments_key(id)
    "comments:#{id}"
    end


    def subCommentsKey id
    def sub_comments_key(id)
    "comment:#{id}:sub"
    end

    end



    def printComments comments, tabs = 0
    comments.each do |comment|
    def print_comments(comments, tabs = 0)
    comments.each { |comment|
    tabs.times { putc "\t" }
    puts "#{comment['comment']}"
    if comment['comments']
    printComments comment['comments'], tabs + 1
    print_comments comment['comments'], tabs + 1
    end
    end
    }
    end


    def saveDummy cr, id
    tree = cr.save id, 'Tree'
    root = cr.save id, 'Root', tree
    trunk = cr.save id, 'Trunk', tree
    branch = cr.save id, 'Branch', trunk
    leaf = cr.save id, 'Leaf', branch
    top = cr.save id, 'Top', tree
    def save_dummy(cr, id)
    tree = cr.save(id, { comment:'Tree',test:1 })
    root = cr.save(id, { comment:'Root',kind:'ginger', age:50 }, tree)
    trunk = cr.save(id, { comment:'Trunk',kmh_to_mph:0.621 }, tree)
    branch = cr.save(id, { comment:'Branch' }, trunk)
    leaf = cr.save(id, { comment:'Leaf' }, branch)
    top = cr.save(id, { comment:'Top' }, tree)
    end


    command = ARGV[0]
    id = ARGV[1]
    cr = CommentsRepository.new


    case command
    when 's'
    saveDummy cr, id
    when 's'
    save_dummy cr, id
    when 'p'
    printComments cr.get id
    when 'l'
    printComments [cr.leaf(id)]
    print_comments cr.get(id)
    when 'l'
    print_comments [cr.leaf(id)]
    when 'd'
    cr.delete cr.get(id), id, id
    when 'r'
    when 'r'
    cr.delete cr.leaf(ARGV[2]), ARGV[3], id, false
    when 'j'
    puts cr.get(id).to_json
    puts JSON.pretty_generate(cr.get(id))
    end
  6. @Loupi Loupi revised this gist May 26, 2012. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions nested-redis.rb
    Original file line number Diff line number Diff line change
    @@ -36,11 +36,13 @@ def get id
    end
    end


    def leaf id
    comment = @redis.hgetall commentKey id
    comment['comments'] = multiFetch subCommentsKey id
    comment
    end


    def delete comment, parentId, itemId, isMainThread = true
    innerDelete comment, parentId, isMainThread
  7. @Loupi Loupi revised this gist May 26, 2012. 1 changed file with 122 additions and 45 deletions.
    167 changes: 122 additions & 45 deletions nested-redis.rb
    Original file line number Diff line number Diff line change
    @@ -1,72 +1,149 @@
    #based on http://forrst.com/posts/Nested_Comments_system_using_Redis-Ror

    require 'ostruct'
    require 'redis'
    require 'securerandom'
    require 'yaml'
    require 'json'


    class CommentsRepository

    def initialize
    @redis = Redis.new
    end


    def save itemId, comment, parentId = nil
    @redis.del "item:#{itemId}:parsedComments"
    data = hashComment itemId, comment, parentId
    id = data[:id]
    if parentId == nil
    @redis.rpush "item:#{itemId}:comments", id
    else
    @redis.hset "comment:#{parentId}", 'hasChildrens', 1
    @redis.rpush "thread:#{data[:parentId]}", id
    now = Time.now.utc.to_i
    id = @redis.incr 1
    comment = {id: id, comment: comment, time: now}
    childsOfKey = parentId ? subCommentsKey(parentId) : commentsKey(itemId)
    @redis.multi do |multi|
    @redis.del jsonKey itemId
    @redis.hmset commentKey(id), comment.map{|k,v| [k,v]}.flatten(1)
    @redis.zadd childsOfKey, now, id
    end

    @redis.hmset "comment:#{id}", data.map{|k,v| [k,v]}.flatten(1)
    id
    end

    def get itemId
    parsedComments = @redis.get "item:#{itemId}:parsedComments"
    if !parsedComments
    data = multiFetch "item:#{itemId}:comments"
    parsedComments = processComments data
    @redis.set "item:#{itemId}:parsedComments", parsedComments.to_yaml

    def get id
    jsonId = jsonKey id
    jsonComments = @redis.get jsonId
    if jsonComments
    JSON.parse jsonComments
    else
    parsedComments = YAML.load parsedComments
    comments = multiFetch commentsKey id
    @redis.set jsonId, comments.to_json
    comments
    end
    parsedComments
    end

    private
    def leaf id
    comment = @redis.hgetall commentKey id
    comment['comments'] = multiFetch subCommentsKey id
    comment
    end

    def hashComment itemId, comment, parentId
    {
    id: SecureRandom.uuid,
    itemId: itemId,
    comment: comment,
    parentId: parentId == nil ? itemId : parentId,
    time: Time.now,
    hasChildrens: 0
    }
    def delete comment, parentId, itemId, isMainThread = true
    innerDelete comment, parentId, isMainThread
    @redis.del jsonKey itemId
    end

    def multiFetch keyName
    commentList = @redis.lrange keyName, 0, -1
    @redis.multi do |multi|
    commentList.map { |commentId| multi.hgetall "comment:#{commentId}" }

    private


    def multiFetch key
    commentIds = @redis.zrange key, 0, -1
    comments = @redis.multi do |multi|
    commentIds.map { |id| multi.hgetall(commentKey id)}
    end
    processComments comments
    end


    def processComments comments
    result = Hash.new
    comments.each do |data|
    data = OpenStruct.new data
    result[data.id] = data
    if data.hasChildrens == '1'
    childData = multiFetch "thread:#{data.id}"
    data.comments = processComments childData
    comments.each do |comment|
    subCommentsId = subCommentsKey comment['id']
    if @redis.zcard(subCommentsId) > 0
    comment['comments'] = multiFetch subCommentsId
    end
    end
    result
    end
    end


    def innerDelete comment, parentId, isMainThread
    if comment.kind_of? Array
    comment.each {|c| innerDelete c, parentId, isMainThread}
    else
    id = comment['id']
    comments = comment['comments']
    childsKey = isMainThread ? commentsKey(parentId) : subCommentsKey(parentId)
    @redis.del commentKey id
    @redis.zrem childsKey, id
    innerDelete comments, id, false if comments
    end
    end


    def jsonKey id
    "comment:#{id}:json"
    end


    def commentKey id
    "comment:#{id}"
    end


    def commentsKey id
    "comments:#{id}"
    end


    def subCommentsKey id
    "comment:#{id}:sub"
    end

    end



    def printComments comments, tabs = 0
    comments.each do |comment|
    tabs.times { putc "\t" }
    puts "#{comment['comment']}"
    if comment['comments']
    printComments comment['comments'], tabs + 1
    end
    end
    end


    def saveDummy cr, id
    tree = cr.save id, 'Tree'
    root = cr.save id, 'Root', tree
    trunk = cr.save id, 'Trunk', tree
    branch = cr.save id, 'Branch', trunk
    leaf = cr.save id, 'Leaf', branch
    top = cr.save id, 'Top', tree
    end


    command = ARGV[0]
    id = ARGV[1]
    cr = CommentsRepository.new


    case command
    when 's'
    saveDummy cr, id
    when 'p'
    printComments cr.get id
    when 'l'
    printComments [cr.leaf(id)]
    when 'd'
    cr.delete cr.get(id), id, id
    when 'r'
    cr.delete cr.leaf(ARGV[2]), ARGV[3], id, false
    when 'j'
    puts cr.get(id).to_json
    end
  8. @Loupi Loupi revised this gist May 22, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion nested-redis.rb
    Original file line number Diff line number Diff line change
    @@ -27,7 +27,7 @@ def save itemId, comment, parentId = nil

    def get itemId
    parsedComments = @redis.get "item:#{itemId}:parsedComments"
    if true#!parsedComments
    if !parsedComments
    data = multiFetch "item:#{itemId}:comments"
    parsedComments = processComments data
    @redis.set "item:#{itemId}:parsedComments", parsedComments.to_yaml
  9. @Loupi Loupi revised this gist May 22, 2012. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions nested-redis.rb
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    #based on http://forrst.com/posts/Nested_Comments_system_using_Redis-Ror

    require 'ostruct'
    require 'redis'
    require 'securerandom'
  10. @invalid-email-address Anonymous created this gist May 22, 2012.
    70 changes: 70 additions & 0 deletions nested-redis.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,70 @@
    require 'ostruct'
    require 'redis'
    require 'securerandom'
    require 'yaml'

    class CommentsRepository
    def initialize
    @redis = Redis.new
    end

    def save itemId, comment, parentId = nil
    @redis.del "item:#{itemId}:parsedComments"
    data = hashComment itemId, comment, parentId
    id = data[:id]
    if parentId == nil
    @redis.rpush "item:#{itemId}:comments", id
    else
    @redis.hset "comment:#{parentId}", 'hasChildrens', 1
    @redis.rpush "thread:#{data[:parentId]}", id
    end

    @redis.hmset "comment:#{id}", data.map{|k,v| [k,v]}.flatten(1)
    id
    end

    def get itemId
    parsedComments = @redis.get "item:#{itemId}:parsedComments"
    if true#!parsedComments
    data = multiFetch "item:#{itemId}:comments"
    parsedComments = processComments data
    @redis.set "item:#{itemId}:parsedComments", parsedComments.to_yaml
    else
    parsedComments = YAML.load parsedComments
    end
    parsedComments
    end

    private

    def hashComment itemId, comment, parentId
    {
    id: SecureRandom.uuid,
    itemId: itemId,
    comment: comment,
    parentId: parentId == nil ? itemId : parentId,
    time: Time.now,
    hasChildrens: 0
    }
    end

    def multiFetch keyName
    commentList = @redis.lrange keyName, 0, -1
    @redis.multi do |multi|
    commentList.map { |commentId| multi.hgetall "comment:#{commentId}" }
    end
    end

    def processComments comments
    result = Hash.new
    comments.each do |data|
    data = OpenStruct.new data
    result[data.id] = data
    if data.hasChildrens == '1'
    childData = multiFetch "thread:#{data.id}"
    data.comments = processComments childData
    end
    end
    result
    end
    end