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

set-operations.mddocs/

0

# Set Operations

1

2

Redis set data type operations providing unordered collections of unique strings. Sets are ideal for tracking unique items, performing set algebra operations (union, intersection, difference), and implementing features like tags, categories, and relationship tracking. Sets can hold up to 2^32 - 1 members.

3

4

## Capabilities

5

6

### Basic Set Operations

7

8

Core set manipulation functions for adding, removing, and testing membership.

9

10

```python { .api }

11

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

12

13

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

14

15

def sismember(self, name: KeyT, value: EncodableT) -> ResponseT: ...

16

17

def smismember(self, name: KeyT, values: Iterable[EncodableT]) -> List[bool]: ...

18

19

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

20

21

def smembers(self, name: KeyT) -> Set[bytes]: ...

22

23

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

24

25

def srandmember(self, name: KeyT, number: Optional[int] = None) -> Union[Optional[bytes], List[bytes]]: ...

26

```

27

28

### Set Algebra Operations

29

30

Functions for performing mathematical set operations between multiple sets.

31

32

```python { .api }

33

def sinter(self, keys: Union[KeyT, Iterable[KeyT]], *args: KeyT) -> Set[bytes]: ...

34

35

def sintercard(self, numkeys: int, keys: Iterable[KeyT], limit: int = 0) -> ResponseT: ...

36

37

def sinterstore(self, dest: KeyT, keys: Union[KeyT, Iterable[KeyT]], *args: KeyT) -> ResponseT: ...

38

39

def sunion(self, keys: Union[KeyT, Iterable[KeyT]], *args: KeyT) -> Set[bytes]: ...

40

41

def sunionstore(self, dest: KeyT, keys: Union[KeyT, Iterable[KeyT]], *args: KeyT) -> ResponseT: ...

42

43

def sdiff(self, keys: Union[KeyT, Iterable[KeyT]], *args: KeyT) -> Set[bytes]: ...

44

45

def sdiffstore(self, dest: KeyT, keys: Union[KeyT, Iterable[KeyT]], *args: KeyT) -> ResponseT: ...

46

```

47

48

### Set Movement Operations

49

50

Functions for moving members between sets.

51

52

```python { .api }

53

def smove(self, src: KeyT, dst: KeyT, value: EncodableT) -> ResponseT: ...

54

```

55

56

### Set Scanning

57

58

Iterator functions for efficiently traversing large sets.

59

60

```python { .api }

61

def sscan(

62

self,

63

name: KeyT,

64

cursor: int = 0,

65

match: Optional[PatternT] = None,

66

count: Optional[int] = None

67

) -> ResponseT: ...

68

69

def sscan_iter(

70

self,

71

name: KeyT,

72

match: Optional[PatternT] = None,

73

count: Optional[int] = None

74

) -> Iterator[bytes]: ...

75

```

76

77

## Usage Examples

78

79

### Basic Set Operations

80

81

```python

82

import fakeredis

83

84

client = fakeredis.FakeRedis()

85

86

# Add members to sets

87

client.sadd('fruits', 'apple', 'banana', 'orange')

88

client.sadd('colors', 'red', 'yellow', 'orange')

89

90

# Check membership

91

is_member = client.sismember('fruits', 'apple')

92

print(f"Apple in fruits: {is_member}") # True

93

94

# Check multiple memberships

95

memberships = client.smismember('fruits', ['apple', 'grape', 'banana'])

96

print(f"Memberships: {memberships}") # [True, False, True]

97

98

# Get set size

99

fruit_count = client.scard('fruits')

100

print(f"Number of fruits: {fruit_count}") # 3

101

102

# Get all members

103

all_fruits = client.smembers('fruits')

104

print("All fruits:", {member.decode() for member in all_fruits})

105

106

# Remove members

107

removed_count = client.srem('fruits', 'banana', 'grape')

108

print(f"Removed {removed_count} items") # 1 (grape doesn't exist)

109

```

110

111

### Random Selection and Sampling

112

113

