icemac: Are there any news on this PR?
petschki: Just pushed the ambiguity suggestion. This fixes #114 too
icemac: After #115 is merged we can run the tests again in CI.
diff --git a/CHANGES.rst b/CHANGES.rst
index cbda9d8..0ae2a95 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -47,6 +47,10 @@ Fixes
 - Fix case where index value is changed to None after previously being indexed.
   (`#100 <https://github.com/zopefoundation/Products.ZCatalog/issues/100>`_)
 
+- Fix case where multiple indexes with similar name seperated by ``_`` were interpreted as options.
+  Fixes #78.
+  [thet]
+
 
 5.1 (2020-04-20)
 ----------------
diff --git a/src/Products/ZCatalog/query.py b/src/Products/ZCatalog/query.py
index 87a7309..f7e5f94 100644
--- a/src/Products/ZCatalog/query.py
+++ b/src/Products/ZCatalog/query.py
@@ -88,8 +88,7 @@ class IndexQuery(object):
 
             for field in request.keys():
                 if field.startswith(iid + '_'):
-                    iid_tmp, op = field.split('_')
-
+                    op = field[len(iid) + 1:]
                     self.set(op, request[field])
 
         self.keys = keys

Query for multiple indexes with similar names broken
/cc @andbag 

When constructing a query with similar named indexes like ``workspace`` and ``workspace_root``, the query is broken since: https://github.com/zopefoundation/Products.ZCatalog/pull/67

Since ``workspace_root`` is available in the request, ``_root`` is interpreted as an option for the ``workspace`` index. This fails with:

```
2019-06-29 20:01:29,299 ERROR   [Zope.SiteErrorLog:18][waitress] 1561831289.29896470.5221127312205188 http://localhost:8080/Plone/cases/ad4/@workspace_overview
Traceback (innermost last):
  Module ZPublisher.WSGIPublisher, line 155, in transaction_pubevents
  Module ZPublisher.WSGIPublisher, line 337, in publish_module
  Module ZPublisher.WSGIPublisher, line 255, in publish
  Module ZPublisher.mapply, line 85, in mapply
  Module Products.PDBDebugMode.wsgi_runcall, line 60, in pdb_runcall
  Module plone.rest.service, line 23, in __call__
  Module plone.restapi.services, line 21, in render
  Module bda.empower.api.services.workspace_overview.get, line 46, in reply
  Module bda.empower.api.services.workspace_overview.get, line 32, in __call__
  Module bda.empower.discourse, line 223, in get_all_workspace_roots
  Module Products.CMFPlone.CatalogTool, line 457, in searchResults
  Module Products.ZCatalog.ZCatalog, line 611, in searchResults
  Module Products.ZCatalog.Catalog, line 1091, in searchResults
  Module Products.ZCatalog.Catalog, line 634, in search
  Module Products.ZCatalog.Catalog, line 562, in _search_index
  Module Products.ZCatalog.query, line 93, in __init__
  Module Products.ZCatalog.query, line 126, in set
ValueError: index 'workspace': option 'root' is not valid
```

This issue is related to: https://github.com/eea/eea.facetednavigation/issues/174
diff --git a/src/Products/ZCatalog/tests/test_query.py b/src/Products/ZCatalog/tests/test_query.py
index cc4e306..21b8431 100644
--- a/src/Products/ZCatalog/tests/test_query.py
+++ b/src/Products/ZCatalog/tests/test_query.py
@@ -69,32 +69,43 @@ class TestIndexQuery(unittest.TestCase):
 
     def test_operator_dict(self):
         request = {'path': {'query': 'foo', 'operator': 'bar'}}
-        self.assertRaises(ValueError,
-                          self._makeOne,
-                          request,
-                          'path',
-                          ('query', 'operator'),
-                          ('or', 'and'))
+        with self.assertRaises(ValueError):
+            self._makeOne(
+                request, 'path', ('query', 'operator'), ('or', 'and'))
 
     def test_operator_string(self):
         request = {'path': 'foo', 'path_operator': 'bar'}
-        self.assertRaises(ValueError,
-                          self._makeOne,
-                          request, 'path',
-                          ('query', 'operator'),
-                          ('or', 'and'))
+        with self.assertRaises(ValueError):
+            self._makeOne(
+                request, 'path', ('query', 'operator'), ('or', 'and'))
 
     def test_options_dict(self):
         request = {'path': {'query': 'foo', 'baropt': 'dummy'}}
