or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

bitmap-operations.mdcore-clients.mdgeneric-operations.mdgeospatial-operations.mdhash-operations.mdindex.mdlist-operations.mdlua-scripting.mdpubsub-operations.mdserver-management.mdserver-operations.mdset-operations.mdsorted-set-operations.mdstack-extensions.mdstream-operations.mdstring-operations.mdtransaction-operations.mdvalkey-support.md

sorted-set-operations.mddocs/

0

# Sorted Set Operations

1

2

Redis sorted set data type operations providing ordered collections of unique strings with associated scores. Sorted sets combine the uniqueness of sets with the ordering capabilities of lists, making them perfect for leaderboards, priority queues, time-series data, and ranked collections. Each member has an associated score used for ordering.

3

4

## Capabilities

5

6

### Basic Sorted Set Operations

7

8

Core sorted set manipulation functions for adding, removing, and querying members with scores.

9

10

```python { .api }

11

def zadd(

12

self,

13

name: KeyT,

14

mapping: Optional[Mapping[AnyKeyT, EncodableT]] = None,

15

nx: bool = False,

16

xx: bool = False,

17

ch: bool = False,

18

incr: bool = False,

19

gt: bool = False,

20

lt: bool = False,

21

**kwargs: EncodableT

22

) -> ResponseT: ...

23

24

def zrem(self, name: KeyT, *values: EncodableT) -> ResponseT: ...

25

26

def zcard(self, name: KeyT) -> ResponseT: ...

27

28

def zcount(self, name: KeyT, min: ZScoreT, max: ZScoreT) -> ResponseT: ...

29

30

def zscore(self, name: KeyT, value: EncodableT) -> Optional[float]: ...

31

32

def zmscore(self, key: KeyT, members: Iterable[EncodableT]) -> List[Optional[float]]: ...

33

34

def zrank(self, name: KeyT, value: EncodableT) -> Optional[int]: ...

35

36

def zrevrank(self, name: KeyT, value: EncodableT) -> Optional[int]: ...

37

38

def zincrby(self, name: KeyT, amount: float, value: EncodableT) -> ResponseT: ...

39

```

40

41

### Range Operations

42

43

Functions for retrieving members by rank or score ranges with flexible ordering options.

44

45

```python { .api }

46

def zrange(

47

self,

48

name: KeyT,

49

start: int,

50

end: int,

51

desc: bool = False,

52

withscores: bool = False,

53

score_cast_func: Optional[Callable] = float,

54

byscore: bool = False,

55

bylex: bool = False,

56

offset: Optional[int] = None,

57

num: Optional[int] = None

58

) -> Union[List[bytes], List[Tuple[bytes, float]]]: ...

59

60

def zrevrange(

61

self,

62

name: KeyT,

63

start: int,

64

end: int,

65

withscores: bool = False,

66

score_cast_func: Optional[Callable] = float

67

) -> Union[List[bytes], List[Tuple[bytes, float]]]: ...

68

69

def zrangebyscore(

70

self,

71

name: KeyT,

72

min: ZScoreT,

73

max: ZScoreT,

74

start: Optional[int] = None,

75

num: Optional[int] = None,

76

withscores: bool = False,

77

score_cast_func: Optional[Callable] = float

78

) -> Union[List[bytes], List[Tuple[bytes, float]]]: ...

79

80

def zrevrangebyscore(

81

self,

82

name: KeyT,

83

max: ZScoreT,

84

min: ZScoreT,

85

start: Optional[int] = None,

86

num: Optional[int] = None,

87

withscores: bool = False,

88

score_cast_func: Optional[Callable] = float

89

) -> Union[List[bytes], List[Tuple[bytes, float]]]: ...

90

91

def zrangebylex(

92

self,

93

name: KeyT,

94

min: EncodableT,

95

max: EncodableT,

96

start: Optional[int] = None,

97

num: Optional[int] = None

98

) -> List[bytes]: ...

99

100

def zrevrangebylex(

101

self,

102

name: KeyT,

103

max: EncodableT,

104

min: EncodableT,

105

start: Optional[int] = None,

106

num: Optional[int] = None

107

) -> List[bytes]: ...

108

```

109

110

### Range Removal Operations

111

112

Functions for removing members by rank or score ranges.