```python

114

import fakeredis

115

116

client = fakeredis.FakeRedis()

117

118

# Setup a set of items

119

client.sadd('items', 'item1', 'item2', 'item3', 'item4', 'item5', 'item6')

120

121

# Get random member without removing

122

random_item = client.srandmember('items')

123

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

124

125

# Get multiple random members (with possible duplicates)

126

random_items = client.srandmember('items', 3)

127

print("Random items:", [item.decode() for item in random_items])

128

129

# Get multiple random members (without duplicates)

130

unique_random = client.srandmember('items', -3) # Negative count = no duplicates

131

print("Unique random items:", [item.decode() for item in unique_random])

132

133

# Pop random members (removes them from set)

134

popped_item = client.spop('items')

135

print(f"Popped item: {popped_item.decode()}")

136

137

# Pop multiple items

138

popped_items = client.spop('items', 2)

139

print("Popped items:", [item.decode() for item in popped_items])

140

141

# Check remaining items

142

remaining = client.smembers('items')

143

print("Remaining items:", {item.decode() for item in remaining})

144

```

145

146

### Set Algebra Operations

147

148

```python

149

import fakeredis

150

151

client = fakeredis.FakeRedis()

152

153

# Setup test sets

154

client.sadd('programming_languages', 'python', 'java', 'javascript', 'go')

155

client.sadd('web_languages', 'javascript', 'php', 'python', 'ruby')

156

client.sadd('compiled_languages', 'java', 'go', 'c', 'rust')

157

158

# Intersection - languages that are both programming and web languages

159

web_and_prog = client.sinter('programming_languages', 'web_languages')

160

print("Web & Programming:", {lang.decode() for lang in web_and_prog})

161

162

# Union - all languages combined

163

all_languages = client.sunion('programming_languages', 'web_languages', 'compiled_languages')

164

print("All languages:", {lang.decode() for lang in all_languages})

165

166

# Difference - programming languages that are not web languages

167

prog_not_web = client.sdiff('programming_languages', 'web_languages')

168

print("Programming but not Web:", {lang.decode() for lang in prog_not_web})

169

170

# Store intersection result

171

client.sinterstore('web_programming', 'programming_languages', 'web_languages')

172

stored_result = client.smembers('web_programming')

173

print("Stored intersection:", {lang.decode() for lang in stored_result})

174

175

# Intersection cardinality (count without returning members)

176

intersection_count = client.sintercard(2, ['programming_languages', 'compiled_languages'])

177

print(f"Programming & Compiled count: {intersection_count}")

178

```

179

180

### Moving Members Between Sets

181

182

```python

183

import fakeredis

184

185

client = fakeredis.FakeRedis()

186

187

# Setup source and destination sets

188

client.sadd('todo', 'task1', 'task2', 'task3')

189

client.sadd('completed', 'old_task')

190

191

# Move task from todo to completed

192

moved = client.smove('todo', 'completed', 'task1')

193

print(f"Moved task1: {moved}") # 1 if successful

194

195

# Try to move non-existent member

196

moved = client.smove('todo', 'completed', 'nonexistent')

197

print(f"Moved nonexistent: {moved}") # 0 if member doesn't exist

198

199

# Check final state

200

todo_items = client.smembers('todo')

201

completed_items = client.smembers('completed')

202

print("TODO:", {item.decode() for item in todo_items})

203

print("Completed:", {item.decode() for item in completed_items})

204

```

205

206

### Scanning Large Sets

207

208

```python

209

import fakeredis

210

211

client = fakeredis.FakeRedis()

212

213

# Create a large set

214

for i in range(1000):

215

client.sadd('large_set', f'member_{i}')

216

217

# Scan through all members

218

cursor = 0

219

all_members = []

220

221

while True:

222

cursor, members = client.sscan('large_set', cursor=cursor, count=100)

223

all_members.extend([member.decode() for member in members])

224

if cursor == 0:

225

break

226

227

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

228

229

# Scan with pattern matching

230

matching_members = []

231

for member in client.sscan_iter('large_set', match='member_1*'):

232

matching_members.append(member.decode())

233

234

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

235

```

236

237

### Pattern: Tag System

238

239

