# frozen_string_literal: true
class Redis
module Commands
module Hashes
# Get the number of fields in a hash.
#
# @param [String] key
# @return [Integer] number of fields in the hash
def hlen(key)
send_command([:hlen, key])
end
# Set one or more hash values.
#
# @example
# redis.hset("hash", "f1", "v1", "f2", "v2") # => 2
# redis.hset("hash", { "f1" => "v1", "f2" => "v2" }) # => 2
#
# @param [String] key
# @param [Array<String> | Hash<String, String>] attrs array or hash of fields and values
# @return [Integer] The number of fields that were added to the hash
def hset(key, *attrs)
attrs = attrs.first.flatten if attrs.size == 1 && attrs.first.is_a?(Hash)
send_command([:hset, key, *attrs])
end
# Set the value of a hash field, only if the field does not exist.
#
# @param [String] key
# @param [String] field
# @param [String] value
# @return [Boolean] whether or not the field was **added** to the hash
def hsetnx(key, field, value)
send_command([:hsetnx, key, field, value], &Boolify)
end
# Set one or more hash values.
#
# @example
# redis.hmset("hash", "f1", "v1", "f2", "v2")
# # => "OK"
#
# @param [String] key
# @param [Array<String>] attrs array of fields and values
# @return [String] `"OK"`
#
# @see #mapped_hmset
def hmset(key, *attrs)
send_command([:hmset, key] + attrs)
end
# Set one or more hash values.
#
# @example
# redis.mapped_hmset("hash", { "f1" => "v1", "f2" => "v2" })
# # => "OK"
#
# @param [String] key
# @param [Hash] hash a non-empty hash with fields mapping to values
# @return [String] `"OK"`
#
# @see #hmset
def mapped_hmset(key, hash)
hmset(key, hash.flatten)
end
# Get the value of a hash field.
#
# @param [String] key
# @param [String] field
# @return [String]
def hget(key, field)
send_command([:hget, key, field])
end
# Get the values of all the given hash fields.
#
# @example
# redis.hmget("hash", "f1", "f2")
# # => ["v1", "v2"]
#
# @param [String] key
# @param [Array<String>] fields array of fields
# @return [Array<String>] an array of values for the specified fields
#
# @see #mapped_hmget
def hmget(key, *fields, &blk)
fields.flatten!(1)
send_command([:hmget, key].concat(fields), &blk)
end
# Get the values of all the given hash fields.
#
# @example
# redis.mapped_hmget("hash", "f1", "f2")
# # => { "f1" => "v1", "f2" => "v2" }
#
# @param [String] key
# @param [Array<String>] fields array of fields
# @return [Hash] a hash mapping the specified fields to their values
#
# @see #hmget
def mapped_hmget(key, *fields)
fields.flatten!(1)
hmget(key, fields) do |reply|
if reply.is_a?(Array)
Hash[fields.zip(reply)]
else
reply
end
end
end
# Get one or more random fields from a hash.
#
# @example Get one random field
# redis.hrandfield("hash")
# # => "f1"
# @example Get multiple random fields
# redis.hrandfield("hash", 2)
# # => ["f1, "f2"]
# @example Get multiple random fields with values
# redis.hrandfield("hash", 2, with_values: true)
# # => [["f1", "s1"], ["f2", "s2"]]
#
# @param [String] key
# @param [Integer] count
# @param [Hash] options
# - `:with_values => true`: include values in output
#
# @return [nil, String, Array<String>, Array<[String, Float]>]
# - when `key` does not exist, `nil`
# - when `count` is not specified, a field name
# - when `count` is specified and `:with_values` is not specified, an array of field names
# - when `:with_values` is specified, an array with `[field, value]` pairs
def hrandfield(key, count = nil, withvalues: false, with_values: withvalues)
if with_values && count.nil?
raise ArgumentError, "count argument must be specified"
end
args = [:hrandfield, key]
args << count if count
args << "WITHVALUES" if with_values
parser = Pairify if with_values
send_command(args, &parser)
end
# Delete one or more hash fields.
#
# @param [String] key
# @param [String, Array<String>] field
# @return [Integer] the number of fields that were removed from the hash
def hdel(key, *fields)
fields.flatten!(1)
send_command([:hdel, key].concat(fields))
end
# Determine if a hash field exists.
#
# @param [String] key
# @param [String] field
# @return [Boolean] whether or not the field exists in the hash
def hexists(key, field)
send_command([:hexists, key, field], &Boolify)
end
# Increment the integer value of a hash field by the given integer number.
#
# @param [String] key
# @param [String] field
# @param [Integer] increment
# @return [Integer] value of the field after incrementing it
def hincrby(key, field, increment)
send_command([:hincrby, key, field, Integer(increment)])
end
# Increment the numeric value of a hash field by the given float number.
#
# @param [String] key
# @param [String] field
# @param [Float] increment
# @return [Float] value of the field after incrementing it
def hincrbyfloat(key, field, increment)
send_command([:hincrbyfloat, key, field, Float(increment)], &Floatify)
end
# Get all the fields in a hash.
#
# @param [String] key
# @return [Array<String>]
def hkeys(key)
send_command([:hkeys, key])
end
# Get all the values in a hash.
#
# @param [String] key
# @return [Array<String>]
def hvals(key)
send_command([:hvals, key])
end
# Get all the fields and values in a hash.
#
# @param [String] key
# @return [Hash<String, String>]
def hgetall(key)
send_command([:hgetall, key], &Hashify)
end
# Scan a hash
#
# @example Retrieve the first batch of key/value pairs in a hash
# redis.hscan("hash", 0)
#
# @param [String, Integer] cursor the cursor of the iteration
# @param [Hash] options
# - `:match => String`: only return keys matching the pattern
# - `:count => Integer`: return count keys at most per iteration
#
# @return [String, Array<[String, String]>] the next cursor and all found keys
#
# See the [Redis Server HSCAN documentation](https://redis.io/docs/latest/commands/hscan/) for further details
def hscan(key, cursor, **options)
_scan(:hscan, cursor, [key], **options) do |reply|
[reply[0], reply[1].each_slice(2).to_a]
end
end
# Scan a hash
#
# @example Retrieve all of the key/value pairs in a hash
# redis.hscan_each("hash").to_a
# # => [["key70", "70"], ["key80", "80"]]
#
# @param [Hash] options
# - `:match => String`: only return keys matching the pattern
# - `:count => Integer`: return count keys at most per iteration
#
# @return [Enumerator] an enumerator for all found keys
#
# See the [Redis Server HSCAN documentation](https://redis.io/docs/latest/commands/hscan/) for further details
def hscan_each(key, **options, &block)
return to_enum(:hscan_each, key, **options) unless block_given?
cursor = 0
loop do
cursor, values = hscan(key, cursor, **options)
values.each(&block)
break if cursor == "0"
end
end
end
end
end