From 9a520a3d7be6dfd65346aa38bdaddb72d142f28c Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Tue, 24 Feb 2026 13:02:35 +0000 Subject: [PATCH] Bug 14962: (QA follow-up) Eliminate N+1 Koha::Items->find in search result rendering Five display-aware helper closures in C4::Search each independently called Koha::Items->find($item->{itemnumber}). For a search page with 20 results and 5 items each, this produced up to 500 extra DB queries per page when UseDisplayModule was enabled. Introduce a shared $item_obj_cache keyed by itemnumber so the item object is fetched at most once per item across all five closures. Sponsored-by: ByWater Solutions Signed-of-by: Martin Renvoize --- C4/Search.pm | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/C4/Search.pm b/C4/Search.pm index e7cf3a9cf54..edeb4f2f524 100644 --- a/C4/Search.pm +++ b/C4/Search.pm @@ -1699,15 +1699,25 @@ sub searchResults { my $itemtypes = Koha::ItemTypes->search_with_localization; my %itemtypes = map { $_->{itemtype} => $_ } @{ $itemtypes->unblessed }; - # Cache for display lookups to avoid repeated queries - my $display_cache = {}; + # Caches shared across all display-aware helper closures + my $display_cache = {}; + my $item_obj_cache = {}; + + # Returns a Koha::Item object for the given itemnumber, cached per result set + my $get_item_obj = sub { + my ($itemnumber) = @_; + unless ( exists $item_obj_cache->{$itemnumber} ) { + $item_obj_cache->{$itemnumber} = Koha::Items->find($itemnumber); + } + return $item_obj_cache->{$itemnumber}; + }; # Helper function to get effective location for search results my $get_effective_collection_code = sub { my ($item) = @_; return $collections->{ $item->{ccode} } unless C4::Context->preference('UseDisplayModule'); - my $item_obj = Koha::Items->find( $item->{itemnumber} ); + my $item_obj = $get_item_obj->( $item->{itemnumber} ); return $collections->{ $item->{ccode} } unless $item_obj; my $effective_collection_code = $item_obj->effective_collection_code; @@ -1732,7 +1742,7 @@ sub searchResults { my ($item) = @_; return $itemtypes{ $item->{itype} }{translated_description} unless C4::Context->preference('UseDisplayModule'); - my $item_obj = Koha::Items->find( $item->{itemnumber} ); + my $item_obj = $get_item_obj->( $item->{itemnumber} ); return $itemtypes{ $item->{itype} }{translated_description} unless $item_obj; my $effective_itemtype = $item_obj->effective_itemtype; @@ -1757,7 +1767,7 @@ sub searchResults { my ($item) = @_; return $shelflocations->{ $item->{location} } unless C4::Context->preference('UseDisplayModule'); - my $item_obj = Koha::Items->find( $item->{itemnumber} ); + my $item_obj = $get_item_obj->( $item->{itemnumber} ); return $shelflocations->{ $item->{location} } unless $item_obj; my $effective_loc = $shelflocations->{ $item_obj->effective_location }; @@ -1780,7 +1790,7 @@ sub searchResults { my ($item) = @_; return $item->{homebranch} unless C4::Context->preference('UseDisplayModule'); - my $item_obj = Koha::Items->find( $item->{itemnumber} ); + my $item_obj = $get_item_obj->( $item->{itemnumber} ); return $item->{homebranch} unless $item_obj; my $effective_homebranch = $item_obj->effective_homebranch; @@ -1805,7 +1815,7 @@ sub searchResults { my ($item) = @_; return $item->{holdingbranch} unless C4::Context->preference('UseDisplayModule'); - my $item_obj = Koha::Items->find( $item->{itemnumber} ); + my $item_obj = $get_item_obj->( $item->{itemnumber} ); return $item->{holdingbranch} unless $item_obj; my $effective_holdingbranch = $item_obj->effective_holdingbranch; -- 2.53.0