############################################################################## # # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.0 (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 # ############################################################################## __version__ = '$Id: PathIndexNG.py,v 1.3 2004/01/24 17:28:38 andreasjung Exp $' from types import StringType import ZODB from Globals import Persistent, DTMLFile from OFS.SimpleItem import SimpleItem from BTrees.IOBTree import IOBTree from BTrees.OOBTree import OOBTree from BTrees.IIBTree import IITreeSet, IISet, multiunion, union from BTrees.Length import Length from zLOG import LOG, ERROR from Products.PluginIndexes import PluggableIndex from Products.PluginIndexes.common.util import parseIndexRequest from Products.PluginIndexes.common import safe_callable class PathIndexNG(Persistent, SimpleItem): """ An efficient ZCatalog index to store path components of persistent objects. """ __implements__ = (PluggableIndex.UniqueValueIndex,) meta_type="IVAPathIndexNG" manage_options= ( {'label': 'Settings', 'action': 'manage_main', 'help': ('PathIndexNG', 'PathIndexNG_Settings.stx')}, ) query_options = () def __init__(self,id, caller=None): self.id = id self.clear() def clear(self): self._length = Length(0) self._index = OOBTree() # path -> Set(docids) self._unindex = IOBTree() # docid -> path def index_object(self, docid, obj ,threshold=100): path = obj.getPhysicalPath() if not isinstance(path, StringType): path = '/'.join(path) if not path.startswith('/'): path = '/' + path if not self._unindex.has_key(docid): self._length.change(1) if not self._index.has_key(path): self._index[path] = IITreeSet() self._index[path].insert(docid) self._unindex[docid] = path return 1 def unindex_object(self, docid): """ hook for (Z)Catalog """ if not self._unindex.has_key(docid): LOG(self.__class__.__name__, ERROR, 'Attempt to unindex nonexistent document' ' with id %s' % docid) return try: path = self._unindex[docid] except KeyError: # ignore broken references silently return try: self._index[path].remove(docid) except KeyError: return if len(self._index[path]) == 0: del self._index[path] del self._unindex[docid] self._length.change(-1) def numObjects(self): """ return the number of indexed objects""" return self._length() def hasUniqueValuesFor(self, name): """has unique values for column name""" return name == self.id def uniqueValues(self, name=None, withLength=0): """ needed to be consistent with the interface """ return self._index.keys() def getIndexSourceNames(self): """ return names of indexed attributes """ return ('getPhysicalPath', ) def getEntryForObject(self, docid, default=None): """ Takes a document ID and returns all the information we have on that specific object. """ try: return self._unindex[docid] except: return default def _apply_index(self, request, cid=''): """ search """ record = parseIndexRequest(request,self.id,self.query_options) if not record.keys: return None query_path = record.keys[0] result_set = IISet() for k in record.keys: query_paths = self._index.keys(k, k+ chr(255)) one_set = multiunion([self._index[path] for path in query_paths]) result_set = multiunion((result_set, one_set)) if result_set: return result_set, (self.id,) else: return IISet(), (self.id,) index_html = DTMLFile('dtml/index', globals()) manage_workspace = DTMLFile('dtml/managePathIndexNG', globals()) manage_addPathIndexNGForm = DTMLFile('dtml/addPathIndexNG', globals()) def manage_addPathIndexNG(self, id, REQUEST=None, RESPONSE=None, URL3=None): """Add a path index""" return self.manage_addIndex(id, 'IVAPathIndexNG', extra=None, \ REQUEST=REQUEST, RESPONSE=RESPONSE, URL1=URL3)