[Bug 42835] New: ElasticSearch should have an items index
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Bug ID: 42835 Summary: ElasticSearch should have an items index Initiative type: --- Sponsorship --- status: Product: Koha Version: Main Hardware: All OS: All Status: NEW Severity: normal Priority: P5 - low Component: Searching - Elasticsearch Assignee: koha-bugs@lists.koha-community.org Reporter: pedro.amorim@openfifth.co.uk QA Contact: testopia@bugs.koha-community.org Target Milestone: --- -- You are receiving this mail because: You are watching all bug changes. You are the assignee for the bug.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|NEW |Needs Signoff -- You are receiving this mail because: You are watching all bug changes. You are the assignee for the bug.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #1 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 200312 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=200312&action=edit Bug 42835: Elasticsearch: Add items index Adds a dedicated 'items' Elasticsearch index where each item is its own flat document keyed by itemnumber, built from Koha::Item data rather than MARC. Adds index_items() and delete_items() to Indexer.pm. Hooks into Item::store() and Item::delete() via _update_es_index() to keep the index in sync on every item change; a skip_items_index flag allows bulk operations to bypass it. Items in their own index can be updated on circulation without re-parsing MARC, keeping availability and facet counts accurate in real time. Adds a noop mock for index_items in the Elasticsearch.t test fixture to prevent store() calls inside marc_records_to_documents tests from trying to index items into ES. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes. You are the assignee for the bug.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #2 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 200313 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=200313&action=edit Bug 42835: Koha::Item: Skip biblio re-index for circulation-only field changes When only fields in %ITEM_CIRC_FIELDS are modified (onloan, issues, renewals, holdingbranch, datelastborrowed, datelastseen, localuse, reserves, timestamp), skip the biblio MARC re-index — the items index is authoritative for this data. notforloan, damaged, itemlost and withdrawn are excluded from the skip set: they affect the available field in the biblios index and must trigger a re-index to keep availability filtering correct. The skip is further gated on _items_index_ready(): the biblio re-index is only skipped when the items index is sufficiently populated (>=95% of DB items indexed). This prevents stale availability data on fresh installs where the items index may contain only a handful of documents. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> -- You are receiving this mail because: You are the assignee for the bug. You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #3 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 200314 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=200314&action=edit Bug 42835: rebuild_elasticsearch.pl: Add --items flag Adds -i|--items to rebuild the items index from the database (not from MARC). Items are not included in the default "index everything" behaviour and must be requested explicitly. Also adds -in|--itemnumber to reindex one or more specific items. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> -- You are receiving this mail because: You are the assignee for the bug. You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #4 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 200315 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=200315&action=edit Bug 42835: Elasticsearch: Route availability and facets through items index Queries the items index for availability filtering and item-level facets (itype, location, ccode, homebranch, holdingbranch). Uses cardinality sub-aggregations for per-biblio facet counts rather than per-item counts. Adds biblionumber as an explicit integer field in the biblios index mapping and stamps it on biblio documents so item facet queries can be scoped to the current search result set via a _biblionumbers aggregation. Fixes a bug where _apply_available_filter assumed available:true was always at must[0]; now scans all must elements. Fixes a bug where biblio_count{value} was tested for hashref truthiness rather than key existence, always preferring it over doc_count. Fixes the biblios index available field to correctly treat notforloan, damaged, and withdrawn items as unavailable — previously only onloan and itemlost were checked. Covers both item-level notforloan and items whose item type has notforloan set. Updates existing tests in Elasticsearch.t that were asserting the previous wrong behaviour. Items index routing is unconditional here; the readiness gate that prevents empty-index regressions on upgrade is added in the next commit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes. You are the assignee for the bug.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #5 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 200316 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=200316&action=edit Bug 42835: OPAC performance: Batch item visibility check to reduce N+1 DB queries Before the main result loop, pre-scan all MARC records for the current page to collect every itemnumber in one pass. Run a single batched filter_by_visible_in_opac call and store the result in a hash. The inner item loop then does a plain hash lookup instead of one Koha::Items DB query per item, reducing N queries to 1 per page load. Falls back to the per-item path if no itemnumbers are found in the pre-scan. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> -- You are receiving this mail because: You are the assignee for the bug. You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #6 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 200317 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=200317&action=edit Bug 42835: Elasticsearch: Gate items index features behind readiness check Adds _items_index_ready() which checks whether the items index is fully populated before enabling availability filtering and facet aggregation. The index is considered ready only when it contains at least 95% of the items in the database — this prevents the gate opening after a single circulation event has indexed one item before rebuild_elasticsearch.pl --items has been run. The result is cached: 5 minutes when ready, 60 seconds when not, so a freshly populated index is detected quickly without a restart. TEST PLAN (Do not apply patches yet) 1) Set up KTD with Elasticsearch 8: ktd --search-engine es8 up 2) Do a search for 'music', you should get 42 results: http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music 3) Click the 'Limit to records with available items' facet link. Notice you get 40 results. 9 of these bibs have all their items unavailable and should NOT be showing. 4) Find a biblio with a single item (the top result of the search): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=437 5) Check-out that item (39999000019186) to a patron 6) Repeat the same search with 'Limit to records with available items'. Notice the biblio above is no longer showing (this is correct). 7) Check the background jobs: http://localhost:8081/cgi-bin/koha/admin/background_jobs.pl 8) Confirm at least 1 'Update Elasticsearch index' entry exists. This is because a full bib reindex is triggered for anytime a check-out happens. 9) Let's pick a 2nd bib from that same search ('music' + only available): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=380 10) Edit that single item, put it 'Not for loan' 11) Refresh the search. Notice the bib is always showing, even though all its items are unavailable (This is incorrect): http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music&weight_search=1&sort_by=relevance&limit=available APPLY PATCHES 1) Set up KTD with Elasticsearch 8: ktd --search-engine es8 up 2) Do a search for 'music', you should get 42 results: http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music 3) Click the 'Limit to records with available items' facet link. Notice you now get 31 results. The previous 9 with all their items unavailable do not show anymore. 4) Find a biblio with a single item (the top result of the search): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=437 5) Check-out that item (39999000019186) to a patron 6) Repeat the same search with 'Limit to records with available items'. Notice the biblio above is no longer showing (this is still correct). 7) Check the background jobs: http://localhost:8081/cgi-bin/koha/admin/background_jobs.pl 8) Confirm at least 1 'Update Elasticsearch index' entry exists. This is because a full bib reindex is triggered for anytime a check-out happens. This is still the case because a full reindex (to create the items index) has not occurred yet. Let's do that 9) Run: $ perl misc/search_tools/rebuild_elasticsearch.pl -i -r -v 10) Do another check-out for a 2nd bib with a single item e.g. (39999000016710): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=380 11) Check the background jobs again: http://localhost:8081/cgi-bin/koha/admin/background_jobs.pl 12) Notice a new full bib record reindex job was not triggered, because this was updated directly on the items index synchronously. Confirm that bib record no longer shows on the 'only available' search: http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music&weight_search=1&sort_by=relevance&limit=available 13) Pick a third bib from the same search http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=374 14) Edit the single item and put it 'not for loan'. 15) Refresh the search. Notice the bib is not showing anymore, because all its items are unavailable (This is now correct): http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music&weight_search=1&sort_by=relevance&limit=available Benefits: 1. notforloan/damaged/withdrawn availability fix 2. Skip biblio re-index on checkout — performance improvement, visible under load 3. Availability filtering via items index — real-time post-checkout 4. Safe on upgrade — falls back to the biblios index until a rebuild with --items is run, no hard requirement to rebuild before the system works correctly. Run tests: prove t/db_dependent/Koha/SearchEngine/Elasticsearch/Search.t prove t/db_dependent/Koha/SearchEngine/Elasticsearch/Indexer.t prove t/db_dependent/Koha/SearchEngine/Elasticsearch.t prove t/db_dependent/Koha/Item.t prove t/db_dependent/Search.t prove t/db_dependent/Koha/SearchEngine/Search.t Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes. You are the assignee for the bug.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 David Nind <david@davidnind.com> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |david@davidnind.com Assignee|koha-bugs@lists.koha-commun |pedro.amorim@openfifth.co.u |ity.org |k --- Comment #7 from David Nind <david@davidnind.com> --- Added assignee 8-) -- You are receiving this mail because: You are watching all bug changes. You are the assignee for the bug.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |pedro.amorim@openfifth.co.u | |k Status|Needs Signoff |Patch doesn't apply -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|Patch doesn't apply |Needs Signoff -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #200312|0 |1 is obsolete| | Attachment #200313|0 |1 is obsolete| | Attachment #200314|0 |1 is obsolete| | Attachment #200315|0 |1 is obsolete| | Attachment #200316|0 |1 is obsolete| | Attachment #200317|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #8 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 200605 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=200605&action=edit Bug 42835: Elasticsearch: Add items index Adds a dedicated 'items' Elasticsearch index where each item is its own flat document keyed by itemnumber, built from Koha::Item data rather than MARC. Adds index_items() and delete_items() to Indexer.pm. Hooks into Item::store() and Item::delete() via _update_es_index() to keep the index in sync on every item change; a skip_items_index flag allows bulk operations to bypass it. Items in their own index can be updated on circulation without re-parsing MARC, keeping availability and facet counts accurate in real time. Adds a noop mock for index_items in the Elasticsearch.t test fixture to prevent store() calls inside marc_records_to_documents tests from trying to index items into ES. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #9 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 200606 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=200606&action=edit Bug 42835: Koha::Item: Skip biblio re-index for circulation-only field changes When only fields in %ITEM_CIRC_FIELDS are modified (onloan, issues, renewals, holdingbranch, datelastborrowed, datelastseen, localuse, reserves, timestamp), skip the biblio MARC re-index — the items index is authoritative for this data. notforloan, damaged, itemlost and withdrawn are excluded from the skip set: they affect the available field in the biblios index and must trigger a re-index to keep availability filtering correct. The skip is further gated on _items_index_ready(): the biblio re-index is only skipped when the items index is sufficiently populated (>=95% of DB items indexed). This prevents stale availability data on fresh installs where the items index may contain only a handful of documents. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #10 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 200607 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=200607&action=edit Bug 42835: rebuild_elasticsearch.pl: Add --items flag Adds -i|--items to rebuild the items index from the database (not from MARC). Items are not included in the default "index everything" behaviour and must be requested explicitly. Also adds -in|--itemnumber to reindex one or more specific items. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #11 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 200608 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=200608&action=edit Bug 42835: Elasticsearch: Route availability and facets through items index Queries the items index for availability filtering and item-level facets (itype, location, ccode, homebranch, holdingbranch). Uses cardinality sub-aggregations for per-biblio facet counts rather than per-item counts. Adds biblionumber as an explicit integer field in the biblios index mapping and stamps it on biblio documents so item facet queries can be scoped to the current search result set via a _biblionumbers aggregation. Fixes a bug where _apply_available_filter assumed available:true was always at must[0]; now scans all must elements. Fixes a bug where biblio_count{value} was tested for hashref truthiness rather than key existence, always preferring it over doc_count. Fixes the biblios index available field to correctly treat notforloan, damaged, and withdrawn items as unavailable — previously only onloan and itemlost were checked. Covers both item-level notforloan and items whose item type has notforloan set. Updates existing tests in Elasticsearch.t that were asserting the previous wrong behaviour. Items index routing is unconditional here; the readiness gate that prevents empty-index regressions on upgrade is added in the next commit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #12 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 200609 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=200609&action=edit Bug 42835: OPAC performance: Batch item visibility check to reduce N+1 DB queries Before the main result loop, pre-scan all MARC records for the current page to collect every itemnumber in one pass. Run a single batched filter_by_visible_in_opac call and store the result in a hash. The inner item loop then does a plain hash lookup instead of one Koha::Items DB query per item, reducing N queries to 1 per page load. Falls back to the per-item path if no itemnumbers are found in the pre-scan. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #13 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 200610 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=200610&action=edit Bug 42835: Elasticsearch: Gate items index features behind readiness check Adds _items_index_ready() which checks whether the items index is fully populated before enabling availability filtering and facet aggregation. The index is considered ready only when it contains at least 95% of the items in the database — this prevents the gate opening after a single circulation event has indexed one item before rebuild_elasticsearch.pl --items has been run. The result is cached: 5 minutes when ready, 60 seconds when not, so a freshly populated index is detected quickly without a restart. TEST PLAN (Do not apply patches yet) 1) Set up KTD with Elasticsearch 8: ktd --search-engine es8 up 2) Do a search for 'music', you should get 42 results: http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music 3) Click the 'Limit to records with available items' facet link. Notice you get 40 results. 9 of these bibs have all their items unavailable and should NOT be showing. 4) Find a biblio with a single item (the top result of the search): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=437 5) Check-out that item (39999000019186) to a patron 6) Repeat the same search with 'Limit to records with available items'. Notice the biblio above is no longer showing (this is correct). 7) Check the background jobs: http://localhost:8081/cgi-bin/koha/admin/background_jobs.pl 8) Confirm at least 1 'Update Elasticsearch index' entry exists. This is because a full bib reindex is triggered for anytime a check-out happens. 9) Let's pick a 2nd bib from that same search ('music' + only available): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=380 10) Edit that single item, put it 'Not for loan' 11) Refresh the search. Notice the bib is always showing, even though all its items are unavailable (This is incorrect): http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music&weight_search=1&sort_by=relevance&limit=available APPLY PATCHES 1) Set up KTD with Elasticsearch 8: ktd --search-engine es8 up 2) Do a search for 'music', you should get 42 results: http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music 3) Click the 'Limit to records with available items' facet link. Notice you now get 31 results. The previous 9 with all their items unavailable do not show anymore. 4) Find a biblio with a single item (the top result of the search): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=437 5) Check-out that item (39999000019186) to a patron 6) Repeat the same search with 'Limit to records with available items'. Notice the biblio above is no longer showing (this is still correct). 7) Check the background jobs: http://localhost:8081/cgi-bin/koha/admin/background_jobs.pl 8) Confirm at least 1 'Update Elasticsearch index' entry exists. This is because a full bib reindex is triggered for anytime a check-out happens. This is still the case because a full reindex (to create the items index) has not occurred yet. Let's do that 9) Run: $ perl misc/search_tools/rebuild_elasticsearch.pl -i -r -v 10) Do another check-out for a 2nd bib with a single item e.g. (39999000016710): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=380 11) Check the background jobs again: http://localhost:8081/cgi-bin/koha/admin/background_jobs.pl 12) Notice a new full bib record reindex job was not triggered, because this was updated directly on the items index synchronously. Confirm that bib record no longer shows on the 'only available' search: http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music&weight_search=1&sort_by=relevance&limit=available 13) Pick a third bib from the same search http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=374 14) Edit the single item and put it 'not for loan'. 15) Refresh the search. Notice the bib is not showing anymore, because all its items are unavailable (This is now correct): http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music&weight_search=1&sort_by=relevance&limit=available Benefits: 1. notforloan/damaged/withdrawn availability fix 2. Skip biblio re-index on checkout — performance improvement, visible under load 3. Availability filtering via items index — real-time post-checkout 4. Safe on upgrade — falls back to the biblios index until a rebuild with --items is run, no hard requirement to rebuild before the system works correctly. Run tests: prove t/db_dependent/Koha/SearchEngine/Elasticsearch/Search.t prove t/db_dependent/Koha/SearchEngine/Elasticsearch/Indexer.t prove t/db_dependent/Koha/SearchEngine/Elasticsearch.t prove t/db_dependent/Koha/Item.t prove t/db_dependent/Search.t prove t/db_dependent/Koha/SearchEngine/Search.t Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |andrew@bywatersolutions.com | |, | |martin.renvoize@openfifth.c | |o.uk, | |nick@bywatersolutions.com -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Blocks| |42643 Referenced Bugs: https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42643 [Bug 42643] [OMNIBUS] Assorted performance and stability work -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Blocks| |42958 Referenced Bugs: https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42958 [Bug 42958] Expand Elasticsearch/Opensearch usage across Koha beyond bibliographic search -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Strategic theme|--- |Performance Target Milestone|--- |26.11 Initiative type|--- |Epic -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Initiative type|Epic |Feature -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Blocks|42643 | Referenced Bugs: https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42643 [Bug 42643] [OMNIBUS] Assorted performance and stability work -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 David Nind <david@davidnind.com> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|Needs Signoff |Signed Off -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 David Nind <david@davidnind.com> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #200605|0 |1 is obsolete| | Attachment #200606|0 |1 is obsolete| | Attachment #200607|0 |1 is obsolete| | Attachment #200608|0 |1 is obsolete| | Attachment #200609|0 |1 is obsolete| | Attachment #200610|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #14 from David Nind <david@davidnind.com> --- Created attachment 202138 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202138&action=edit Bug 42835: Elasticsearch: Add items index Adds a dedicated 'items' Elasticsearch index where each item is its own flat document keyed by itemnumber, built from Koha::Item data rather than MARC. Adds index_items() and delete_items() to Indexer.pm. Hooks into Item::store() and Item::delete() via _update_es_index() to keep the index in sync on every item change; a skip_items_index flag allows bulk operations to bypass it. Items in their own index can be updated on circulation without re-parsing MARC, keeping availability and facet counts accurate in real time. Adds a noop mock for index_items in the Elasticsearch.t test fixture to prevent store() calls inside marc_records_to_documents tests from trying to index items into ES. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #15 from David Nind <david@davidnind.com> --- Created attachment 202139 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202139&action=edit Bug 42835: Koha::Item: Skip biblio re-index for circulation-only field changes When only fields in %ITEM_CIRC_FIELDS are modified (onloan, issues, renewals, holdingbranch, datelastborrowed, datelastseen, localuse, reserves, timestamp), skip the biblio MARC re-index — the items index is authoritative for this data. notforloan, damaged, itemlost and withdrawn are excluded from the skip set: they affect the available field in the biblios index and must trigger a re-index to keep availability filtering correct. The skip is further gated on _items_index_ready(): the biblio re-index is only skipped when the items index is sufficiently populated (>=95% of DB items indexed). This prevents stale availability data on fresh installs where the items index may contain only a handful of documents. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #16 from David Nind <david@davidnind.com> --- Created attachment 202140 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202140&action=edit Bug 42835: rebuild_elasticsearch.pl: Add --items flag Adds -i|--items to rebuild the items index from the database (not from MARC). Items are not included in the default "index everything" behaviour and must be requested explicitly. Also adds -in|--itemnumber to reindex one or more specific items. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #17 from David Nind <david@davidnind.com> --- Created attachment 202141 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202141&action=edit Bug 42835: Elasticsearch: Route availability and facets through items index Queries the items index for availability filtering and item-level facets (itype, location, ccode, homebranch, holdingbranch). Uses cardinality sub-aggregations for per-biblio facet counts rather than per-item counts. Adds biblionumber as an explicit integer field in the biblios index mapping and stamps it on biblio documents so item facet queries can be scoped to the current search result set via a _biblionumbers aggregation. Fixes a bug where _apply_available_filter assumed available:true was always at must[0]; now scans all must elements. Fixes a bug where biblio_count{value} was tested for hashref truthiness rather than key existence, always preferring it over doc_count. Fixes the biblios index available field to correctly treat notforloan, damaged, and withdrawn items as unavailable — previously only onloan and itemlost were checked. Covers both item-level notforloan and items whose item type has notforloan set. Updates existing tests in Elasticsearch.t that were asserting the previous wrong behaviour. Items index routing is unconditional here; the readiness gate that prevents empty-index regressions on upgrade is added in the next commit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #18 from David Nind <david@davidnind.com> --- Created attachment 202142 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202142&action=edit Bug 42835: OPAC performance: Batch item visibility check to reduce N+1 DB queries Before the main result loop, pre-scan all MARC records for the current page to collect every itemnumber in one pass. Run a single batched filter_by_visible_in_opac call and store the result in a hash. The inner item loop then does a plain hash lookup instead of one Koha::Items DB query per item, reducing N queries to 1 per page load. Falls back to the per-item path if no itemnumbers are found in the pre-scan. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #19 from David Nind <david@davidnind.com> --- Created attachment 202143 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202143&action=edit Bug 42835: Elasticsearch: Gate items index features behind readiness check Adds _items_index_ready() which checks whether the items index is fully populated before enabling availability filtering and facet aggregation. The index is considered ready only when it contains at least 95% of the items in the database — this prevents the gate opening after a single circulation event has indexed one item before rebuild_elasticsearch.pl --items has been run. The result is cached: 5 minutes when ready, 60 seconds when not, so a freshly populated index is detected quickly without a restart. TEST PLAN (Do not apply patches yet) 1) Set up KTD with Elasticsearch 8: ktd --search-engine es8 up 2) Do a search for 'music', you should get 42 results: http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music 3) Click the 'Limit to records with available items' facet link. Notice you get 40 results. 9 of these bibs have all their items unavailable and should NOT be showing. 4) Find a biblio with a single item (the top result of the search): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=437 5) Check-out that item (39999000019186) to a patron 6) Repeat the same search with 'Limit to records with available items'. Notice the biblio above is no longer showing (this is correct). 7) Check the background jobs: http://localhost:8081/cgi-bin/koha/admin/background_jobs.pl 8) Confirm at least 1 'Update Elasticsearch index' entry exists. This is because a full bib reindex is triggered for anytime a check-out happens. 9) Let's pick a 2nd bib from that same search ('music' + only available): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=380 10) Edit that single item, put it 'Not for loan' 11) Refresh the search. Notice the bib is always showing, even though all its items are unavailable (This is incorrect): http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music&weight_search=1&sort_by=relevance&limit=available APPLY PATCHES 1) Set up KTD with Elasticsearch 8: ktd --search-engine es8 up 2) Do a search for 'music', you should get 42 results: http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music 3) Click the 'Limit to records with available items' facet link. Notice you now get 31 results. The previous 9 with all their items unavailable do not show anymore. 4) Find a biblio with a single item (the top result of the search): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=437 5) Check-out that item (39999000019186) to a patron 6) Repeat the same search with 'Limit to records with available items'. Notice the biblio above is no longer showing (this is still correct). 7) Check the background jobs: http://localhost:8081/cgi-bin/koha/admin/background_jobs.pl 8) Confirm at least 1 'Update Elasticsearch index' entry exists. This is because a full bib reindex is triggered for anytime a check-out happens. This is still the case because a full reindex (to create the items index) has not occurred yet. Let's do that 9) Run: $ perl misc/search_tools/rebuild_elasticsearch.pl -i -r -v 10) Do another check-out for a 2nd bib with a single item e.g. (39999000016710): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=380 11) Check the background jobs again: http://localhost:8081/cgi-bin/koha/admin/background_jobs.pl 12) Notice a new full bib record reindex job was not triggered, because this was updated directly on the items index synchronously. Confirm that bib record no longer shows on the 'only available' search: http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music&weight_search=1&sort_by=relevance&limit=available 13) Pick a third bib from the same search http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=374 14) Edit the single item and put it 'not for loan'. 15) Refresh the search. Notice the bib is not showing anymore, because all its items are unavailable (This is now correct): http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music&weight_search=1&sort_by=relevance&limit=available Benefits: 1. notforloan/damaged/withdrawn availability fix 2. Skip biblio re-index on checkout — performance improvement, visible under load 3. Availability filtering via items index — real-time post-checkout 4. Safe on upgrade — falls back to the biblios index until a rebuild with --items is run, no hard requirement to rebuild before the system works correctly. Run tests: prove t/db_dependent/Koha/SearchEngine/Elasticsearch/Search.t prove t/db_dependent/Koha/SearchEngine/Elasticsearch/Indexer.t prove t/db_dependent/Koha/SearchEngine/Elasticsearch.t prove t/db_dependent/Koha/Item.t prove t/db_dependent/Search.t prove t/db_dependent/Koha/SearchEngine/Search.t Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 David Nind <david@davidnind.com> changed: What |Removed |Added ---------------------------------------------------------------------------- Keywords| |release-notes-needed --- Comment #20 from David Nind <david@davidnind.com> --- Testing notes (using KTD): 1. Tested using Elasticsearch 9: ktd --search-engine es9 up -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|Signed Off |Patch doesn't apply -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|Patch doesn't apply |Signed Off -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #202138|0 |1 is obsolete| | Attachment #202139|0 |1 is obsolete| | Attachment #202140|0 |1 is obsolete| | Attachment #202141|0 |1 is obsolete| | Attachment #202142|0 |1 is obsolete| | Attachment #202143|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #21 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 202229 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202229&action=edit Bug 42835: Elasticsearch: Add items index Adds a dedicated 'items' Elasticsearch index where each item is its own flat document keyed by itemnumber, built from Koha::Item data rather than MARC. Adds index_items() and delete_items() to Indexer.pm. Hooks into Item::store() and Item::delete() via _update_es_index() to keep the index in sync on every item change; a skip_items_index flag allows bulk operations to bypass it. Items in their own index can be updated on circulation without re-parsing MARC, keeping availability and facet counts accurate in real time. Adds a noop mock for index_items in the Elasticsearch.t test fixture to prevent store() calls inside marc_records_to_documents tests from trying to index items into ES. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #22 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 202230 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202230&action=edit Bug 42835: Koha::Item: Skip biblio re-index for circulation-only field changes When only fields in %ITEM_CIRC_FIELDS are modified (onloan, issues, renewals, holdingbranch, datelastborrowed, datelastseen, localuse, reserves, timestamp), skip the biblio MARC re-index — the items index is authoritative for this data. notforloan, damaged, itemlost and withdrawn are excluded from the skip set: they affect the available field in the biblios index and must trigger a re-index to keep availability filtering correct. The skip is further gated on _items_index_ready(): the biblio re-index is only skipped when the items index is sufficiently populated (>=95% of DB items indexed). This prevents stale availability data on fresh installs where the items index may contain only a handful of documents. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #23 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 202231 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202231&action=edit Bug 42835: rebuild_elasticsearch.pl: Add --items flag Adds -i|--items to rebuild the items index from the database (not from MARC). Items are not included in the default "index everything" behaviour and must be requested explicitly. Also adds -in|--itemnumber to reindex one or more specific items. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #24 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 202232 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202232&action=edit Bug 42835: Elasticsearch: Route availability and facets through items index Queries the items index for availability filtering and item-level facets (itype, location, ccode, homebranch, holdingbranch). Uses cardinality sub-aggregations for per-biblio facet counts rather than per-item counts. Adds biblionumber as an explicit integer field in the biblios index mapping and stamps it on biblio documents so item facet queries can be scoped to the current search result set via a _biblionumbers aggregation. Fixes a bug where _apply_available_filter assumed available:true was always at must[0]; now scans all must elements. Fixes a bug where biblio_count{value} was tested for hashref truthiness rather than key existence, always preferring it over doc_count. Fixes the biblios index available field to correctly treat notforloan, damaged, and withdrawn items as unavailable — previously only onloan and itemlost were checked. Covers both item-level notforloan and items whose item type has notforloan set. Updates existing tests in Elasticsearch.t that were asserting the previous wrong behaviour. Items index routing is unconditional here; the readiness gate that prevents empty-index regressions on upgrade is added in the next commit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #25 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 202233 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202233&action=edit Bug 42835: OPAC performance: Batch item visibility check to reduce N+1 DB queries Before the main result loop, pre-scan all MARC records for the current page to collect every itemnumber in one pass. Run a single batched filter_by_visible_in_opac call and store the result in a hash. The inner item loop then does a plain hash lookup instead of one Koha::Items DB query per item, reducing N queries to 1 per page load. Falls back to the per-item path if no itemnumbers are found in the pre-scan. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #26 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 202234 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202234&action=edit Bug 42835: Elasticsearch: Gate items index features behind readiness check Adds _items_index_ready() which checks whether the items index is fully populated before enabling availability filtering and facet aggregation. The index is considered ready only when it contains at least 95% of the items in the database — this prevents the gate opening after a single circulation event has indexed one item before rebuild_elasticsearch.pl --items has been run. The result is cached: 5 minutes when ready, 60 seconds when not, so a freshly populated index is detected quickly without a restart. TEST PLAN (Do not apply patches yet) 1) Set up KTD with Elasticsearch 8: ktd --search-engine es8 up 2) Do a search for 'music', you should get 42 results: http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music 3) Click the 'Limit to records with available items' facet link. Notice you get 40 results. 9 of these bibs have all their items unavailable and should NOT be showing. 4) Find a biblio with a single item (the top result of the search): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=437 5) Check-out that item (39999000019186) to a patron 6) Repeat the same search with 'Limit to records with available items'. Notice the biblio above is no longer showing (this is correct). 7) Check the background jobs: http://localhost:8081/cgi-bin/koha/admin/background_jobs.pl 8) Confirm at least 1 'Update Elasticsearch index' entry exists. This is because a full bib reindex is triggered for anytime a check-out happens. 9) Let's pick a 2nd bib from that same search ('music' + only available): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=380 10) Edit that single item, put it 'Not for loan' 11) Refresh the search. Notice the bib is always showing, even though all its items are unavailable (This is incorrect): http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music&weight_search=1&sort_by=relevance&limit=available APPLY PATCHES 1) Set up KTD with Elasticsearch 8: ktd --search-engine es8 up 2) Do a search for 'music', you should get 42 results: http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music 3) Click the 'Limit to records with available items' facet link. Notice you now get 31 results. The previous 9 with all their items unavailable do not show anymore. 4) Find a biblio with a single item (the top result of the search): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=437 5) Check-out that item (39999000019186) to a patron 6) Repeat the same search with 'Limit to records with available items'. Notice the biblio above is no longer showing (this is still correct). 7) Check the background jobs: http://localhost:8081/cgi-bin/koha/admin/background_jobs.pl 8) Confirm at least 1 'Update Elasticsearch index' entry exists. This is because a full bib reindex is triggered for anytime a check-out happens. This is still the case because a full reindex (to create the items index) has not occurred yet. Let's do that 9) Run: $ perl misc/search_tools/rebuild_elasticsearch.pl -i -r -v 10) Do another check-out for a 2nd bib with a single item e.g. (39999000016710): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=380 11) Check the background jobs again: http://localhost:8081/cgi-bin/koha/admin/background_jobs.pl 12) Notice a new full bib record reindex job was not triggered, because this was updated directly on the items index synchronously. Confirm that bib record no longer shows on the 'only available' search: http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music&weight_search=1&sort_by=relevance&limit=available 13) Pick a third bib from the same search http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=374 14) Edit the single item and put it 'not for loan'. 15) Refresh the search. Notice the bib is not showing anymore, because all its items are unavailable (This is now correct): http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music&weight_search=1&sort_by=relevance&limit=available Benefits: 1. notforloan/damaged/withdrawn availability fix 2. Skip biblio re-index on checkout — performance improvement, visible under load 3. Availability filtering via items index — real-time post-checkout 4. Safe on upgrade — falls back to the biblios index until a rebuild with --items is run, no hard requirement to rebuild before the system works correctly. Run tests: prove t/db_dependent/Koha/SearchEngine/Elasticsearch/Search.t prove t/db_dependent/Koha/SearchEngine/Elasticsearch/Indexer.t prove t/db_dependent/Koha/SearchEngine/Elasticsearch.t prove t/db_dependent/Koha/Item.t prove t/db_dependent/Search.t prove t/db_dependent/Koha/SearchEngine/Search.t Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Andrew Fuerste-Henry <andrew@bywatersolutions.com> changed: What |Removed |Added ---------------------------------------------------------------------------- Blocks| |43151 Referenced Bugs: https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=43151 [Bug 43151] ES should apply search filters on each item discretely -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #202229|0 |1 is obsolete| | Attachment #202230|0 |1 is obsolete| | Attachment #202231|0 |1 is obsolete| | Attachment #202232|0 |1 is obsolete| | Attachment #202233|0 |1 is obsolete| | Attachment #202234|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #27 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 202480 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202480&action=edit Bug 42835: Elasticsearch: Add items index Adds a dedicated 'items' Elasticsearch index where each item is its own flat document keyed by itemnumber, built from Koha::Item data rather than MARC. Adds index_items() and delete_items() to Indexer.pm. Hooks into Item::store() and Item::delete() via _update_es_index() to keep the index in sync on every item change; a skip_items_index flag allows bulk operations to bypass it. Items in their own index can be updated on circulation without re-parsing MARC, keeping availability and facet counts accurate in real time. Adds a noop mock for index_items in the Elasticsearch.t test fixture to prevent store() calls inside marc_records_to_documents tests from trying to index items into ES. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #28 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 202481 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202481&action=edit Bug 42835: Koha::Item: Skip biblio re-index for circulation-only field changes When only fields in %ITEM_CIRC_FIELDS are modified (onloan, issues, renewals, holdingbranch, datelastborrowed, datelastseen, localuse, reserves, timestamp), skip the biblio MARC re-index — the items index is authoritative for this data. notforloan, damaged, itemlost and withdrawn are excluded from the skip set: they affect the available field in the biblios index and must trigger a re-index to keep availability filtering correct. The skip is further gated on _items_index_ready(): the biblio re-index is only skipped when the items index is sufficiently populated (>=95% of DB items indexed). This prevents stale availability data on fresh installs where the items index may contain only a handful of documents. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #29 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 202482 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202482&action=edit Bug 42835: rebuild_elasticsearch.pl: Add --items flag Adds -i|--items to rebuild the items index from the database (not from MARC). Items are not included in the default "index everything" behaviour and must be requested explicitly. Also adds -in|--itemnumber to reindex one or more specific items. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #30 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 202483 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202483&action=edit Bug 42835: Elasticsearch: Route availability and facets through items index Queries the items index for availability filtering and item-level facets (itype, location, ccode, homebranch, holdingbranch). Uses cardinality sub-aggregations for per-biblio facet counts rather than per-item counts. Adds biblionumber as an explicit integer field in the biblios index mapping and stamps it on biblio documents so item facet queries can be scoped to the current search result set via a _biblionumbers aggregation. Fixes a bug where _apply_available_filter assumed available:true was always at must[0]; now scans all must elements. Fixes a bug where biblio_count{value} was tested for hashref truthiness rather than key existence, always preferring it over doc_count. Fixes the biblios index available field to correctly treat notforloan, damaged, and withdrawn items as unavailable — previously only onloan and itemlost were checked. Covers both item-level notforloan and items whose item type has notforloan set. Updates existing tests in Elasticsearch.t that were asserting the previous wrong behaviour. Items index routing is unconditional here; the readiness gate that prevents empty-index regressions on upgrade is added in the next commit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #31 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 202484 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202484&action=edit Bug 42835: OPAC performance: Batch item visibility check to reduce N+1 DB queries Before the main result loop, pre-scan all MARC records for the current page to collect every itemnumber in one pass. Run a single batched filter_by_visible_in_opac call and store the result in a hash. The inner item loop then does a plain hash lookup instead of one Koha::Items DB query per item, reducing N queries to 1 per page load. Falls back to the per-item path if no itemnumbers are found in the pre-scan. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #32 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 202485 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202485&action=edit Bug 42835: Elasticsearch: Gate items index features behind readiness check Adds _items_index_ready() which checks whether the items index is fully populated before enabling availability filtering and facet aggregation. The index is considered ready only when it contains at least 95% of the items in the database — this prevents the gate opening after a single circulation event has indexed one item before rebuild_elasticsearch.pl --items has been run. The result is cached: 5 minutes when ready, 60 seconds when not, so a freshly populated index is detected quickly without a restart. TEST PLAN (Do not apply patches yet) 1) Set up KTD with Elasticsearch 8: ktd --search-engine es8 up 2) Do a search for 'music', you should get 42 results: http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music 3) Click the 'Limit to records with available items' facet link. Notice you get 40 results. 9 of these bibs have all their items unavailable and should NOT be showing. 4) Find a biblio with a single item (the top result of the search): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=437 5) Check-out that item (39999000019186) to a patron 6) Repeat the same search with 'Limit to records with available items'. Notice the biblio above is no longer showing (this is correct). 7) Check the background jobs: http://localhost:8081/cgi-bin/koha/admin/background_jobs.pl 8) Confirm at least 1 'Update Elasticsearch index' entry exists. This is because a full bib reindex is triggered for anytime a check-out happens. 9) Let's pick a 2nd bib from that same search ('music' + only available): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=380 10) Edit that single item, put it 'Not for loan' 11) Refresh the search. Notice the bib is always showing, even though all its items are unavailable (This is incorrect): http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music&weight_search=1&sort_by=relevance&limit=available APPLY PATCHES 1) Set up KTD with Elasticsearch 8: ktd --search-engine es8 up 2) Do a search for 'music', you should get 42 results: http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music 3) Click the 'Limit to records with available items' facet link. Notice you now get 31 results. The previous 9 with all their items unavailable do not show anymore. 4) Find a biblio with a single item (the top result of the search): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=437 5) Check-out that item (39999000019186) to a patron 6) Repeat the same search with 'Limit to records with available items'. Notice the biblio above is no longer showing (this is still correct). 7) Check the background jobs: http://localhost:8081/cgi-bin/koha/admin/background_jobs.pl 8) Confirm at least 1 'Update Elasticsearch index' entry exists. This is because a full bib reindex is triggered for anytime a check-out happens. This is still the case because a full reindex (to create the items index) has not occurred yet. Let's do that 9) Run: $ perl misc/search_tools/rebuild_elasticsearch.pl -i -r -v 10) Do another check-out for a 2nd bib with a single item e.g. (39999000016710): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=380 11) Check the background jobs again: http://localhost:8081/cgi-bin/koha/admin/background_jobs.pl 12) Notice a new full bib record reindex job was not triggered, because this was updated directly on the items index synchronously. Confirm that bib record no longer shows on the 'only available' search: http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music&weight_search=1&sort_by=relevance&limit=available 13) Pick a third bib from the same search http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=374 14) Edit the single item and put it 'not for loan'. 15) Refresh the search. Notice the bib is not showing anymore, because all its items are unavailable (This is now correct): http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music&weight_search=1&sort_by=relevance&limit=available Benefits: 1. notforloan/damaged/withdrawn availability fix 2. Skip biblio re-index on checkout — performance improvement, visible under load 3. Availability filtering via items index — real-time post-checkout 4. Safe on upgrade — falls back to the biblios index until a rebuild with --items is run, no hard requirement to rebuild before the system works correctly. Run tests: prove t/db_dependent/Koha/SearchEngine/Elasticsearch/Search.t prove t/db_dependent/Koha/SearchEngine/Elasticsearch/Indexer.t prove t/db_dependent/Koha/SearchEngine/Elasticsearch.t prove t/db_dependent/Koha/Item.t prove t/db_dependent/Search.t prove t/db_dependent/Koha/SearchEngine/Search.t Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #33 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 202486 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202486&action=edit Bug 42835: (follow-up) Cache items index queries for availability and facets _get_available_biblionumbers() and _get_item_facets() hit the items index on every search, unscoped and uncached. Fine on a small catalog, brutal at real scale - hundreds of round-trips per "available" search. Cache both for 15s. Single key for availability (same result for everyone); MD5-of-biblionumbers key for facets (result is per-search). Test plan: 1) prove t/db_dependent/Koha/SearchEngine/Elasticsearch/Search.t 2) Run demo_cache_fix.pl - second call to each function should be near-instant vs the first Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #34 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 202487 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=202487&action=edit Bug 42835: [DO NOT PUSH]: demo script for items index caching fix Standalone script demonstrating the caching fix in the previous commit. Wraps the real ES client to count calls, then calls _get_available_biblionumbers() and _get_item_facets() twice each against a live KTD Elasticsearch instance - first call should hit ES, second should be near-instant from cache. Run: perl demo_cache_fix.pl This commit is for local testing only and must not be pushed upstream. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #35 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Squashed in a fix restoring the Try::Tiny import to Koha/Item.pm, removed by bug 42391. Added caching to items index queries for availability and facets. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Andrew Fuerste-Henry <andrew@bywatersolutions.com> changed: What |Removed |Added ---------------------------------------------------------------------------- Sponsorship status|--- |Sponsored Comma delimited| |Cuyahoga County Public list of Sponsors| |Library -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Lisette Scheer <lisette@bywatersolutions.com> changed: What |Removed |Added ---------------------------------------------------------------------------- QA Contact|testopia@bugs.koha-communit |Laura.escamilla@bywatersolu |y.org |tions.com CC| |lisette@bywatersolutions.co | |m -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Blocks| |35052 Referenced Bugs: https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=35052 [Bug 35052] OpacHiddenItemsHidesRecord system preference should be considered on index time instead -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|Signed Off |Failed QA --- Comment #36 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Some things coming out of a QA review: 1. misc/search_tools/rebuild_elasticsearch.pl:269-271, _do_reindex_items — the new --items path isn't slice-aware. Biblios/authorities reindexing forks --processes N workers and gives each a slice => {index, count} so the table is partitioned across them. _do_reindex_items ignores %iterator_options entirely and does Koha::Items->search({}) unconditionally — every forked worker (not just the parent) reindexes the entire items table independently. Still correct (idempotent by itemnumber), but on a large catalog --items --processes 4 does 4x the ES writes and DB scans instead of dividing the work, defeating the point of --processes. 2. Koha/Item.pm:73-78 (%ITEM_CIRC_FIELDS) — holdingbranch is deliberately in the "skip biblio reindex" set (per the commit message: "AddIssue always sets it alongside onloan"), so ordinary checkouts still qualify for the fast path. But that means a genuine branch transfer (also just a holdingbranch change) skips the biblio reindex too. The items-index facets stay correct (they're queried live), but the ES-stored MARC blob used to render search-result rows (branch/location shown per hit) goes stale until some unrelated reindex trigger fires. Net effect: after a transfer, the facet count and the actual result-row "held at" branch can disagree. 3. No test coverage for the actual N+1 fix — C4/Search.pm's new pre-scan/batched visibility check (searchResults() ~1740-1765, ~1895-1905) has no corresponding change in t/db_dependent/Search.t. Every other piece of this patchset (readiness gate, caching, items index CRUD) has solid test coverage confirmed by direct reading; this is the one production change that ships untested — worth a test with a mixed visible/hidden batch, an empty batch, and the fallback-to-per-item path. And, some possible nice to haves: 1. C4/Search.pm:1750/1752 vs 1774/1778 — the visibility pre-scan parses every page's MARC record with MARC::Record->new_from_usmarc/new_record_from_zebra, then the main loop parses the same record again. Cheap relative to the DB round-trips saved, but stashing the already-parsed record from the pre-scan and reusing it would remove the duplicate work entirely. 2. Koha/SearchEngine/Elasticsearch.pm — adds Readonly our $ITEMS_INDEX => 'items'; locally even though the file now also does use Koha::SearchEngine;, which defines the same constant. Matches the file's pre-existing (already duplicated) $BIBLIOS_INDEX/$AUTHORITIES_INDEX pattern, so not new, but a third duplicate constant is a good excuse to collapse to one source of truth. 3. POD is thin on Koha::Item::_update_es_index and Koha::SearchEngine::Elasticsearch::Indexer::_item_to_document (headers present, no real description). 4. Search.pm:search_compat now always adds a _biblionumbers terms aggregation (size => 9_000) plus a second ES round-trip to the items index for facets, on every biblios-index search once the items index is ready — even for callers that don't use the returned facets. Not a bug, but worth confirming it doesn't add measurable per-search latency on large result sets. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #37 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 203718 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=203718&action=edit Bug 42835: (QA follow-up) Make --items slice-aware for --processes _do_reindex_items() ignored %iterator_options and always reindexed the whole items table per forked process, instead of just its own slice. Test plan: 1) Run: perl misc/search_tools/rebuild_elasticsearch.pl --items --reset --processes 3 --verbose 2) Check the output: you should see 3 different "Processing slice" lines and 3 different item counts that add up to your total, not the same full count three times. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #38 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 203719 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=203719&action=edit Bug 42835: (QA follow-up) Don't skip biblio re-index on branch transfer A standalone holdingbranch change (transfer) was wrongly treated as safe to skip, same as a checkout. Now, when holdingbranch changes, the skip only applies if onloan or issues changed too. Test plan: 1) Run: prove t/db_dependent/Koha/Item.t Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #39 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 203720 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=203720&action=edit Bug 42835: (QA follow-up) Add test coverage for OPAC item visibility batching C4::Search::searchResults() gained a pre-scan that batches the OPAC item-visibility check into one DB call per page instead of one per item, but shipped with no corresponding test. Add coverage for a mixed visible/hidden batch, a batch with no items at all, and the per-item fallback path. The fallback is exercised by mocking MARC parsing to fail only during the pre-scan, forcing the old per-item lookup to run instead of the batched one. Test plan: 1) Run: prove t/db_dependent/Search.t Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #40 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 203721 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=203721&action=edit Bug 42835: (QA follow-up) Reuse pre-scanned MARC record in searchResults() The OPAC item-visibility pre-scan already parses every record on the page to collect itemnumbers, then the main loop parsed the same records again a few lines later. Stash the parsed record from the pre-scan and reuse it, instead of parsing twice. Test plan: 1) Run: prove t/db_dependent/Search.t Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #41 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 203722 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=203722&action=edit Bug 42835: (QA follow-up) Document Koha::Item::_update_es_index This file documents its other private subs (_add_statistic, _status, _set_found_trigger) with a usage synopsis and description; _update_es_index only had a bare header. Fill it in to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #42 from Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> --- Created attachment 203723 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=203723&action=edit Bug 42835: [DO NOT PUSH] demo script for facets round-trip latency Benchmarks search_compat()'s two added costs on ~9k synthetic biblios (matching the _biblionumbers aggregation's own size cap): the extra terms aggregation on the biblios query, and the second round-trip to _get_item_facets. Cold-cache worst case adds ~25-55ms on top of a ~20-25ms baseline query, but the item-facets round-trip is cached, so repeated searches against the same result set pay far less. The script's verdict line checks the total round-trip against a configurable "still feels instant" budget ($ACCEPTABLE_TOTAL_MS, currently 100ms) rather than asserting a fixed conclusion -- every run so far has landed well under that budget, so no follow-up fix is warranted here. This commit is for local testing only and must not be pushed upstream. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Pedro Amorim (ammopt) <pedro.amorim@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|Failed QA |Signed Off -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Blocks|35052 | Referenced Bugs: https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=35052 [Bug 35052] OpacHiddenItemsHidesRecord system preference should be considered on index time instead -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #43 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Nice work Pedro.. apologies I didn't come back to this sooner.. I somehow missed the follow-ups had been posted :) -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #44 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- The items-index availability/facets caches (_get_available_biblionumbers(), _get_item_facets() in Koha/SearchEngine/Elasticsearch/Search.pm) now uses a 15s TTL to avoid re-scanning the items index on every "available" search. I looked at adding immediate cache invalidation on every circulation event (checkout/checkin/transfer/edit), but decided against it: it would require a global invalidation trigger that fires on any item change anywhere in the instance, which significantly reduces the caching benefit during busy circulation periods — exactly when the caching matters most for protecting Elasticsearch from load. I felt a 15s worst-case staleness window on availability/facets is close enough to real-time that it shouldn't be perceptible to end users in normal use. Documenting this as a known, accepted trade-off rather than fixing it: for up to 15 seconds after a checkout/checkin, the "available" search filter and item-count facets may not yet reflect the very latest circulation activity for an affected biblio. Everything else (item state itself, the items index, non-cached lookups) is updated synchronously. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #202480|0 |1 is obsolete| | Attachment #202481|0 |1 is obsolete| | Attachment #202482|0 |1 is obsolete| | Attachment #202483|0 |1 is obsolete| | Attachment #202484|0 |1 is obsolete| | Attachment #202485|0 |1 is obsolete| | Attachment #202486|0 |1 is obsolete| | Attachment #202487|0 |1 is obsolete| | Attachment #203718|0 |1 is obsolete| | Attachment #203719|0 |1 is obsolete| | Attachment #203720|0 |1 is obsolete| | Attachment #203721|0 |1 is obsolete| | Attachment #203722|0 |1 is obsolete| | Attachment #203723|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #45 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 205410 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=205410&action=edit Bug 42835: Elasticsearch: Add items index Adds a dedicated 'items' Elasticsearch index where each item is its own flat document keyed by itemnumber, built from Koha::Item data rather than MARC. Adds index_items() and delete_items() to Indexer.pm. Hooks into Item::store() and Item::delete() via _update_es_index() to keep the index in sync on every item change; a skip_items_index flag allows bulk operations to bypass it. Items in their own index can be updated on circulation without re-parsing MARC, keeping availability and facet counts accurate in real time. Adds a noop mock for index_items in the Elasticsearch.t test fixture to prevent store() calls inside marc_records_to_documents tests from trying to index items into ES. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #46 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 205411 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=205411&action=edit Bug 42835: Koha::Item: Skip biblio re-index for circulation-only field changes When only fields in %ITEM_CIRC_FIELDS are modified (onloan, issues, renewals, holdingbranch, datelastborrowed, datelastseen, localuse, reserves, timestamp), skip the biblio MARC re-index — the items index is authoritative for this data. notforloan, damaged, itemlost and withdrawn are excluded from the skip set: they affect the available field in the biblios index and must trigger a re-index to keep availability filtering correct. The skip is further gated on _items_index_ready(): the biblio re-index is only skipped when the items index is sufficiently populated (>=95% of DB items indexed). This prevents stale availability data on fresh installs where the items index may contain only a handful of documents. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #47 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 205412 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=205412&action=edit Bug 42835: rebuild_elasticsearch.pl: Add --items flag Adds -i|--items to rebuild the items index from the database (not from MARC). Items are not included in the default "index everything" behaviour and must be requested explicitly. Also adds -in|--itemnumber to reindex one or more specific items. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #48 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 205413 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=205413&action=edit Bug 42835: Elasticsearch: Route availability and facets through items index Queries the items index for availability filtering and item-level facets (itype, location, ccode, homebranch, holdingbranch). Uses cardinality sub-aggregations for per-biblio facet counts rather than per-item counts. Adds biblionumber as an explicit integer field in the biblios index mapping and stamps it on biblio documents so item facet queries can be scoped to the current search result set via a _biblionumbers aggregation. Fixes a bug where _apply_available_filter assumed available:true was always at must[0]; now scans all must elements. Fixes a bug where biblio_count{value} was tested for hashref truthiness rather than key existence, always preferring it over doc_count. Fixes the biblios index available field to correctly treat notforloan, damaged, and withdrawn items as unavailable — previously only onloan and itemlost were checked. Covers both item-level notforloan and items whose item type has notforloan set. Updates existing tests in Elasticsearch.t that were asserting the previous wrong behaviour. Items index routing is unconditional here; the readiness gate that prevents empty-index regressions on upgrade is added in the next commit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #49 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 205414 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=205414&action=edit Bug 42835: OPAC performance: Batch item visibility check to reduce N+1 DB queries Before the main result loop, pre-scan all MARC records for the current page to collect every itemnumber in one pass. Run a single batched filter_by_visible_in_opac call and store the result in a hash. The inner item loop then does a plain hash lookup instead of one Koha::Items DB query per item, reducing N queries to 1 per page load. Falls back to the per-item path if no itemnumbers are found in the pre-scan. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #50 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 205415 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=205415&action=edit Bug 42835: Elasticsearch: Gate items index features behind readiness check Adds _items_index_ready() which checks whether the items index is fully populated before enabling availability filtering and facet aggregation. The index is considered ready only when it contains at least 95% of the items in the database — this prevents the gate opening after a single circulation event has indexed one item before rebuild_elasticsearch.pl --items has been run. The result is cached: 5 minutes when ready, 60 seconds when not, so a freshly populated index is detected quickly without a restart. TEST PLAN (Do not apply patches yet) 1) Set up KTD with Elasticsearch 8: ktd --search-engine es8 up 2) Do a search for 'music', you should get 42 results: http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music 3) Click the 'Limit to records with available items' facet link. Notice you get 40 results. 9 of these bibs have all their items unavailable and should NOT be showing. 4) Find a biblio with a single item (the top result of the search): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=437 5) Check-out that item (39999000019186) to a patron 6) Repeat the same search with 'Limit to records with available items'. Notice the biblio above is no longer showing (this is correct). 7) Check the background jobs: http://localhost:8081/cgi-bin/koha/admin/background_jobs.pl 8) Confirm at least 1 'Update Elasticsearch index' entry exists. This is because a full bib reindex is triggered for anytime a check-out happens. 9) Let's pick a 2nd bib from that same search ('music' + only available): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=380 10) Edit that single item, put it 'Not for loan' 11) Refresh the search. Notice the bib is always showing, even though all its items are unavailable (This is incorrect): http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music&weight_search=1&sort_by=relevance&limit=available APPLY PATCHES 1) Set up KTD with Elasticsearch 8: ktd --search-engine es8 up 2) Do a search for 'music', you should get 42 results: http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music 3) Click the 'Limit to records with available items' facet link. Notice you now get 31 results. The previous 9 with all their items unavailable do not show anymore. 4) Find a biblio with a single item (the top result of the search): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=437 5) Check-out that item (39999000019186) to a patron 6) Repeat the same search with 'Limit to records with available items'. Notice the biblio above is no longer showing (this is still correct). 7) Check the background jobs: http://localhost:8081/cgi-bin/koha/admin/background_jobs.pl 8) Confirm at least 1 'Update Elasticsearch index' entry exists. This is because a full bib reindex is triggered for anytime a check-out happens. This is still the case because a full reindex (to create the items index) has not occurred yet. Let's do that 9) Run: $ perl misc/search_tools/rebuild_elasticsearch.pl -i -r -v 10) Do another check-out for a 2nd bib with a single item e.g. (39999000016710): http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=380 11) Check the background jobs again: http://localhost:8081/cgi-bin/koha/admin/background_jobs.pl 12) Notice a new full bib record reindex job was not triggered, because this was updated directly on the items index synchronously. Confirm that bib record no longer shows on the 'only available' search: http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music&weight_search=1&sort_by=relevance&limit=available 13) Pick a third bib from the same search http://localhost:8081/cgi-bin/koha/catalogue/detail.pl?biblionumber=374 14) Edit the single item and put it 'not for loan'. 15) Refresh the search. Notice the bib is not showing anymore, because all its items are unavailable (This is now correct): http://localhost:8081/cgi-bin/koha/catalogue/search.pl?idx=&q=music&weight_search=1&sort_by=relevance&limit=available Benefits: 1. notforloan/damaged/withdrawn availability fix 2. Skip biblio re-index on checkout — performance improvement, visible under load 3. Availability filtering via items index — real-time post-checkout 4. Safe on upgrade — falls back to the biblios index until a rebuild with --items is run, no hard requirement to rebuild before the system works correctly. Run tests: prove t/db_dependent/Koha/SearchEngine/Elasticsearch/Search.t prove t/db_dependent/Koha/SearchEngine/Elasticsearch/Indexer.t prove t/db_dependent/Koha/SearchEngine/Elasticsearch.t prove t/db_dependent/Koha/Item.t prove t/db_dependent/Search.t prove t/db_dependent/Koha/SearchEngine/Search.t Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: David Nind <david@davidnind.com> Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #51 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 205416 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=205416&action=edit Bug 42835: (follow-up) Cache items index queries for availability and facets _get_available_biblionumbers() and _get_item_facets() hit the items index on every search, unscoped and uncached. Fine on a small catalog, brutal at real scale - hundreds of round-trips per "available" search. Cache both for 15s. Single key for availability (same result for everyone); MD5-of-biblionumbers key for facets (result is per-search). Test plan: 1) prove t/db_dependent/Koha/SearchEngine/Elasticsearch/Search.t 2) Run demo_cache_fix.pl - second call to each function should be near-instant vs the first Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #52 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 205417 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=205417&action=edit Bug 42835: [DO NOT PUSH]: demo script for items index caching fix Standalone script demonstrating the caching fix in the previous commit. Wraps the real ES client to count calls, then calls _get_available_biblionumbers() and _get_item_facets() twice each against a live KTD Elasticsearch instance - first call should hit ES, second should be near-instant from cache. Run: perl demo_cache_fix.pl This commit is for local testing only and must not be pushed upstream. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #53 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 205418 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=205418&action=edit Bug 42835: (QA follow-up) Make --items slice-aware for --processes _do_reindex_items() ignored %iterator_options and always reindexed the whole items table per forked process, instead of just its own slice. Test plan: 1) Run: perl misc/search_tools/rebuild_elasticsearch.pl --items --reset --processes 3 --verbose 2) Check the output: you should see 3 different "Processing slice" lines and 3 different item counts that add up to your total, not the same full count three times. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #54 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 205419 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=205419&action=edit Bug 42835: (QA follow-up) Don't skip biblio re-index on branch transfer A standalone holdingbranch change (transfer) was wrongly treated as safe to skip, same as a checkout. Now, when holdingbranch changes, the skip only applies if onloan or issues changed too. Test plan: 1) Run: prove t/db_dependent/Koha/Item.t Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #55 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 205420 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=205420&action=edit Bug 42835: (QA follow-up) Add test coverage for OPAC item visibility batching C4::Search::searchResults() gained a pre-scan that batches the OPAC item-visibility check into one DB call per page instead of one per item, but shipped with no corresponding test. Add coverage for a mixed visible/hidden batch, a batch with no items at all, and the per-item fallback path. The fallback is exercised by mocking MARC parsing to fail only during the pre-scan, forcing the old per-item lookup to run instead of the batched one. Test plan: 1) Run: prove t/db_dependent/Search.t Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #56 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 205421 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=205421&action=edit Bug 42835: (QA follow-up) Reuse pre-scanned MARC record in searchResults() The OPAC item-visibility pre-scan already parses every record on the page to collect itemnumbers, then the main loop parsed the same records again a few lines later. Stash the parsed record from the pre-scan and reuse it, instead of parsing twice. Test plan: 1) Run: prove t/db_dependent/Search.t Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #57 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 205422 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=205422&action=edit Bug 42835: (QA follow-up) Document Koha::Item::_update_es_index This file documents its other private subs (_add_statistic, _status, _set_found_trigger) with a usage synopsis and description; _update_es_index only had a bare header. Fill it in to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #58 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 205423 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=205423&action=edit Bug 42835: [DO NOT PUSH] demo script for facets round-trip latency Benchmarks search_compat()'s two added costs on ~9k synthetic biblios (matching the _biblionumbers aggregation's own size cap): the extra terms aggregation on the biblios query, and the second round-trip to _get_item_facets. Cold-cache worst case adds ~25-55ms on top of a ~20-25ms baseline query, but the item-facets round-trip is cached, so repeated searches against the same result set pay far less. The script's verdict line checks the total round-trip against a configurable "still feels instant" budget ($ACCEPTABLE_TOTAL_MS, currently 100ms) rather than asserting a fixed conclusion -- every run so far has landed well under that budget, so no follow-up fix is warranted here. This commit is for local testing only and must not be pushed upstream. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #59 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 205424 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=205424&action=edit Bug 42835: (QA follow-up) Stop items-index ES calls and SearchEngine pref leaking out of Item.t The 'skip biblio re-index for circ-only field changes' subtest mocks SearchEngine to 'Elasticsearch' via t::lib::Mocks::mock_preference, which overrides a process-global hash with no automatic restore. It was never reset back, so every later store() call in the file - including the many inside the 'Recalls tests' subtest - went on to run with SearchEngine still set to 'Elasticsearch'. Koha::Item::store() calls _update_es_index() unconditionally on that preference, which builds a real Koha::SearchEngine::Elasticsearch::Indexer and calls index_items()/delete_items() on it. Only index_records() was mocked on that class, so those calls were free to reach a real (or unreachable) Elasticsearch cluster from a plain Perl unit test - in the latter case, the resulting warn() would fail the file's Test::NoWarnings check. Mock index_items()/delete_items() as no-ops alongside index_records(), and restore SearchEngine to 'Zebra' at the end of the subtest, matching the pattern already used elsewhere (e.g. t/db_dependent/Koha/Authorities.t). Test plan: 1) Run: prove t/db_dependent/Koha/Item.t Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #60 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 205425 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=205425&action=edit Bug 42835: (QA follow-up) Only treat a genuine holdingbranch value change as a transfer The previous fix for skipping the biblio re-index on branch transfers used exists $updated_columns{onloan} / {issues} as a proxy for "this is a checkout, not a transfer". That misses the case where a checkout also moves the item to a different branch than it currently has: onloan being dirty made is_branch_transfer false, so the branch-facet-relevant change was silently skipped along with the safe circ-only fields. Compare the actual holdingbranch value against the pre-store item instead of looking at which other columns happen to be dirty. A holdingbranch value that hasn't actually changed (AddIssue re-asserting the current branch alongside onloan) is still skippable; any real branch change now always forces a re-index, regardless of what else changed in the same store() call. Test plan: 1) Run: prove t/db_dependent/Koha/Item.t Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Andrew Fuerste-Henry <andrew@bywatersolutions.com> changed: What |Removed |Added ---------------------------------------------------------------------------- Blocks| |43017 Referenced Bugs: https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=43017 [Bug 43017] [OMNIBUS] Interface optimization -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Lisette Scheer <lisette@bywatersolutions.com> changed: What |Removed |Added ---------------------------------------------------------------------------- QA Contact|Laura.escamilla@bywatersolu |aleisha@catalyst.net.nz |tions.com | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Lisette Scheer <lisette@bywatersolutions.com> changed: What |Removed |Added ---------------------------------------------------------------------------- QA Contact|aleisha@catalyst.net.nz |nick@bywatersolutions.com -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Luis Bataller <luis.bataller@xercode.es> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |luis.bataller@xercode.es -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 David Cook <dcook@prosentient.com.au> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |dcook@prosentient.com.au -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #61 from David Cook <dcook@prosentient.com.au> --- I'll have to come take a look at this later. Sounds interesting though... -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |melia@bywatersolutions.com --- Comment #62 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- *** Bug 8589 has been marked as a duplicate of this bug. *** -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #63 from Luis Bataller <luis.bataller@xercode.es> --- Hi, Thanks for picking this up — indexing item data in Elasticsearch is something Koha has needed for a long time. We went through this same design question some months ago and evaluated both options: a separate item index, and items as `nested` objects inside the biblio document. We ended up choosing `nested`. We picked up the existing bug 8589 to offer that work to the community, but we never attached the patches there — we offered them and then let it drop, mostly on our side. The implementation is now in pre-production on an installation with roughly 1.5 million biblio records and 8 million items. A search filtered only by item attributes works fine with a separate index — that matches what bug 42835 currently implements, as far as I can tell. The difficulty we hit was with queries mixing item-level and biblio-level criteria, which is the common shape in both the OPAC and the staff interface. What follows is a summary of the analysis we carried out against that catalogue. --- Our reference case was the most frequent one: **library = b, title like "..."**. In that catalogue the largest library holds around half a million items, which resolves to somewhere between 140,000 and 320,000 distinct biblionumbers depending on the local items-per-record ratio — 10-20% of the whole catalogue in a single intermediate set. Two problems followed: 1. **Intermediate set size.** Passing that many biblionumbers as a `terms` filter exceeds `index.max_terms_count` (65,536 by default). The workarounds we considered were splitting the query into chunks, which breaks global scoring and relevance ordering, or a `terms lookup`, which introduces a synchronous write plus a refresh in the middle of a read path. 2. **Relevance pagination.** The selective criterion is the title, so the biblio query has to run first, but then there is no way to know how many of its hits will survive the item filter. Filling a page of 20 results becomes an iterative loop with an unpredictable number of round-trips, or requires fetching the full candidate set and re-sorting in application code, losing BM25 scoring. --- So my main question is: have you already worked out how general searches combining item and biblio criteria would be resolved, particularly these two points? If you have a solution, I would genuinely like to understand it — the separate-index approach has real advantages we would benefit from, particularly a much lower cost for full reindexing when an item field is added. If it turns out to be an open problem, we would be glad to share what we have. The patches are not attached to bug 8589 yet, but we can post them there promptly if there is interest, whether as something to contribute or just as a reference for comparison. We are also happy to run comparative tests against our dataset if that would help validate either design. Thanks again for working on this. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Fridolin Somers <fridolin.somers@biblibre.com> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |fridolin.somers@biblibre.co | |m --- Comment #64 from Fridolin Somers <fridolin.somers@biblibre.com> --- Ohhh looks great :D -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #65 from David Cook <dcook@prosentient.com.au> --- (In reply to Luis Bataller from comment #63)
Hi,
Thanks for picking this up — indexing item data in Elasticsearch is something Koha has needed for a long time.
We went through this same design question some months ago and evaluated both options: a separate item index, and items as `nested` objects inside the biblio document. We ended up choosing `nested`.
Great to meet you at the dev meeting last night, Luis. I agree with your analysis regarding having a separate item index vs having nested objects. To me, having a separate item index doesn't make sense, because it would be very inefficient at a large scale. That said, what have you observed with bib records with many (e.g. 200-1000 items) nested inside the JSON document in Elasticsearch? I am supportive of having nested items, because it allows for better searching, but I have only tried it with relatively small numbers of items embedded in the bib index. Actually with Elasticsearch I think that you can do includes/excludes with source filtering... so you could do a search against a large record but just retrieve the bib record when showing the search results... that could be very very useful. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #66 from David Cook <dcook@prosentient.com.au> --- @Luis, I mentioned it a bit in the chat last night, but the reason we haven't used nested items in the past in Elasticsearch is because we were trying to keep feature parity between Elasticsearch and Zebra, and Zebra has a flat index structure. However, as the use of Elasticsearch in Koha has matured/evolved, we have started doing more Elasticsearch-specific changes. And I think that your idea for nested items would be a really good Elasticsearch-specific change. I think we should be taking advantage of Elasticsearch's capabilities to improve Koha's search here. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #67 from David Cook <dcook@prosentient.com.au> --- @Pedro @Martin I'm short on time, so I was hoping you could give me a concise summary of how your patches work having a separate item index? -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #68 from Luis Bataller <luis.bataller@xercode.es> --- (In reply to David Cook from comment #65)
(In reply to Luis Bataller from comment #63)
Hi,
Thanks for picking this up — indexing item data in Elasticsearch is something Koha has needed for a long time.
We went through this same design question some months ago and evaluated both options: a separate item index, and items as `nested` objects inside the biblio document. We ended up choosing `nested`.
Great to meet you at the dev meeting last night, Luis.
I agree with your analysis regarding having a separate item index vs having nested objects. To me, having a separate item index doesn't make sense, because it would be very inefficient at a large scale.
That said, what have you observed with bib records with many (e.g. 200-1000 items) nested inside the JSON document in Elasticsearch?
I am supportive of having nested items, because it allows for better searching, but I have only tried it with relatively small numbers of items embedded in the bib index.
Actually with Elasticsearch I think that you can do includes/excludes with source filtering... so you could do a search against a large record but just retrieve the bib record when showing the search results... that could be very very useful.
(In reply to David Cook from comment #66)
@Luis, I mentioned it a bit in the chat last night, but the reason we haven't used nested items in the past in Elasticsearch is because we were trying to keep feature parity between Elasticsearch and Zebra, and Zebra has a flat index structure.
However, as the use of Elasticsearch in Koha has matured/evolved, we have started doing more Elasticsearch-specific changes. And I think that your idea for nested items would be a really good Elasticsearch-specific change.
I think we should be taking advantage of Elasticsearch's capabilities to improve Koha's search here.
Hi David, great to meet you at the meeting too. You're right that this approach wouldn't work with Zebra. The changes were made to the ES indexing only, leaving Zebra with the same behaviour as before. I understand that not being able to use nested items with Zebra may be disappointing for Zebra users. Regarding the numbers, here is a table with real indexing time measurements: items total(s) gen gen es_wall KB % only items (gen+es_wall) (total) (only items) ----- ------------- ------- ------------ ------- ------ ------------ 0 0.043 0.030 0.003 0.013 2.0 7.0 % 1 0.075 0.065 0.032 0.010 3.7 42.7 % 10 0.208 0.193 0.145 0.015 12.7 69.7 % 50 0.729 0.707 0.662 0.022 51.4 90.8 % 100 1.327 1.295 1.244 0.032 99.6 93.7 % 250 3.434 3.393 3.297 0.041 245.2 96.0 % 500 7.066 7.004 6.852 0.062 487.8 97.0 % 1000 13.864 13.718 13.267 0.146 1502.4 95.7 % As you can see, indexing time grows a lot with the number of items; but if you look closely, most of the time (over 90% from 50 items on) is spent building the nested items. We are working on a solution to remove that hydration time, bringing it down to the time it takes to hydrate a single item. The idea is to take the already hydrated document from ES and replace only the item or items that have changed, so the cost of indexing a change would no longer depend on how many items the record has. Any comments or suggestions you may have would be much appreciated. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #69 from David Cook <dcook@prosentient.com.au> --- (In reply to Luis Bataller from comment #68)
Regarding the numbers, here is a table with real indexing time measurements:
items total(s) gen gen es_wall KB % only items (gen+es_wall) (total) (only items) ----- ------------- ------- ------------ ------- ------ ------------ 0 0.043 0.030 0.003 0.013 2.0 7.0 % 1 0.075 0.065 0.032 0.010 3.7 42.7 % 10 0.208 0.193 0.145 0.015 12.7 69.7 % 50 0.729 0.707 0.662 0.022 51.4 90.8 % 100 1.327 1.295 1.244 0.032 99.6 93.7 % 250 3.434 3.393 3.297 0.041 245.2 96.0 % 500 7.066 7.004 6.852 0.062 487.8 97.0 % 1000 13.864 13.718 13.267 0.146 1502.4 95.7 %
As you can see, indexing time grows a lot with the number of items; but if you look closely, most of the time (over 90% from 50 items on) is spent building the nested items. We are working on a solution to remove that hydration time, bringing it down to the time it takes to hydrate a single item. The idea is to take the already hydrated document from ES and replace only the item or items that have changed, so the cost of indexing a change would no longer depend on how many items the record has.
Any comments or suggestions you may have would be much appreciated.
I don't know if "hydration" is the right term to use there, since that refers to rendering data on a web page with Javascript. When you say "[time] spent building the nested items", are you referring to the Perl process of building the JSON document? Depend on the structure of the JSON document, doing partial updates might be the way to go for sure. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #70 from Luis Bataller <luis.bataller@xercode.es> --- (In reply to David Cook from comment #69)
(In reply to Luis Bataller from comment #68)
Regarding the numbers, here is a table with real indexing time measurements:
items total(s) gen gen es_wall KB % only items (gen+es_wall) (total) (only items) ----- ------------- ------- ------------ ------- ------ ------------ 0 0.043 0.030 0.003 0.013 2.0 7.0 % 1 0.075 0.065 0.032 0.010 3.7 42.7 % 10 0.208 0.193 0.145 0.015 12.7 69.7 % 50 0.729 0.707 0.662 0.022 51.4 90.8 % 100 1.327 1.295 1.244 0.032 99.6 93.7 % 250 3.434 3.393 3.297 0.041 245.2 96.0 % 500 7.066 7.004 6.852 0.062 487.8 97.0 % 1000 13.864 13.718 13.267 0.146 1502.4 95.7 %
As you can see, indexing time grows a lot with the number of items; but if you look closely, most of the time (over 90% from 50 items on) is spent building the nested items. We are working on a solution to remove that hydration time, bringing it down to the time it takes to hydrate a single item. The idea is to take the already hydrated document from ES and replace only the item or items that have changed, so the cost of indexing a change would no longer depend on how many items the record has.
Any comments or suggestions you may have would be much appreciated.
I don't know if "hydration" is the right term to use there, since that refers to rendering data on a web page with Javascript.
When you say "[time] spent building the nested items", are you referring to the Perl process of building the JSON document?
Depend on the structure of the JSON document, doing partial updates might be the way to go for sure.
Hi David. Yes, the spend building time is the time Perl needs to obtain all the data required to generate the JSON document. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #71 from David Cook <dcook@prosentient.com.au> --- (In reply to Luis Bataller from comment #70)
Hi David. Yes, the spend building time is the time Perl needs to obtain all the data required to generate the JSON document.
Ok cool. Perhaps there's ways we could optimize that. Are you using Koha::Items to get the items? The ORM isn't optimised for performance, so it might be necessary to use a more targeted approach to get the time down. I've got some approaches/patterns for that. (I work on Perl systems which deal with a much higher volume of data than Koha so I'm happy to share lessons learned.) -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #72 from Luis Bataller <luis.bataller@xercode.es> --- Hi all, As requested, we've set up a GitHub repo where you can try out the nested items work: https://github.com/xercode/koha-item-index (branch bugXXXXX-nested-items) It's already functional. We're now working on some optimizations: - Drop the flat item fields from the biblio document completely (some sort options still rely on them). - Move the item MARC from the biblio document into each item's nested document, which should speed up indexing. - Incremental updates: update only the affected items in the index instead of the whole document. Any feedback is welcome -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Jonathan Druart <jonathan.druart@gmail.com> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |jonathan.druart@gmail.com See Also| |https://bugs.koha-community | |.org/bugzilla3/show_bug.cgi | |?id=38181 --- Comment #73 from Jonathan Druart <jonathan.druart@gmail.com> --- From https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=207793 Bug 38181: Look up item statuses once per record in buildKohaItemsNamespace On a record with 1000 items (koha-testing-docker) the routine goes from 0.85s to 0.09s, and an OPAC searchResults call for that record from 1.2s to 0.45s. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #74 from Luis Bataller <luis.bataller@xercode.es> --- Hi all! Quick update on the nested item index (PR #1 on xercode/koha-item-index: https://github.com/xercode/koha-item-index/pull/1). Since our last post we've been working on indexing speed, especially for records with lots of items, and we've merged two improvements into the branch: 1. Look up the item hook plugins once per batch. Building each item called get_enabled_plugins twice per item. Now we call it once per indexing batch and reuse the result. 2. Store each item's MARC field in the nested items (items.marc). The item fields are no longer embedded in the record's MARC in the main document; instead, each item's MARC field is stored in its nested item. For records with many items, the record fits in ISO2709 again instead of falling back to MARCXML, so their documents are about 40% smaller and decoding them is much faster. Here's what it takes to reindex a single record from start to finish: Items per record Before After improvement 1 After improvement 2 1 0.07 s 0.06 s 0.07 s 10 0.14 s 0.09 s 0.07 s 100 0.88 s 0.31 s 0.14 s 1,000 9.1 s 2.9 s 0.73 s 5,000 47.0 s 15.6 s 3.96 s For a full reindex of our test catalogue (MARC21, 2,068 records and 25,631 items, one process): 255 s → 94 s → 40 s. Under a burst of circulation (with the indexer's 10-second pause between batches disabled), the median delay before a change shows up in the index dropped from 34 s to about 1 s. What's next: dropping the flat item fields Right now every item field mapped to a search field (952/995) is indexed twice: once as a flat field on the record, which merges all the items together, and once in the nested items. We already have a branch that removes the flat copies. With it the index is about 26% smaller, big records index faster, and items hidden in the OPAC can no longer make a record show up in search results. The catch is that it changes how search behaves: - keyword searches no longer match item fields such as public notes - custom mappings on 952/995 have to be searched with the items. prefix - facets on item fields other than the default ones need extra work We have workarounds for most of these, but they come at a cost: slightly bigger documents, more complex queries and somewhat slower keyword searches. And a keyword search mixing record and item terms (a word from the title plus one from an item note) would still not match. So we don't want to merge it before talking it over with you. How would you approach this? Should we carry on in this thread, or would a new one be better for this discussion? Thanks -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #75 from Nick Clemens (kidclamp) <nick@bywatersolutions.com> --- I am working here on testing both methods to compare, based on the description of issues from Luis I think that subdocuments may be a better path, it was what I had looked into when researching this previously. A quick google provides a few pro cons for the two: parent-child is faster for indexing, but slower for searching subdocuments can be used in sort - not in parent-child parent-child is recommended for write heavy operations due to indexing subdocuments requiring the whole object be reindexed Overall, I think subdocuments perform as we want and we will need to focus on indexing speed. I am currently building out a large dataset for testing and will update as I progress -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #76 from David Cook <dcook@prosentient.com.au> --- (In reply to Nick Clemens (kidclamp) from comment #75)
I am working here on testing both methods to compare, based on the description of issues from Luis I think that subdocuments may be a better path, it was what I had looked into when researching this previously.
A quick google provides a few pro cons for the two: parent-child is faster for indexing, but slower for searching
subdocuments can be used in sort - not in parent-child
parent-child is recommended for write heavy operations due to indexing subdocuments requiring the whole object be reindexed
Overall, I think subdocuments perform as we want and we will need to focus on indexing speed.
I am currently building out a large dataset for testing and will update as I progress
It might be worthwhile to expand a bit on the terminology / concepts, Nick. I had no idea until now that Elasticsearch had capacity to do joins between different documents in the same index: https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/parent... According to that page "The join field shouldn’t be used like joins in a relation database. In Elasticsearch the key to good performance is to de-normalize your data into documents. Each join field, has_child or has_parent query adds a significant tax to your query performance. It can also trigger global ordinals to be built." -- By subdocuments do you mean "nested documents" like https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/nested ? It seems like the maximum number of nested JSON objects in a single document defaults to 10,000. I imagine that should be suitable for most libraries? I hope... -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #77 from David Cook <dcook@prosentient.com.au> --- At the moment, I'm in the process of indexing tens/hundreds of millions of rows of non-Koha data into Elasticsearch, but it's a fairly straight forward de-normalised JSON document for each row, so not the same use case at all, but it'll be interesting from a performance perspective... -- Looking again at parent-join it says: "The only case where the join field makes sense is if your data contains a one-to-many relationship where one entity significantly outnumbers the other entity. An example of such case is a use case with products and offers for these products. In the case that offers significantly outnumbers the number of products then it makes sense to model the product as parent document and the offer as child document." Hmm tricky... that does seem to describe the biblio to item model... -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #78 from David Cook <dcook@prosentient.com.au> --- Question... what do these 2 options have in common and what do they not have in common in terms of code? Would it be possible to incorporate both options into Koha on an experimental basis? -- On other bug reports and on Mattermost, I've been advocating for a design approach that embraces modularity more, so that we can more easily swap out modules. As a community, we probably don't have the resources to support both, but if we design an internal API that allows us to use different modules, it will allow for easier innovations in the short-erm, which should also help with decision making for the longer term. We could also end up choosing one as the community default, while a support company could choose the other option, and support it themselves. I think there's some interesting possibilities here... -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #79 from Luis Bataller <luis.bataller@xercode.es> --- (In reply to David Cook from comment #78)
Question... what do these 2 options have in common and what do they not have in common in terms of code?
Would it be possible to incorporate both options into Koha on an experimental basis?
--
On other bug reports and on Mattermost, I've been advocating for a design approach that embraces modularity more, so that we can more easily swap out modules.
As a community, we probably don't have the resources to support both, but if we design an internal API that allows us to use different modules, it will allow for easier innovations in the short-erm, which should also help with decision making for the longer term.
We could also end up choosing one as the community default, while a support company could choose the other option, and support it themselves.
I think there's some interesting possibilities here...
I think it's complex, but feasible. We could create two search subclasses for Elasticsearch: Koha::SearchEngine::ElasticsearchNested and Koha::SearchEngine::ElasticsearchParentChild. But I think the main challenge lies in the definitions (mappings, facets, sort fields, etc.) and in handling item updates that shouldn't trigger a reindex of the parent document. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #80 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 208519 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=208519&action=edit Benchmark kit: items index vs nested items (scripts + preliminary results) Thank you Luis for all the work you have put into the nested items approach, and for sharing the branch and your measurements so openly. We are going to do a proper QA review of it and look at the comparison more closely before deciding how best to proceed - more to follow. In the meantime I have attached the scripts we used for a first comparison (a synthetic 250k biblio / 1.26M item catalogue, run in KTD against both branches), so that anyone can reproduce or extend it. Please treat the numbers in the README/RESULTS as preliminary. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #81 from Luis Bataller <luis.bataller@xercode.es> --- Hi Martin, Thank you for taking the time to check this. A note about something in the development that we hadn't explained. When we started, we decided not to change the meaning of the "available" field: "all items except lost and checked-out ones", which we understood as availability from the staff point of view. For patron availability we created a new field, items.available_for_patron. Its default conditions exclude lost, withdrawn and damaged items, items in transit and items held for a hold. Plugins can override these conditions. We added this because availability policies vary between organizations. About the "cross-item" result in Q9: this branch doesn't remove the flat item fields from the record document yet (that is the next phase of the development). As a result, the legacy available limit falls back to the flat. The same happens with itype/ccode limits. The UI doesn't emit those forms: facets emit items.itype:BK and the advanced search emits mc-itype:BK. To force the nested, use items.available:true or items.available_for_patron:true -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #205410|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #205411|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #205412|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #205413|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #205414|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #205415|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #205416|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #205417|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #205418|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #205419|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #205420|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #205421|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #205422|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #205423|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #205424|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #205425|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|Signed Off |ASSIGNED -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #82 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Hi all, Thank you again, Luis, and thanks to everyone who has contributed to the discussion here. We have now benchmarked the two designs against each other on the same dataset (the scripts are in attachment 208519), and we have discussed the result internally, including with Pedro, who wrote the separate items index patches on this bug. Our conclusion is that nested items is the right way forward for Koha, and that we should pick that one design rather than try to maintain two. In short: * On a 250k biblio / 1.26M item catalogue where one library holds 40% of the items, queries combining a title with an item-level limit (library, availability, item type + collection) took between 0.8 and 12.8 seconds cold on the separate index, against roughly 100 to 200 ms with nested items. The separate index has to fetch every matching biblionumber from the items index and pass them back as an ids filter, so its cost grows with the item-side set rather than with the query; a 39-hit title search limited to the large library took 7.3 seconds. Hit counts matched the independent oracle on both sides for same-item combinations. * Nested items gives same-item semantics, sorting and relevance-ordered pagination natively, without the intermediate set that Luis described in comment 63. * The write-side advantage of a separate index (no biblio reindex on a checkout) turned out to be small next to the read-side cost: on nested, a checkout-shaped change cost 27-39 ms at store time, plus a background reindex of 50-370 ms for records with 1 to 900 items. What this means for this bug: 1. We have obsoleted the patches attached here (the separate items index series). Pedro and I have talked this through and he is happy to proceed this way. Thank you Pedro, for framing the problem so well; several of the ideas carry over (the availability definition, batching the OPAC visibility checks, the reactive reindex triggers). 2. We would like to repurpose this bug (42835) as the home of the first piece of the nested items work: "index items as nested objects in the biblio document, together with the reindexing that keeps them correct". Luis, we would love for you to attach that first part of your branch here. 3. The branch is substantial (38 commits, about 5,600 lines), and it will be much easier for the community to review, test and QA if it is split across a small number of dependent bugs. We have done a QA review of it and will post that, together with a suggested split and some notes on the submission process, in a follow-up comment. The short version: the design is sound and the work is impressive. There are some changes needed, but all of them are fixable, and we would like to help get this over the line. David, thank you for the suggestion of supporting both. Given the limited QA resources we all have, we think picking one is the better path for now, but the modularity idea is a good one to keep in mind for the longer term. Thanks all. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #83 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Hi Luis (and all), First, a genuinely big thank you. This is an impressive piece of work. The nested design is the right one, the reactive reindexing is thorough (checkouts and returns, holds, transfers, stock rotation, library geolocation), the test coverage is extensive, and the commit messages explain the why very well. What follows is a QA review plus a proposal for getting this into community Koha. Everything below is fixable and none of it undermines the design. I would be glad to help you through the process. == How this was reviewed == Range 541cbd84a53..cc618963d67 of https://github.com/xercode/koha-item-index (branch bugXXXXX-nested-items): 38 commits, 47 files, +5625/-208 lines. I ran koha-qa.pl with --more-tests in KTD (Elasticsearch 9.4), reviewed it area by area (indexing, query/security, templates and preferences, tests, error handling), and ran it against the 250k biblio / 1.26M item catalogue from the benchmark. The review used AI-assisted tooling, and I reproduced the key findings by hand. The branch applies cleanly to main as of 16 October. With the item-limit forms the UI emits (items.itype:, mc-itype:, items.available:true, items.available_for_patron:true) every query I tried returned exactly the expected hit counts, in 110-210 ms cold. Thank you for the explanation of available versus available_for_patron in comment 81, it cleared up several things I had misread. == Blockers (need fixing before this can go in) == 1. OPAC hidden items can be matched through crafted limits. _extract_item_filters accepts any items.<field>:<value> limit, and _item_filter_clauses OR-merges filters on the same field, including the automatic OPAC hiding filters, which share that namespace. I reproduced it: with OpacHiddenItems set so that a record's only item is hidden, an anonymous OPAC search returns 0 hits normally and with a legitimate items.homebranch limit, but 1 hit with limit=items.hidden_in_opac_by_preferences:true (and likewise items.lost:1 against hidelostitems). Item details are still blanked at display time, but record presence, hit counts and facet counts leak, and the OPAC then redirects to opac-detail.pl?biblionumber= with an empty id. Suggested fix: allowlist the items.* fields a user limit may reference (the facet fields, available, available_for_patron, onrequest, geo, and the operand fields), and never merge the automatic hiding filters into user filter slots (build them as separate clauses). 2. No upgrade path. The items nested mapping is only added when an index is created. items_schema_version is written to documents but is never read, and the item facet aggregations are built as nested unconditionally. On an existing index, searches would error until rebuild_elasticsearch.pl -b -r is run, and a rebuild without -r cannot fix the dynamically mapped items field. Suggested fix: a query-side guard that falls back to the current behaviour when the mapping or schema version is missing, plus a clear warning in the atomicupdate and release notes. 3. Zebra. The new on-request and patron-availability facets render on Zebra installs, and Zebra cannot parse items.onrequest:* (CCL error). items.available_for_patron:true only works by accident, because the broad /available/ match in C4::Search::buildQuery turns it into the old availability limit. Suggested fix: guard the facets with the SearchEngine preference, or map the limits explicitly in buildQuery. == Important == * A plugin that dies in item_hidden_in_opac is treated as "not hidden", so items become visible (fails open). Please fail closed and log at error level. The same applies to item_available_for_patron. $plugin->get_metadata inside the error branch can itself escape the eval and kill the whole batch. * A library limit (branch:) combined with an items.homebranch facet is OR-merged rather than AND-ed (_library_limit_to_item_filter feeds the same equality slot), so the combination widens instead of narrowing. * StaffSearchResultsShowOnlyMatchingItems defaults to 1, which changes staff behaviour on upgrade. Koha prefers defaults that preserve existing behaviour. The pref text also mentions inner_hits; plain wording will be friendlier for librarians. * Elasticsearch defaults to a maximum of 10,000 nested objects per document, which contradicts the serials use case in index_config.yaml. Please set index.mapping.nested_objects.limit explicitly (or document the cap) and make sure bulk item errors are surfaced rather than a record silently dropping out of the index. * Changes to OpacHiddenItems, plugins and item-level_itypes leave the index stale until a reindex. This is documented in POD only. A warning on the preferences, or a reindex job as for geolocation, would help. * Every hold place, cancel, fill and confirm now enqueues a whole-record reindex. That is worth benchmarking on a hold-heavy system, and perhaps debouncing. * Several post-commit index_records calls (Hold, Transfer, StockRotationItem, AddReturn, ModItemTransfer) and Library::store are unguarded, so a queue or Elasticsearch failure turns into an error after the circulation write has already committed. Please wrap them in try/catch and log. The geolocation job also reports "finished" on failure, and "success" there means enqueued rather than indexed. * Perl-built user-visible strings (_describe_item_filters) are not translatable. * Barcode becomes an exact term and call number a *term* wildcard on keyword subfields, so matching becomes case sensitive where the old analysed fields were not. A lowercase normaliser would fix this. * The live-Elasticsearch test (NestedItems.t) never pings Elasticsearch before creating an index, has no Test::NoWarnings, and leaves its index behind if it dies. There are no tests yet for facet and count leakage under hidden items, escaping of limits and operands, the upgrade case, a plugin that dies, or SearchLimitLibrary set to both against a live index. * koha-qa.pl: QueryBuilder.pm has non-ASCII dashes in POD before any =encoding (use -- or add =encoding utf8); tidiness fails on C4/Circulation.pm, facets.inc, advsearch.tt, opac-facets.inc, opac-advsearch.tt and t/db_dependent/Circulation.t (perl misc/devel/tidy.pl <file>). * Bug numbers: every commit and bug_XXXXX.pl must carry the real bug number. The four fixup commits (exec bits, copyright notices, tidy, return undef) should be squashed into the commits they amend so that each commit passes QA on its own. One commit message (df956bb5a95) refers to internal phase/step names and an unrelated bug number. == Suggestions == * A leading-wildcard *term* on call numbers is expensive on large catalogues; consider an ngram or prefix approach. * max_inner_result_window of 50000 raises a protective limit 500 times; consider a lower window plus the existing graceful truncation. * Validate geo lat/lon/distance (non-numeric currently becomes 0,0 or an ES parse error). * The C4/Search.pm multibranchlimit fix for Zebra is useful but is a Zebra behaviour change; better as its own tiny bug. * The per-record Koha::Items count for the top-level available flag is now derivable from the item documents already built in memory. * Warn once per plugin per batch rather than once per item when a plugin dies during a full rebuild. * _item_marc_to_field runs on the result-decode path; a malformed marc string would kill the whole page, so a small guard would be cheap insurance. * Whitespace-only churn in Circulation.t and Items.t, and a duplicated id="available-items" in opac-advsearch.tt. == What is already solid == Trigger coverage for reactive reindexing; the atomicupdate (executable, idempotent, Koha::Installer::Output); the TT escaping; POD on the new subs; no SQL injection (everything is parameterised DBIC and a structured DSL); OpacHiddenItemsExceptions is honoured; truncated inner_hits degrade safely; and the commit messages with their test plans. == Suggested split == 38 commits and about 2,900 non-test lines is a lot for reviewers and testers. Several pieces are genuinely separate enhancements (the two new plugin hooks, the staff "show only matching items" feature, the on-request filter, geolocation indexing) and will move faster on their own. The reactive reindexing, on the other hand, belongs with the foundation: without it the nested items would go stale and the feature would not stand on its own. A proposed split, in dependency order (happy to adjust it with you): 1. Bug 42835 (this bug, repurposed): nested items in the biblio document, together with the reindexing that keeps them correct. Mapping and nested item documents with statuses, available and items_count, acquisition date and effective item type; the reindex triggers for transfers, holds, stock rotation and returns blocked by withdrawn items; the live-ES tests; plus the upgrade guard and the nested-object limit. Roughly commits 1, 2, 5 (without the geolocation job), 13, 15, 27, 28, 29, 30, 31 and the availability part of 6. 2. NEW: index library geolocation of items and reindex when it changes (the UpdateBiblioItemsGeolocation job), plus the fix that keeps the rest of the query when searching by geolocation. The geolocation part of 5, and 24. Depends on 1. 3. Bug 43151 (repurposed; it asks for filters to be applied to each item discretely): same-item filters and facets on nested items, including library limits and groups. Roughly commits 3, 4, 7, 10, 12, 14, 22, 23. Depends on 1. 4. NEW: advanced search item fields and operands on nested items (item type, collection, location, barcode, call number, acquisition date) and saved-filter routing. Roughly commits 16, 17, 25, 26. Depends on 3. This also addresses the long-standing complaint in bug 8589. 5. Bug 35052 (repurposed): OpacHiddenItems / OpacHiddenItemsHidesRecord at search time on nested items, with the field allowlist from blocker 1. Roughly commits 8, 9, 32. Depends on 3. 6. NEW: plugin hooks item_hidden_in_opac and item_available_for_patron (new in this branch; neither exists in current main), with the hidden_in_opac_custom and available_for_patron fields, and fail-closed handling. Roughly commits 18, 34 and the hook part of 6. Depends on 5. 7. NEW: staff search "show only matching items" (inner_hits, StaffSearchResultsShowOnlyMatchingItems and its atomicupdate). Roughly commit 19. Depends on 3. 8. NEW: on-request limit in OPAC and staff search. Roughly commit 11, with the Zebra guard. Depends on 3. 9. NEW: store each item's MARC in the nested items and stop embedding items when reading records to index. Roughly commits 35, 36, 37. This is a performance change with its own review surface (Koha::Item->as_marc_field, BiblioUtils), so it is better reviewed once the foundation is in. Depends on 1. 10. NEW (tiny): C4::Search multibranchlimit with an unknown group no longer dies on Zebra. Independent of everything else. The four fixup commits (exec bits, copyright notices, tidy, return undef) should be squashed into the commits they amend so that each commit passes QA on its own. One commit message (df956bb5a95) refers to internal phase/step names and an unrelated bug number. == Suggestions == * A leading-wildcard *term* on call numbers is expensive on large catalogues; consider an ngram or prefix approach. * max_inner_result_window of 50000 raises a protective limit 500 times; consider a lower window plus the existing graceful truncation. * Validate geo lat/lon/distance (non-numeric currently becomes 0,0 or an ES parse error). * The C4/Search.pm multibranchlimit fix for Zebra is useful but is a Zebra behaviour change; better as its own tiny bug. * The per-record Koha::Items count for the top-level available flag is now derivable from the item documents already built in memory. * Warn once per plugin per batch rather than once per item when a plugin dies during a full rebuild. * _item_marc_to_field runs on the result-decode path; a malformed marc string would kill the whole page, so a small guard would be cheap insurance. * Whitespace-only churn in Circulation.t and Items.t, and a duplicated id="available-items" in opac-advsearch.tt. == What is already solid == Trigger coverage for reactive reindexing; the atomicupdate (executable, idempotent, Koha::Installer::Output); the TT escaping; POD on the new subs; no SQL injection (everything is parameterised DBIC and a structured DSL); OpacHiddenItemsExceptions is honoured; truncated inner_hits degrade safely; and the commit messages with their test plans. == Suggested split == 38 commits and about 2,900 non-test lines is a lot for reviewers and testers. Several pieces are genuinely separate enhancements (the two new plugin hooks, the staff "show only matching items" feature, the on-request filter, geolocation indexing) and will move faster on their own. A proposed split, in dependency order (happy to adjust it with you): 1. Bug 42835 (this bug, repurposed): index items as nested objects in the biblio document. Mapping and nested item documents with statuses, available and items_count, acquisition date, effective item type, live-ES tests, plus the upgrade guard and nested-object limit. Roughly commits 1, 2, 13, 15, 31 and the availability part of 6. 2. NEW: reindex the record when item-related state changes. Transfers, holds, stock rotation and returns blocked by withdrawn items. Roughly commits 5 (without the geolocation job), 27, 28, 29, 30. Depends on 1. 3. NEW: index library geolocation of items and reindex when it changes (the UpdateBiblioItemsGeolocation job), plus the fix that keeps the rest of the query when searching by geolocation. Part of 5, and 24. Depends on 1. 4. Bug 43151 (repurposed; it asks for filters to be applied to each item discretely): same-item filters and facets on nested items, including library limits and groups. Roughly commits 3, 4, 7, 10, 12, 14, 22, 23. Depends on 1. 5. NEW: advanced search item fields and operands on nested items (item type, collection, location, barcode, call number, acquisition date) and saved-filter routing. Roughly commits 16, 17, 25, 26. Depends on 4. This also addresses the long-standing complaint in bug 8589. 6. Bug 35052 (repurposed): OpacHiddenItems / OpacHiddenItemsHidesRecord at search time on nested items, with the field allowlist from blocker 1. Roughly commits 8, 9, 32. Depends on 4. 7. NEW: plugin hooks item_hidden_in_opac and item_available_for_patron (new in this branch; neither exists in current main), with the hidden_in_opac_custom and available_for_patron fields, and fail-closed handling. Roughly commits 18, 34 and the hook part of 6. Depends on 6. 8. NEW: staff search "show only matching items" (inner_hits, StaffSearchResultsShowOnlyMatchingItems and its atomicupdate). Roughly commit 19. Depends on 4. 9. NEW: on-request limit in OPAC and staff search. Roughly commit 11, with the Zebra guard. Depends on 4. 10. NEW: store each item's MARC in the nested items and stop embedding items when reading records to index. Roughly commits 35, 36, 37. This is a performance change with its own review surface (Koha::Item->as_marc_field, BiblioUtils), so it is better reviewed once the foundation is in. Depends on 1. 11. NEW (tiny): C4::Search multibranchlimit with an unknown group no longer dies on Zebra. Independent of everything else. The four fixup commits (20, 21, 33, 38) get squashed into the commits they amend. Commits 5, 6, 9, 10 and 19 mix themes and will need splitting (git rebase -i and git add -p). If it would help, I am happy to prototype the split as a stack of branches for you to review and adopt. == How to submit this to the community == A walk-through of what the community expects, so that you are not guessing: 1. File one bug per piece in Bugzilla (product Koha, component "Searching - Elasticsearch" for most of them), each with a one-sentence summary, a description of the problem and the approach, and a test plan. Link them with "Depends on" in the order above. Re-using 42835, 43151 and 35052 where they fit keeps the history together. 2. Make each bug a small, self-contained patch series. One commit per logical change, subject "Bug NNNNN: Short summary", body explaining why. Never leave "Bug XXXXX" in commits, filenames or code comments. Put the test plan in the commit message of the first patch (or a comment) so testers can follow it step by step. 3. Attach patches with git bz (git bz attach), which sets the right patch flags and status "Needs Signoff". git bz create can also create the bug. 4. Before attaching, in KTD: run perl misc/devel/tidy.pl on touched files, koha-qa.pl -c <number of commits>, and the tests (prove) for everything you touched. Each commit should pass on its own, so squash fixups and keep the series bisectable. 5. Tests: use Test::NoWarnings, wrap DB tests in a transaction, skip cleanly when an optional service is not available, and add tests for security-relevant behaviour (hidden items, escaping) as well as the happy path. 6. DB and preferences: atomicupdate/bug_NNNNN.pl (executable, idempotent), matching entry in sysprefs.sql and the .pref file, and a default that preserves existing behaviour. Where an upgrade needs a reindex, say so in the "Text to go in the release notes" field on the bug. 7. Keep Zebra working: anything Elasticsearch-only needs to be guarded in templates and code. 8. Expect the sequence Needs Signoff, then a tester adds a Signed-off-by, then QA (Passed QA or Failed QA with comments), then the release manager pushes. Failed QA is a normal part of the process, not a rejection; respond to comments on the bug and re-attach. 9. Useful references: the wiki pages "Commit messages", "Coding guidelines" and "Submitting a patch", and the Koha manual's section on contributing. I am glad to review each bug as it arrives, help with the splitting, and test on a large dataset. Pedro and I will also look at what from our earlier series is worth carrying over on top (the availability definition for notforloan, damaged and withdrawn items, and the OPAC N+1 visibility batching). Thanks again for the quality of this work, I am looking forward to getting it in. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #84 from Luis Bataller <luis.bataller@xercode.es> --- Hi all, Thanks a lot to everyone for the time and work you've put into this: the benchmark, the review and the discussion. Pedro, thanks for being so generous about your patches. If that works for you, our plan is to start with the three blockers, fixing them on the current branch first. For the Zebra one, we'll do both of the suggested fixes, the second is usefull in case an installation switches from Elasticsearch to Zebra and has saved searches or URLs. We'll also include two of the important points, as they're simple to fix and affect data integrity and visibility: failing closed when a plugin dies, and wrapping the index_records calls and Library::store in try/catch. Martin, once that's done we'll put a split proposal based on your suggestions. Since you have much more experience with this, we'd really appreciate it if you could review it before we file the bugs. Thanks -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=42835 --- Comment #85 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Hi Luis, I'd be more than happy to review and lend a hand wherever I can, just give me a shout either here on on mattermost (ashimema) Many thanks for all the work you've put into this already Martin -- You are receiving this mail because: You are watching all bug changes.
participants (1)
-
bugzilla-daemon@bugs.koha-community.org