[Zope-Checkins] SVN: Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/ initial commit of CompositeIndex
Andreas Gabriel
gabriel at hrz.uni-marburg.de
Tue Sep 28 16:53:06 EDT 2010
Log message for revision 117037:
initial commit of CompositeIndex
Changed:
A Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/
A Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/CompositeIndex.py
A Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/__init__.py
A Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/dtml/
A Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/dtml/addCompositeIndex.dtml
A Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/dtml/browseIndex.dtml
A Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/dtml/manageCompositeIndex.dtml
A Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/help/
A Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/help/CompositeIndex_searchResults.stx
A Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/util.py
-=-
Added: Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/CompositeIndex.py
===================================================================
--- Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/CompositeIndex.py (rev 0)
+++ Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/CompositeIndex.py 2010-09-28 20:53:06 UTC (rev 117037)
@@ -0,0 +1,552 @@
+##############################################################################
+#
+# Copyright (c) 2010 Zope Foundation and Contributors.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+
+import sys
+import logging
+
+from Acquisition import aq_parent
+
+from Globals import DTMLFile
+
+from time import time
+
+from BTrees.IIBTree import IIBTree, IITreeSet, IISet, union, intersection, difference
+from BTrees.OOBTree import OOBTree
+from BTrees.IOBTree import IOBTree
+import BTrees.Length
+
+from ZODB.POSException import ConflictError
+
+from Products.PluginIndexes.interfaces import IUniqueValueIndex
+from Products.PluginIndexes.KeywordIndex.KeywordIndex import KeywordIndex
+from Products.PluginIndexes.common.util import parseIndexRequest
+
+from util import PermuteKeywordList
+
+from config import PROJECTNAME
+
+_marker = []
+
+logger = logging.getLogger(PROJECTNAME)
+
+class CompositeIndex(KeywordIndex):
+
+ """Index for composition of simple fields.
+ or sequences of items
+ """
+
+
+ meta_type="CompositeIndex"
+
+ manage_options= (
+ {'label': 'Settings',
+ 'action': 'manage_main',
+ 'help': ('CompositeIndex','CompositeIndex_Settings.stx')},
+ {'label': 'Browse',
+ 'action': 'manage_browse',
+ 'help': ('CompositeIndex','CompositeIndex_Settings.stx')},
+ )
+
+ def clear(self):
+ self._length = BTrees.Length.Length()
+ self._index = IOBTree()
+ self._unindex = IOBTree()
+
+ # translation from hash key to human readable composite key
+ self._tindex = IOBTree()
+
+ # component indexes
+ self._cindexes = OOBTree()
+ for i in self.getComponentIndexNames():
+ self._cindexes[i] = OOBTree()
+
+
+ def _apply_index(self, request, cid='', type=type):
+ """ Apply the index to query parameters given in the request arg. """
+
+ record = parseIndexRequest(request, self.id, self.query_options)
+ if record.keys==None: return None
+
+ if len(record.keys) > 0 and not isinstance(record.keys[0][1],parseIndexRequest):
+ if isinstance(record.keys[0],tuple):
+ for i,k in enumerate(record.keys):
+ record.keys[i] = hash(k)
+
+ return super(CompositeIndex,self)._apply_index(request, cid=cid, type=type)
+
+ operator = self.useOperator
+
+ rank=[]
+
+ for c, rec in record.keys:
+ # experimental code for specifing the operator
+ if operator == self.useOperator:
+ operator = rec.get('operator',operator)
+
+ if not operator in self.operators :
+ raise RuntimeError,"operator not valid: %s" % escape(operator)
+
+ res = self._apply_component_index(rec,c)
+
+ if res is None:
+ continue
+
+ res, dummy = res
+
+ rank.append((len(res),res))
+
+
+ # sort from short to long sets
+ rank.sort()
+
+ k = None
+ for l,res in rank:
+
+ k = intersection(k, res)
+
+ if not k:
+ break
+
+
+ # if any operator of composite indexes is set to "and"
+ # switch to intersecton mode
+
+ if operator == 'or':
+ set_func = union
+ else:
+ set_func = intersection
+
+
+ rank=[]
+ if set_func == intersection:
+ for key in k:
+ set=self._index.get(key, IISet())
+ rank.append((len(set),key))
+
+ # sort from short to long sets
+ rank.sort()
+
+ else:
+ # dummy length
+ if k:
+ rank = enumerate(k)
+
+ res = None
+ # collect docIds
+
+ for l,key in rank:
+
+ set=self._index.get(key, None)
+ if set is None:
+ set = IISet(())
+ elif isinstance(set, int):
+ set = IISet((set,))
+ res = set_func(res, set)
+ if not res and set_func is intersection:
+ break
+
+
+ if isinstance(res, int): r=IISet((res,))
+
+ if res is None:
+ return IISet(),(self.id,)
+
+ return res, (self.id,)
+
+
+ def _apply_component_index(self, record, cid):
+ """ Apply the component index to query parameters given in the record arg. """
+
+ if record.keys==None: return None
+
+ index = self._cindexes[cid]
+ r = None
+ opr = None
+
+
+ # Range parameter
+ range_parm = record.get('range',None)
+ if range_parm:
+ opr = "range"
+ opr_args = []
+ if range_parm.find("min")>-1:
+ opr_args.append("min")
+ if range_parm.find("max")>-1:
+ opr_args.append("max")
+
+ if record.get('usage',None):
+ # see if any usage params are sent to field
+ opr = record.usage.lower().split(':')
+ opr, opr_args=opr[0], opr[1:]
+
+ if opr=="range": # range search
+ if 'min' in opr_args: lo = min(record.keys)
+ else: lo = None
+ if 'max' in opr_args: hi = max(record.keys)
+ else: hi = None
+ if hi:
+ setlist = index.items(lo,hi)
+ else:
+ setlist = index.items(lo)
+
+ for k, set in setlist:
+ if isinstance(set, tuple):
+ set = IISet((set,))
+ r = union(r, set)
+ else: # not a range search
+ for key in record.keys:
+ set=index.get(key, None)
+ if set is None:
+ set = IISet(())
+ elif isinstance(set, int):
+ set = IISet((set,))
+ r = union(r, set)
+
+ if isinstance(r, int):
+ r=IISet((r,))
+
+ if r is None:
+ return IISet(), (cid,)
+
+ return r, (cid,)
+
+
+
+ def index_object(self, documentId, obj, threshold=None):
+ """ wrapper to handle indexing of multiple attributes """
+
+ res = self._index_object(documentId, obj, threshold)
+
+ return res
+
+
+ def _index_object(self, documentId, obj, threshold=None, attr=''):
+ """ index an object 'obj' with integer id 'i'
+
+ Ideally, we've been passed a sequence of some sort that we
+ can iterate over. If however, we haven't, we should do something
+ useful with the results. In the case of a string, this means
+ indexing the entire string as a keyword."""
+
+ # First we need to see if there's anything interesting to look at
+ # self.id is the name of the index, which is also the name of the
+ # attribute we're interested in. If the attribute is callable,
+ # we'll do so.
+
+ # unhashed keywords
+ newUKeywords = self._get_object_keywords(obj, attr)
+
+
+ # hashed keywords
+ newKeywords = map(lambda x: hash(x),newUKeywords)
+
+ for i, kw in enumerate(newKeywords):
+ if not self._tindex.get(kw,None):
+ self._tindex[kw]=newUKeywords[i]
+
+
+
+ newKeywords = map(lambda x: hash(x),newUKeywords)
+
+ oldKeywords = self._unindex.get(documentId, None)
+
+ if oldKeywords is None:
+ # we've got a new document, let's not futz around.
+ try:
+ for kw in newKeywords:
+ self.insertForwardIndexEntry(kw, documentId)
+ self._unindex[documentId] = list(newKeywords)
+ except TypeError:
+ return 0
+ else:
+ # we have an existing entry for this document, and we need
+ # to figure out if any of the keywords have actually changed
+ if type(oldKeywords) is not IISet:
+ oldKeywords = IISet(oldKeywords)
+ newKeywords = IISet(newKeywords)
+ fdiff = difference(oldKeywords, newKeywords)
+ rdiff = difference(newKeywords, oldKeywords)
+ if fdiff or rdiff:
+ # if we've got forward or reverse changes
+ self._unindex[documentId] = list(newKeywords)
+ if fdiff:
+ self.unindex_objectKeywords(documentId, fdiff)
+
+ for kw in fdiff:
+ indexRow = self._index.get(kw, _marker)
+ try:
+ del self._tindex[kw]
+ except KeyError:
+ # XXX should not happen
+ pass
+
+ if rdiff:
+ for kw in rdiff:
+ self.insertForwardIndexEntry(kw, documentId)
+
+ return 1
+
+
+ def insertForwardIndexEntry(self, entry, documentId):
+ """Take the entry provided and put it in the correct place
+ in the forward index.
+
+ This will also deal with creating the entire row if necessary.
+ """
+ super(CompositeIndex,self).insertForwardIndexEntry(entry, documentId)
+ self._insertComponentIndexEntry(entry)
+
+
+ def removeForwardIndexEntry(self, entry, documentId):
+ """Take the entry provided and remove any reference to documentId
+ in its entry in the index.
+ """
+ super(CompositeIndex,self).removeForwardIndexEntry(entry, documentId)
+ self._removeComponentIndexEntry(entry)
+
+
+ def _insertComponentIndexEntry(self, entry):
+ """Take the entry provided, extract its components and
+ put it in the correct place of the component index.
+ entry - hashed composite key """
+
+ # get the composite key and extract its component values
+ components = self._tindex[entry]
+
+ for i,c in enumerate(self.getComponentIndexNames()):
+ ci = self._cindexes[c]
+ cd = components[i]
+
+ indexRow = ci.get(cd, _marker)
+ if indexRow is _marker:
+ ci[cd] = entry
+
+ else:
+ try:
+ indexRow.insert(entry)
+ except AttributeError:
+ # index row is not a IITreeSet
+ indexRow = IITreeSet((indexRow, entry))
+ ci[cd] = indexRow
+
+
+ def _removeComponentIndexEntry(self, entry):
+ """ Take the entry provided, extract its components and
+ remove any reference to composite key of each component index.
+ entry - hashed composite key"""
+
+ # get the composite key and extract its component values
+ components = self._tindex[entry]
+
+ for i,c in enumerate(self.getComponentIndexNames()):
+ ci = self._cindexes[c]
+ cd = components[i]
+ indexRow = ci.get(cd, _marker)
+ if indexRow is not _marker:
+ try:
+ indexRow.remove(entry)
+ if not indexRow:
+ del ci[cd]
+ except ConflictError:
+ raise
+
+ except AttributeError:
+ # index row is an int
+ try:
+ del ci[cd]
+ except KeyError:
+ pass
+
+ except:
+ logger.error('%s: unindex_object could not remove '
+ 'entry %s from component index %s[%s]. This '
+ 'should not happen.' % (self.__class__.__name__,
+ str(components),str(self.id),str(c)),
+ exc_info=sys.exc_info())
+
+ else:
+ logger.error('%s: unindex_object tried to retrieve set %s '
+ 'from component index %s[%s] but couldn\'t. This '
+ 'should not happen.' % (self.__class__.__name__,
+ repr(components),str(self.id),str(c)))
+
+ def _get_object_keywords(self, obj, attr):
+ """ composite keyword lists """
+
+ fields = self.getComponentIndexAttributes()
+
+ kw_list = []
+
+ for attributes in fields:
+ kw = []
+ for attr in attributes:
+ kw.extend(list(super(CompositeIndex,self)._get_object_keywords(obj, attr)))
+ kw_list.append(kw)
+
+ pkl = PermuteKeywordList(kw_list)
+
+ return pkl.keys
+
+ def getComponentIndexNames(self):
+ """ returns component index names to composite """
+
+ ids = []
+
+ fields = self.getIndexSourceNames()
+ for attr in fields:
+ c = attr.split(':')
+ ids.append(c.pop())
+
+ return tuple(ids)
+
+ def getComponentIndexAttributes(self):
+ """ returns list of attributes of each component index to composite"""
+
+ attributes=[]
+
+ fields = self.getIndexSourceNames()
+ for idx in fields:
+ attr = idx.split(':')
+ if len(attr) == 1:
+ attributes.append(attr)
+ else:
+ attributes.append(attr[1:])
+
+ return tuple(attributes)
+
+ def getEntryForObject(self, documentId, default=_marker):
+ """Takes a document ID and returns all the information we have
+ on that specific object.
+ """
+ datum = super(CompositeIndex,self).getEntryForObject(documentId, default=default)
+
+ if isinstance(datum, int):
+ datum = IISet((datum,))
+
+ entry = map(lambda k : self._tindex.get(k,k), datum)
+
+ return entry
+
+ def keyForDocument(self, id):
+ # This method is superceded by documentToKeyMap
+ logger.warn('keyForDocument: return hashed key')
+ return super(CompositeIndex,self).keyForDocument(id)
+
+ def documentToKeyMap(self):
+ logger.warn('documentToKeyMap: return hashed key map')
+ return self._unindex
+
+ def items(self):
+ items = []
+ for k,v in self._index.items():
+ if isinstance(v, int):
+ v = IISet((v,))
+
+ kw = self._tindex.get(k,k)
+ items.append((kw, v))
+ return items
+
+
+ manage = manage_main = DTMLFile('dtml/manageCompositeIndex', globals())
+ manage_main._setName('manage_main')
+ manage_browse = DTMLFile('dtml/browseIndex', globals())
+
+
+manage_addCompositeIndexForm = DTMLFile('dtml/addCompositeIndex', globals())
+
+def manage_addCompositeIndex(self, id, extra=None,
+ REQUEST=None, RESPONSE=None, URL3=None):
+ """Add a composite index"""
+ return self.manage_addIndex(id, 'CompositeIndex', extra=extra, \
+ REQUEST=REQUEST, RESPONSE=RESPONSE, URL1=URL3)
+
+
+
+class compositeSearchArgumentsMap:
+ """ parse a request from the ZPublisher to optimize the query by means
+ of CompositeIndexes
+ """
+
+ keywords = {}
+
+ def __init__(self, catalog, request):
+ """ indexes -- dict of index objects
+ request -- the request dictionary send from the ZPublisher
+ """
+
+ indexes = catalog.indexes
+
+ parent = aq_parent(catalog)
+
+ if parent.hasProperty('unimr.compositeindex') and not parent.getProperty('unimr.compositeindex',True):
+ logger.warn('skip compositeSearchArgumentsMap')
+ return
+
+ cRank=[]
+ for index in indexes.values():
+ if isinstance(index,CompositeIndex):
+
+ cId = index.id
+ logger.debug('CompositeIndex "%s" found' % cId)
+ # get indexes managed by CompositeIndex
+ cIdxs = index.getComponentIndexNames()
+ cRank.append((cId,cIdxs))
+
+ # sort from specific to unspecific CompositeIndex
+ cRank.sort(lambda x,y: cmp((len(y[1]),y[1]),(len(x[1]),x[1])))
+
+ for cId, cIdxs in cRank:
+ records=[]
+ for i in cIdxs:
+ index = indexes.get(i,None)
+ abort = False
+
+ if index:
+ rec = parseIndexRequest(request, index.id, index.query_options)
+
+ if not IUniqueValueIndex.providedBy(index):
+ logger.warn('index %s: not an instance of IUniqueValueIndex' % index.id)
+ abort = True
+
+ if abort or rec.keys is None:
+ continue
+
+ records.append((i, rec))
+
+
+ # transform request only if more than one component of the composite key is applied
+ if len(records) > 1:
+
+ query = { cId: { 'query': records } }
+
+ logger.debug('composite query build "%s"' % query)
+
+
+ # delete obsolete query attributes from request
+ for i in cIdxs[:len(records)+1]:
+
+ if isinstance(request, dict):
+ if request.has_key(i):
+ del request[i]
+ else:
+ if request.keywords.has_key(i):
+ del request.keywords[i]
+ if isinstance(request.request, dict) and \
+ request.request.has_key(i):
+
+ del request.request[i]
+
+ request.keywords.update(query)
+
+
+
Added: Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/__init__.py
===================================================================
--- Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/__init__.py (rev 0)
+++ Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/__init__.py 2010-09-28 20:53:06 UTC (rev 117037)
@@ -0,0 +1 @@
+# empty comment for winzip and friends
Added: Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/dtml/addCompositeIndex.dtml
===================================================================
--- Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/dtml/addCompositeIndex.dtml (rev 0)
+++ Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/dtml/addCompositeIndex.dtml 2010-09-28 20:53:06 UTC (rev 117037)
@@ -0,0 +1,70 @@
+<dtml-var manage_page_header>
+
+<dtml-var "manage_form_title(this(), _,
+ form_title='Add CompositeIndex',
+ )">
+
+
+<p class="form-help">
+<strong>Composite Indexes</strong>. Indexes
+containing more than one attribute to index an object are called
+"composite index". Such indexes should be created if you expect to run
+queries that will have multiple attributes in the search phrase and
+all attributes combined will give significantly less hits than the any
+of the attributes alone. The key of a composite index is called
+"composite key" and is composed of two or more attributes of an
+object.
+</p>
+
+
+<form action="manage_addCompositeIndex" method="post" enctype="multipart/form-data">
+<table cellspacing="0" cellpadding="2" border="0">
+ <tr>
+ <td align="left" valign="top">
+ <div class="form-label">
+ Id
+ </div>
+ </td>
+ <td align="left" valign="top">
+ <input type="text" name="id" size="40" />
+ </td>
+ </tr>
+
+ <tr>
+ <td align="left" valign="top">
+ <div class="form-label">
+ Composite key<br/>
+ (names of attributes to concatenate)
+ </div>
+ </td>
+ <td align="left" valign="top">
+ <input type="text" name="extra.indexed_attrs:record:string" size="40" /><br/>
+ <em>indexId1,indexId2,...</em> or<br/>
+ <em>indexId1:attribute11:attribute12:...,indexId2:attribute21,...</em>
+ </td>
+ </tr>
+
+ <tr>
+ <td align="left" valign="top">
+ <div class="form-optional">
+ Type
+ </div>
+ </td>
+ <td align="left" valign="top">
+ Composite Index
+ </td>
+ </tr>
+ <tr>
+ <td align="left" valign="top">
+ </td>
+ <td align="left" valign="top">
+ <div class="form-element">
+ <input class="form-element" type="submit" name="submit"
+ value=" Add " />
+ </div>
+ </td>
+ </tr>
+</table>
+</form>
+
+<dtml-var manage_page_footer>
Added: Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/dtml/browseIndex.dtml
===================================================================
--- Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/dtml/browseIndex.dtml (rev 0)
+++ Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/dtml/browseIndex.dtml 2010-09-28 20:53:06 UTC (rev 117037)
@@ -0,0 +1,64 @@
+<dtml-var manage_page_header>
+<dtml-var manage_tabs>
+
+<dtml-call "REQUEST.RESPONSE.setHeader('Content-Type', 'text/html; charset=UTF-8')" >
+
+<p class="form-text">
+The index "&dtml-getId;" contains <dtml-var items fmt=collection-length thousands_commas> distinct values
+</p>
+
+<dtml-let size="20"> <!-- batch size -->
+
+ <div class="form-text">
+ <dtml-in items previous size=size start=query_start >
+ <a href="&dtml-URL;?query_start=&dtml-previous-sequence-start-number;">
+ [Previous <dtml-var previous-sequence-size> entries]
+ </a>
+ </dtml-in>
+ <dtml-in items next size=size start=query_start >
+ <a href="&dtml-URL;?query_start=&dtml-next-sequence-start-number;">
+ [Next <dtml-var next-sequence-size> entries]
+ </a>
+ </dtml-in>
+ </div>
+
+ <table border="1" align="center" width="100%" class="form-help">
+
+ <tr><th>composite key (internally managed by integer hash)</th><th>object path</th></tr>
+ <dtml-in items start=query_start size=size>
+ <tr>
+ <td>
+ <dtml-if "meta_type in ('DateIndex',)">
+ <dtml-comment><!--
+ DateIndexes store dates packed into an integer, unpack
+ into year, month, day, hour and minute, no seconds and UTC.
+ --></dtml-comment>
+ <dtml-var "DateTime((_['sequence-key'] / 535680),
+ (_['sequence-key'] / 44640 ) % 12,
+ (_['sequence-key'] / 1440 ) % 31,
+ (_['sequence-key'] / 60 ) % 24,
+ (_['sequence-key'] ) % 60,
+ 0, 'UTC')">
+ <dtml-else>
+ &dtml-sequence-key;
+ </dtml-if>
+ </td>
+ <td>
+ <ul>
+ <dtml-let v="_['sequence-item']">
+ <dtml-if "isinstance(v, int)">
+ <li><a href="<dtml-var "getpath(v)">"<dtml-var "getpath(v)"></a></li>
+ <dtml-else>
+ <dtml-in "v.keys()">
+ <li> <a href="<dtml-var "getpath(_['sequence-item'])">"><dtml-var "getpath(_['sequence-item'])"></a></li>
+ </dtml-in>
+ </dtml-if>
+ </dtml-let>
+ </ul>
+ </td>
+ </tr>
+ </dtml-in>
+ </table>
+</dtml-let>
+
+<dtml-var manage_page_footer>
Added: Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/dtml/manageCompositeIndex.dtml
===================================================================
--- Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/dtml/manageCompositeIndex.dtml (rev 0)
+++ Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/dtml/manageCompositeIndex.dtml 2010-09-28 20:53:06 UTC (rev 117037)
@@ -0,0 +1,12 @@
+<dtml-var manage_page_header>
+<dtml-var manage_tabs>
+
+<p class="form-help">
+Objects indexed: <dtml-var numObjects>
+<br>
+Distinct values: <dtml-var indexSize>
+</p>
+
+
+
+<dtml-var manage_page_footer>
Added: Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/help/CompositeIndex_searchResults.stx
===================================================================
--- Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/help/CompositeIndex_searchResults.stx (rev 0)
+++ Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/help/CompositeIndex_searchResults.stx 2010-09-28 20:53:06 UTC (rev 117037)
@@ -0,0 +1,23 @@
+ZCatalog - searchResults: specifying parameters for a search query
+
+ The searchResults() method of the ZCatalog accepts parameters that
+ define a query to be made on that catalog. A query can either be
+ passed as keyword argument to searchResults(), as a mapping, or as
+ part of a Zope REQUEST object, typically from HTML forms.
+
+ The index of the catalog to query is either the name of the
+ keyword argument, a key in a mapping, or an attribute of a record
+ object.
+
+ Attributes of record objects
+
+ 'query' -- either a sequence of objects or a single value to be
+ passed as query to the index (mandatory)
+
+ 'operator' -- specifies the combination of search results when
+ query is a sequence of values. (optional, default: 'or').
+
+ Allowed values:
+
+ 'and', 'or'
+
Added: Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/util.py
===================================================================
--- Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/util.py (rev 0)
+++ Zope/branches/andig-compositeindex/src/Products/PluginIndexes/CompositeIndex/util.py 2010-09-28 20:53:06 UTC (rev 117037)
@@ -0,0 +1,97 @@
+##############################################################################
+#
+# Copyright (c) 2010 Zope Foundation and Contributors.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+
+
+_marker = []
+
+class PermuteKeywordList:
+ """
+ returns a flat list of a sequential
+ permutation of keyword lists.
+ Example:
+
+ A = [[1,2,3],[4,5],[6,7]]
+
+ tree permutation
+
+ 6
+ /
+ 4
+ / \
+ / 7
+ 1
+ \ 6
+ \ /
+ 5
+ \
+ 7
+
+ 6
+ /
+ 4
+ / \
+ / 7
+ 2
+ \ 6
+ \ /
+ 5
+ \
+ 7
+
+ 6
+ /
+ 4
+ / \
+ / 7
+ 3
+ \ 6
+ \ /
+ 5
+ \
+ 7
+
+
+ corresponds to following flat list
+
+ [[1,4,6],[1,4,7],[1,5,6],[1,5,7],
+ [2,4,6],[2,4,7],[2,5,6],[2,5,7],
+ [3,4,6],[3,4,7],[3,5,6],[3,5,7]]
+
+
+ """
+
+
+
+ def __init__(self,A):
+ """ A -- list of keyword lists"""
+ self.keys=[]
+ self.walking(A)
+
+
+ def walking(self,A,f=_marker):
+
+ if f is _marker:
+ f = []
+
+ if A[:1]:
+ first = A[0]
+ for l in first:
+ next = f[:] + [l]
+ self.walking(A[1:],next)
+
+ else:
+ self.keys.append(tuple(f))
+
+
+
+
More information about the Zope-Checkins
mailing list