Class: Familia::SortedSet

Inherits:
DataType show all
Defined in:
lib/familia/data_type/types/sorted_set.rb

Instance Attribute Summary collapse

Attributes included from Settings

#current_key_version, #default_expiration, #delim, #encryption_keys, #encryption_personalization, #logical_database, #prefix, #suffix, #transaction_mode

Instance Method Summary collapse

Methods included from Features::Autoloader

autoload_files, included, normalize_to_config_name

Methods included from DataType::Serialization

#deserialize_value, #deserialize_values, #deserialize_values_with_nil, #serialize_value

Methods included from DataType::DatabaseCommands

#current_expiration, #delete!, #echo, #exists?, #expire, #expireat, #move, #persist, #rename, #renamenx, #type

Methods included from DataType::Connection

#dbclient, #dbkey, #direct_access, #uri

Methods included from Connection::Behavior

#connect, #create_dbclient, #multi, #normalize_uri, #pipeline, #pipelined, #transaction, #uri=, #url, #url=

Methods included from Settings

#configure, #default_suffix, #pipelined_mode, #pipelined_mode=

Methods included from Base

add_feature, #as_json, #expired?, #expires?, find_feature, #generate_id, #to_json, #to_s, #ttl, #update_expiration, #uuid

Constructor Details

This class inherits a constructor from Familia::DataType

Instance Attribute Details

#features_enabledObject (readonly) Originally defined in module Features

Returns the value of attribute features_enabled.

#logical_database(val = nil) ⇒ Object Originally defined in module DataType::ClassMethods

#parentObject Originally defined in module DataType::ClassMethods

Returns the value of attribute parent.

#prefixObject Originally defined in module DataType::ClassMethods

Returns the value of attribute prefix.

#suffixObject Originally defined in module DataType::ClassMethods

Returns the value of attribute suffix.

#uri(val = nil) ⇒ Object Originally defined in module DataType::ClassMethods

Returns the value of attribute uri.

Instance Method Details

#<<(val) ⇒ Integer

Note:

This is a non-standard operation for sorted sets as it doesn't allow specifying a custom score. Use add or []= for more control.

Adds a new element to the sorted set with the current timestamp as the score.

This method provides a convenient way to add elements to the sorted set without explicitly specifying a score. It uses the current Unix timestamp as the score, which effectively sorts elements by their insertion time.

Examples:

sorted_set << "new_element"

Parameters:

  • val (Object)

    The value to be added to the sorted set.

Returns:

  • (Integer)

    Returns 1 if the element is new and added, 0 if the element already existed and the score was updated.



36
37
38
# File 'lib/familia/data_type/types/sorted_set.rb', line 36

def <<(val)
  add(val)
end

#[]=(val, score) ⇒ Object

NOTE: The argument order is the reverse of #add. We do this to more naturally align with how the [] and []= methods are used.

e.g. obj.metrics[VALUE] = SCORE obj.metrics[VALUE] # => SCORE



47
48
49
# File 'lib/familia/data_type/types/sorted_set.rb', line 47

def []=(val, score)
  add val, score
end

#add(val, score = nil, nx: false, xx: false, gt: false, lt: false, ch: false) ⇒ Boolean Also known as: add_element

Note:

GT and LT options do NOT prevent adding new elements, they only affect update behavior for existing elements.

Note:

Default behavior (no options) adds new elements and updates existing ones unconditionally, matching standard Redis ZADD semantics.

Note:

INCR option is not supported. Use the increment method for ZINCRBY operations.

Adds an element to the sorted set with an optional score and ZADD options.