113

114

```python { .api }

115

def zremrangebyrank(self, name: KeyT, start: int, end: int) -> ResponseT: ...

116

117

def zremrangebyscore(self, name: KeyT, min: ZScoreT, max: ZScoreT) -> ResponseT: ...

118

119

def zremrangebylex(self, name: KeyT, min: EncodableT, max: EncodableT) -> ResponseT: ...

120

```

121

122

### Pop Operations

123

124

Functions for atomically removing and returning members with highest or lowest scores.

125

126

```python { .api }

127

def zpopmin(self, name: KeyT, count: Optional[int] = None) -> Union[List[Union[bytes, float]], List[Tuple[bytes, float]]]: ...

128

129

def zpopmax(self, name: KeyT, count: Optional[int] = None) -> Union[List[Union[bytes, float]], List[Tuple[bytes, float]]]: ...

130

131

def bzpopmin(

132

self,

133

keys: Union[KeyT, Iterable[KeyT]],

134

timeout: float = 0

135

) -> Optional[Tuple[bytes, bytes, float]]: ...

136

137

def bzpopmax(

138

self,

139

keys: Union[KeyT, Iterable[KeyT]],

140

timeout: float = 0

141

) -> Optional[Tuple[bytes, bytes, float]]: ...

142

```

143

144

### Set Algebra Operations

145

146

Functions for performing operations between multiple sorted sets.

147

148

```python { .api }

149

def zinter(

150

self,

151

keys: Iterable[KeyT],

152

aggregate: Optional[Literal["SUM", "MIN", "MAX"]] = None,

153

withscores: bool = False

154

) -> Union[List[bytes], List[Tuple[bytes, float]]]: ...

155

156

def zinterstore(

157

self,

158

dest: KeyT,

159

keys: Union[Mapping[AnyKeyT, float], Iterable[KeyT]],

160

aggregate: Optional[Literal["SUM", "MIN", "MAX"]] = None

161

) -> ResponseT: ...

162

163

def zunionstore(

164

self,

165

dest: KeyT,

166

keys: Union[Mapping[AnyKeyT, float], Iterable[KeyT]],

167

aggregate: Optional[Literal["SUM", "MIN", "MAX"]] = None

168

) -> ResponseT: ...

169

170

def zdiff(

171

self,

172

keys: Iterable[KeyT],

173

withscores: bool = False

174

) -> Union[List[bytes], List[Tuple[bytes, float]]]: ...

175

176

def zdiffstore(self, dest: KeyT, keys: Iterable[KeyT]) -> ResponseT: ...

177

```

178

179

### Random Selection and Scanning

180

181

Functions for random member selection and iterating through large sorted sets.

182

183

```python { .api }

184

def zrandmember(

185

self,

186

key: KeyT,

187

count: Optional[int] = None,

188

withscores: bool = False

189

) -> Union[Optional[bytes], List[bytes], List[Tuple[bytes, float]]]: ...

190

191

def zscan(

192

self,

193

name: KeyT,

194

cursor: int = 0,

195

match: Optional[PatternT] = None,

196

count: Optional[int] = None,

197

score_cast_func: Optional[Callable] = float

198

) -> ResponseT: ...

199

200

def zscan_iter(

201

self,

202

name: KeyT,

203

match: Optional[PatternT] = None,

204

count: Optional[int] = None,

205

score_cast_func: Optional[Callable] = float

206

) -> Iterator[Tuple[bytes, float]]: ...

207

```

208

209

## Usage Examples

210

211

### Basic Sorted Set Operations

212

213

```python

214

import fakeredis

215

216

client = fakeredis.FakeRedis()

217

218

# Add members with scores

219

client.zadd('leaderboard', {

220

'alice': 100,

221

'bob': 85,

222

'charlie': 92,

223

'david': 78

224

})

225

226

# Add single member

227

client.zadd('leaderboard', {'eve': 95})

228

229

# Get sorted set size

230

size = client.zcard('leaderboard')

231

print(f"Leaderboard size: {size}") # 5

232

233

# Get member score

234

alice_score = client.zscore('leaderboard', 'alice')

235

print(f"Alice's score: {alice_score}") # 100.0

236

237

# Get multiple scores

238

scores = client.zmscore('leaderboard', ['alice', 'bob', 'unknown'])

239

print(f"Scores: {scores}") # [100.0, 85.0, None]

240

241

# Get member rank (0-based, lowest score = rank 0)

242

alice_rank = client.zrank('leaderboard', 'alice')

243

print(f"Alice's rank (asc): {alice_rank}") # 4 (highest score)

244

245

# Get reverse rank (highest score = rank 0)

246

alice_revrank = client.zrevrank('leaderboard', 'alice')

247

print(f"Alice's rank (desc): {alice_revrank}") # 0 (highest score)

248

```