-        self.assertRaises(ValueError,
-                          self._makeOne,
-                          request,
-                          'path',
-                          ('query', 'operator'))
+        with self.assertRaises(ValueError):
+            self._makeOne(
+                request, 'path', ('query', 'operator'))
 
     def test_options_string(self):
         request = {'path': 'foo', 'path_baropt': 'dummy'}
-        self.assertRaises(ValueError,
-                          self._makeOne,
-                          request, 'path',
-                          ('query', 'operator'))
+        with self.assertRaises(ValueError):
+            self._makeOne(
+                request, 'path', ('query', 'operator'))
+
+    def test_get_string_with_underscores(self):
+        request = {
+            'extra_path': 'foo',
+            'extra_path_level': 0,
+            'extra_path_operator': 'and',
+        }
+        parser = self._makeOne(
+            request, 'extra_path', ('query', 'level', 'operator'))
+
+        self.assertEqual(parser.get('keys'), ['foo'])
+        self.assertEqual(parser.get('level'), 0)
+        self.assertEqual(parser.get('operator'), 'and')
+
+    def test_operator_string_with_underscores(self):
+        request = {'extra_path': 'foo', 'extra_path_operator': 'bar'}
+        with self.assertRaises(ValueError):
+            self._makeOne(
+                request, 'extra_path', ('query', 'operator'), ('or', 'and'))

pip install -e .[dev]
parse_log_pytest_v2
requirements.txt
pytest
apt-get update apt-get install -y gcc
3.9
requirements/base.txt
pytest -xvs
AccessControl==7.2
Acquisition==6.1
AuthEncoding==6.0
beautifulsoup4==4.13.3
BTrees==6.1
cffi==1.17.1
Chameleon==4.6.0
DateTime==5.5
DocumentTemplate==4.6
exceptiongroup==1.2.2
ExtensionClass==6.0
importlib_metadata==8.6.1
iniconfig==2.1.0
Missing==5.0
MultiMapping==5.0
multipart==1.2.1
packaging==24.2
PasteDeploy==3.1.0
Persistence==5.1
persistent==6.1.1
pluggy==1.5.0
-e git+https://github.com/zopefoundation/Products.ZCatalog.git@7930d2fa78c1213ab8937bee6ae09ead559c7114#egg=Products.ZCatalog
pycparser==2.22
pytest==8.3.5
python-gettext==5.0
pytz==2025.2
Record==4.1
RestrictedPython==8.0
roman==5.0
six==1.17.0
soupsieve==2.6
tomli==2.2.1
transaction==5.0
typing_extensions==4.13.0
waitress==3.0.2
WebOb==1.8.9
WebTest==3.0.4
WSGIProxy2==0.5.1
z3c.pt==4.5
zc.lockfile==3.0.post1
ZConfig==4.2
zExceptions==5.0
zipp==3.21.0
ZODB==6.0
zodbpickle==4.2
Zope==5.13
zope.annotation==5.1
zope.browser==3.1
zope.browsermenu==5.1
zope.browserpage==5.0
zope.browserresource==5.2
zope.cachedescriptors==5.1
zope.component==6.0
zope.configuration==6.0
zope.container==6.1
zope.contentprovider==6.0
zope.contenttype==5.2
zope.datetime==5.1
zope.deferredimport==5.0
zope.deprecation==5.1
zope.dottedname==6.0
zope.event==5.0
zope.exceptions==5.2
zope.filerepresentation==6.1
zope.globalrequest==2.0
zope.hookable==7.0
zope.i18n==5.2
zope.i18nmessageid==7.0
zope.interface==7.2
zope.lifecycleevent==5.1
zope.location==5.1
zope.pagetemplate==5.1
zope.processlifetime==3.1
zope.proxy==6.1
zope.ptresource==5.1
zope.publisher==7.3
zope.schema==7.0.1
zope.security==7.3
zope.sequencesort==5.1
zope.site==5.1
zope.size==5.1
zope.structuredtext==5.0
zope.tal==5.1
zope.tales==6.1
zope.testbrowser==7.0.1
zope.testing==5.1
zope.traversing==5.1
zope.viewlet==5.1

