Skip to content

Commit 3972d7d

Browse files
committed
Update documentation to use model-level indexing syntax
- Update models.md and README.md examples to use index=True on model class - Add section explaining field exclusion with Field(index=False) - Document migration from field-level to model-level indexing - Remove redundant index=True from individual fields in examples - Add comprehensive explanation of new indexing approach
1 parent a7daec2 commit 3972d7d

File tree

2 files changed

+98
-48
lines changed

2 files changed

+98
-48
lines changed

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

docs/models.md

Lines changed: 86 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ Here is a table of the settings available in the Meta object and what they contr
124124
| primary_key_pattern | A format string producing the base string for a Redis key representing this model. This string should accept a "pk" format argument. **Note:** This is a "new style" format string, which will be called with `.format()`. | "{pk}" |
125125
| database | A redis.asyncio.Redis or redis.Redis client instance that the model will use to communicate with Redis. | A new instance created with connections.get_redis_connection(). |
126126
| primary_key_creator_cls | A class that adheres to the PrimaryKeyCreator protocol, which Redis OM will use to create a primary key for a new model instance. | UlidPrimaryKey |
127-
| index_name | The RediSearch index name to use for this model. Only used if at least one of the model's fields are marked as indexable (`index=True`). | "{global_key_prefix}:{model_key_prefix}:index" |
127+
| index_name | The RediSearch index name to use for this model. Only used if the model is indexed (`index=True` on the model class). | "{global_key_prefix}:{model_key_prefix}:index" |
128128
| embedded | Whether or not this model is "embedded." Embedded models are not included in migrations that create and destroy indexes. Instead, their indexed fields are included in the index for the parent model. **Note**: Only `JsonModel` can have embedded models. | False |
129129
| encoding | The default encoding to use for strings. This encoding is given to redis-py at the connection level. In both cases, Redis OM will decode binary strings from Redis using your chosen encoding. | "utf-8" |
130130
## Configuring Pydantic
@@ -230,25 +230,77 @@ print(andrew.bio) # <- So we got the default value.
230230

231231
The model will then save this default value to Redis the next time you call `save()`.
232232

233-
## Marking a Field as Indexed
233+
## Model-Level Indexing
234234

235-
If you're using the RediSearch module in your Redis instance, you can mark a field as "indexed." As soon as you mark any field in a model as indexed, Redis OM will automatically create and manage an secondary index for the model for you, allowing you to query on any indexed field.
235+
If you're using the RediSearch module in your Redis instance, you can make your entire model indexed by adding `index=True` to the model class declaration. This automatically creates and manages a secondary index for the model, allowing you to query on any field.
236236

237-
To mark a field as indexed, you need to use the Redis OM `Field()` helper, like this:
237+
To make a model indexed, add `index=True` to your model class:
238238

239239
```python
240-
from redis_om import (
241-
Field,
242-
HashModel,
243-
)
240+
from redis_om import HashModel
244241

245242

246-
class Customer(HashModel):
243+
class Customer(HashModel, index=True):
247244
first_name: str
245+
last_name: str
246+
email: str
247+
age: int
248+
```
249+
250+
In this example, all fields in the `Customer` model will be indexed automatically.
251+
252+
### Excluding Fields from Indexing
253+
254+
By default, all fields in an indexed model are indexed. You can exclude specific fields from indexing using `Field(index=False)`:
255+
256+
```python
257+
from redis_om import HashModel, Field
258+
259+
260+
class Customer(HashModel, index=True):
261+
first_name: str = Field(index=False) # Not indexed
262+
last_name: str # Indexed (default)
263+
email: str # Indexed (default)
264+
age: int # Indexed (default)
265+
```
266+
267+
### Field-Specific Index Options
268+
269+
While you no longer need to specify `index=True` on individual fields (since the model is indexed), you can still use field-specific options to control indexing behavior:
270+
271+
```python
272+
from redis_om import HashModel, Field
273+
274+
275+
class Customer(HashModel, index=True):
276+
first_name: str = Field(index=False) # Excluded from index
277+
last_name: str # Indexed as TAG (default)
278+
bio: str = Field(full_text_search=True) # Indexed as TEXT for full-text search
279+
age: int = Field(sortable=True) # Indexed as NUMERIC, sortable
280+
category: str = Field(case_sensitive=False) # Indexed as TAG, case-insensitive
281+
```
282+
283+
### Migration from Field-Level Indexing
284+
285+
**Redis OM 1.0+ uses model-level indexing.** If you're upgrading from an earlier version, you'll need to update your models:
286+
287+
```python
288+
# Old way (0.x) - field-by-field indexing
289+
class Customer(HashModel):
290+
first_name: str = Field(index=True)
248291
last_name: str = Field(index=True)
292+
email: str = Field(index=True)
293+
age: int = Field(index=True, sortable=True)
294+
295+
# New way (1.0+) - model-level indexing
296+
class Customer(HashModel, index=True):
297+
first_name: str
298+
last_name: str
299+
email: str
300+
age: int = Field(sortable=True)
249301
```
250302