This method supports Redis ZADD options for conditional adds and updates:

  • NX: Only add new elements (don't update existing)
  • XX: Only update existing elements (don't add new)
  • GT: Only update if new score > current score
  • LT: Only update if new score < current score
  • CH: Return changed count (new + updated) instead of just new count

Examples:

Add new element with timestamp

metrics.add('pageview', Time.now.to_f)  #=> true

Preserve original timestamp on subsequent saves

index.add(email, Time.now.to_f, nx: true)  #=> true
index.add(email, Time.now.to_f, nx: true)  #=> false (unchanged)

Update timestamp only for existing entries

index.add(email, Time.now.to_f, xx: true)  #=> false (if doesn't exist)

Only update if new score is higher (leaderboard)

scores.add(player, 1000, gt: true)  #=> true (new entry)
scores.add(player, 1500, gt: true)  #=> false (updated)
scores.add(player, 1200, gt: true)  #=> false (not updated, score lower)

Track total changes for analytics

changed = metrics.add(user, score, ch: true)  #=> true (new or updated)

Combined options: only update existing, only if score increases

index.add(key, new_score, xx: true, gt: true)

Parameters:

  • val (Object)

    The value to add to the sorted set

  • score (Numeric, nil) (defaults to: nil)

    The score for ranking (defaults to current timestamp)

  • nx (Boolean) (defaults to: false)

    Only add new elements, don't update existing (default: false)

  • xx (Boolean) (defaults to: false)

    Only update existing elements, don't add new (default: false)

  • gt (Boolean) (defaults to: false)

    Only update if new score > current score (default: false)

  • lt (Boolean) (defaults to: false)

    Only update if new score < current score (default: false)

  • ch (Boolean) (defaults to: false)

    Return changed count instead of added count (default: false)

Returns:

  • (Boolean)

    Returns the return value from the redis gem's ZADD command. Returns true if element was added or changed (with CH option), false if element score was updated without change tracking or no operation occurred due to option constraints (NX, XX, GT, LT).

Raises:

  • (ArgumentError)

    If mutually exclusive options are specified together (NX+XX, GT+LT, NX+GT, NX+LT)



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/familia/data_type/types/sorted_set.rb', line 105

def add(val, score = nil, nx: false, xx: false, gt: false, lt: false, ch: false)
  score ||= Familia.now

  # Validate mutual exclusivity
  validate_zadd_options!(nx: nx, xx: xx, gt: gt, lt: lt)

  # Build options hash for redis gem
  opts = {}
  opts[:nx] = true if nx
  opts[:xx] = true if xx
  opts[:gt] = true if gt
  opts[:lt] = true if lt
  opts[:ch] = true if ch

  # Pass options to ZADD
  ret = if opts.empty?
    dbclient.zadd(dbkey, score, serialize_value(val))
  else
    dbclient.zadd(dbkey, score, serialize_value(val), **opts)
  end

  update_expiration
  ret
end

#at(idx) ⇒ Object



292
293
294
# File 'lib/familia/data_type/types/sorted_set.rb', line 292

def at(idx)
  range(idx, idx).first
end

#collectObject



187
188
189
# File 'lib/familia/data_type/types/sorted_set.rb', line 187

def collect(&)
  members.collect(&)
end

#collectrawObject



203
204
205
# File 'lib/familia/data_type/types/sorted_set.rb', line 203

def collectraw(&)
  membersraw.collect(&)
end

#decrement(val, by = 1) ⇒ Object Also known as: decr, decrby



277
278
279
# File 'lib/familia/data_type/types/sorted_set.rb', line 277

def decrement(val, by = 1)
  increment val, -by
end

#diff(*other_sets, withscores: false) ⇒ Array

Returns the difference between this sorted set and other sorted sets.

Examples:

Difference of two sorted sets

zset.diff(other_zset)  #=> ["unique_member"]

Parameters:

  • other_sets (Array<SortedSet, String>)

    Other sorted sets or key names

  • withscores (Boolean) (defaults to: false)

    Whether to include scores in result

Returns:

  • (Array)

    Members in this set but not in other sets



599
600
601
602
603
604
605
606
607
608
609
# File 'lib/familia/data_type/types/sorted_set.rb', line 599

def diff(*other_sets, withscores: false)
  keys = [dbkey] + resolve_set_keys(other_sets)

  result = if withscores
    dbclient.zdiff(*keys, withscores: true)
  else
    dbclient.zdiff(*keys)
  end

  process_set_operation_result(result, withscores: withscores)
end

#diffstore(destination, *other_sets) ⇒ Integer

Stores the difference of sorted sets into a destination key.

Parameters:

  • destination (String)

    Destination key name

  • other_sets (Array<SortedSet, String>)

    Other sorted sets or key names

Returns:

  • (Integer)

    Number of elements in the resulting sorted set



617
618
619
620
# File 'lib/familia/data_type/types/sorted_set.rb', line 617

def diffstore(destination, *other_sets)
  keys = [dbkey] + resolve_set_keys(other_sets)
  dbclient.zdiffstore(destination, keys)
end

#eachObject



179
180
181
# File 'lib/familia/data_type/types/sorted_set.rb', line 179

def each(&)
  members.each(&)
end

#each_with_indexObject



183
184
185
# File 'lib/familia/data_type/types/sorted_set.rb', line 183

def each_with_index(&)
  members.each_with_index(&)
end

#eachrawObject



195
196
197
# File 'lib/familia/data_type/types/sorted_set.rb', line 195

def eachraw(&)
  membersraw.each(&)
end

#eachraw_with_indexObject



199
200
201
# File 'lib/familia/data_type/types/sorted_set.rb', line 199

def eachraw_with_index(&)
  membersraw.each_with_index(&)
end

#element_countInteger Also known as: size, length, count

Returns the number of elements in the sorted set

Returns:

  • (Integer)

    number of elements



9
10
11
# File 'lib/familia/data_type/types/sorted_set.rb', line 9

def element_count
  dbclient.zcard dbkey
end

#empty?Boolean

Returns:

  • (Boolean)


16
17
18
# File 'lib/familia/data_type/types/sorted_set.rb', line 16

def empty?
  element_count.zero?
end

#firstObject

Return the first element in the list. Redis: ZRANGE(0)



297
298
299
# File 'lib/familia/data_type/types/sorted_set.rb', line 297

def first
  at(0)
end

#increment(val, by = 1) ⇒ Object Also known as: incr, incrby



271
272
273
# File 'lib/familia/data_type/types/sorted_set.rb', line 271

def increment(val, by = 1)
  dbclient.zincrby(dbkey, by, serialize_value(val)).to_i
end

#inter(*other_sets, weights: nil, aggregate: nil, withscores: false) ⇒ Array

Returns the intersection of this sorted set with other sorted sets.

Examples:

Intersection of two sorted sets

zset.inter(other_zset)  #=> ["common_member"]

Parameters:

  • other_sets (Array<SortedSet, String>)

    Other sorted sets or key names

  • weights (Array<Numeric>, nil) (defaults to: nil)

    Multiplication factors for each set's scores

  • aggregate (Symbol, nil) (defaults to: nil)

    How to aggregate scores (:sum, :min, :max)

Returns:

  • (Array)

    Array of members (or [member, score] pairs with withscores)



427
428
429
430
431
432
433
# File 'lib/familia/data_type/types/sorted_set.rb', line 427

def inter(*other_sets, weights: nil, aggregate: nil, withscores: false)
  keys = [dbkey] + resolve_set_keys(other_sets)
  opts = build_set_operation_opts(weights: weights, aggregate: aggregate, withscores: withscores)

  result = dbclient.zinter(*keys, **opts)
  process_set_operation_result(result, withscores: withscores)
end

#interstore(destination, *other_sets, weights: nil, aggregate: nil) ⇒ Integer

Stores the intersection of sorted sets into a destination key.

Parameters:

  • destination (String)

    Destination key name

  • other_sets (Array<SortedSet, String>)

    Other sorted sets or key names

  • weights (Array<Numeric>, nil) (defaults to: nil)

    Multiplication factors for each set's scores

  • aggregate (Symbol, nil) (defaults to: nil)

    How to aggregate scores (:sum, :min, :max)

Returns:

  • (Integer)

    Number of elements in the resulting sorted set



583
584
585
586
587
588
# File 'lib/familia/data_type/types/sorted_set.rb', line 583

def interstore(destination, *other_sets, weights: nil, aggregate: nil)
  keys = [dbkey] + resolve_set_keys(other_sets)
  opts = build_set_operation_opts(weights: weights, aggregate: aggregate)

  dbclient.zinterstore(destination, keys, **opts)
end

#lastObject

Return the last element in the list. Redis: ZRANGE(-1)



302
303
304
# File 'lib/familia/data_type/types/sorted_set.rb', line 302

def last
  at(-1)
end

#lexcount(min, max) ⇒ Integer

Counts members in a lexicographical range.

Parameters:

  • min (String)

    Minimum lex value

  • max (String)

    Maximum lex value

Returns:

  • (Integer)

    Number of members in the range



489
490
491
# File 'lib/familia/data_type/types/sorted_set.rb', line 489

def lexcount(min, max)
  dbclient.zlexcount(dbkey, min, max)
end

#member?(val) ⇒ Boolean Also known as: include?

Returns:

  • (Boolean)


137
138
139
140
# File 'lib/familia/data_type/types/sorted_set.rb', line 137

def member?(val)
  Familia.trace :MEMBER, nil, "#{val}<#{val.class}>" if Familia.debug?
  !rank(val).nil?
end

#members(count = -1,, opts = {}) ⇒ Object Also known as: to_a, all



155
156
157
158
159
# File 'lib/familia/data_type/types/sorted_set.rb', line 155

def members(count = -1, opts = {})
  count -= 1 if count.positive?
  elements = membersraw count, opts
  deserialize_values(*elements)
end

#membersraw(count = -1,, opts = {}) ⇒ Object



163
164
165
166
# File 'lib/familia/data_type/types/sorted_set.rb', line 163

def membersraw(count = -1, opts = {})
  count -= 1 if count.positive?
  rangeraw 0, count, opts
end

#mscore(*members) ⇒ Array<Float, nil>

Gets scores for multiple members at once.

Examples:

Get scores for multiple members

zset.mscore('member1', 'member2', 'member3')  #=> [1.0, 2.0, nil]

Parameters:

  • members (Array<Object>)

    Members to get scores for

Returns:

  • (Array<Float, nil>)

    Scores for each member (nil if member doesn't exist)



385
386
387
388
389
390
391
# File 'lib/familia/data_type/types/sorted_set.rb', line 385

def mscore(*members)
  return [] if members.empty?

  serialized = members.map { |m| serialize_value(m) }
  result = dbclient.zmscore(dbkey, *serialized)
  result.map { |s| s&.to_f }
end

#popmax(count = 1) ⇒ Array?

Removes and returns the member(s) with the highest score(s).

Examples:

Pop single highest-scoring member

zset.popmax  #=> ["member1", 100.0]

Pop multiple highest-scoring members

zset.popmax(3)  #=> [["member3", 100.0], ["member2", 90.0], ["member1", 80.0]]

Parameters:

  • count (Integer) (defaults to: 1)

    Number of members to pop (default: 1)

Returns:

  • (Array, nil)

    Array of [member, score] pairs, or single pair if count=1, or nil if set is empty



345
346
347
348
349
350
351
352
353
354
355
356
357
358
# File 'lib/familia/data_type/types/sorted_set.rb', line 345

def popmax(count = 1)
  result = dbclient.zpopmax(dbkey, count)
  return nil if result.nil? || result.empty?

  update_expiration

  if count == 1 && result.is_a?(Array) && result.length == 2 && !result[0].is_a?(Array)
    # Single result: [member, score]
    [deserialize_value(result[0]), result[1].to_f]
  else
    # Multiple results: [[member, score], ...]
    result.map { |member, score| [deserialize_value(member), score.to_f] }
  end
end

#popmin(count = 1) ⇒ Array?

Removes and returns the member(s) with the lowest score(s).

Examples:

Pop single lowest-scoring member

zset.popmin  #=> ["member1", 1.0]

Pop multiple lowest-scoring members

zset.popmin(3)  #=> [["member1", 1.0], ["member2", 2.0], ["member3", 3.0]]

Parameters:

  • count (Integer) (defaults to: 1)

    Number of members to pop (default: 1)

Returns:

  • (Array, nil)

    Array of [member, score] pairs, or single pair if count=1, or nil if set is empty



318
319
320
321
322
323
324
325
326
327
328
329
330
331
# File 'lib/familia/data_type/types/sorted_set.rb', line 318

def popmin(count = 1)
  result = dbclient.zpopmin(dbkey, count)
  return nil if result.nil? || result.empty?

  update_expiration

  if count == 1 && result.is_a?(Array) && result.length == 2 && !result[0].is_a?(Array)
    # Single result: [member, score]
    [deserialize_value(result[0]), result[1].to_f]
  else
    # Multiple results: [[member, score], ...]
    result.map { |member, score| [deserialize_value(member), score.to_f] }
  end
end

#randmember(count = nil, withscores: false) ⇒ Object, ...

Returns random member(s) from the sorted set.

Examples:

Get single random member

zset.randmember  #=> "member1"

Get 3 random members

zset.randmember(3)  #=> ["member1", "member2", "member3"]

Get random member with score

zset.randmember(1, withscores: true)  #=> [["member1", 1.0]]

Parameters:

  • count (Integer, nil) (defaults to: nil)

    Number of members to return (nil for single member)

  • withscores (Boolean) (defaults to: false)

    Whether to include scores in result

Returns:

  • (Object, Array, nil)

    Random member(s), or nil if set is empty



508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
# File 'lib/familia/data_type/types/sorted_set.rb', line 508

def randmember(count = nil, withscores: false)
  if count.nil?
    result = dbclient.zrandmember(dbkey)
    return nil if result.nil?

    deserialize_value(result)
  else
    result = if withscores
      dbclient.zrandmember(dbkey, count, withscores: true)
    else
      dbclient.zrandmember(dbkey, count)
    end

    return [] if result.nil? || result.empty?

    if withscores
      result.map { |member, score| [deserialize_value(member), score.to_f] }
    else
      deserialize_values(*result)
    end
  end
end

#range(sidx, eidx, opts = {}) ⇒ Object



211
212
213
214
215
# File 'lib/familia/data_type/types/sorted_set.rb', line 211

def range(sidx, eidx, opts = {})
  echo :range, Familia.pretty_stack(limit: 1) if Familia.debug
  elements = rangeraw(sidx, eidx, opts)
  deserialize_values(*elements)
end

#rangebylex(min, max, limit: nil) ⇒ Array

Returns members in a lexicographical range (requires all members have same score).

Examples:

Get members between 'a' and 'z' (inclusive)

zset.rangebylex('[a', '[z')  #=> ["apple", "banana", "cherry"]

Get first 10 members starting with 'a'

zset.rangebylex('[a', '(b', limit: [0, 10])

Parameters:

  • min (String)

    Minimum lex value (use '-' for unbounded, '[' or '(' prefix for inclusive/exclusive)

  • max (String)

    Maximum lex value (use '+' for unbounded, '[' or '(' prefix for inclusive/exclusive)

  • limit (Array<Integer>, nil) (defaults to: nil)

    [offset, count] for pagination

Returns:

  • (Array)

    Members in the lexicographical range



448
449
450
451
452
453
454
# File 'lib/familia/data_type/types/sorted_set.rb', line 448

def rangebylex(min, max, limit: nil)
  args = [dbkey, min, max]
  args.push(:limit, *limit) if limit

  result = dbclient.zrangebylex(*args)
  deserialize_values(*result)
end

#rangebyscore(sscore, escore, opts = {}) ⇒ Object

e.g. obj.metrics.rangebyscore (now-12.hours), now, :limit => [0, 10]



239
240
241
242
243
# File 'lib/familia/data_type/types/sorted_set.rb', line 239

def rangebyscore(sscore, escore, opts = {})
  echo :rangebyscore, Familia.pretty_stack(limit: 1) if Familia.debug
  elements = rangebyscoreraw(sscore, escore, opts)
  deserialize_values(*elements)
end

#rangebyscoreraw(sscore, escore, opts = {}) ⇒ Object



245
246
247
248
# File 'lib/familia/data_type/types/sorted_set.rb', line 245

def rangebyscoreraw(sscore, escore, opts = {})
  echo :rangebyscoreraw, Familia.pretty_stack(limit: 1) if Familia.debug
  dbclient.zrangebyscore(dbkey, sscore, escore, **opts)
end

#rangeraw(sidx, eidx, opts = {}) ⇒ Object



217
218
219
220
221
222
223
224
225
226
# File 'lib/familia/data_type/types/sorted_set.rb', line 217

def rangeraw(sidx, eidx, opts = {})
  # NOTE: :withscores (no underscore) is the correct naming for the
  # redis-4.x gem. We pass :withscores through explicitly b/c
  # dbclient.zrange et al only accept that one optional argument.
  # Passing `opts`` through leads to an ArgumentError:
  #
  #   sorted_sets.rb:374:in `zrevrange': wrong number of arguments (given 4, expected 3) (ArgumentError)
  #
  dbclient.zrange(dbkey, sidx, eidx, **opts)
end

#rank(v) ⇒ Object

rank of member +v+ when ordered lowest to highest (starts at 0)



144
145
146
147
# File 'lib/familia/data_type/types/sorted_set.rb', line 144

def rank(v)
  ret = dbclient.zrank dbkey, serialize_value(v)
  ret&.to_i
end

#remove_element(value) ⇒ Integer Also known as: remove

Removes a member from the sorted set

Parameters:

  • value

    The value to remove from the sorted set

Returns:

  • (Integer)

    The number of members that were removed (0 or 1)



286
287
288
289
# File 'lib/familia/data_type/types/sorted_set.rb', line 286

def remove_element(value)
  Familia.trace :REMOVE_ELEMENT, nil, "#{value}<#{value.class}>" if Familia.debug?
  dbclient.zrem dbkey, serialize_value(value)
end

#remrangebylex(min, max) ⇒ Integer

Removes members in a lexicographical range.

Parameters:

  • min (String)

    Minimum lex value

  • max (String)

    Maximum lex value

Returns:

  • (Integer)

    Number of members removed



477
478
479
480
481
# File 'lib/familia/data_type/types/sorted_set.rb', line 477

def remrangebylex(min, max)
  result = dbclient.zremrangebylex(dbkey, min, max)
  update_expiration
  result
end

#remrangebyrank(srank, erank) ⇒ Object



263
264
265
# File 'lib/familia/data_type/types/sorted_set.rb', line 263

def remrangebyrank(srank, erank)
  dbclient.zremrangebyrank dbkey, srank, erank
end

#remrangebyscore(sscore, escore) ⇒ Object



267
268
269
# File 'lib/familia/data_type/types/sorted_set.rb', line 267

def remrangebyscore(sscore, escore)
  dbclient.zremrangebyscore dbkey, sscore, escore
end

#revmembers(count = -1,, opts = {}) ⇒ Object



168
169
170
171
172
# File 'lib/familia/data_type/types/sorted_set.rb', line 168

def revmembers(count = -1, opts = {})
  count -= 1 if count.positive?
  elements = revmembersraw count, opts
  deserialize_values(*elements)
end

#revmembersraw(count = -1,, opts = {}) ⇒ Object



174
175
176
177
# File 'lib/familia/data_type/types/sorted_set.rb', line 174

def revmembersraw(count = -1, opts = {})
  count -= 1 if count.positive?
  revrangeraw 0, count, opts
end

#revrange(sidx, eidx, opts = {}) ⇒ Object



228
229
230
231
232
# File 'lib/familia/data_type/types/sorted_set.rb', line 228

def revrange(sidx, eidx, opts = {})
  echo :revrange, Familia.pretty_stack(limit: 1) if Familia.debug
  elements = revrangeraw(sidx, eidx, opts)
  deserialize_values(*elements)
end

#revrangebylex(max, min, limit: nil) ⇒ Array

Returns members in reverse lexicographical range.

Parameters:

  • max (String)

    Maximum lex value (use '+' for unbounded)

  • min (String)

    Minimum lex value (use '-' for unbounded)

  • limit (Array<Integer>, nil) (defaults to: nil)

    [offset, count] for pagination

Returns:

  • (Array)

    Members in reverse lexicographical range



463
464
465
466
467
468
469
# File 'lib/familia/data_type/types/sorted_set.rb', line 463

def revrangebylex(max, min, limit: nil)
  args = [dbkey, max, min]
  args.push(:limit, *limit) if limit

  result = dbclient.zrevrangebylex(*args)
  deserialize_values(*result)
end

#revrangebyscore(sscore, escore, opts = {}) ⇒ Object

e.g. obj.metrics.revrangebyscore (now-12.hours), now, :limit => [0, 10]



251
252
253
254
255
# File 'lib/familia/data_type/types/sorted_set.rb', line 251

def revrangebyscore(sscore, escore, opts = {})
  echo :revrangebyscore, Familia.pretty_stack(limit: 1) if Familia.debug
  elements = revrangebyscoreraw(sscore, escore, opts)
  deserialize_values(*elements)
end

#revrangebyscoreraw(sscore, escore, opts = {}) ⇒ Object



257
258
259
260
261
# File 'lib/familia/data_type/types/sorted_set.rb', line 257

def revrangebyscoreraw(sscore, escore, opts = {})
  echo :revrangebyscoreraw, Familia.pretty_stack(limit: 1) if Familia.debug
  opts[:with_scores] = true if opts[:withscores]
  dbclient.zrevrangebyscore(dbkey, sscore, escore, opts)
end

#revrangeraw(sidx, eidx, opts = {}) ⇒ Object



234
235
236
# File 'lib/familia/data_type/types/sorted_set.rb', line 234

def revrangeraw(sidx, eidx, opts = {})
  dbclient.zrevrange(dbkey, sidx, eidx, **opts)
end

#revrank(v) ⇒ Object

rank of member +v+ when ordered highest to lowest (starts at 0)



150
151
152
153
# File 'lib/familia/data_type/types/sorted_set.rb', line 150

def revrank(v)
  ret = dbclient.zrevrank dbkey, serialize_value(v)
  ret&.to_i
end

#scan(cursor = 0, match: nil, count: nil) ⇒ Array

Iterates over members using cursor-based scanning.

Examples:

Scan all members

cursor = 0
loop do
  cursor, members = zset.scan(cursor)
  members.each { |member, score| puts "#{member}: #{score}" }
  break if cursor == 0
end

Scan with pattern matching

cursor, members = zset.scan(0, match: 'user:*', count: 100)

Parameters:

  • cursor (Integer) (defaults to: 0)

    Cursor position (0 to start)

  • match (String, nil) (defaults to: nil)

    Pattern to match member names

  • count (Integer, nil) (defaults to: nil)

    Hint for number of elements to return per call

Returns:

  • (Array)

    [new_cursor, [[member, score], ...]]



549
550
551
552
553
554
555
556
557
558
# File 'lib/familia/data_type/types/sorted_set.rb', line 549

def scan(cursor = 0, match: nil, count: nil)
  opts = {}
  opts[:match] = match if match
  opts[:count] = count if count

  new_cursor, result = dbclient.zscan(dbkey, cursor, **opts)

  members = result.map { |member, score| [deserialize_value(member), score.to_f] }
  [new_cursor.to_i, members]
end

#score(val) ⇒ Object Also known as: []



131
132
133
134
# File 'lib/familia/data_type/types/sorted_set.rb', line 131

def score(val)
  ret = dbclient.zscore dbkey, serialize_value(val)
  ret&.to_f
end

#score_count(min, max) ⇒ Integer Also known as: zcount

Counts members within a score range.

Examples:

Count members with scores between 10 and 100

zset.score_count(10, 100)  #=> 5

Count members with scores up to 50

zset.score_count('-inf', 50)  #=> 3

Parameters:

  • min (Numeric, String)

    Minimum score (use '-inf' for unbounded)

  • max (Numeric, String)

    Maximum score (use '+inf' for unbounded)

Returns:

  • (Integer)

    Number of members with scores in the range



372
373
374
# File 'lib/familia/data_type/types/sorted_set.rb', line 372

def score_count(min, max)
  dbclient.zcount(dbkey, min, max)
end

#selectObject



191
192
193
# File 'lib/familia/data_type/types/sorted_set.rb', line 191

def select(&)
  members.select(&)
end

#selectrawObject



207
208
209
# File 'lib/familia/data_type/types/sorted_set.rb', line 207

def selectraw(&)
  membersraw.select(&)
end

#union(*other_sets, weights: nil, aggregate: nil, withscores: false) ⇒ Array

Returns the union of this sorted set with other sorted sets.

Examples:

Union of two sorted sets

zset.union(other_zset)  #=> ["member1", "member2", "member3"]

Union with weighted scores

zset.union(other_zset, weights: [1, 2])

Union with score aggregation

zset.union(other_zset, aggregate: :max)

Parameters:

  • other_sets (Array<SortedSet, String>)

    Other sorted sets or key names

  • weights (Array<Numeric>, nil) (defaults to: nil)

    Multiplication factors for each set's scores

  • aggregate (Symbol, nil) (defaults to: nil)

    How to aggregate scores (:sum, :min, :max)

Returns:

  • (Array)

    Array of members (or [member, score] pairs with withscores)



409
410
411
412
413
414
415
# File 'lib/familia/data_type/types/sorted_set.rb', line 409

def union(*other_sets, weights: nil, aggregate: nil, withscores: false)
  keys = [dbkey] + resolve_set_keys(other_sets)
  opts = build_set_operation_opts(weights: weights, aggregate: aggregate, withscores: withscores)

  result = dbclient.zunion(*keys, **opts)
  process_set_operation_result(result, withscores: withscores)
end

#unionstore(destination, *other_sets, weights: nil, aggregate: nil) ⇒ Integer

Stores the union of sorted sets into a destination key.

Parameters:

  • destination (String)

    Destination key name

  • other_sets (Array<SortedSet, String>)

    Other sorted sets or key names

  • weights (Array<Numeric>, nil) (defaults to: nil)

    Multiplication factors for each set's scores

  • aggregate (Symbol, nil) (defaults to: nil)

    How to aggregate scores (:sum, :min, :max)

Returns:

  • (Integer)

    Number of elements in the resulting sorted set



568
569
570
571
572
573
# File 'lib/familia/data_type/types/sorted_set.rb', line 568

def unionstore(destination, *other_sets, weights: nil, aggregate: nil)
  keys = [dbkey] + resolve_set_keys(other_sets)
  opts = build_set_operation_opts(weights: weights, aggregate: aggregate)

  dbclient.zunionstore(destination, keys, **opts)
end