From 918382685ed9ea8b5f150c5a34bef7c5789c3228 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Mon, 5 Oct 2026 13:10:16 -0400 Subject: [PATCH] items: fix +index crash on unmatched itemid When the index view is rooted at an item addressed by something other than a name, such as /+index/@itemid/... for an itemid that matches nothing, the item has no NAME in its meta and thus no subitem prefixes. build_index_query indexed subitem_prefixes[0] unconditionally and died with IndexError instead of rendering an empty index -- unlike get_subitem_revs, which already skips the prefix filter when the item has no names. Fix by returning a NullQuery matching nothing when there are no prefixes; the view then renders an empty index as it does for a name with no subitems. The regression test roots an index at a nonexistent itemid and locks in the empty result; it fails with IndexError on the old code. --- src/moin/items/__init__.py | 11 +++++++++-- src/moin/items/_tests/test_Item.py | 9 +++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/moin/items/__init__.py b/src/moin/items/__init__.py index 8033f19a5..6cdfaa069 100644 --- a/src/moin/items/__init__.py +++ b/src/moin/items/__init__.py @@ -39,7 +39,7 @@ from urllib.parse import unquote -from whoosh.query import Term, Prefix, And, Or, Not +from whoosh.query import Term, Prefix, And, Or, Not, NullQuery from moin import current_app, flaskg, log from moin.constants.chartypes import CHARS_UPPER, CHARS_LOWER @@ -1425,8 +1425,15 @@ def build_index_query(self, startswith=None, selected_groups=None, isglobalindex isglobalindex: ??? Output: - Returns a whoosh.query.Prefix object for the input parameters + Returns a whoosh.query.Prefix object for the input parameters, + or a NullQuery matching nothing when the item has no subitem + prefixes (e.g. an index rooted at an itemid that matches nothing). """ + if not isglobalindex and not self.subitem_prefixes: + # an item addressed by something other than a name, such as a + # nonexistent itemid, has no names and thus no subitem prefixes; + # no stored item can be one of its subitems + return NullQuery() prefix = "" if isglobalindex else self.subitem_prefixes[0] if startswith: query = Prefix(NAME_EXACT, prefix + startswith) | Prefix(NAME_EXACT, prefix + startswith.swapcase()) diff --git a/src/moin/items/_tests/test_Item.py b/src/moin/items/_tests/test_Item.py index 3751b53f4..507a9d8bc 100644 --- a/src/moin/items/_tests/test_Item.py +++ b/src/moin/items/_tests/test_Item.py @@ -215,6 +215,15 @@ def testIndex(self): fix_files, fix_builds = fix_meta(files, builds) assert fix_files == fix_builds + def testIndexMissingItemid(self): + # an index rooted at an itemid that matches nothing has no names, + # so there are no subitem prefixes; it must yield an empty index + # rather than crash on subitem_prefixes[0] + item = Item.create("@itemid/" + "0" * 32) + assert isinstance(item, NonExistent) + assert item.names == [] + assert item.get_index() == ([], []) + def test_meta_filter(self): name = "Test_item" contenttype = "text/plain;charset=utf-8"