Log message for revision 116949: Added a new BooleanIndex to the standard PluginIndexes, sponsored by Jarn AS and based on work in the Products.BooleanIndex distribution.
Changed: U Zope/trunk/doc/CHANGES.rst A Zope/trunk/src/Products/PluginIndexes/BooleanIndex/ A Zope/trunk/src/Products/PluginIndexes/BooleanIndex/BooleanIndex.py A Zope/trunk/src/Products/PluginIndexes/BooleanIndex/__init__.py A Zope/trunk/src/Products/PluginIndexes/BooleanIndex/dtml/ A Zope/trunk/src/Products/PluginIndexes/BooleanIndex/dtml/addBooleanIndex.dtml A Zope/trunk/src/Products/PluginIndexes/BooleanIndex/dtml/manageBooleanIndex.dtml A Zope/trunk/src/Products/PluginIndexes/BooleanIndex/tests.py U Zope/trunk/src/Products/PluginIndexes/__init__.py -=- Modified: Zope/trunk/doc/CHANGES.rst =================================================================== --- Zope/trunk/doc/CHANGES.rst 2010-09-25 21:57:29 UTC (rev 116948) +++ Zope/trunk/doc/CHANGES.rst 2010-09-25 22:03:22 UTC (rev 116949) @@ -19,6 +19,8 @@ Features Added ++++++++++++++ +- Added a new BooleanIndex to the standard PluginIndexes. + - Update to Zope Toolkit 1.0c1. - Add ability to define extra zopectl commands via setuptools entrypoints. Added: Zope/trunk/src/Products/PluginIndexes/BooleanIndex/BooleanIndex.py =================================================================== --- Zope/trunk/src/Products/PluginIndexes/BooleanIndex/BooleanIndex.py (rev 0) +++ Zope/trunk/src/Products/PluginIndexes/BooleanIndex/BooleanIndex.py 2010-09-25 22:03:22 UTC (rev 116949) @@ -0,0 +1,155 @@ +############################################################################## +# +# Copyright (c) 2002 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. +# +############################################################################## + +from logging import getLogger + +from App.special_dtml import DTMLFile +from BTrees.IIBTree import IIBTree, IITreeSet, IISet +from BTrees.IIBTree import union, intersection, difference +import BTrees.Length +from ZODB.POSException import ConflictError + +from Products.PluginIndexes.common.util import parseIndexRequest +from Products.PluginIndexes.common.UnIndex import UnIndex + +_marker = object() +LOG = getLogger('BooleanIndex.UnIndex') + + +class BooleanIndex(UnIndex): + """Index for booleans + + self._index = set([documentId1, documentId2]) + self._unindex = {documentId:[True/False]} + + False doesn't have actual entries in _index. + """ + + meta_type = "BooleanIndex" + + manage_options= ( + {'label': 'Settings', + 'action': 'manage_main'}, + {'label': 'Browse', + 'action': 'manage_browse'}, + ) + + query_options = ["query"] + + manage = manage_main = DTMLFile('dtml/manageBooleanIndex', globals()) + manage_main._setName('manage_main') + manage_browse = DTMLFile('../dtml/browseIndex', globals()) + + def clear(self): + self._length = BTrees.Length.Length() + self._index = IITreeSet() + self._unindex = IIBTree() + + def insertForwardIndexEntry(self, entry, documentId): + """If True, insert directly into treeset + """ + if entry: + self._index.insert(documentId) + self._length.change(1) + + def removeForwardIndexEntry(self, entry, documentId): + """Take the entry provided and remove any reference to documentId + in its entry in the index. + """ + try: + if entry: + self._index.remove(documentId) + self._length.change(-1) + except ConflictError: + raise + except Exception: + LOG.exception('%s: unindex_object could not remove ' + 'documentId %s from index %s. This ' + 'should not happen.' % (self.__class__.__name__, + str(documentId), str(self.id))) + + def _index_object(self, documentId, obj, threshold=None, attr=''): + """ index and object 'obj' with integer id 'documentId'""" + returnStatus = 0 + + # First we need to see if there's anything interesting to look at + datum = self._get_object_datum(obj, attr) + + # Make it boolean, int as an optimization + datum = int(bool(datum)) + + # We don't want to do anything that we don't have to here, so we'll + # check to see if the new and existing information is the same. + oldDatum = self._unindex.get(documentId, _marker) + if datum != oldDatum: + if oldDatum is not _marker: + self.removeForwardIndexEntry(oldDatum, documentId) + if datum is _marker: + try: + del self._unindex[documentId] + except ConflictError: + raise + except Exception: + LOG.error('Should not happen: oldDatum was there, now ' + 'its not, for document with id %s' % + documentId) + + if datum is not _marker: + if datum: + self.insertForwardIndexEntry(datum, documentId) + self._unindex[documentId] = datum + + returnStatus = 1 + + return returnStatus + + def _apply_index(self, request, resultset=None): + record = parseIndexRequest(request, self.id, self.query_options) + if record.keys is None: + return None + + index = self._index + + for key in record.keys: + if key: + # If True, check index + return (intersection(index, resultset), (self.id, )) + else: + # Otherwise, remove from resultset or _unindex + if resultset is None: + return (union(difference(self._unindex, index), IISet([])), + (self.id, )) + else: + return (difference(resultset, index), (self.id, )) + return (IISet(), (self.id, )) + + def indexSize(self): + """Return distinct values, as an optimization we always claim 2.""" + return 2 + + def items(self): + items = [] + for v, k in self._unindex.items(): + if isinstance(v, int): + v = IISet((v, )) + items.append((k, v)) + return items + +manage_addBooleanIndexForm = DTMLFile('dtml/addBooleanIndex', globals()) + + +def manage_addBooleanIndex(self, id, extra=None, + REQUEST=None, RESPONSE=None, URL3=None): + """Add a boolean index""" + return self.manage_addIndex(id, 'BooleanIndex', extra=extra, \ + REQUEST=REQUEST, RESPONSE=RESPONSE, URL1=URL3) Property changes on: Zope/trunk/src/Products/PluginIndexes/BooleanIndex/BooleanIndex.py ___________________________________________________________________ Added: svn:eol-style + native Property changes on: Zope/trunk/src/Products/PluginIndexes/BooleanIndex/__init__.py ___________________________________________________________________ Added: svn:eol-style + native Added: Zope/trunk/src/Products/PluginIndexes/BooleanIndex/dtml/addBooleanIndex.dtml =================================================================== --- Zope/trunk/src/Products/PluginIndexes/BooleanIndex/dtml/addBooleanIndex.dtml (rev 0) +++ Zope/trunk/src/Products/PluginIndexes/BooleanIndex/dtml/addBooleanIndex.dtml 2010-09-25 22:03:22 UTC (rev 116949) @@ -0,0 +1,60 @@ +<dtml-var manage_page_header> + +<dtml-var "manage_form_title(this(), _, form_title='Add BooleanIndex')"> + + +<p class="form-help"> +<strong>Boolean Indexes</strong> can be used for keeping track of +whether objects fulfills a certain contract, like isFolderish +</p> + + +<form action="manage_addBooleanIndex" 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"> + Indexed attributes + </div> + </td> + <td align="left" valign="top"> + <input type="text" name="extra.indexed_attrs:record:string" size="40" /> + <em>attribute1,attribute2,...</em> or leave empty + </td> + </tr> + + <tr> + <td align="left" valign="top"> + <div class="form-optional"> + Type + </div> + </td> + <td align="left" valign="top"> + Boolean 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> Property changes on: Zope/trunk/src/Products/PluginIndexes/BooleanIndex/dtml/addBooleanIndex.dtml ___________________________________________________________________ Added: svn:eol-style + native Added: Zope/trunk/src/Products/PluginIndexes/BooleanIndex/dtml/manageBooleanIndex.dtml =================================================================== --- Zope/trunk/src/Products/PluginIndexes/BooleanIndex/dtml/manageBooleanIndex.dtml (rev 0) +++ Zope/trunk/src/Products/PluginIndexes/BooleanIndex/dtml/manageBooleanIndex.dtml 2010-09-25 22:03:22 UTC (rev 116949) @@ -0,0 +1,10 @@ +<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> Property changes on: Zope/trunk/src/Products/PluginIndexes/BooleanIndex/dtml/manageBooleanIndex.dtml ___________________________________________________________________ Added: svn:eol-style + native Added: Zope/trunk/src/Products/PluginIndexes/BooleanIndex/tests.py =================================================================== --- Zope/trunk/src/Products/PluginIndexes/BooleanIndex/tests.py (rev 0) +++ Zope/trunk/src/Products/PluginIndexes/BooleanIndex/tests.py 2010-09-25 22:03:22 UTC (rev 116949) @@ -0,0 +1,104 @@ +############################################################################## +# +# 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 unittest + +from BTrees.IIBTree import IISet + + +class Dummy(object): + + def __init__(self, docid, truth): + self.id = docid + self.truth = truth + + +class TestBooleanIndex(unittest.TestCase): + + def _getTargetClass(self): + from Products.PluginIndexes.BooleanIndex import BooleanIndex + return BooleanIndex.BooleanIndex + + def _makeOne(self, attr='truth'): + return self._getTargetClass()(attr) + + def test_index_true(self): + index = self._makeOne() + obj = Dummy(1, True) + index._index_object(obj.id, obj, attr='truth') + self.failUnless(1 in index._unindex) + self.failUnless(1 in index._index) + + def test_index_false(self): + index = self._makeOne() + obj = Dummy(1, False) + index._index_object(obj.id, obj, attr='truth') + self.failUnless(1 in index._unindex) + self.failIf(1 in index._index) + + def test_search_true(self): + index = self._makeOne() + obj = Dummy(1, True) + index._index_object(obj.id, obj, attr='truth') + obj = Dummy(2, False) + index._index_object(obj.id, obj, attr='truth') + + res, idx = index._apply_index({'truth': True}) + self.failUnlessEqual(idx, ('truth', )) + self.failUnlessEqual(list(res), [1]) + + def test_search_false(self): + index = self._makeOne() + obj = Dummy(1, True) + index._index_object(obj.id, obj, attr='truth') + obj = Dummy(2, False) + index._index_object(obj.id, obj, attr='truth') + + res, idx = index._apply_index({'truth': False}) + self.failUnlessEqual(idx, ('truth', )) + self.failUnlessEqual(list(res), [2]) + + def test_search_inputresult(self): + index = self._makeOne() + obj = Dummy(1, True) + index._index_object(obj.id, obj, attr='truth') + obj = Dummy(2, False) + index._index_object(obj.id, obj, attr='truth') + + res, idx = index._apply_index({'truth': True}, resultset=IISet([])) + self.failUnlessEqual(idx, ('truth', )) + self.failUnlessEqual(list(res), []) + + res, idx = index._apply_index({'truth': True}, resultset=IISet([2])) + self.failUnlessEqual(idx, ('truth', )) + self.failUnlessEqual(list(res), []) + + res, idx = index._apply_index({'truth': True}, resultset=IISet([1])) + self.failUnlessEqual(idx, ('truth', )) + self.failUnlessEqual(list(res), [1]) + + res, idx = index._apply_index({'truth': True}, resultset=IISet([1, 2])) + self.failUnlessEqual(idx, ('truth', )) + self.failUnlessEqual(list(res), [1]) + + res, idx = index._apply_index({'truth': False}, + resultset=IISet([1, 2])) + self.failUnlessEqual(idx, ('truth', )) + self.failUnlessEqual(list(res), [2]) + + +def test_suite(): + from unittest import TestSuite, makeSuite + suite = TestSuite() + suite.addTest(makeSuite(TestBooleanIndex)) + return suite Property changes on: Zope/trunk/src/Products/PluginIndexes/BooleanIndex/tests.py ___________________________________________________________________ Added: svn:eol-style + native Modified: Zope/trunk/src/Products/PluginIndexes/__init__.py =================================================================== --- Zope/trunk/src/Products/PluginIndexes/__init__.py 2010-09-25 21:57:29 UTC (rev 116948) +++ Zope/trunk/src/Products/PluginIndexes/__init__.py 2010-09-25 22:03:22 UTC (rev 116949) @@ -91,3 +91,17 @@ icon='www/index.gif', visibility=None, ) + + from Products.PluginIndexes.BooleanIndex.BooleanIndex import BooleanIndex + from Products.PluginIndexes.BooleanIndex.BooleanIndex import \ + manage_addBooleanIndex + from Products.PluginIndexes.BooleanIndex.BooleanIndex import \ + manage_addBooleanIndexForm + + context.registerClass(BooleanIndex, + permission='Add Pluggable Index', + constructors=(manage_addBooleanIndexForm, + manage_addBooleanIndex), + icon='www/index.gif', + visibility=None, + ) _______________________________________________ Zope-Checkins maillist - Zope-Checkins@zope.org https://mail.zope.org/mailman/listinfo/zope-checkins