```python

240

import fakeredis

241

242

class TagSystem:

243

def __init__(self, client):

244

self.client = client

245

246

def add_tags(self, item_id, *tags):

247

"""Add tags to an item"""

248

return self.client.sadd(f'item:{item_id}:tags', *tags)

249

250

def remove_tags(self, item_id, *tags):

251

"""Remove tags from an item"""

252

return self.client.srem(f'item:{item_id}:tags', *tags)

253

254

def get_tags(self, item_id):

255

"""Get all tags for an item"""

256

tags = self.client.smembers(f'item:{item_id}:tags')

257

return {tag.decode() for tag in tags}

258

259

def has_tag(self, item_id, tag):

260

"""Check if item has a specific tag"""

261

return bool(self.client.sismember(f'item:{item_id}:tags', tag))

262

263

def get_items_with_all_tags(self, *tags):

264

"""Get items that have all specified tags"""

265

# This is a simplified version - in practice, you'd maintain reverse indexes

266

# Here we demonstrate the set operations concept

267

tag_keys = [f'tag:{tag}:items' for tag in tags]

268

if tag_keys:

269

item_ids = self.client.sinter(tag_keys)

270

return {item_id.decode() for item_id in item_ids}

271

return set()

272

273

def add_item_to_tag_index(self, item_id, tag):

274

"""Add item to tag's reverse index"""

275

return self.client.sadd(f'tag:{tag}:items', item_id)

276

277

def remove_item_from_tag_index(self, item_id, tag):

278

"""Remove item from tag's reverse index"""

279

return self.client.srem(f'tag:{tag}:items', item_id)

280

281

# Usage

282

client = fakeredis.FakeRedis()

283

tag_system = TagSystem(client)

284

285

# Add tags to items

286

tag_system.add_tags('article:1', 'python', 'programming', 'tutorial')

287

tag_system.add_tags('article:2', 'python', 'web', 'flask')

288

tag_system.add_tags('article:3', 'javascript', 'programming', 'tutorial')

289

290

# Build reverse indexes

291

for item, tags in [

292

('article:1', ['python', 'programming', 'tutorial']),

293

('article:2', ['python', 'web', 'flask']),

294

('article:3', ['javascript', 'programming', 'tutorial'])

295

]:

296

for tag in tags:

297

tag_system.add_item_to_tag_index(item, tag)

298

299

# Query operations

300

python_articles = client.smembers('tag:python:items')

301

print("Python articles:", {item.decode() for item in python_articles})

302

303

# Find articles that are both programming and tutorials

304

prog_tutorials = client.sinter('tag:programming:items', 'tag:tutorial:items')

305

print("Programming tutorials:", {item.decode() for item in prog_tutorials})

306

307

# Check article tags

308

article_tags = tag_system.get_tags('article:1')

309

print("Article 1 tags:", article_tags)

310

```

311

312

### Pattern: User Permissions System

313

314

```python

315

import fakeredis

316

317

class PermissionSystem:

318

def __init__(self, client):

319

self.client = client

320

321

def grant_permission(self, user_id, *permissions):

322

"""Grant permissions to a user"""

323

return self.client.sadd(f'user:{user_id}:permissions', *permissions)

324

325

def revoke_permission(self, user_id, *permissions):

326

"""Revoke permissions from a user"""

327

return self.client.srem(f'user:{user_id}:permissions', *permissions)

328

329

def has_permission(self, user_id, permission):

330

"""Check if user has a specific permission"""

331

return bool(self.client.sismember(f'user:{user_id}:permissions', permission))

332

333

def has_any_permission(self, user_id, *permissions):

334

"""Check if user has any of the specified permissions"""

335

user_perms = self.client.smembers(f'user:{user_id}:permissions')

336

user_perms_set = {perm.decode() for perm in user_perms}

337

return any(perm in user_perms_set for perm in permissions)

338

339

def has_all_permissions(self, user_id, *permissions):

340

"""Check if user has all specified permissions"""

341

memberships = self.client.smismember(f'user:{user_id}:permissions', permissions)

342

return all(memberships)

343

344

def get_user_permissions(self, user_id):

345

"""Get all permissions for a user"""

346

perms = self.client.smembers(f'user:{user_id}:permissions')

347

return {perm.decode() for perm in perms}

348

349

def get_common_permissions(self, *user_ids):

350

"""Get permissions common to all specified users"""

351

if not user_ids:

352

return set()

353

354

keys = [f'user:{user_id}:permissions' for user_id in user_ids]

355

common = self.client.sinter(keys)

356

return {perm.decode() for perm in common}

357

358

# Usage

359

client = fakeredis.FakeRedis()

360

perms = PermissionSystem(client)

361

362

# Grant permissions

363

perms.grant_permission('user:alice', 'read_posts', 'write_posts', 'edit_profile')

364

perms.grant_permission('user:bob', 'read_posts', 'edit_profile', 'admin_panel')

365

perms.grant_permission('user:charlie', 'read_posts', 'moderate_comments')

366

367

# Check permissions

368

can_write = perms.has_permission('user:alice', 'write_posts')

369

print(f"Alice can write posts: {can_write}")

370

371

can_admin = perms.has_any_permission('user:bob', 'admin_panel', 'super_admin')

372

print(f"Bob has admin permissions: {can_admin}")

373

374

has_basic_perms = perms.has_all_permissions('user:charlie', 'read_posts', 'write_posts')

375

print(f"Charlie has basic permissions: {has_basic_perms}")

376

377

# Find common permissions

378

common = perms.get_common_permissions('user:alice', 'user:bob', 'user:charlie')

379

print(f"Common permissions: {common}")

380

```

