Skip to content

Commit 78b73b2

Browse files
authored
Add support for querying datetime fields (#711)
This PR makes a few changes, all to support datetime fields better. ## Store datetime fields as NUMERIC data in Redis Previously, we stored datetimes as TAGs (why, I don't know). This means any query you might think would work with dates would not actually work. Now, we convert datetime fields on models to and from NUMERIC fields in Redis, storing them as UNIX timestamps. This means we can support rich datetime queries by converting the input to a timestamp and comparing it against the stored timestamp. ## New data migrations system Introduces a data migrations system and CLI command (`om migrate-data`). The first built-in data migration will migrate datetime fields indexed as TAG data to NUMERIC data. **Important:** You must run this migration in order to use this version of Redis OM with datetime fields, as the models will now expect to get NUMERIC data for datetimes fields. ## New top-level `om` CLI command Moves OM CLI commands into a new top-level `om` command while preserving backwards compatibility for the old `migrate` command. This makes room for normal migration and now data migration commands. ## Expanded schema migrations system To match the new `om migrate-data` sub-commands, such as `run` and `create`, we expand the schema migrations system to support these features through file-based migrations. This introduces the possibility to roll back migrations, as well as practical concerns like deploying migrations to different environments and checking whether or not they have applied.
1 parent a473702 commit 78b73b2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+5675
-160
lines changed

.codespellrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[codespell]
2+
skip = .git,poetry.lock,*.pyc,__pycache__,env,venv,.venv,.env,node_modules,*.egg-info,build,dist
3+
ignore-words-list = redis,migrator,datetime,timestamp,asyncio,redisearch,pydantic,ulid,hnsw

.github/wordlist.txt

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,35 @@ unix
7070
utf
7171
validator
7272
validators
73-
virtualenv
73+
virtualenv
74+
datetime
75+
Datetime
76+
reindex
77+
schemas
78+
Pre
79+
DataMigrationError
80+
ConnectionError
81+
TimeoutError
82+
ValidationError
83+
RTO
84+
benchmarked
85+
SSD
86+
Benchmarking
87+
ai
88+
claude
89+
unasync
90+
RedisModel
91+
EmbeddedJsonModel
92+
JsonModels
93+
Metaclass
94+
HNSW
95+
KNN
96+
DateTime
97+
yml
98+
pyproject
99+
toml
100+
github
101+
ULID
102+
booleans
103+
instantiation
104+
MyModel

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ dmypy.json
128128

129129
# Pyre type checker
130130
.pyre/
131-
data
131+
/data
132132

133133
# Makefile install checker
134134
.install.stamp

.pre-commit-config.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
repos:
2+
- repo: https://github.com/codespell-project/codespell
3+
rev: v2.2.6
4+
hooks:
5+
- id: codespell
6+
args: [--write-changes]
7+
exclude: ^(poetry\.lock|\.git/|docs/.*\.md)$

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ lint: $(INSTALL_STAMP) dist
5454
$(POETRY) run isort --profile=black --lines-after-imports=2 ./tests/ $(NAME) $(SYNC_NAME)
5555
$(POETRY) run black ./tests/ $(NAME)
5656
$(POETRY) run flake8 --ignore=E231,E501,E712,E731,F401,W503 ./tests/ $(NAME) $(SYNC_NAME)
57-
$(POETRY) run mypy ./tests/ $(NAME) $(SYNC_NAME) --ignore-missing-imports --exclude migrate.py --exclude _compat\.py$
57+
$(POETRY) run mypy ./tests/ --ignore-missing-imports --exclude migrate.py --exclude _compat\.py$$
5858
$(POETRY) run bandit -r $(NAME) $(SYNC_NAME) -s B608
5959

6060
.PHONY: format

README.md

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ Next, we'll show you the **rich query expressions** and **embedded models** Redi
216216

217217
Redis OM comes with a rich query language that allows you to query Redis with Python expressions.
218218

219-
To show how this works, we'll make a small change to the `Customer` model we defined earlier. We'll add `Field(index=True)` to tell Redis OM that we want to index the `last_name` and `age` fields:
219+
To show how this works, we'll make a small change to the `Customer` model we defined earlier. We'll add `index=True` to the model class to tell Redis OM that we want to index all fields in the model:
220220

221221
```python
222222
import datetime
@@ -225,18 +225,17 @@ from typing import Optional
225225
from pydantic import EmailStr
226226

227227
from redis_om import (
228-
Field,
229228
HashModel,
230229
Migrator
231230
)
232231

233232

234-
class Customer(HashModel):
233+
class Customer(HashModel, index=True):
235234
first_name: str
236-
last_name: str = Field(index=True)
235+
last_name: str
237236
email: EmailStr
238237
join_date: datetime.date
239-
age: int = Field(index=True)
238+
age: int
240239
bio: Optional[str] = None
241240

242241

@@ -294,14 +293,13 @@ class Address(EmbeddedJsonModel):
294293
postal_code: str = Field(index=True)
295294

296295

297-
class Customer(JsonModel):
298-
first_name: str = Field(index=True)
299-
last_name: str = Field(index=True)
300-
email: str = Field(index=True)
296+
class Customer(JsonModel, index=True):
297+
first_name: str
298+
last_name: str
299+
email: str
301300
join_date: datetime.date
302-
age: int = Field(index=True)
303-
bio: Optional[str] = Field(index=True, full_text_search=True,
304-
default="")
301+
age: int
302+
bio: Optional[str] = Field(full_text_search=True, default="")
305303

306304
# Creates an embedded model.
307305
address: Address
@@ -392,9 +390,9 @@ credential_provider = create_from_default_azure_credential(
392390

393391
db = Redis(host="cluster-name.region.redis.azure.net", port=10000, ssl=True, ssl_cert_reqs=None, credential_provider=credential_provider)
394392
db.flushdb()
395-
class User(HashModel):
393+
class User(HashModel, index=True):
396394
first_name: str
397-
last_name: str = Field(index=True)
395+
last_name: str
398396

399397
class Meta:
400398
database = db

aredis_om/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from .async_redis import redis # isort:skip
22
from .checks import has_redis_json, has_redisearch
33
from .connections import get_redis_connection
4-
from .model.migrations.migrator import MigrationError, Migrator
4+
from .model.migrations.schema.legacy_migrator import MigrationError, Migrator
55
from .model.model import (
66
EmbeddedJsonModel,
77
Field,

aredis_om/cli/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# CLI package

aredis_om/cli/main.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""
2+
Redis OM CLI - Main entry point for the async 'om' command.
3+
"""
4+
5+
import click
6+
7+
from ..model.cli.migrate import migrate
8+
from ..model.cli.migrate_data import migrate_data
9+
10+
11+
@click.group()
12+
@click.version_option()
13+
def om():
14+
"""Redis OM Python CLI - Object mapping and migrations for Redis."""
15+
pass
16+
17+
18+
# Add subcommands
19+
om.add_command(migrate)
20+
om.add_command(migrate_data, name="migrate-data")
21+
22+
23+
if __name__ == "__main__":
24+
om()

aredis_om/model/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from .migrations.migrator import MigrationError, Migrator
1+
from .migrations.schema.legacy_migrator import MigrationError, Migrator
22
from .model import (
33
EmbeddedJsonModel,
44
Field,

0 commit comments

Comments
 (0)