251-
In this example, we marked `Customer.last_name` as indexed.
303+
For detailed migration instructions, see the [0.x to 1.0 Migration Guide](migration_guide_0x_to_1x.md).
252304

253305
### Field Index Types
254306

@@ -265,17 +317,17 @@ Redis OM automatically chooses the appropriate RediSearch field type based on th
265317
By default, string fields are indexed as TAG fields, which only support exact matching and cannot be sorted. To make a string field sortable, you must create a TEXT field by adding `full_text_search=True`:
266318

267319
```python
268-
class Customer(HashModel):
320+
class Customer(HashModel, index=True):
269321
# TAG field - exact matching only, cannot be sorted
270-
category: str = Field(index=True)
271-
322+
category: str
323+
272324
# TEXT field - supports full-text search and sorting
273-
name: str = Field(index=True, sortable=True, full_text_search=True)
325+
name: str = Field(sortable=True, full_text_search=True)
274326
```
275327

276328
Only NUMERIC, TEXT, and GEO field types support sorting in RediSearch.
277329

278-
To create the indexes for any models that have indexed fields, use the `migrate` CLI command that Redis OM installs in your Python environment.
330+
To create the indexes for any models that are indexed (have `index=True`), use the `om migrate` CLI command that Redis OM installs in your Python environment.
279331

280332
This command detects any `JsonModel` or `HashModel` instances in your project and does the following for each model that isn't abstract or embedded:
281333

@@ -311,11 +363,11 @@ The `.values()` method returns query results as dictionaries instead of model in
311363
```python
312364
from redis_om import HashModel, Field
313365

314-
class Customer(HashModel):
315-
first_name: str = Field(index=True)
316-
last_name: str = Field(index=True)
317-
email: str = Field(index=True)
318-
age: int = Field(index=True)
366+
class Customer(HashModel, index=True):
367+
first_name: str
368+
last_name: str
369+
email: str
370+
age: int
319371
bio: str
320372

321373
# Get all fields as dictionaries
@@ -354,11 +406,11 @@ Both methods use Redis's `RETURN` clause for efficient field projection at the d
354406
Redis OM automatically converts field values to their proper Python types based on your model field definitions:
355407

356408
```python
357-
class Product(HashModel):
358-
name: str = Field(index=True)
359-
price: float = Field(index=True)
360-
in_stock: bool = Field(index=True)
361-
created_at: datetime.datetime = Field(index=True)
409+
class Product(HashModel, index=True):
410+
name: str
411+
price: float
412+
in_stock: bool
413+
created_at: datetime.datetime
362414

363415
# Values are automatically converted to correct types
364416
products = Product.find().values("name", "price", "in_stock")
@@ -397,15 +449,15 @@ from redis_om import JsonModel, Field
397449
class Address(JsonModel):
398450
street: str
399451
city: str
400-
zipcode: str = Field(index=True)
452+
zipcode: str = Field(index=True) # Specific field indexing for embedded model
401453
country: str = "USA"
402-
454+
403455
class Meta:
404456
embedded = True
405457

406458
class Customer(JsonModel, index=True):
407-
name: str = Field(index=True)
408-
age: int = Field(index=True)
459+
name: str
460+
age: int
409461
address: Address
410462
metadata: dict = Field(default_factory=dict)
411463

@@ -525,11 +577,11 @@ For `JsonModel`, complex field types (embedded models, dictionaries, lists) cann
525577

526578
```python
527579
# ✓ Supported for efficient projection (all model types)
528-
class Product(HashModel): # or JsonModel
529-
name: str = Field(index=True) # ✓ String fields
530-
price: float = Field(index=True) # ✓ Numeric fields
531-
active: bool = Field(index=True) # ✓ Boolean fields
532-
created: datetime = Field(index=True) # ✓ DateTime fields
580+
class Product(HashModel, index=True): # or JsonModel
581+
name: str # ✓ String fields
582+
price: float # ✓ Numeric fields
583+
active: bool # ✓ Boolean fields
584+
created: datetime # ✓ DateTime fields
533585

534586
# JsonModel: These use fallback strategy (still supported)
535587
class Customer(JsonModel):

0 commit comments

Comments
 (0)