diff --git a/src/Products/PluginIndexes/PathIndex/PathIndex.py b/src/Products/PluginIndexes/PathIndex/PathIndex.py index 4dc48d86..d9c5a534 100644 --- a/src/Products/PluginIndexes/PathIndex/PathIndex.py +++ b/src/Products/PluginIndexes/PathIndex/PathIndex.py @@ -85,6 +85,9 @@ def getIndexSourceNames(self): """ return (self.id, 'getPhysicalPath', ) + def getIndexQueryNames(self): + return (self.id,) + def index_object(self, docid, obj ,threshold=100): """ See IPluggableIndex. """ diff --git a/src/Products/PluginIndexes/TopicIndex/TopicIndex.py b/src/Products/PluginIndexes/TopicIndex/TopicIndex.py index 84c6784e..ed2f7c67 100644 --- a/src/Products/PluginIndexes/TopicIndex/TopicIndex.py +++ b/src/Products/PluginIndexes/TopicIndex/TopicIndex.py @@ -186,6 +186,9 @@ def getIndexSourceNames(self): """ return names of indexed attributes """ return ('n/a',) + def getIndexQueryNames(self): + return (self.id,) + def manage_clearFilteredSet(self, filter_ids=[], URL1=None, \ REQUEST=None,RESPONSE=None): """ clear a list of FilteredSets""" diff --git a/src/Products/PluginIndexes/common/UnIndex.py b/src/Products/PluginIndexes/common/UnIndex.py index 720c766a..1a0e8e9e 100644 --- a/src/Products/PluginIndexes/common/UnIndex.py +++ b/src/Products/PluginIndexes/common/UnIndex.py @@ -457,6 +457,10 @@ def getIndexSourceNames(self): # BBB: older indexes didn't have 'indexed_attrs' return getattr(self, 'indexed_attrs', [self.id]) + def getIndexQueryNames(self): + """Indicate that this index applies to queries for the index's name.""" + return (self.id,) + def uniqueValues(self, name=None, withLengths=0): """returns the unique values for name diff --git a/src/Products/PluginIndexes/interfaces.py b/src/Products/PluginIndexes/interfaces.py index 7740a5f0..883b5f54 100644 --- a/src/Products/PluginIndexes/interfaces.py +++ b/src/Products/PluginIndexes/interfaces.py @@ -29,6 +29,10 @@ def getIndexSourceNames(): """Get a sequence of attribute names that are indexed by the index. """ + def getIndexQueryNames(): + """Get a sequence of query parameter names to which this index applies. + """ + def index_object(documentId, obj, threshold=None): """Index an object. diff --git a/src/Products/ZCatalog/Catalog.py b/src/Products/ZCatalog/Catalog.py index 72037352..1f2545a1 100644 --- a/src/Products/ZCatalog/Catalog.py +++ b/src/Products/ZCatalog/Catalog.py @@ -470,14 +470,20 @@ def make_query(self, request): query[iid] = value return query + def _get_index_query_names(self, index): + if hasattr(index, 'getIndexQueryNames'): + return index.getIndexQueryNames() + return (index.getId(),) + def _sorted_search_indexes(self, query): # Simple implementation doing no ordering. query_keys = query.keys() order = [] for name, index in self.indexes.items(): - if name not in query_keys: - continue - order.append((ILimitedResultIndex.providedBy(index), name)) + for attr in self._get_index_query_names(index): + if attr in query_keys: + order.append((ILimitedResultIndex.providedBy(index), name)) + break order.sort() return [i[1] for i in order] diff --git a/src/Products/ZCatalog/plan.py b/src/Products/ZCatalog/plan.py index fc403cc2..a1b187a8 100644 --- a/src/Products/ZCatalog/plan.py +++ b/src/Products/ZCatalog/plan.py @@ -151,6 +151,11 @@ class CatalogPlan(object): def __init__(self, catalog, query=None, threshold=0.1): self.catalog = catalog self.cid = self.get_id() + querykey_to_index = {} + for index in self.catalog.indexes.values(): + for querykey in self.catalog._get_index_query_names(index): + querykey_to_index[querykey] = index.getId() + self.querykey_to_index = querykey_to_index self.query = query self.key = self.make_key(query) self.benchmark = {} @@ -281,6 +286,7 @@ def stop(self): self.duration = self.end_time - self.start_time # Make absolutely sure we never omit query keys from the plan for key in self.query.keys(): + key = self.querykey_to_index.get(key, key) if key not in self.benchmark.keys(): self.benchmark[key] = Benchmark(0, 0, False) PriorityMap.set_entry(self.cid, self.key, self.benchmark) diff --git a/src/Products/ZCatalog/tests/test_catalog.py b/src/Products/ZCatalog/tests/test_catalog.py index 3288a2c4..f7751b5b 100644 --- a/src/Products/ZCatalog/tests/test_catalog.py +++ b/src/Products/ZCatalog/tests/test_catalog.py @@ -50,6 +50,7 @@ class dummy(ExtensionClass.Base): att1 = 'att1' att2 = 'att2' att3 = ['att3'] + foo = 'foo' def __init__(self, num): self.num = num @@ -64,6 +65,12 @@ def col3(self): return ['col3'] +class MultiFieldIndex(FieldIndex): + + def getIndexQueryNames(self): + return [self.id, 'bar'] + + class objRS(ExtensionClass.Base): def __init__(self, num): @@ -198,11 +205,13 @@ def setUp(self): index_factory=OkapiIndex, lexicon_id='lexicon') att3 = KeywordIndex('att3') num = FieldIndex('num') + foo = MultiFieldIndex('foo') self._catalog.addIndex('att1', att1) self._catalog.addIndex('att2', att2) self._catalog.addIndex('att3', att3) self._catalog.addIndex('num', num) + self._catalog.addIndex('foo', foo) self._catalog.addColumn('att1') self._catalog.addColumn('att2') self._catalog.addColumn('att3') @@ -303,6 +312,15 @@ def test_sorted_search_indexes_priority(self): result = self._catalog._sorted_search_indexes(query) self.assertEquals(result.index('att1'), 2) + def test_sorted_search_indexes_match_alternate_attr(self): + query = {'bar': 'b'} + result = self._catalog._sorted_search_indexes(query) + self.assertEquals(result, ['foo']) + + def test_sorted_search_indexes_no_match(self): + result = self._catalog._sorted_search_indexes({'baz': 'a'}) + self.assertEquals(result, []) + # search def test_sortResults(self):