381

382

### Pattern: Friend/Follower System

383

384

```python

385

import fakeredis

386

387

class SocialSystem:

388

def __init__(self, client):

389

self.client = client

390

391

def follow(self, follower_id, following_id):

392

"""User follows another user"""

393

# Add to follower's following set

394

self.client.sadd(f'user:{follower_id}:following', following_id)

395

# Add to followed user's followers set

396

self.client.sadd(f'user:{following_id}:followers', follower_id)

397

398

def unfollow(self, follower_id, following_id):

399

"""User unfollows another user"""

400

self.client.srem(f'user:{follower_id}:following', following_id)

401

self.client.srem(f'user:{following_id}:followers', follower_id)

402

403

def get_followers(self, user_id):

404

"""Get all followers of a user"""

405

followers = self.client.smembers(f'user:{user_id}:followers')

406

return {follower.decode() for follower in followers}

407

408

def get_following(self, user_id):

409

"""Get all users that this user is following"""

410

following = self.client.smembers(f'user:{user_id}:following')

411

return {user.decode() for user in following}

412

413

def is_following(self, follower_id, following_id):

414

"""Check if one user is following another"""

415

return bool(self.client.sismember(f'user:{follower_id}:following', following_id))

416

417

def get_mutual_follows(self, user1_id, user2_id):

418

"""Get users that both users are following"""

419

mutual = self.client.sinter(

420

f'user:{user1_id}:following',

421

f'user:{user2_id}:following'

422

)

423

return {user.decode() for user in mutual}

424

425

def suggest_follows(self, user_id):

426

"""Suggest users to follow based on mutual connections"""

427

following = self.get_following(user_id)

428

suggestions = set()

429

430

# Find followers of people this user follows

431

for followed_user in following:

432

their_following = self.get_following(followed_user)

433

suggestions.update(their_following)

434

435

# Remove already following and self

436

suggestions.discard(user_id)

437

suggestions -= following

438

439

return suggestions

440

441

# Usage

442

client = fakeredis.FakeRedis()

443

social = SocialSystem(client)

444

445

# Build social network

446

social.follow('alice', 'bob')

447

social.follow('alice', 'charlie')

448

social.follow('bob', 'charlie')

449

social.follow('bob', 'david')

450

social.follow('charlie', 'david')

451

social.follow('david', 'alice')

452

453

# Query the network

454

alice_following = social.get_following('alice')

455

print(f"Alice is following: {alice_following}")

456

457

bob_followers = social.get_followers('bob')

458

print(f"Bob's followers: {bob_followers}")

459

460

mutual = social.get_mutual_follows('alice', 'bob')

461

print(f"Alice and Bob both follow: {mutual}")

462

463

# Get suggestions for Alice

464

suggestions = social.suggest_follows('alice')

465

print(f"Suggested follows for Alice: {suggestions}")

466

```