Add QueryBuilder with fluent API and composable filter expressions#118
Add QueryBuilder with fluent API and composable filter expressions#118abelmilash-msft merged 29 commits intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Implements the QueryBuilder feature from the SDK redesign design doc:
- Fluent query builder (client.query.builder("table")) with 20 chainable methods
- Composable filter expression tree (models/filters.py) with &, |, ~ operators
- filter_in, filter_between, and where() for expression tree composition
- Automatic column name lowercasing and OData value formatting
- datetime/date/uuid.UUID auto-formatting in filter values
309 tests passing (126 new for QueryBuilder + filters).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- README: Promote QueryBuilder as primary query method with fluent, expression tree, and filter_in/between examples; demote raw OData to fallback - Walkthrough: Add Section 7 with 5 QueryBuilder demos (basic fluent, filter_in, filter_between, where() expression tree, combined + paging) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
execute() now yields individual records instead of pages, abstracting away OData paging. Pass by_page=True for explicit page-level iteration. This follows the abstraction-level heuristic: QueryBuilder is the "abstract away OData" API, so paging should be transparent. The raw records.get() API retains paged iteration for backward compatibility. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Run black on 5 files that failed formatting check - Fix _FunctionFilter.__init__ value param: str -> Any (matches _format_value which handles int, float, datetime, etc.) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9f65211 to
c400243
Compare
e95e4de to
5c2a17c
Compare
…filter_raw warning
b8a4db6
Release changelog for v0.1.0b8. ## Changes included in this release ### Added - Batch API: `client.batch` namespace for deferred-execution batch operations (#129) - Batch DataFrame integration: `client.batch.dataframe` namespace (#129) - `client.records.upsert()` and `client.batch.records.upsert()` with alternate-key support (#129) - QueryBuilder: `client.query.builder()` with fluent API and composable filter expressions (#118) - Memo/multiline column type support in `client.tables.create()` and `client.tables.add_columns()` (#155) ### Changed - Picklist label resolution now uses a single bulk API call per table with 1-hour TTL cache (#154) ### Fixed - `client.query.sql()` now paginates and returns all rows instead of truncating at 5,000 (#157) - Alternate key fields no longer merged into `UpsertMultiple` request body (#129) - Docstring type annotations corrected for Microsoft Learn compatibility (#153) --------- Co-authored-by: Abel Milash <abelmilash@microsoft.com>
Release changelog for v0.1.0b8. ## Changes included in this release ### Added - Batch API: `client.batch` namespace for deferred-execution batch operations (#129) - Batch DataFrame integration: `client.batch.dataframe` namespace (#129) - `client.records.upsert()` and `client.batch.records.upsert()` with alternate-key support (#129) - QueryBuilder: `client.query.builder()` with fluent API and composable filter expressions (#118) - Memo/multiline column type support in `client.tables.create()` and `client.tables.add_columns()` (#155) ### Changed - Picklist label resolution now uses a single bulk API call per table with 1-hour TTL cache (#154) ### Fixed - `client.query.sql()` now paginates and returns all rows instead of truncating at 5,000 (#157) - Alternate key fields no longer merged into `UpsertMultiple` request body (#129) - Docstring type annotations corrected for Microsoft Learn compatibility (#153) --------- Co-authored-by: Abel Milash <abelmilash@microsoft.com>
Summary
Implements the QueryBuilder feature from the SDK redesign design doc (ADO PR 1504429):
client.query.builder("table")with 20 chainable methods includingselect,filter_eq/ne/gt/ge/lt/le,filter_contains/startswith/endswith,filter_in,filter_between,filter_null/not_null,filter_raw,where,order_by,top,page_size,expand, andexecutemodels/filters.py) with Python operator overloads (&,|,~) for AND, OR, NOT compositionstr,int,float,bool,None,datetime,date,uuid.UUIDUsage examples
Design decisions
_filter_partslist — preserves call order when mixingfilter_*()andwhere()execute()callsbuild()internally — single source of truth for filter compilationget()on QueryOperations — onlybuilder()added; paginated queries remain onrecords.get()filter_between—(col ge low and col le high)for correct precedenceFiles changed
src/.../models/filters.pysrc/.../models/query_builder.pysrc/.../operations/query.pybuilder()to QueryOperationssrc/.../models/__init__.pytests/.../models/test_filters.pytests/.../models/test_query_builder.pytests/.../test_query_operations.pyMerge conflict note
operations/query.pymay conflict with PR #115 (typed return models) — resolution is straightforward since we only add abuilder()method.Test plan
pytest tests/unit/models/test_filters.py— 57 passedpytest tests/unit/models/test_query_builder.py— 69 passedpytest tests/unit/test_query_operations.py— 9 passedpytest tests/— 309 passed, 0 failed🤖 Generated with Claude Code