-
Notifications
You must be signed in to change notification settings - Fork 121
[Local catalog] Implement local search strategy with GRDB #16374
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Local catalog] Implement local search strategy with GRDB #16374
Conversation
Implements a new fetch strategy that searches the local GRDB catalog instead of making remote API calls. The strategy will be integrated with the factory pattern in a subsequent PR. Changes: - Add PointOfSaleLocalSearchPurchasableItemFetchStrategy - Searches local catalog using posProductSearch() query - Fetches PersistedProducts in single transaction - Converts to POSProduct outside transaction (avoids nesting) - Supports pagination with proper hasMorePages calculation - Delegates variations to remote (deferred to FTS) - Tracks analytics for first page results - Add comprehensive test suite with 15 test cases: - Analytics tracking (first page only, subsequent pages) - Search by name, SKU, and global unique ID - Pagination edge cases and hasMorePages logic - Filtering (downloadable products, product types) - Site isolation and result sorting - Empty results handling - Variation delegation to remote Implementation follows PointOfSaleLocalBarcodeScanService pattern for safe GRDB transaction handling. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Replaces remote API calls with local GRDB queries for fetching product variations: - Uses `posVariationsRequest()` to query variations from local catalog - Implements pagination matching product search pattern - Converts `PersistedProductVariation` to `POSProductVariation` with attributes/images - Automatically filters downloadable variations (handled by query) - Returns correct `PagedItems` with `hasMorePages` and `totalItems` Adds comprehensive test coverage: - Basic variation fetching from local catalog - Downloadable variation filtering - Empty results handling - Pagination with multiple pages - Parent product isolation Analytics tracking will be added in final PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Integrates the local search strategy with the existing fetch strategy factory pattern and wires it up to the POS UI. The factory automatically chooses between local and remote search based on GRDB manager availability. Changes: - Update PointOfSaleItemFetchStrategyFactory: - Add optional grdbManager parameter to initializer - Add localSearchStrategy() factory method - Update searchStrategy() to prefer local when GRDB manager available - Falls back to remote search when local unavailable - Update POSTabCoordinator: - Pass ServiceLocator.grdbManager to factory initialization - Enables local search when local catalog is available - Update analytics infrastructure: - Add trackSearchLocalResultsFetchComplete() to protocol - Update mock analytics tracker for testing Behavior: - When GRDB manager is available → uses local catalog search - When GRDB manager is nil → falls back to existing remote search - No changes to UI layer (transparent integration) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
- Add isLocalCatalogEnabled parameter to factory init - Replace implicit GRDB presence check with explicit flag check - Remove side-effecty localSearchStrategy helper method - Add stub implementation for trackSearchLocalResultsFetchComplete - Pass isLocalCatalogEligible flag when creating factories in POSTabCoordinator - Convert lazy factory properties to factory methods with eligibility parameter - Makes intent clear and improves testability The factory now explicitly checks the isLocalCatalogEnabled flag rather than relying on the presence of GRDBManager to determine which search strategy to use. This makes the behavior more predictable and testable. The POSTabCoordinator now dynamically creates factories with the current eligibility state each time POS is presented, ensuring the correct search strategy is used based on real-time eligibility. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
- Rename analytics event from pointOfSaleSearchLocalResultsFetched to pointOfSaleSearchResultsFetched - Change stat name from search_local_results_fetched to search_results_fetched - Add analytics tracking to local search strategy fetchProducts method - Track milliseconds since request sent and total results count - Only track on first page to avoid duplicate analytics The new event name reflects that this will be the only search method in the future when local catalog is fully rolled out. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Introduces a new enum to define different debouncing behaviors for search operations: - `.smart(duration)`: Skip debounce on first keystroke after search completes, then debounce subsequent keystrokes. Optimized for slow network searches. - `.simple(duration, loadingDelayThreshold?)`: Always debounce every keystroke. Optionally delays showing loading indicators until threshold is exceeded. Optimized for fast local searches. - `.immediate`: No debouncing for non-search operations. This allows different search types (local vs remote) to use appropriate debouncing strategies tailored to their performance characteristics. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Adds a `debounceStrategy` property to both PointOfSalePurchasableItemFetchStrategy and PointOfSaleCouponFetchStrategy protocols with default implementations returning `.immediate`. This allows each fetch strategy implementation to declare its optimal debouncing behavior: - Remote search strategies can use `.smart()` for network latency - Local search strategies can use `.simple()` with loading delay thresholds - Default strategies use `.immediate` for no debouncing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Implements the debouncing strategy pattern in the search UI layer: - Adds `debounceStrategy` property to POSSearchable protocol - Updates POSSearchField's onChange handler to execute different debouncing logic based on strategy: - `.smart`: Skip debounce on first keystroke, debounce subsequent - `.simple`: Always debounce, with optional delayed loading indicators - `.immediate`: No debouncing - Adds `currentDebounceStrategy` to item and coupon controllers to expose fetch strategy's debouncing behavior - Implements protocol conformance in POSProductSearchable, POSOrderListView, and preview helpers The `.simple` strategy with loading delay threshold prevents flicker on fast local searches by only showing loading indicators if the search exceeds the threshold duration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Implements `.simple(duration: 150ms, loadingDelayThreshold: 300ms)` for local GRDB product searches. This strategy: - Always debounces every keystroke by 150ms to prevent excessive queries - Delays showing loading indicators until 300ms threshold is exceeded - Prevents loading flicker for fast local searches (< 300ms) - Only shows loading indicators for slower searches (> 300ms) The combination of debouncing with delayed loading provides a responsive feel while avoiding visual flickering on fast local database queries. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Adds comprehensive test coverage for the debouncing strategy pattern: - Tests for SearchDebounceStrategy enum equality - Tests verifying each fetch strategy returns the correct debouncing behavior: - Local product search: `.simple` with loading delay threshold - Remote product search: `.smart` for network latency - Coupon search: `.smart` for network latency - Default strategies: `.immediate` for no debouncing Tests ensure the debouncing strategies are correctly configured across all search types for optimal UX. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
|
|
Only access ServiceLocator.grdbManager when isLocalCatalogEnabled is true to avoid fatal error when pointOfSaleLocalCatalog feature flag is disabled.
Update mocks to conform to PointOfSaleSearchingItemsControllerProtocol which now requires currentDebounceStrategy property.
Accidentally changed version number in commit 3e82715. Reverting to correct version.
The issue was that the debounce strategy was determined by the current fetch strategy, but the fetch strategy didn't change from "popular products" to "search" until performSearch() was called. This meant the first keystroke used the `.immediate` strategy from popular products, which set `didFinishSearch=false` but didn't call `clearSearchResults()` to show loading. Solution: Added `searchDebounceStrategy` property to POSSearchable protocol that returns the debounce strategy that will be used for searches, regardless of the current fetch mode. The onChange handler now captures this strategy synchronously before creating the Task, ensuring we use the search strategy (.smart) from the very first keystroke. Changes: - Added `searchDebounceStrategy` to POSSearchable protocol - Updated POSSearchView to use searchDebounceStrategy for non-empty searches - Implemented searchDebounceStrategy in controllers by creating temporary search strategy to query its debounce settings - Updated all mocks and preview helpers to conform to new protocol 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
The POSSearchable protocol had two confusingly named properties: - debounceStrategy: The currently active strategy - searchDebounceStrategy: The strategy that will be used for searches Renamed debounceStrategy → currentDebounceStrategy to make the distinction clear: currentDebounceStrategy reflects what's active right now, while searchDebounceStrategy reflects what will be used when searching. This improves code readability without changing any behavior.
iamgabrielma
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM ✅
...ests/YosemiteTests/PointOfSale/PointOfSaleLocalSearchPurchasableItemFetchStrategyTests.swift
Show resolved
Hide resolved
Co-authored-by: Gabriel Maldonado <[email protected]>
This reverts commit 300cd55.
…ch' into woomob-1112-woo-poslocal-catalog-local-search-strategy
… into woomob-1112-woo-poslocal-catalog-factory-integration
…nto woomob-1112-woo-poslocal-catalog-analytics
…-1112-configurable-search-debounce-strategies
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
… into woomob-1112-woo-poslocal-catalog-factory-integration
…nto woomob-1112-woo-poslocal-catalog-analytics
…-1112-configurable-search-debounce-strategies
Co-authored-by: Gabriel Maldonado <[email protected]>
769a65e
into
woomob-1112-woo-poslocal-catalog-implement-product-search
Generated by 🚫 Danger |

Part of: WOOMOB-1112
Description
Implements the local search strategy for POS items using GRDB instead of remote API calls. This includes:
PointOfSaleLocalSearchPurchasableItemFetchStrategythat queries the local GRDB catalogThis PR is stacked on #16373 and is the second in a series of 5 PRs implementing local catalog search.
Test Steps
No visible changes – this whole stack is probably best tested using #16377, the last one in the stack.
Note that #16377 resolves all the periphery issues – I'll merge them from that down, so the final merge to trunk will pass periphery. It seems silly to put a load of ignore statements in for the sake of this.
RELEASE-NOTES.txtif necessary.