[Zope-Checkins] CVS: Zope/lib/python/Products/PluginIndexes/DateIndex - DateIndex.py:1.2 README.txt:1.1 __init__.py:1.2
Tres Seaver
tseaver@zope.com
Thu, 6 Jun 2002 01:31:46 -0400
Update of /cvs-repository/Zope/lib/python/Products/PluginIndexes/DateIndex
In directory cvs.zope.org:/tmp/cvs-serv22737/DateIndex
Added Files:
DateIndex.py README.txt __init__.py
Log Message:
- Land new pluggable index types, specialized for date values and
date ranges, from branch.
=== Zope/lib/python/Products/PluginIndexes/DateIndex/DateIndex.py 1.1 => 1.2 ===
+#
+# 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
+#
+##############################################################################
+
+from DateTime.DateTime import DateTime
+from Products.PluginIndexes import PluggableIndex
+from Products.PluginIndexes.common.UnIndex import UnIndex
+from Products.PluginIndexes.common.util import parseIndexRequest
+from types import StringType, FloatType, IntType
+
+from Globals import DTMLFile
+from BTrees.IOBTree import IOBTree
+from BTrees.OIBTree import OIBTree
+from BTrees.IIBTree import IISet, union, intersection
+
+_marker = []
+
+
+class DateIndex(UnIndex):
+ """ Index for Dates """
+
+ __implements__ = (PluggableIndex.PluggableIndexInterface,)
+
+ meta_type = 'DateIndex'
+ query_options = ['query', 'range']
+
+ manage = manage_main = DTMLFile( 'dtml/manageDateIndex', globals() )
+ manage_main._setName( 'manage_main' )
+ manage_options = ( { 'label' : 'Settings'
+ , 'action' : 'manage_main'
+ },
+ )
+
+ def clear( self ):
+ """ Complete reset """
+ self._index = IOBTree()
+ self._unindex = OIBTree()
+
+
+ def index_object( self, documentId, obj, threshold=None ):
+ """index an object, normalizing the indexed value to an integer
+
+ o Normalized value has granularity of one minute.
+
+ o Objects which have 'None' as indexed value are *omitted*,
+ by design.
+ """
+ returnStatus = 0
+
+ try:
+ date_attr = getattr( obj, self.id )
+ if callable( date_attr ):
+ date_attr = date_attr()
+
+ ConvertedDate = self._convert( value=date_attr, default=_marker )
+ except AttributeError:
+ ConvertedDate = _marker
+
+ oldConvertedDate = self._unindex.get( documentId, _marker )
+
+ if ConvertedDate != oldConvertedDate:
+ if oldConvertedDate is not _marker:
+ self.removeForwardIndexEntry(oldConvertedDate, documentId)
+
+ if ConvertedDate is not _marker:
+ self.insertForwardIndexEntry( ConvertedDate, documentId )
+ self._unindex[documentId] = ConvertedDate
+
+ returnStatus = 1
+
+ return returnStatus
+
+
+ def _apply_index( self, request, cid='', type=type, None=None ):
+ """Apply the index to query parameters given in the argument
+
+ Normalize the 'query' arguments into integer values at minute
+ precision before querying.
+ """
+ record = parseIndexRequest( request, self.id, self.query_options )
+ if record.keys == None:
+ return None
+
+ keys = map( self._convert, record.keys )
+
+ index = self._index
+ r = None
+ opr = None
+
+ #experimental code for specifing the operator
+ operator = record.get( 'operator', self.useOperator )
+ if not operator in self.operators :
+ raise RuntimeError, "operator not valid: %s" % operator
+
+ # depending on the operator we use intersection or union
+ if operator=="or":
+ set_func = union
+ else:
+ set_func = intersection
+
+ # range parameter
+ range_arg = record.get('range',None)
+ if range_arg:
+ opr = "range"
+ opr_args = []
+ if range_arg.find("min") > -1:
+ opr_args.append("min")
+ if range_arg.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(keys)
+ else:
+ lo = None
+
+ if 'max' in opr_args:
+ hi = max(keys)
+ else:
+ hi = None
+
+ if hi:
+ setlist = index.items(lo,hi)
+ else:
+ setlist = index.items(lo)
+
+ # XXX: Use multiunion!
+ for k, set in setlist:
+ if type(set) is IntType:
+ set = IISet((set,))
+ r = set_func(r, set)
+
+ else: # not a range search
+ for key in keys:
+ set = index.get(key, None)
+ if set is not None:
+ if type(set) is IntType:
+ set = IISet((set,))
+ r = set_func(r, set)
+
+ if type(r) is IntType:
+ r = IISet((r,))
+
+ if r is None:
+ return IISet(), (self.id,)
+ else:
+ return r, (self.id,)
+
+
+ def _convert( self, value, default=None ):
+ """Convert Date/Time value to our internal representation"""
+ if isinstance( value, DateTime ):
+ t_tup = value.parts()
+ elif type( value ) is FloatType:
+ t_tup = time.gmtime( value )
+ elif type( value ) is StringType:
+ t_obj = DateTime( value )
+ t_tup = t_obj.parts()
+ else:
+ return default
+
+ yr = t_tup[0]
+ mo = t_tup[1]
+ dy = t_tup[2]
+ hr = t_tup[3]
+ mn = t_tup[4]
+
+ t_val = ( ( ( ( yr * 12 + mo ) * 31 + dy ) * 24 + hr ) * 60 + mn )
+
+ return t_val
+
+
+manage_addDateIndexForm = DTMLFile( 'dtml/addDateIndex', globals() )
+
+def manage_addDateIndex( self, id, REQUEST=None, RESPONSE=None, URL3=None):
+ """Add a Date index"""
+ return self.manage_addIndex(id, 'DateIndex', extra=None, \
+ REQUEST=REQUEST, RESPONSE=RESPONSE, URL1=URL3)
+
=== Added File Zope/lib/python/Products/PluginIndexes/DateIndex/README.txt ===
DateIndex README
Overview
Normal FieldIndexes *can* be used to index values which are DateTime
instances, but they are hideously expensive:
o DateTime instances are *huge*, both in RAM and on disk.
o DateTime instances maintain an absurd amount of precision, far
beyond any reasonable search criteria for "normal" cases.
DateIndex is a pluggable index which addresses these two issues
as follows:
o It normalizes the indexed value to an integer representation
with a granularity of one minute.
o It normalizes the 'query' value into the same form.
o Objects which return 'None' for the index query are omitted from
the index.
=== Zope/lib/python/Products/PluginIndexes/DateIndex/__init__.py 1.1 => 1.2 ===