name: Products.ZCatalog
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - accesscontrol==7.2
      - acquisition==6.1
      - authencoding==6.0
      - beautifulsoup4==4.13.3
      - btrees==6.1
      - cffi==1.17.1
      - chameleon==4.6.0
      - datetime==5.5
      - documenttemplate==4.6
      - exceptiongroup==1.2.2
      - extensionclass==6.0
      - importlib-metadata==8.6.1
      - iniconfig==2.1.0
      - missing==5.0
      - multimapping==5.0
      - multipart==1.2.1
      - packaging==24.2
      - pastedeploy==3.1.0
      - persistence==5.1
      - persistent==6.1.1
      - pluggy==1.5.0
      - pycparser==2.22
      - pytest==8.3.5
      - python-gettext==5.0
      - pytz==2025.2
      - record==4.1
      - restrictedpython==8.0
      - roman==5.0
      - six==1.17.0
      - soupsieve==2.6
      - tomli==2.2.1
      - transaction==5.0
      - typing-extensions==4.13.0
      - waitress==3.0.2
      - webob==1.8.9
      - webtest==3.0.4
      - wsgiproxy2==0.5.1
      - z3c-pt==4.5
      - zc-lockfile==3.0.post1
      - zconfig==4.2
      - zexceptions==5.0
      - zipp==3.21.0
      - zodb==6.0
      - zodbpickle==4.2
      - zope==5.13
      - zope-annotation==5.1
      - zope-browser==3.1
      - zope-browsermenu==5.1
      - zope-browserpage==5.0
      - zope-browserresource==5.2
      - zope-cachedescriptors==5.1
      - zope-component==6.0
      - zope-configuration==6.0
      - zope-container==6.1
      - zope-contentprovider==6.0
      - zope-contenttype==5.2
      - zope-datetime==5.1
      - zope-deferredimport==5.0
      - zope-deprecation==5.1
      - zope-dottedname==6.0
      - zope-event==5.0
      - zope-exceptions==5.2
      - zope-filerepresentation==6.1
      - zope-globalrequest==2.0
      - zope-hookable==7.0
      - zope-i18n==5.2
      - zope-i18nmessageid==7.0
      - zope-interface==7.2
      - zope-lifecycleevent==5.1
      - zope-location==5.1
      - zope-pagetemplate==5.1
      - zope-processlifetime==3.1
      - zope-proxy==6.1
      - zope-ptresource==5.1
      - zope-publisher==7.3
      - zope-schema==7.0.1
      - zope-security==7.3
      - zope-sequencesort==5.1
      - zope-site==5.1
      - zope-size==5.1
      - zope-structuredtext==5.0
      - zope-tal==5.1
      - zope-tales==6.1
      - zope-testbrowser==7.0.1
      - zope-testing==5.1
      - zope-traversing==5.1
      - zope-viewlet==5.1
prefix: /opt/conda/envs/Products.ZCatalog

src/Products/ZCatalog/tests/test_query.py::TestIndexQuery::test_get_string_with_underscores src/Products/ZCatalog/tests/test_query.py::TestIndexQuery::test_operator_dict src/Products/ZCatalog/tests/test_query.py::TestIndexQuery::test_operator_string src/Products/ZCatalog/tests/test_query.py::TestIndexQuery::test_operator_string_with_underscores src/Products/ZCatalog/tests/test_query.py::TestIndexQuery::test_options_dict src/Products/ZCatalog/tests/test_query.py::TestIndexQuery::test_options_string
src/Products/ZCatalog/tests/test_query.py::TestIndexQuery::test_get_dict src/Products/ZCatalog/tests/test_query.py::TestIndexQuery::test_get_not_dict src/Products/ZCatalog/tests/test_query.py::TestIndexQuery::test_get_not_dict_list src/Products/ZCatalog/tests/test_query.py::TestIndexQuery::test_get_not_int src/Products/ZCatalog/tests/test_query.py::TestIndexQuery::test_get_not_string src/Products/ZCatalog/tests/test_query.py::TestIndexQuery::test_get_not_string_list src/Products/ZCatalog/tests/test_query.py::TestIndexQuery::test_get_string
CHANGES.rst src/Products/ZCatalog/query.py
CHANGES.rst src/Products/ZCatalog/query.py