[Zope-Checkins]
CVS: Zope/lib/python/Products/PluginIndexes/PathIndex
- PathIndex.py:1.35.2.1
Andreas Jung
andreas at andreas-jung.com
Thu Sep 18 10:16:45 EDT 2003
Update of /cvs-repository/Zope/lib/python/Products/PluginIndexes/PathIndex
In directory cvs.zope.org:/tmp/cvs-serv6537/lib/python/Products/PluginIndexes/PathIndex
Modified Files:
Tag: Zope-2_7-branch
PathIndex.py
Log Message:
Merge from HEAD:
- PathIndex and TopicIndex are now using a counter for the number
of indexed objects instead of using a very expensive calculation
based on the keys of their indexes.
=== Zope/lib/python/Products/PluginIndexes/PathIndex/PathIndex.py 1.35 => 1.35.2.1 ===
--- Zope/lib/python/Products/PluginIndexes/PathIndex/PathIndex.py:1.35 Tue Jun 17 15:01:07 2003
+++ Zope/lib/python/Products/PluginIndexes/PathIndex/PathIndex.py Thu Sep 18 10:16:14 2003
@@ -13,24 +13,24 @@
__version__ = '$Id$'
-from Products.PluginIndexes import PluggableIndex
-from Products.PluginIndexes.common.util import parseIndexRequest
-from Products.PluginIndexes.common import safe_callable
+import warnings
+from types import StringType, ListType, TupleType
from Globals import Persistent, DTMLFile
-from Acquisition import Implicit
-
+from OFS.SimpleItem import SimpleItem
from BTrees.IOBTree import IOBTree
from BTrees.OOBTree import OOBTree
from BTrees.IIBTree import IITreeSet, IISet, intersection, union
-from OFS.SimpleItem import SimpleItem
+from BTrees.Length import Length
from zLOG import LOG, ERROR
-from types import StringType, ListType, TupleType
-import warnings
+
+from Products.PluginIndexes import PluggableIndex
+from Products.PluginIndexes.common.util import parseIndexRequest
+from Products.PluginIndexes.common import safe_callable
_marker = []
-class PathIndex(Persistent, Implicit, SimpleItem):
+class PathIndex(Persistent, SimpleItem):
""" A path index stores all path components of the physical
path of an object:
@@ -41,7 +41,7 @@
- every component is kept as a key of a OOBTree in self._indexes
- the value is a mapping 'level of the path component' to
- 'all documentIds with this path component on this level'
+ 'all docids with this path component on this level'
"""
@@ -55,33 +55,26 @@
'help': ('PathIndex','PathIndex_Settings.stx')},
)
- query_options = ["query", "level", "operator"]
-
+ query_options = ("query", "level", "operator")
def __init__(self,id,caller=None):
self.id = id
-
- # experimental code for specifing the operator
- self.operators = ['or','and']
+ self.operators = ('or','and')
self.useOperator = 'or'
-
self.clear()
-
def clear(self):
- """ clear everything """
-
- self._depth = 0
- self._index = OOBTree()
+ self._depth = 0
+ self._index = OOBTree()
self._unindex = IOBTree()
-
+ self._length = Length(0)
def insertEntry(self, comp, id, level):
"""Insert an entry.
- comp is a path component (generated by splitPath() )
- id is the documentId
- level is the level of the component inside the path
+ comp is a path component
+ id is the docid
+ level is the level of the component inside the path
"""
if not self._index.has_key(comp):
@@ -94,16 +87,11 @@
if level > self._depth:
self._depth = level
-
- def index_object(self, documentId, obj ,threshold=100):
+ def index_object(self, docid, obj ,threshold=100):
""" hook for (Z)Catalog """
- # first we check if the object provide an attribute or
- # method to be used as hook for the PathIndex
-
- if hasattr(obj, self.id):
- f = getattr(obj, self.id)
-
+ f = getattr(obj, self.id, None)
+ if f is not None:
if safe_callable(f):
try:
path = f()
@@ -112,8 +100,7 @@
else:
path = f
- if not (isinstance(path, StringType) or
- isinstance(path, TupleType)):
+ if not isinstance(path, (StringType, TupleType)):
raise TypeError('path value must be string or tuple of strings')
else:
try:
@@ -121,36 +108,34 @@
except AttributeError:
return 0
- if type(path) in (ListType, TupleType):
+ if isinstance(path, (ListType, TupleType)):
path = '/'+ '/'.join(path[1:])
-
- comps = self.splitPath(path, obj)
+ comps = filter(None, path.split('/'))
+
+ if not self._unindex.has_key(docid):
+ self._length.change(1)
for i in range(len(comps)):
- self.insertEntry(comps[i], documentId, i)
-
- self._unindex[documentId] = path
-
+ self.insertEntry(comps[i], docid, i)
+ self._unindex[docid] = path
return 1
-
- def unindex_object(self, documentId):
+ def unindex_object(self, docid):
""" hook for (Z)Catalog """
- if not self._unindex.has_key(documentId):
+ if not self._unindex.has_key(docid):
LOG(self.__class__.__name__, ERROR,
'Attempt to unindex nonexistent document'
- ' with id %s' % documentId)
+ ' with id %s' % docid)
return
- path = self._unindex[documentId]
- comps = path.split('/')
+ comps = self._unindex[docid].split('/')
for level in range(len(comps[1:])):
comp = comps[level+1]
try:
- self._index[comp][level].remove(documentId)
+ self._index[comp][level].remove(docid)
if not self._index[comp][level]:
del self._index[comp][level]
@@ -160,34 +145,10 @@
except KeyError:
LOG(self.__class__.__name__, ERROR,
'Attempt to unindex document'
- ' with id %s failed' % documentId)
-
- del self._unindex[documentId]
-
-
- def printIndex(self):
- for k,v in self._index.items():
- print "-"*78
- print k
- for k1,v1 in v.items():
- print k1,v1,
-
- print
-
-
- def splitPath(self, path, obj=None):
- """ split physical path of object. If the object has
- as function splitPath() we use this user-defined function
- to split the path
- """
-
- if hasattr(obj, "splitPath"):
- comps = obj.splitPath(path)
- else:
- comps = filter(None, path.split('/'))
-
- return comps
+ ' with id %s failed' % docid)
+ self._length.change(-1)
+ del self._unindex[docid]
def search(self, path, default_level=0):
"""
@@ -199,95 +160,61 @@
level < 0 not implemented yet
"""
- if isinstance(path,StringType):
+ if isinstance(path, StringType):
level = default_level
else:
level = int(path[1])
path = path[0]
- comps = self.splitPath(path)
+ comps = filter(None, path.split('/'))
if len(comps) == 0:
return IISet(self._unindex.keys())
if level >= 0:
-
results = []
for i in range(len(comps)):
comp = comps[i]
-
if not self._index.has_key(comp): return IISet()
if not self._index[comp].has_key(level+i): return IISet()
-
results.append( self._index[comp][level+i] )
res = results[0]
-
for i in range(1,len(results)):
res = intersection(res,results[i])
-
return res
else:
-
results = IISet()
-
for level in range(0,self._depth + 1):
-
ids = None
error = 0
-
for cn in range(0,len(comps)):
comp = comps[cn]
-
try:
ids = intersection(ids,self._index[comp][level+cn])
except KeyError:
error = 1
-
if error==0:
results = union(results,ids)
-
return results
-
-
- def __len__(self):
- """ len """
- # XXX REALLY inefficient
- return len(self._index)
-
-
def numObjects(self):
""" return the number of indexed objects"""
- # XXX REALLY inefficient
- return len(self._unindex)
-
-
- def keys(self):
- """ return list of all path components """
- # XXX Could this be lazy, does it need to be a list?
- return list(self._index.keys())
-
-
- def values(self):
- # XXX Could this be lazy, does it need to be a list?
- return list(self._index.values())
-
-
- def items(self):
- """ mapping path components : documentIds """
- # XXX Could this be lazy, does it need to be a list?
- return list(self._index.items())
-
+ try:
+ return self._length()
+ except AttributeError: # backward compatibility
+ l = len(self._unindex)
+ self._length = Length(l)
+ return l
def _apply_index(self, request, cid=''):
""" hook for (Z)Catalog
- request mapping type (usually {"path": "..." }
- additionaly a parameter "path_level" might be passed
- to specify the level (see search())
+ 'request' -- mapping type (usually {"path": "..." }
+ additionaly a parameter "path_level" might be passed
+ to specify the level (see search())
- cid ???
+ 'cid' -- ???
"""
record = parseIndexRequest(request,self.id,self.query_options)
@@ -299,19 +226,14 @@
"Please use a mapping object and the "
"'level' key to specify the operator." % cid)
-
- # get the level parameter
level = record.get("level",0)
-
- # experimental code for specifing the operator
operator = record.get('operator',self.useOperator).lower()
# depending on the operator we use intersection of union
- if operator=="or": set_func = union
- else: set_func = intersection
+ if operator == "or": set_func = union
+ else: set_func = intersection
res = None
-
for k in record.keys:
rows = self.search(k,level)
res = set_func(res,rows)
@@ -325,26 +247,23 @@
"""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, documentId, default=_marker):
- """ Takes a document ID and returns all the information we have
- on that specific object. """
+ def getEntryForObject(self, docid, default=_marker):
+ """ Takes a document ID and returns all the information
+ we have on that specific object.
+ """
try:
- return self._unindex[documentId]
+ return self._unindex[docid]
except KeyError:
# XXX Why is default ignored?
return None
-
index_html = DTMLFile('dtml/index', globals())
manage_workspace = DTMLFile('dtml/managePathIndex', globals())
More information about the Zope-Checkins
mailing list