/
hashes.rb
258 lines (236 loc) · 7.96 KB
/
hashes.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# 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