249

250

### Range Queries

251

252

```python

253

import fakeredis

254

255

client = fakeredis.FakeRedis()

256

257

# Setup leaderboard

258

client.zadd('scores', {

259

'player1': 1500,

260

'player2': 1200,

261

'player3': 1800,

262

'player4': 900,

263

'player5': 1650

264

})

265

266

# Get top 3 players (highest scores)

267

top_players = client.zrevrange('scores', 0, 2, withscores=True)

268

print("Top 3 players:")

269

for player, score in top_players:

270

print(f" {player.decode()}: {score}")

271

272

# Get bottom 2 players (lowest scores)

273

bottom_players = client.zrange('scores', 0, 1, withscores=True)

274

print("Bottom 2 players:")

275

for player, score in bottom_players:

276

print(f" {player.decode()}: {score}")

277

278

# Get players with scores between 1000 and 1600

279

mid_range = client.zrangebyscore('scores', 1000, 1600, withscores=True)

280

print("Mid-range players (1000-1600):")

281

for player, score in mid_range:

282

print(f" {player.decode()}: {score}")

283

284

# Count players in score range

285

count = client.zcount('scores', 1200, 1700)

286

print(f"Players with scores 1200-1700: {count}")

287

```

288

289

### Leaderboard Implementation

290

291

```python

292

import fakeredis

293

294

class Leaderboard:

295

def __init__(self, client, name):

296

self.client = client

297

self.name = name

298

299

def add_score(self, player, score):

300

"""Add or update player score"""

301

return self.client.zadd(self.name, {player: score})

302

303

def increment_score(self, player, amount):

304

"""Increment player's score"""

305

return self.client.zincrby(self.name, amount, player)

306

307

def get_score(self, player):

308

"""Get player's current score"""

309

return self.client.zscore(self.name, player)

310

311

def get_rank(self, player):

312

"""Get player's rank (1-based, 1 = highest score)"""

313

rank = self.client.zrevrank(self.name, player)

314

return rank + 1 if rank is not None else None

315

316

def get_top_players(self, limit=10):

317

"""Get top N players"""

318

return self.client.zrevrange(self.name, 0, limit - 1, withscores=True)

319

320

def get_player_range(self, start_rank, end_rank):

321

"""Get players in rank range (1-based)"""

322

return self.client.zrevrange(

323

self.name,

324

start_rank - 1,

325

end_rank - 1,

326

withscores=True

327

)

328

329

def get_around_player(self, player, context=2):

330

"""Get players around a specific player"""

331

rank = self.client.zrevrank(self.name, player)

332

if rank is None:

333

return []

334

335

start = max(0, rank - context)

336

end = rank + context

337

return self.client.zrevrange(self.name, start, end, withscores=True)

338

339

def remove_player(self, player):

340

"""Remove player from leaderboard"""

341

return self.client.zrem(self.name, player)

342

343

# Usage

344

client = fakeredis.FakeRedis()

345

leaderboard = Leaderboard(client, 'game_scores')

346

347

# Add initial scores

348

players_scores = {

349

'Alice': 2500,

350

'Bob': 1800,

351

'Charlie': 2200,

352

'Diana': 1900,

353

'Eve': 2100

354

}

355

356

for player, score in players_scores.items():

357

leaderboard.add_score(player, score)

358

359

# Game events

360

leaderboard.increment_score('Bob', 300) # Bob scores 300 points

361

leaderboard.increment_score('Diana', 150) # Diana scores 150 points

362

363

# Query leaderboard

364

print("Top 3 players:")

365

for i, (player, score) in enumerate(leaderboard.get_top_players(3), 1):

366

print(f"{i}. {player.decode()}: {score}")

367

368

# Find Alice's position

369

alice_rank = leaderboard.get_rank('Alice')

370

alice_score = leaderboard.get_score('Alice')

371

print(f"Alice: Rank {alice_rank}, Score {alice_score}")

372

373

# Show players around Bob

374

around_bob = leaderboard.get_around_player('Bob')

375

print("Players around Bob:")

376

for player, score in around_bob:

377

print(f" {player.decode()}: {score}")

378

```

