Skip to content

Optimize FindQuery.update() with partial writes - #843

Open
lh0156 wants to merge 2 commits into
redis:mainfrom
lh0156:agent/find-query-update-777
Open

Optimize FindQuery.update() with partial writes#843
lh0156 wants to merge 2 commits into
redis:mainfrom
lh0156:agent/find-query-update-777

Conversation

@lh0156

@lh0156 lh0156 commented Jul 26, 2026

Copy link
Copy Markdown

Summary

Implements #777 by updating matching records without materializing full Pydantic models.

What changed

  • Uses FT.SEARCH ... NOCONTENT to collect matching keys page by page.
  • Validates and serializes update values once, including Pydantic constraints and Redis OM storage conversions.
  • Uses partial HSET mappings for HashModel.
  • Uses path-level JSON.SET commands for JsonModel, including nested __ paths.
  • Preserves the use_transaction flag and returns the number of updated records.
  • Keeps query projections out of the key-only search and preserves KNN state when copying queries.
  • Documents the new return value.

Testing

  • pytest -q tests --ignore tests/test_benchmarks.py: 265 passed.
  • pytest -q tests/test_hash_model.py tests/test_json_model.py: 147 passed.
  • Generated sync tests for the new coverage plus Hash/JSON model tests: 157 passed.
  • ruff check and ruff format --check on changed source/tests.
  • python -m compileall -q aredis_om tests.
  • bandit -q -r aredis_om/model/model.py -s B608.

Redis Stack was run locally via Docker Compose for the integration tests; benchmark tests were excluded from the full run.


Note

Medium Risk
Bulk updates change execution semantics (no per-instance save/load) and touch core query/update paths for both Hash and JSON models, though behavior is covered by new tests and mirrors save encoding.

Overview
FindQuery.update() no longer loads full documents or calls save() per match. It validates and serializes kwargs once (Pydantic constraints plus existing Hash/JSON storage encoding), paginates FT.SEARCH with NOCONTENT to collect keys (ignoring .only() projections), then applies updates in a pipeline: HSET mappings for HashModel, path-level JSON.SET for JsonModel (including __ nested paths). It returns the number of updated keys and still honors use_transaction.

FindQuery.copy() now preserves knn in query state. Docs note the new return value; tests cover validation-before-search, pagination, and Hash vs JSON update paths.

Reviewed by Cursor Bugbot for commit 408cc9f. Bugbot is set up for automated code reviews on this repo. Configure here.

@lh0156
lh0156 marked this pull request as ready for review July 30, 2026 09:12

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 408cc9f. Configure here.

Comment thread aredis_om/model/model.py
and hasattr(candidate, "model_fields")
),
None,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nested model detection ignores Pydantic v1 attribute name

Low Severity

In _get_update_field, the field lookup at the top of the loop correctly falls back to __fields__ for Pydantic v1, but the nested model detection on line 2041 only checks hasattr(candidate, "model_fields"). Pydantic v1 models use __fields__ instead of model_fields, so nested update paths (e.g., address__city) will always fail with a QuerySyntaxError on v1, even when the path is valid.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 408cc9f. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant