Repository navigation
Search the operands of an OR in batches of SQL terms - #105
Merged
Merged
Conversation
Port of DID-matlab ec95ef7 (DID-matlab#233). _search_doc_ids searched an OR one SQL query per operand. _search_or_operands now searches those that are single SQL terms up to 200 at a time in one query: SELECTs combined with UNION over a whole branch, and one restricted query with the conditions ORed when restricted to candidate documents. ORed base.id lookups among them go to _search_base_ids. ANDs, negated ORs, negated terms and brute-forced terms are searched one at a time as before, and a batch whose SQL fails is retried one operand at a time. tests/test_batched_or.py compares the batched search with one query per operand for every operation the SQL builder supports, in mixed ORs and inside ANDs. Closes #104 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Z8rXBFDpnZo3FeCE3ymyZ
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Z8rXBFDpnZo3FeCE3ymyZ
# Conflicts: # src/did/did_matlab_python_bridge.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #104. Port of VH-Lab/DID-matlab#234 (for VH-Lab/DID-matlab#233). The bridge records DID-matlab
e4e02dc, #234's head, which also contains DID-matlab#235.Update: #106 / DID-matlab#235 merged in
main(with Test that depends_on matches as in DID-matlab, case for case #106) is merged into this branch. The one conflict was thedatabaseentry'smatlab_last_sync_hash: Test that depends_on matches as in DID-matlab, case for case #106 set124cf5e, this PR setse4e02dc.e4e02dcis #234 with DID-matlab#235 merged in, so its history contains124cf5e. Test that depends_on matches as in DID-matlab, case for case #106'sdecision_lognotes are kept.depends_onthat SQL can't match exactly, and #234 keeps such operands out of its OR batches. Python already did both:_query_struct_to_sql_strreturnsNonefor them, so_search_or_operandsleaves them to_search_doc_ids. No code change was needed.test_batched_or.pynow also coversdepends_on/~depends_onwith an empty name.What changed
SQLiteDB._search_doc_idsused to search an OR with one SQL query per operand. Now the OR's operands go to a new method,_search_or_operands, which sorts them into three groups:_or_batch_terms()=_MAX_OR_BATCH_TERMS= 200 per query. A single SQL term is a non-negated operand that isn't an OR and for which_query_struct_to_sql_strreturns a clause.base.idlookups among the operands are collected into one_search_base_idscall, as an all-base.idOR already was._search_doc_idsone operand at a time, as before: ANDs, nested~ors, negated terms, and terms with no SQL form (which are brute-forced).If a batch's SQL fails, its operands are retried one at a time, so each one falls back to brute force exactly as it would on its own.
The SQL, and why
This is the same approach as the MATLAB PR:
SELECTper operand, each exactly the query the operand would run on its own, joined withUNION. SQLite plans each member separately, so each keeps its index, andisakeeps itsLIMIT -1distinct-superclass subquery._search_query_head(candidates)(CROSS JOIN…INDEXED BY doc_data_doc_idx) and the conditions joined byOR.I measured the alternative, one ORed
WHEREover a whole branch, on the SQL in the DID-matlab PR (#234). It loses the indexes: about 1 s for 10 names, 6 s for 100, and at 1,000 terms it exceeds SQLite's expression-depth limit. Restricted to candidates the opposite holds: every term reads every candidate's rows, so reading them once with an ORedWHEREwins. Full numbers are in VH-Lab/DID-matlab#234.Timings
These use the real
SQLiteDB, opened on a synthetic database: DID schema, 100,000 docs × 30 fields (3Mdoc_datarows), withSQLiteDBadding its search indexes on open.base.nameexact_stringterms.exact_string,exact_number,depends_on,isa,exact_string_anycase,base.idandgreaterthan.base.ids."Before" is the same code with
_or_batch_terms()returning 1, which is the old one-query-per-operand path.In-process
sqlite3makes per-query overhead small, so the whole-branch gain is modest here; the restricted case is where Python gains. The "mixed" whole-branch rows are dominated by terms that match most of the database, which cost the same either way.Tests
New
tests/test_batched_or.py. Each comparison runs the search three ways — default batching, batches of 3, and one query per operand — and checks they give the same documents.test_every_operation_in_an_or: every operation_query_struct_to_sql_strturns into SQL (exact_string,exact_string_anycase,contains_string,regexp,exact_number,lessthan,lessthaneq,greaterthan,greaterthaneq,hasfield,isa,depends_onwith a name and with*), each ORed with other terms. It also covers the negated forms and some brute-forced terms (hasmember, a non-numericexact_number,hasanysubfield_contains_string,depends_on/~depends_onwith an empty name), which stay per operand.test_all_sql_operations_in_one_or: all the SQL operations in one OR, which is checked to run as one query.test_every_operation_in_an_or_inside_an_and: the same insideisa demoB AND (…), the restricted path. Also compared with the search over the whole branch, and checked to run as 2 queries.test_mixed_or:base.idlookups, an AND, a nested~or, negated and brute-forced terms in one OR, alone and inside an AND.test_long_or_is_searched_in_batches: 450 names take 3 queries instead of 450, and 1 + 3 when restricted.test_without_search_indexesandtest_failing_batch_falls_back_to_per_operand.Bridge
databaseentry'smatlab_last_sync_hashis nowe4e02dc(was0de9cf2before Test that depends_on matches as in DID-matlab, case for case #106 and this PR).decision_lognotes that the port is indid/implementations/sqlitedb.py, sodid/database.pydoesn't change.Checks run locally (after the merge)
black --check src testsandruff check src tests(pinned versions): clean.pytest: 841 passed, 10 skipped, 1 xfailed.bin/check_bridge_coverage.py, all six checks: 0 problems, against both DID-matlab #234's head (e4e02dc) and DID-matlabmain(2092b81) with full history, which is how the CI job runs it.Merge order: VH-Lab/DID-matlab#234 first, with a merge commit so
e4e02dcstays reachable frommain, then this PR.🤖 Generated with Claude Code
https://claude.ai/code/session_016Z8rXBFDpnZo3FeCE3ymyZ