379

380

### Time-based Rankings

381

382

```python

383

import fakeredis

384

import time

385

386

class TimeBasedRanking:

387

def __init__(self, client, name):

388

self.client = client

389

self.name = name

390

391

def add_event(self, item, timestamp=None):

392

"""Add item with timestamp as score"""

393

if timestamp is None:

394

timestamp = time.time()

395

return self.client.zadd(self.name, {item: timestamp})

396

397

def get_recent_items(self, limit=10):

398

"""Get most recent items"""

399

return self.client.zrevrange(self.name, 0, limit - 1, withscores=True)

400

401

def get_items_since(self, since_timestamp):

402

"""Get items added since a specific timestamp"""

403

return self.client.zrangebyscore(

404

self.name,

405

since_timestamp,

406

'+inf',

407

withscores=True

408

)

409

410

def get_items_in_time_range(self, start_time, end_time):

411

"""Get items in a specific time range"""

412

return self.client.zrangebyscore(

413

self.name,

414

start_time,

415

end_time,

416

withscores=True

417

)

418

419

def cleanup_old_items(self, before_timestamp):

420

"""Remove items older than timestamp"""

421

return self.client.zremrangebyscore(self.name, '-inf', before_timestamp)

422

423

# Usage

424

client = fakeredis.FakeRedis()

425

activity = TimeBasedRanking(client, 'user_activity')

426

427

# Record activities with timestamps

428

current_time = time.time()

429

activity.add_event('login', current_time - 3600) # 1 hour ago

430

activity.add_event('page_view', current_time - 1800) # 30 min ago

431

activity.add_event('purchase', current_time - 600) # 10 min ago

432

activity.add_event('logout', current_time) # Now

433

434

# Get recent activities

435

recent = activity.get_recent_items(5)

436

print("Recent activities:")

437

for event, timestamp in recent:

438

print(f" {event.decode()}: {timestamp}")

439

440

# Get activities in last 45 minutes

441

since_45_min = activity.get_items_since(current_time - 2700)

442

print("Activities in last 45 minutes:")

443

for event, timestamp in since_45_min:

444

print(f" {event.decode()}: {timestamp}")

445

```

446

447

### Priority Queue Implementation

448

449

```python

450

import fakeredis

451

452

class PriorityQueue:

453

def __init__(self, client, name):

454

self.client = client

455

self.name = name

456

457

def enqueue(self, item, priority):

458

"""Add item with priority (higher number = higher priority)"""

459

return self.client.zadd(self.name, {item: priority})

460

461

def dequeue(self):

462

"""Remove and return highest priority item"""

463

result = self.client.zpopmax(self.name, 1)

464

if result:

465

return result[0][0].decode(), result[0][1] # item, priority

466

return None, None

467

468

def dequeue_multiple(self, count):

469

"""Remove and return multiple highest priority items"""

470

result = self.client.zpopmax(self.name, count)

471

return [(item.decode(), priority) for item, priority in result]

472

473

def peek(self):

474

"""Look at highest priority item without removing"""

475

result = self.client.zrevrange(self.name, 0, 0, withscores=True)

476

if result:

477

return result[0][0].decode(), result[0][1]

478

return None, None

479

480

def update_priority(self, item, new_priority):

481

"""Update item's priority"""

482

return self.client.zadd(self.name, {item: new_priority})

483

484

def size(self):

485

"""Get queue size"""

486

return self.client.zcard(self.name)

487

488

def is_empty(self):

489

"""Check if queue is empty"""

490

return self.size() == 0

491

492

# Usage

493

client = fakeredis.FakeRedis()

494

pq = PriorityQueue(client, 'task_queue')

495

496

# Add tasks with priorities

497

pq.enqueue('send_email', 1)

498

pq.enqueue('backup_database', 5)

499

pq.enqueue('process_payment', 10)

500

pq.enqueue('generate_report', 3)

501

502

# Process tasks by priority

503

while not pq.is_empty():

504

task, priority = pq.dequeue()

505

print(f"Processing: {task} (priority: {priority})")

506

```

