Redis Set¶
RedisSet is currently experimental. The API may change in future releases based on feedback.
Only JSON-serializable value types are supported:
str, int, float, and bool.
RedisSet is an unordered, unique-member collection backed by a Redis SET. Items appear only once and have no defined order.
Unlike regular Redis types (RedisStr, RedisList, etc.), RedisSet stores its data in a separate Redis key โ not inline with the model's JSON. It is a pure Redis proxy: every mutation goes straight to Redis and the local mirror is never used to overwrite server state.
from pydantic import Field
from rapyer import AtomicRedisModel
from rapyer.types import RedisSet
class Article(AtomicRedisModel):
title: str = "untitled"
tags: RedisSet[str] = Field(default_factory=RedisSet)
Plain set[...] annotations are auto-converted to RedisSet as well:
class Article(AtomicRedisModel):
tags: set[str] = Field(default_factory=set) # becomes RedisSet[str]
Basic Usage¶
article = Article(title="Hello Redis")
await article.asave()
await article.tags.aadd("python")
await article.tags.aadd("redis")
await article.tags.aadd("python") # idempotent โ set still has 2 members
await article.tags.acontains("python") # True
await article.tags.asize() # 2
await article.tags.amembers() # {"python", "redis"}
Generic Type Support¶
RedisSet accepts any JSON-serializable type as its generic parameter:
class Numbers(AtomicRedisModel):
values: RedisSet[int] = Field(default_factory=RedisSet)
class Flags(AtomicRedisModel):
bits: RedisSet[bool] = Field(default_factory=RedisSet)
Async Operations¶
| Operation | Method | Description |
|---|---|---|
| add | await article.tags.aadd(value) |
Add a single member |
| add many | await article.tags.aadd_many(values) |
Add multiple members in one command |
| remove | await article.tags.aremove(value) |
Remove a member (returns bool) |
| pop | await article.tags.apop() |
Remove and return a random member |
| clear | await article.tags.aclear() |
Remove all members |
| contains | await article.tags.acontains(value) |
Check membership |
| members | await article.tags.amembers() |
Return all members as a Python set |
| size | await article.tags.asize() |
Return the number of members |
Set Algebra Across Models¶
aunion, aintersect, and adifference operate against other RedisSet fields and run entirely on the Redis side:
a = Article(title="A")
b = Article(title="B")
await a.asave()
await b.asave()
await a.tags.aadd_many(["alpha", "beta", "gamma"])
await b.tags.aadd_many(["gamma", "delta"])
await a.tags.aunion(b.tags) # {"alpha", "beta", "gamma", "delta"}
await a.tags.aintersect(b.tags) # {"gamma"}
await a.tags.adifference(b.tags) # {"alpha", "beta"}
All three accept any number of other RedisSet operands.
Pipeline (Sync) Operations¶
Inside a pipeline context, the standard Python set mutators are batched into atomic Redis commands:
async with article.apipeline():
article.tags.add("python")
article.tags.update(["redis", "asyncio"])
article.tags.discard("legacy")
article.tags |= {"backend"}
Supported sync mutators:
add(value),update(*iterables)remove(value),discard(value),clear()difference_update,intersection_update,symmetric_difference_update|=,&=,-=,^=
No sync pop()
RedisSet does not support sync pop(). Use await tags.apop() for an atomic Redis-side pick.
remove inside a pipeline does not raise
Outside a pipeline, remove(value) matches Python's set.remove and raises KeyError for a missing member. Inside apipeline() the local mirror is not authoritative, so remove behaves like discard and silently queues SREM.
Optional Fields¶
RedisSet fields can be optional:
Assign a value after init to start using it:
How It Works¶
RedisSet is a special field type โ it stores data in a separate Redis key derived from the parent model's key:
This means:
- Save/delete are automatic โ
asave()andadelete()on the parent model handle the set's Redis key and TTL. - TTL is inherited โ if the parent model has a TTL configured, the set's key gets the same expiration.
- No inline state โ every async operation hits Redis directly.
Cannot use aupdate() with special fields
Special fields manage their own Redis storage and cannot be passed to aupdate(). Use the field's own methods instead.