507

508

### Score Manipulation and Set Operations

509

510

```python

511

import fakeredis

512

513

client = fakeredis.FakeRedis()

514

515

# Setup multiple score sets

516

client.zadd('math_scores', {'alice': 95, 'bob': 87, 'charlie': 92})

517

client.zadd('english_scores', {'alice': 88, 'bob': 91, 'diana': 89})

518

client.zadd('science_scores', {'alice': 93, 'charlie': 85, 'diana': 94})

519

520

# Increment scores

521

client.zincrby('math_scores', 5, 'bob') # Bob improves by 5 points

522

523

# Conditional adds (nx = only if doesn't exist, xx = only if exists)

524

client.zadd('math_scores', {'eve': 90}, nx=True) # Add new student

525

client.zadd('math_scores', {'alice': 100}, xx=True) # Update existing student

526

527

# Union of scores (sum by default)

528

client.zunionstore('total_scores', ['math_scores', 'english_scores', 'science_scores'])

529

530

# Union with weights

531

client.zunionstore(

532

'weighted_scores',

533

{'math_scores': 0.4, 'english_scores': 0.3, 'science_scores': 0.3}

534

)

535

536

# Intersection (students who have all three scores)

537

client.zinterstore('complete_scores', ['math_scores', 'english_scores', 'science_scores'])

538

539

# Get final rankings

540

total_rankings = client.zrevrange('weighted_scores', 0, -1, withscores=True)

541

print("Final weighted rankings:")

542

for student, score in total_rankings:

543

print(f" {student.decode()}: {score:.1f}")

544

```

545

546

### Random Selection from Sorted Set

547

548

```python

549

import fakeredis

550

551

client = fakeredis.FakeRedis()

552

553

# Setup a sorted set of items with popularity scores

554

client.zadd('popular_items', {

555

'item_a': 100,

556

'item_b': 85,

557

'item_c': 70,

558

'item_d': 95,

559

'item_e': 60

560

})

561

562

# Get random item

563

random_item = client.zrandmember('popular_items')

564

print(f"Random item: {random_item.decode()}")

565

566

# Get multiple random items with scores

567

random_items = client.zrandmember('popular_items', 3, withscores=True)

568

print("Random items with scores:")

569

for item, score in random_items:

570

print(f" {item.decode()}: {score}")

571

572

# Weighted random selection (simulate by score ranges)

573

def weighted_random_selection(client, key, count=1):

574

# Get total score sum

575

all_items = client.zrange(key, 0, -1, withscores=True)

576

total_score = sum(score for _, score in all_items)

577

578

# This is a simplified example - in practice you'd implement

579

# proper weighted random selection

580

return client.zrandmember(key, count, withscores=True)

581

582

weighted_selection = weighted_random_selection(client, 'popular_items', 2)

583

print("Weighted random selection:")

584

for item, score in weighted_selection:

585

print(f" {item.decode()}: {score}")

586

```

587

588

### Scanning Large Sorted Sets

589

590

```python

591

import fakeredis

592

593

client = fakeredis.FakeRedis()

594

595

# Create a large sorted set

596

for i in range(1000):

597

client.zadd('large_sorted_set', {f'member_{i}': i})

598

599

# Scan through all members

600

cursor = 0

601

all_members = []

602

603

while True:

604

cursor, members = client.zscan('large_sorted_set', cursor=cursor, count=100)

605

all_members.extend(members)

606

if cursor == 0:

607

break

608

609

print(f"Total members scanned: {len(all_members)}")

610

611

# Scan with pattern matching

612

matching_members = []

613

for member, score in client.zscan_iter('large_sorted_set', match='member_1*'):

614

matching_members.append((member.decode(), score))

615

616

print(f"Members matching 'member_1*': {len(matching_members)}")

617

618

# Use the iterator for efficient processing

619

high_scorers = []

620

for member, score in client.zscan_iter('large_sorted_set'):

621

if score > 900:

622

high_scorers.append((member.decode(), score))

623

624

print(f"High scorers (>900): {len(high_scorers)}")

625

```