Commit c485385d authored by 's avatar

added some z3 interfaces for catalog related classes

parents 86cef76b 733b0fbd
...@@ -7,28 +7,31 @@ ...@@ -7,28 +7,31 @@
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Date index.
"""$Id$ $Id$
""" """
import time
from datetime import date, datetime
from datetime import tzinfo, timedelta from datetime import tzinfo, timedelta
from types import StringType, FloatType, IntType from types import StringType, FloatType, IntType
from BTrees.IIBTree import IISet, union, intersection, multiunion
from BTrees.IOBTree import IOBTree
from BTrees.OIBTree import OIBTree
from DateTime.DateTime import DateTime from DateTime.DateTime import DateTime
from Globals import DTMLFile
from OFS.PropertyManager import PropertyManager from OFS.PropertyManager import PropertyManager
from datetime import date, datetime from zope.interface import implements
from Products.PluginIndexes import PluggableIndex
from Products.PluginIndexes.common import safe_callable
from Products.PluginIndexes.common.UnIndex import UnIndex from Products.PluginIndexes.common.UnIndex import UnIndex
from Products.PluginIndexes.common.util import parseIndexRequest from Products.PluginIndexes.common.util import parseIndexRequest
from Products.PluginIndexes.common import safe_callable from Products.PluginIndexes.interfaces import IDateIndex
from Globals import DTMLFile
from BTrees.IOBTree import IOBTree
from BTrees.OIBTree import OIBTree
from BTrees.IIBTree import IISet, union, intersection, multiunion
import time
_marker = [] _marker = []
...@@ -73,17 +76,20 @@ class LocalTimezone(tzinfo): ...@@ -73,17 +76,20 @@ class LocalTimezone(tzinfo):
Local = LocalTimezone() Local = LocalTimezone()
############################################################################### ###############################################################################
class DateIndex(UnIndex, PropertyManager): class DateIndex(UnIndex, PropertyManager):
""" Index for Dates """
__implements__ = (PluggableIndex.UniqueValueIndex, """Index for dates.
PluggableIndex.SortIndex) """
__implements__ = UnIndex.__implements__
implements(IDateIndex)
meta_type = 'DateIndex' meta_type = 'DateIndex'
query_options = ['query', 'range'] query_options = ['query', 'range']
index_naive_time_as_local = True # False means index as UTC index_naive_time_as_local = True # False means index as UTC
_properties=({'id':'index_naive_time_as_local', _properties=({'id':'index_naive_time_as_local',
'type':'boolean', 'type':'boolean',
'mode':'w'},) 'mode':'w'},)
...@@ -138,7 +144,6 @@ class DateIndex(UnIndex, PropertyManager): ...@@ -138,7 +144,6 @@ class DateIndex(UnIndex, PropertyManager):
return returnStatus return returnStatus
def _apply_index( self, request, cid='', type=type ): def _apply_index( self, request, cid='', type=type ):
"""Apply the index to query parameters given in the argument """Apply the index to query parameters given in the argument
...@@ -220,7 +225,6 @@ class DateIndex(UnIndex, PropertyManager): ...@@ -220,7 +225,6 @@ class DateIndex(UnIndex, PropertyManager):
else: else:
return r, (self.id,) return r, (self.id,)
def _convert( self, value, default=None ): def _convert( self, value, default=None ):
"""Convert Date/Time value to our internal representation""" """Convert Date/Time value to our internal representation"""
# XXX: Code patched 20/May/2003 by Kiran Jonnalagadda to # XXX: Code patched 20/May/2003 by Kiran Jonnalagadda to
......
...@@ -7,18 +7,27 @@ ...@@ -7,18 +7,27 @@
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""DateIndex unit tests.
$Id$
"""
import Zope2
import unittest import unittest
import Testing
import Zope2
Zope2.startup()
from DateTime import DateTime
from datetime import date, datetime, tzinfo, timedelta from datetime import date, datetime, tzinfo, timedelta
from Products.PluginIndexes.DateIndex.DateIndex import DateIndex, Local
from types import IntType, FloatType
import time import time
from types import IntType, FloatType
from DateTime import DateTime
from Products.PluginIndexes.DateIndex.DateIndex import DateIndex, Local
class Dummy: class Dummy:
...@@ -96,6 +105,7 @@ class USTimeZone(tzinfo): ...@@ -96,6 +105,7 @@ class USTimeZone(tzinfo):
Eastern = USTimeZone(-5, "Eastern", "EST", "EDT") Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
############################################################################### ###############################################################################
class DI_Tests(unittest.TestCase): class DI_Tests(unittest.TestCase):
def setUp(self): def setUp(self):
self._values = ( self._values = (
...@@ -109,13 +119,13 @@ class DI_Tests(unittest.TestCase): ...@@ -109,13 +119,13 @@ class DI_Tests(unittest.TestCase):
(7, Dummy('f', 1072742900)), # 1073545928 (7, Dummy('f', 1072742900)), # 1073545928
(8, Dummy('g', date(2034,2,5))), # 1073599200 (8, Dummy('g', date(2034,2,5))), # 1073599200
(9, Dummy('h', datetime(2034,2,5,15,20,5))), # (varies) (9, Dummy('h', datetime(2034,2,5,15,20,5))), # (varies)
(10, Dummy('i', datetime(2034,2,5,10,17,5, (10, Dummy('i', datetime(2034,2,5,10,17,5,
tzinfo=Eastern))), # 1073600117 tzinfo=Eastern))), # 1073600117
) )
self._index = DateIndex('date') self._index = DateIndex('date')
self._noop_req = {'bar': 123} self._noop_req = {'bar': 123}
self._request = {'date': DateTime(0)} self._request = {'date': DateTime(0)}
self._min_req = {'date': {'query': DateTime('2032-05-08 15:16:17'), self._min_req = {'date': {'query': DateTime('2032-05-08 15:16:17'),
'range': 'min'}} 'range': 'min'}}
self._max_req = {'date': {'query': DateTime('2032-05-08 15:16:17'), self._max_req = {'date': {'query': DateTime('2032-05-08 15:16:17'),
'range': 'max'}} 'range': 'max'}}
...@@ -154,6 +164,18 @@ class DI_Tests(unittest.TestCase): ...@@ -154,6 +164,18 @@ class DI_Tests(unittest.TestCase):
yr, mo, dy, hr, mn = dt.toZone('UTC').parts()[:5] yr, mo, dy, hr, mn = dt.toZone('UTC').parts()[:5]
return (((yr * 12 + mo) * 31 + dy) * 24 + hr) * 60 + mn return (((yr * 12 + mo) * 31 + dy) * 24 + hr) * 60 + mn
def test_z3interfaces(self):
from Products.PluginIndexes.interfaces import IDateIndex
from Products.PluginIndexes.interfaces import IPluggableIndex
from Products.PluginIndexes.interfaces import ISortIndex
from Products.PluginIndexes.interfaces import IUniqueValueIndex
from zope.interface.verify import verifyClass
verifyClass(IDateIndex, DateIndex)
verifyClass(IPluggableIndex, DateIndex)
verifyClass(ISortIndex, DateIndex)
verifyClass(IUniqueValueIndex, DateIndex)
def test_empty(self): def test_empty(self):
empty = self._index empty = self._index
...@@ -204,28 +226,24 @@ class DI_Tests(unittest.TestCase): ...@@ -204,28 +226,24 @@ class DI_Tests(unittest.TestCase):
self._checkApply(self._range_req, values[2:] ) self._checkApply(self._range_req, values[2:] )
self._checkApply(self._float_req, [values[6]] ) self._checkApply(self._float_req, [values[6]] )
self._checkApply(self._int_req, [values[7]] ) self._checkApply(self._int_req, [values[7]] )
def test_naive_convert_to_utc(self): def test_naive_convert_to_utc(self):
values = self._values values = self._values
index = self._index index = self._index
index.index_naive_time_as_local = False index.index_naive_time_as_local = False
self._populateIndex() self._populateIndex()
for k, v in values[9:]: for k, v in values[9:]:
# assert that the timezone is effectively UTC for item 9, # assert that the timezone is effectively UTC for item 9,
# and still correct for item 10 # and still correct for item 10
yr, mo, dy, hr, mn = v.date().utctimetuple()[:5] yr, mo, dy, hr, mn = v.date().utctimetuple()[:5]
val = (((yr * 12 + mo) * 31 + dy) * 24 + hr) * 60 + mn val = (((yr * 12 + mo) * 31 + dy) * 24 + hr) * 60 + mn
self.failUnlessEqual(self._index.getEntryForObject(k), val) self.failUnlessEqual(self._index.getEntryForObject(k), val)
def test_suite(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()
suite.addTest( unittest.makeSuite( DI_Tests ) ) suite.addTest( unittest.makeSuite( DI_Tests ) )
return suite return suite
def run():
unittest.TextTestRunner().run(test_suite())
if __name__ == '__main__': if __name__ == '__main__':
run() unittest.main(defaultTest='test_suite')
...@@ -7,56 +7,58 @@ ...@@ -7,56 +7,58 @@
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Date range index.
"""$Id$ $Id$
""" """
import os import os
from Products.PluginIndexes import PluggableIndex from AccessControl import ClassSecurityInfo
from Products.PluginIndexes.common.UnIndex import UnIndex
from Products.PluginIndexes.common.util import parseIndexRequest
from Products.PluginIndexes.common import safe_callable
from BTrees.IOBTree import IOBTree
from BTrees.IIBTree import IISet, IITreeSet, union, intersection, multiunion from BTrees.IIBTree import IISet, IITreeSet, union, intersection, multiunion
from BTrees.IOBTree import IOBTree
import BTrees.Length import BTrees.Length
from Globals import package_home, DTMLFile, InitializeClass
from AccessControl import ClassSecurityInfo
from DateTime.DateTime import DateTime from DateTime.DateTime import DateTime
from Globals import package_home, DTMLFile, InitializeClass
from zope.interface import implements
from Products.PluginIndexes.common import safe_callable
from Products.PluginIndexes.common.UnIndex import UnIndex
from Products.PluginIndexes.common.util import parseIndexRequest
from Products.PluginIndexes.interfaces import IDateRangeIndex
_dtmldir = os.path.join( package_home( globals() ), 'dtml' ) _dtmldir = os.path.join( package_home( globals() ), 'dtml' )
VIEW_PERMISSION = 'View' VIEW_PERMISSION = 'View'
INDEX_MGMT_PERMISSION = 'Manage ZCatalogIndex Entries' INDEX_MGMT_PERMISSION = 'Manage ZCatalogIndex Entries'
class DateRangeIndex(UnIndex): class DateRangeIndex(UnIndex):
"""
Index a date range, such as the canonical "effective-expiration"
range in the CMF. Any object may return None for either the
start or the end date: for the start date, this should be
the logical equivalent of "since the beginning of time"; for the
end date, "until the end of time".
Therefore, divide the space of indexed objects into four containers: """Index for date ranges, such as the "effective-expiration" range in CMF.
- Objects which always match ( i.e., they returned None for both ); Any object may return None for either the start or the end date: for the
start date, this should be the logical equivalent of "since the beginning
of time"; for the end date, "until the end of time".
- Objects which match after a given time ( i.e., they returned None Therefore, divide the space of indexed objects into four containers:
for the end date );
- Objects which match until a given time ( i.e., they returned None - Objects which always match (i.e., they returned None for both);
for the start date );
- Objects which match only during a specific interval. - Objects which match after a given time (i.e., they returned None for the
end date);
- Objects which match until a given time (i.e., they returned None for the
start date);
- Objects which match only during a specific interval.
""" """
__implements__ = (PluggableIndex.UniqueValueIndex, __implements__ = UnIndex.__implements__
PluggableIndex.SortIndex) implements(IDateRangeIndex)
security = ClassSecurityInfo() security = ClassSecurityInfo()
...@@ -83,27 +85,21 @@ class DateRangeIndex(UnIndex): ...@@ -83,27 +85,21 @@ class DateRangeIndex(UnIndex):
self._edit(since_field, until_field) self._edit(since_field, until_field)
self.clear() self.clear()
security.declareProtected( VIEW_PERMISSION security.declareProtected(VIEW_PERMISSION, 'getSinceField')
, 'getSinceField' def getSinceField(self):
) """Get the name of the attribute indexed as start date.
def getSinceField( self ):
"""
""" """
return self._since_field return self._since_field
security.declareProtected( VIEW_PERMISSION security.declareProtected(VIEW_PERMISSION, 'getUntilField')
, 'getUntilField' def getUntilField(self):
) """Get the name of the attribute indexed as end date.
def getUntilField( self ):
"""
""" """
return self._until_field return self._until_field
manage_indexProperties = DTMLFile( 'manageDateRangeIndex', _dtmldir ) manage_indexProperties = DTMLFile( 'manageDateRangeIndex', _dtmldir )
security.declareProtected( INDEX_MGMT_PERMISSION security.declareProtected(INDEX_MGMT_PERMISSION, 'manage_edit')
, 'manage_edit'
)
def manage_edit( self, since_field, until_field, REQUEST ): def manage_edit( self, since_field, until_field, REQUEST ):
""" """
""" """
...@@ -113,7 +109,7 @@ class DateRangeIndex(UnIndex): ...@@ -113,7 +109,7 @@ class DateRangeIndex(UnIndex):
% REQUEST.get('URL2') % REQUEST.get('URL2')
) )
security.declarePrivate( '_edit' ) security.declarePrivate('_edit')
def _edit( self, since_field, until_field ): def _edit( self, since_field, until_field ):
""" """
Update the fields used to compute the range. Update the fields used to compute the range.
...@@ -121,10 +117,7 @@ class DateRangeIndex(UnIndex): ...@@ -121,10 +117,7 @@ class DateRangeIndex(UnIndex):
self._since_field = since_field self._since_field = since_field
self._until_field = until_field self._until_field = until_field
security.declareProtected(INDEX_MGMT_PERMISSION, 'clear')
security.declareProtected( INDEX_MGMT_PERMISSION
, 'clear'
)
def clear( self ): def clear( self ):
""" """
Start over fresh. Start over fresh.
...@@ -308,7 +301,7 @@ class DateRangeIndex(UnIndex): ...@@ -308,7 +301,7 @@ class DateRangeIndex(UnIndex):
# #
# ZCatalog needs this, although it isn't (yet) part of the interface. # ZCatalog needs this, although it isn't (yet) part of the interface.
# #
security.declareProtected( VIEW_PERMISSION , 'numObjects' ) security.declareProtected(VIEW_PERMISSION , 'numObjects')
def numObjects( self ): def numObjects( self ):
""" """ """ """
return len( self._unindex ) return len( self._unindex )
......
...@@ -7,16 +7,24 @@ ...@@ -7,16 +7,24 @@
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""DateRangeIndex unit tests.
$Id$
"""
import Zope2
import unittest import unittest
import Testing
import Zope2
Zope2.startup()
import sys import sys
from Products.PluginIndexes.DateRangeIndex.DateRangeIndex import DateRangeIndex from Products.PluginIndexes.DateRangeIndex.DateRangeIndex import DateRangeIndex
class Dummy: class Dummy:
def __init__( self, name, start, stop ): def __init__( self, name, start, stop ):
...@@ -63,6 +71,7 @@ def matchingDummies( value ): ...@@ -63,6 +71,7 @@ def matchingDummies( value ):
return result return result
class DRI_Tests( unittest.TestCase ): class DRI_Tests( unittest.TestCase ):
def setUp( self ): def setUp( self ):
...@@ -71,6 +80,18 @@ class DRI_Tests( unittest.TestCase ): ...@@ -71,6 +80,18 @@ class DRI_Tests( unittest.TestCase ):
def tearDown( self ): def tearDown( self ):
pass pass
def test_z3interfaces(self):
from Products.PluginIndexes.interfaces import IDateRangeIndex
from Products.PluginIndexes.interfaces import IPluggableIndex
from Products.PluginIndexes.interfaces import ISortIndex
from Products.PluginIndexes.interfaces import IUniqueValueIndex
from zope.interface.verify import verifyClass
verifyClass(IDateRangeIndex, DateRangeIndex)
verifyClass(IPluggableIndex, DateRangeIndex)
verifyClass(ISortIndex, DateRangeIndex)
verifyClass(IUniqueValueIndex, DateRangeIndex)
def test_empty( self ): def test_empty( self ):
empty = DateRangeIndex( 'empty' ) empty = DateRangeIndex( 'empty' )
...@@ -120,13 +141,11 @@ class DRI_Tests( unittest.TestCase ): ...@@ -120,13 +141,11 @@ class DRI_Tests( unittest.TestCase ):
bad = Dummy( 'bad', long(sys.maxint) + 1, long(sys.maxint) + 1 ) bad = Dummy( 'bad', long(sys.maxint) + 1, long(sys.maxint) + 1 )
work.index_object( 0, bad ) work.index_object( 0, bad )
def test_suite(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()
suite.addTest( unittest.makeSuite( DRI_Tests ) ) suite.addTest( unittest.makeSuite( DRI_Tests ) )
return suite return suite
def run():
unittest.TextTestRunner().run(test_suite())
if __name__ == '__main__': if __name__ == '__main__':
run() unittest.main(defaultTest='test_suite')
############################################################################# ##############################################################################
# #
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# #
...@@ -7,24 +7,25 @@ ...@@ -7,24 +7,25 @@
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Simple column indices.
"""Simple column indices
$Id$ $Id$
""" """
from Products.PluginIndexes import PluggableIndex from Globals import DTMLFile
from Products.PluginIndexes.common.UnIndex import UnIndex from Products.PluginIndexes.common.UnIndex import UnIndex
from Globals import DTMLFile
class FieldIndex(UnIndex): class FieldIndex(UnIndex):
"""Field Indexes"""
__implements__ = (PluggableIndex.UniqueValueIndex, """Index for simple fields.
PluggableIndex.SortIndex) """
__implements__ = UnIndex.__implements__
meta_type="FieldIndex" meta_type="FieldIndex"
......
...@@ -7,14 +7,22 @@ ...@@ -7,14 +7,22 @@
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""FieldIndex unit tests.
$Id$
"""
import unittest
import Testing
import Zope2
Zope2.startup()
import os, sys, unittest
import ZODB
from Products.PluginIndexes.FieldIndex.FieldIndex import FieldIndex from Products.PluginIndexes.FieldIndex.FieldIndex import FieldIndex
class Dummy: class Dummy:
def __init__( self, foo ): def __init__( self, foo ):
...@@ -28,9 +36,9 @@ class Dummy: ...@@ -28,9 +36,9 @@ class Dummy:
__repr__ = __str__ __repr__ = __str__
class TestCase( unittest.TestCase ):
""" class FieldIndexTests(unittest.TestCase):
Test FieldIndex objects. """Test FieldIndex objects.
""" """
def setUp( self ): def setUp( self ):
...@@ -68,7 +76,6 @@ class TestCase( unittest.TestCase ): ...@@ -68,7 +76,6 @@ class TestCase( unittest.TestCase ):
self._zero_req = { 'foo': 0 } self._zero_req = { 'foo': 0 }
self._none_req = { 'foo': None } self._none_req = { 'foo': None }
def tearDown( self ): def tearDown( self ):
""" """
""" """
...@@ -87,6 +94,16 @@ class TestCase( unittest.TestCase ): ...@@ -87,6 +94,16 @@ class TestCase( unittest.TestCase ):
for k, v in expectedValues: for k, v in expectedValues:
assert k in result assert k in result
def test_z3interfaces(self):
from Products.PluginIndexes.interfaces import IPluggableIndex
from Products.PluginIndexes.interfaces import ISortIndex
from Products.PluginIndexes.interfaces import IUniqueValueIndex
from zope.interface.verify import verifyClass
verifyClass(IPluggableIndex, FieldIndex)
verifyClass(ISortIndex, FieldIndex)
verifyClass(IUniqueValueIndex, FieldIndex)
def testEmpty( self ): def testEmpty( self ):
"Test an empty FieldIndex." "Test an empty FieldIndex."
...@@ -205,11 +222,11 @@ class TestCase( unittest.TestCase ): ...@@ -205,11 +222,11 @@ class TestCase( unittest.TestCase ):
assert r2 == r assert r2 == r
def test_suite():
return unittest.makeSuite( TestCase )
def main(): def test_suite():
unittest.TextTestRunner().run(test_suite()) return unittest.TestSuite((
unittest.makeSuite(FieldIndexTests),
))
if __name__ == '__main__': if __name__ == '__main__':
main() unittest.main(defaultTest='test_suite')
...@@ -7,26 +7,36 @@ ...@@ -7,26 +7,36 @@
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Keyword index.
$Id$
"""
from types import StringType, UnicodeType from types import StringType, UnicodeType
from logging import getLogger from logging import getLogger
from BTrees.OOBTree import OOSet, difference
from BTrees.OOBTree import OOSet, difference
from Globals import DTMLFile from Globals import DTMLFile
from Products.PluginIndexes import PluggableIndex
from Products.PluginIndexes.common.UnIndex import UnIndex
from Products.PluginIndexes.common import safe_callable from Products.PluginIndexes.common import safe_callable
from Products.PluginIndexes.common.UnIndex import UnIndex
LOG = getLogger('Zope.KeywordIndex') LOG = getLogger('Zope.KeywordIndex')
class KeywordIndex(UnIndex): class KeywordIndex(UnIndex):
__implements__ = (PluggableIndex.UniqueValueIndex, """Like an UnIndex only it indexes sequences of items.
PluggableIndex.SortIndex)
Searches match any keyword.
This should have an _apply_index that returns a relevance score
"""
__implements__ = UnIndex.__implements__
meta_type="KeywordIndex" meta_type="KeywordIndex"
...@@ -41,14 +51,6 @@ class KeywordIndex(UnIndex): ...@@ -41,14 +51,6 @@ class KeywordIndex(UnIndex):
query_options = ("query","operator", "range") query_options = ("query","operator", "range")
"""Like an UnIndex only it indexes sequences of items
Searches match any keyword.
This should have an _apply_index that returns a relevance score
"""
def _index_object(self, documentId, obj, threshold=None, attr=''): def _index_object(self, documentId, obj, threshold=None, attr=''):
""" index an object 'obj' with integer id 'i' """ index an object 'obj' with integer id 'i'
...@@ -128,13 +130,11 @@ class KeywordIndex(UnIndex): ...@@ -128,13 +130,11 @@ class KeywordIndex(UnIndex):
LOG.error('Attempt to unindex nonexistent' LOG.error('Attempt to unindex nonexistent'
' document id %s' % documentId) ' document id %s' % documentId)
index_html = DTMLFile('dtml/index', globals()) index_html = DTMLFile('dtml/index', globals())
manage_workspace = DTMLFile('dtml/manageKeywordIndex', globals()) manage_workspace = DTMLFile('dtml/manageKeywordIndex', globals())
manage_browse = DTMLFile('../dtml/browseIndex', globals()) manage_browse = DTMLFile('../dtml/browseIndex', globals())
manage_addKeywordIndexForm = DTMLFile('dtml/addKeywordIndex', globals()) manage_addKeywordIndexForm = DTMLFile('dtml/addKeywordIndex', globals())
def manage_addKeywordIndex(self, id, extra=None, def manage_addKeywordIndex(self, id, extra=None,
......
...@@ -7,13 +7,24 @@ ...@@ -7,13 +7,24 @@
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
import os, sys, unittest, zLOG """KeywordIndex unit tests.
$Id$
"""
import unittest
import Testing
import Zope2
Zope2.startup()
import zLOG
from Products.PluginIndexes.KeywordIndex.KeywordIndex import KeywordIndex from Products.PluginIndexes.KeywordIndex.KeywordIndex import KeywordIndex
class Dummy: class Dummy:
def __init__( self, foo ): def __init__( self, foo ):
...@@ -35,6 +46,7 @@ def sortedUnique(seq): ...@@ -35,6 +46,7 @@ def sortedUnique(seq):
unique.sort() unique.sort()
return unique return unique
class TestKeywordIndex( unittest.TestCase ): class TestKeywordIndex( unittest.TestCase ):
""" """
Test KeywordIndex objects. Test KeywordIndex objects.
...@@ -102,6 +114,16 @@ class TestKeywordIndex( unittest.TestCase ): ...@@ -102,6 +114,16 @@ class TestKeywordIndex( unittest.TestCase ):
for k, v in expectedValues: for k, v in expectedValues:
assert k in result assert k in result
def test_z3interfaces(self):
from Products.PluginIndexes.interfaces import IPluggableIndex
from Products.PluginIndexes.interfaces import ISortIndex
from Products.PluginIndexes.interfaces import IUniqueValueIndex
from zope.interface.verify import verifyClass
verifyClass(IPluggableIndex, KeywordIndex)
verifyClass(ISortIndex, KeywordIndex)
verifyClass(IUniqueValueIndex, KeywordIndex)
def testAddObjectWOKeywords(self): def testAddObjectWOKeywords(self):
self._catch_log_errors() self._catch_log_errors()
...@@ -212,7 +234,7 @@ class TestKeywordIndex( unittest.TestCase ): ...@@ -212,7 +234,7 @@ class TestKeywordIndex( unittest.TestCase ):
# #
record[ 'foo' ][ 'range' ] = 'min:max' record[ 'foo' ][ 'range' ] = 'min:max'
self._checkApply( record, self._values[6:7] ) self._checkApply( record, self._values[6:7] )
def testDuplicateKeywords(self): def testDuplicateKeywords(self):
self._catch_log_errors() self._catch_log_errors()
try: try:
...@@ -248,14 +270,11 @@ class TestKeywordIndex( unittest.TestCase ): ...@@ -248,14 +270,11 @@ class TestKeywordIndex( unittest.TestCase ):
} }
self._checkApply(record, values[5:7]) self._checkApply(record, values[5:7])
def test_suite(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()
suite.addTest( unittest.makeSuite( TestKeywordIndex ) ) suite.addTest( unittest.makeSuite( TestKeywordIndex ) )
return suite return suite
def main():
unittest.main(defaultTest='test_suite')
if __name__ == '__main__': if __name__ == '__main__':
main() unittest.main(defaultTest='test_suite')
...@@ -7,11 +7,13 @@ ...@@ -7,11 +7,13 @@
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Path index.
__version__ = '$Id$' $Id$
"""
from types import StringType, ListType, TupleType from types import StringType, ListType, TupleType
from logging import getLogger from logging import getLogger
...@@ -22,17 +24,23 @@ from BTrees.IOBTree import IOBTree ...@@ -22,17 +24,23 @@ from BTrees.IOBTree import IOBTree
from BTrees.OOBTree import OOBTree from BTrees.OOBTree import OOBTree
from BTrees.IIBTree import IITreeSet, IISet, intersection, union from BTrees.IIBTree import IITreeSet, IISet, intersection, union
from BTrees.Length import Length from BTrees.Length import Length
from zope.interface import implements
from Products.PluginIndexes import PluggableIndex from Products.PluginIndexes import PluggableIndex
from Products.PluginIndexes.common.util import parseIndexRequest
from Products.PluginIndexes.common import safe_callable from Products.PluginIndexes.common import safe_callable
from Products.PluginIndexes.common.util import parseIndexRequest
from Products.PluginIndexes.interfaces import IPathIndex
from Products.PluginIndexes.interfaces import IUniqueValueIndex
_marker = [] _marker = []
LOG = getLogger('Zope.PathIndex') LOG = getLogger('Zope.PathIndex')
class PathIndex(Persistent, SimpleItem): class PathIndex(Persistent, SimpleItem):
""" A path index stores all path components of the physical
path of an object: """Index for paths returned by getPhysicalPath.
A path index stores all path components of the physical path of an object.
Internal datastructure: Internal datastructure:
...@@ -42,10 +50,10 @@ class PathIndex(Persistent, SimpleItem): ...@@ -42,10 +50,10 @@ class PathIndex(Persistent, SimpleItem):
- the value is a mapping 'level of the path component' to - the value is a mapping 'level of the path component' to
'all docids with this path component on this level' 'all docids with this path component on this level'
""" """
__implements__ = (PluggableIndex.UniqueValueIndex,) __implements__ = (PluggableIndex.UniqueValueIndex,)
implements(IPathIndex, IUniqueValueIndex)
meta_type="PathIndex" meta_type="PathIndex"
...@@ -72,7 +80,7 @@ class PathIndex(Persistent, SimpleItem): ...@@ -72,7 +80,7 @@ class PathIndex(Persistent, SimpleItem):
def insertEntry(self, comp, id, level): def insertEntry(self, comp, id, level):
"""Insert an entry. """Insert an entry.
comp is a path component comp is a path component
id is the docid id is the docid
level is the level of the component inside the path level is the level of the component inside the path
""" """
...@@ -111,7 +119,7 @@ class PathIndex(Persistent, SimpleItem): ...@@ -111,7 +119,7 @@ class PathIndex(Persistent, SimpleItem):
if isinstance(path, (ListType, TupleType)): if isinstance(path, (ListType, TupleType)):
path = '/'+ '/'.join(path[1:]) path = '/'+ '/'.join(path[1:])
comps = filter(None, path.split('/')) comps = filter(None, path.split('/'))
if not self._unindex.has_key(docid): if not self._unindex.has_key(docid):
self._length.change(1) self._length.change(1)
...@@ -250,8 +258,8 @@ class PathIndex(Persistent, SimpleItem): ...@@ -250,8 +258,8 @@ class PathIndex(Persistent, SimpleItem):
return ('getPhysicalPath', ) return ('getPhysicalPath', )
def getEntryForObject(self, docid, default=_marker): def getEntryForObject(self, docid, default=_marker):
""" Takes a document ID and returns all the information """ Takes a document ID and returns all the information
we have on that specific object. we have on that specific object.
""" """
try: try:
return self._unindex[docid] return self._unindex[docid]
......
...@@ -7,14 +7,22 @@ ...@@ -7,14 +7,22 @@
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""PathIndex unit tests.
$Id$
"""
import unittest import unittest
import Testing
import Zope2
Zope2.startup()
from Products.PluginIndexes.PathIndex.PathIndex import PathIndex from Products.PluginIndexes.PathIndex.PathIndex import PathIndex
class Dummy: class Dummy:
meta_type="foo" meta_type="foo"
...@@ -30,7 +38,8 @@ class Dummy: ...@@ -30,7 +38,8 @@ class Dummy:
__repr__ = __str__ __repr__ = __str__
class TestCase( unittest.TestCase ):
class PathIndexTests(unittest.TestCase):
""" Test PathIndex objects """ """ Test PathIndex objects """
def setUp(self): def setUp(self):
...@@ -60,6 +69,14 @@ class TestCase( unittest.TestCase ): ...@@ -60,6 +69,14 @@ class TestCase( unittest.TestCase ):
for k, v in self._values.items(): for k, v in self._values.items():
self._index.index_object( k, v ) self._index.index_object( k, v )
def test_z3interfaces(self):
from Products.PluginIndexes.interfaces import IPathIndex
from Products.PluginIndexes.interfaces import IUniqueValueIndex
from zope.interface.verify import verifyClass
verifyClass(IPathIndex, PathIndex)
verifyClass(IUniqueValueIndex, PathIndex)
def testEmpty(self): def testEmpty(self):
self.assertEqual(self._index.numObjects() ,0) self.assertEqual(self._index.numObjects() ,0)
self.assertEqual(self._index.getEntryForObject(1234), None) self.assertEqual(self._index.getEntryForObject(1234), None)
...@@ -87,7 +104,6 @@ class TestCase( unittest.TestCase ): ...@@ -87,7 +104,6 @@ class TestCase( unittest.TestCase ):
self._index.index_object(19, o) self._index.index_object(19, o)
self.assertEqual(self._index.numObjects(), 19) self.assertEqual(self._index.numObjects(), 19)
def testUnIndexError(self): def testUnIndexError(self):
self._populateIndex() self._populateIndex()
# this should not raise an error # this should not raise an error
...@@ -135,7 +151,6 @@ class TestCase( unittest.TestCase ): ...@@ -135,7 +151,6 @@ class TestCase( unittest.TestCase ):
lst = list(res[0].keys()) lst = list(res[0].keys())
self.assertEqual(lst,results) self.assertEqual(lst,results)
def testSimpleTests(self): def testSimpleTests(self):
self._populateIndex() self._populateIndex()
...@@ -201,11 +216,11 @@ class TestCase( unittest.TestCase ): ...@@ -201,11 +216,11 @@ class TestCase( unittest.TestCase ):
lst = list(res[0].keys()) lst = list(res[0].keys())
self.assertEqual(lst,results) self.assertEqual(lst,results)
def test_suite():
return unittest.makeSuite( TestCase )
def main(): def test_suite():
unittest.TextTestRunner().run(test_suite()) return unittest.TestSuite((
unittest.makeSuite(PathIndexTests),
))
if __name__ == '__main__': if __name__ == '__main__':
main() unittest.main(defaultTest='test_suite')
...@@ -7,36 +7,38 @@ ...@@ -7,36 +7,38 @@
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Deprecated text index. Please use ZCTextIndex instead.
"""Text Index $Id$
""" """
__version__ = '$Revision: 1.36 $'[11:-2]
import operator, warnings
import re
from cgi import escape
from types import *
import re from Globals import Persistent, DTMLFile
import operator,warnings
from Globals import Persistent,DTMLFile
from zLOG import LOG, ERROR from zLOG import LOG, ERROR
from Acquisition import Implicit from Acquisition import Implicit
from Products.PluginIndexes.common.ResultList import ResultList
from Products.PluginIndexes import PluggableIndex
from Products.PluginIndexes.common.util import parseIndexRequest
from OFS.SimpleItem import SimpleItem from OFS.SimpleItem import SimpleItem
from BTrees.IOBTree import IOBTree from BTrees.IOBTree import IOBTree
from BTrees.OIBTree import OIBTree from BTrees.IIBTree import IIBTree, IIBucket, IISet
from BTrees.IIBTree import IIBTree, IIBucket, IISet, IITreeSet
from BTrees.IIBTree import difference, weightedIntersection from BTrees.IIBTree import difference, weightedIntersection
from BTrees.OIBTree import OIBTree
from zope.interface import implements
from Products.PluginIndexes import PluggableIndex
from Products.PluginIndexes.common import safe_callable from Products.PluginIndexes.common import safe_callable
from Products.PluginIndexes.common.ResultList import ResultList
from Products.PluginIndexes.common.util import parseIndexRequest
from Products.PluginIndexes.interfaces import IPluggableIndex
from Products.PluginIndexes.interfaces import ITextIndex
from Lexicon import Lexicon from Lexicon import Lexicon
from types import *
from cgi import escape
class Op: class Op:
def __init__(self, name): def __init__(self, name):
...@@ -54,7 +56,9 @@ operator_dict = {'andnot': AndNot, 'and': And, 'or': Or, ...@@ -54,7 +56,9 @@ operator_dict = {'andnot': AndNot, 'and': And, 'or': Or,
'...': Near, 'near': Near, '...': Near, 'near': Near,
AndNot: AndNot, And: And, Or: Or, Near: Near} AndNot: AndNot, And: And, Or: Or, Near: Near}
class TextIndex(Persistent, Implicit, SimpleItem): class TextIndex(Persistent, Implicit, SimpleItem):
"""Full-text index. """Full-text index.
There is a ZCatalog UML model that sheds some light on what is There is a ZCatalog UML model that sheds some light on what is
...@@ -64,16 +68,17 @@ class TextIndex(Persistent, Implicit, SimpleItem): ...@@ -64,16 +68,17 @@ class TextIndex(Persistent, Implicit, SimpleItem):
{'bob' : {1 : 5, 2 : 3, 42 : 9}} {'bob' : {1 : 5, 2 : 3, 42 : 9}}
{'uncle' : {1 : 1}} {'uncle' : {1 : 1}}
The '_unindex' attribute is a mapping from document id to word The '_unindex' attribute is a mapping from document id to word
ids. This mapping allows the catalog to unindex an object: ids. This mapping allows the catalog to unindex an object:
{42 : ('bob', 'is', 'your', 'uncle') {42 : ('bob', 'is', 'your', 'uncle')
This isn't exactly how things are represented in memory, many This isn't exactly how things are represented in memory, many
optimizations happen along the way.""" optimizations happen along the way.
"""
__implements__ = (PluggableIndex.PluggableIndexInterface,) __implements__ = (PluggableIndex.PluggableIndexInterface,)
implements(ITextIndex, IPluggableIndex)
meta_type='TextIndex' meta_type='TextIndex'
...@@ -114,11 +119,9 @@ class TextIndex(Persistent, Implicit, SimpleItem): ...@@ -114,11 +119,9 @@ class TextIndex(Persistent, Implicit, SimpleItem):
self.call_methods = call_methods self.call_methods = call_methods
self.catalog = caller self.catalog = caller
# Default text index operator (should be visible to ZMI) # Default text index operator (should be visible to ZMI)
self.useOperator = 'or' self.useOperator = 'or'
if extra: self.vocabulary_id = extra.vocabulary if extra: self.vocabulary_id = extra.vocabulary
else: self.vocabulary_id = "Vocabulary" else: self.vocabulary_id = "Vocabulary"
...@@ -132,11 +135,12 @@ class TextIndex(Persistent, Implicit, SimpleItem): ...@@ -132,11 +135,12 @@ class TextIndex(Persistent, Implicit, SimpleItem):
self._lexicon = lexicon self._lexicon = lexicon
self.vocabulary_id = '__userdefined__' self.vocabulary_id = '__userdefined__'
def getId(self):
def getId(self): return self.id return self.id
def getLexicon(self, vocab_id=None): def getLexicon(self, vocab_id=None):
"""Return the Lexicon in use. Removed lots of stinking code""" """Get the Lexicon in use.
"""
if self._lexicon is None: if self._lexicon is None:
## if no lexicon is provided, create a default one ## if no lexicon is provided, create a default one
...@@ -152,12 +156,9 @@ class TextIndex(Persistent, Implicit, SimpleItem): ...@@ -152,12 +156,9 @@ class TextIndex(Persistent, Implicit, SimpleItem):
return self._lexicon return self._lexicon
def __nonzero__(self): def __nonzero__(self):
return not not self._unindex return not not self._unindex
def clear(self): def clear(self):
"""Reinitialize the text index.""" """Reinitialize the text index."""
self._index = IOBTree() self._index = IOBTree()
...@@ -186,7 +187,6 @@ class TextIndex(Persistent, Implicit, SimpleItem): ...@@ -186,7 +187,6 @@ class TextIndex(Persistent, Implicit, SimpleItem):
scores=IIBTree(scores) scores=IIBTree(scores)
return scores return scores
convert(_index, self._index, threshold, convertScores) convert(_index, self._index, threshold, convertScores)
_unindex=self._unindex _unindex=self._unindex
...@@ -205,7 +205,6 @@ class TextIndex(Persistent, Implicit, SimpleItem): ...@@ -205,7 +205,6 @@ class TextIndex(Persistent, Implicit, SimpleItem):
return histogram return histogram
def getEntryForObject(self, rid, default=None): def getEntryForObject(self, rid, default=None):
"""Get all information contained for a specific object. """Get all information contained for a specific object.
...@@ -219,7 +218,6 @@ class TextIndex(Persistent, Implicit, SimpleItem): ...@@ -219,7 +218,6 @@ class TextIndex(Persistent, Implicit, SimpleItem):
return tuple(map(self.getLexicon().getWord, return tuple(map(self.getLexicon().getWord,
results)) results))
def insertForwardIndexEntry(self, entry, documentId, score=1): def insertForwardIndexEntry(self, entry, documentId, score=1):
"""Uses the information provided to update the indexes. """Uses the information provided to update the indexes.
...@@ -303,7 +301,6 @@ class TextIndex(Persistent, Implicit, SimpleItem): ...@@ -303,7 +301,6 @@ class TextIndex(Persistent, Implicit, SimpleItem):
except (AttributeError, TypeError): except (AttributeError, TypeError):
encoding = 'latin1' encoding = 'latin1'
lexicon = self.getLexicon() lexicon = self.getLexicon()
splitter = lexicon.Splitter splitter = lexicon.Splitter
...@@ -444,7 +441,6 @@ class TextIndex(Persistent, Implicit, SimpleItem): ...@@ -444,7 +441,6 @@ class TextIndex(Persistent, Implicit, SimpleItem):
return r return r
def _apply_index(self, request, cid=''): def _apply_index(self, request, cid=''):
""" Apply the index to query parameters given in the argument, """ Apply the index to query parameters given in the argument,
request request
...@@ -500,7 +496,6 @@ class TextIndex(Persistent, Implicit, SimpleItem): ...@@ -500,7 +496,6 @@ class TextIndex(Persistent, Implicit, SimpleItem):
return (IIBucket(), (self.id,)) return (IIBucket(), (self.id,))
def positions(self, docid, words, def positions(self, docid, words,
# This was never tested: obj # This was never tested: obj
): ):
...@@ -513,14 +508,12 @@ class TextIndex(Persistent, Implicit, SimpleItem): ...@@ -513,14 +508,12 @@ class TextIndex(Persistent, Implicit, SimpleItem):
# The code below here is broken and requires an API change to fix # The code below here is broken and requires an API change to fix
# it. Waaaaa. # it. Waaaaa.
if self._schema is None: if self._schema is None:
f = getattr f = getattr
else: else:
f = operator.__getitem__ f = operator.__getitem__
id = self._schema[self.id] id = self._schema[self.id]
if self.call_methods: if self.call_methods:
doc = str(f(obj, self.id)()) doc = str(f(obj, self.id)())
else: else:
...@@ -531,8 +524,6 @@ class TextIndex(Persistent, Implicit, SimpleItem): ...@@ -531,8 +524,6 @@ class TextIndex(Persistent, Implicit, SimpleItem):
r = r+self.getLexicon().Splitter(doc).indexes(word) r = r+self.getLexicon().Splitter(doc).indexes(word)
return r return r
def query(self, s, default_operator=Or): def query(self, s, default_operator=Or):
""" Evaluate a query string. """ Evaluate a query string.
...@@ -566,7 +557,6 @@ class TextIndex(Persistent, Implicit, SimpleItem): ...@@ -566,7 +557,6 @@ class TextIndex(Persistent, Implicit, SimpleItem):
# evalute the final 'expression' # evalute the final 'expression'
return self.evaluate(q) return self.evaluate(q)
def get_operands(self, q, i): def get_operands(self, q, i):
"""Evaluate and return the left and right operands for an operator""" """Evaluate and return the left and right operands for an operator"""
try: try:
...@@ -593,8 +583,6 @@ class TextIndex(Persistent, Implicit, SimpleItem): ...@@ -593,8 +583,6 @@ class TextIndex(Persistent, Implicit, SimpleItem):
return (left, right) return (left, right)
def evaluate(self, query): def evaluate(self, query):
"""Evaluate a parsed query""" """Evaluate a parsed query"""
# Strip off meaningless layers # Strip off meaningless layers
...@@ -649,17 +637,14 @@ class TextIndex(Persistent, Implicit, SimpleItem): ...@@ -649,17 +637,14 @@ class TextIndex(Persistent, Implicit, SimpleItem):
""" return name of indexed attributes """ """ return name of indexed attributes """
return (self.id, ) return (self.id, )
def numObjects(self): def numObjects(self):
""" return number of index objects """ """ return number of index objects """
return len(self._index) return len(self._index)
def manage_setPreferences(self,vocabulary, def manage_setPreferences(self,vocabulary,
REQUEST=None,RESPONSE=None,URL2=None): REQUEST=None,RESPONSE=None,URL2=None):
""" preferences of TextIndex """ """ preferences of TextIndex """
if self.vocabulary_id != vocabulary: if self.vocabulary_id != vocabulary:
self.clear() self.clear()
self.vocabulary_id = vocabulary self.vocabulary_id = vocabulary
...@@ -667,10 +652,10 @@ class TextIndex(Persistent, Implicit, SimpleItem): ...@@ -667,10 +652,10 @@ class TextIndex(Persistent, Implicit, SimpleItem):
if RESPONSE: if RESPONSE:
RESPONSE.redirect(URL2 + '/manage_main?manage_tabs_message=Preferences%20saved') RESPONSE.redirect(URL2 + '/manage_main?manage_tabs_message=Preferences%20saved')
manage_workspace = DTMLFile("dtml/manageTextIndex",globals()) manage_workspace = DTMLFile("dtml/manageTextIndex",globals())
manage_vocabulary = DTMLFile("dtml/manageVocabulary",globals()) manage_vocabulary = DTMLFile("dtml/manageVocabulary",globals())
def parse(s): def parse(s):
"""Parse parentheses and quotes""" """Parse parentheses and quotes"""
l = [] l = []
...@@ -713,7 +698,6 @@ def parse2(q, default_operator, operator_dict=operator_dict): ...@@ -713,7 +698,6 @@ def parse2(q, default_operator, operator_dict=operator_dict):
return q return q
def parens(s, parens_re=re.compile('[()]').search): def parens(s, parens_re=re.compile('[()]').search):
mo = parens_re(s) mo = parens_re(s)
if mo is None: if mo is None:
...@@ -737,7 +721,6 @@ def parens(s, parens_re=re.compile('[()]').search): ...@@ -737,7 +721,6 @@ def parens(s, parens_re=re.compile('[()]').search):
raise QueryError, "Mismatched parentheses" raise QueryError, "Mismatched parentheses"
def quotes(s): def quotes(s):
if '"' not in s: if '"' not in s:
...@@ -766,7 +749,6 @@ def quotes(s): ...@@ -766,7 +749,6 @@ def quotes(s):
return filter(None, splitted) return filter(None, splitted)
manage_addTextIndexForm = DTMLFile('dtml/addTextIndex', globals()) manage_addTextIndexForm = DTMLFile('dtml/addTextIndex', globals())
def manage_addTextIndex(self, id, extra=None, REQUEST=None, RESPONSE=None, URL3=None): def manage_addTextIndex(self, id, extra=None, REQUEST=None, RESPONSE=None, URL3=None):
......
...@@ -7,16 +7,22 @@ ...@@ -7,16 +7,22 @@
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""ZCatalog product""" """Vocabulary for deprecated text index.
$Id$
"""
from Globals import DTMLFile, MessageDialog from Globals import DTMLFile, MessageDialog
import Globals, AccessControl.Role import Globals, AccessControl.Role
from Acquisition import Implicit from Acquisition import Implicit
from Persistence import Persistent from Persistence import Persistent
from OFS.SimpleItem import Item from OFS.SimpleItem import Item
from zope.interface import implements
from Products.PluginIndexes.interfaces import IVocabulary
from Products.PluginIndexes.TextIndex import Lexicon, GlobbingLexicon from Products.PluginIndexes.TextIndex import Lexicon, GlobbingLexicon
from Products.PluginIndexes.TextIndex.Lexicon import stop_word_dict from Products.PluginIndexes.TextIndex.Lexicon import stop_word_dict
from Products.PluginIndexes.TextIndex import Splitter from Products.PluginIndexes.TextIndex import Splitter
...@@ -39,18 +45,16 @@ def manage_addVocabulary(self, id, title, globbing=None, extra=None, ...@@ -39,18 +45,16 @@ def manage_addVocabulary(self, id, title, globbing=None, extra=None,
class _extra: pass class _extra: pass
class Vocabulary(Item, Persistent, Implicit, class Vocabulary(Item, Persistent, Implicit, AccessControl.Role.RoleManager):
AccessControl.Role.RoleManager,
):
"""
A Vocabulary is a user-managable realization of a Lexicon object.
"""A Vocabulary is a user-managable realization of a Lexicon object.
""" """
implements(IVocabulary)
meta_type = "Vocabulary" meta_type = "Vocabulary"
_isAVocabulary = 1 _isAVocabulary = 1
manage_options=( manage_options=(
( (
{'label': 'Vocabulary', 'action': 'manage_main', {'label': 'Vocabulary', 'action': 'manage_main',
...@@ -73,8 +77,6 @@ class Vocabulary(Item, Persistent, Implicit, ...@@ -73,8 +77,6 @@ class Vocabulary(Item, Persistent, Implicit,
['Anonymous', 'Manager']), ['Anonymous', 'Manager']),
) )
manage_main = DTMLFile('dtml/manage_vocab', globals()) manage_main = DTMLFile('dtml/manage_vocab', globals())
manage_query = DTMLFile('dtml/vocab_query', globals()) manage_query = DTMLFile('dtml/vocab_query', globals())
...@@ -115,7 +117,6 @@ class Vocabulary(Item, Persistent, Implicit, ...@@ -115,7 +117,6 @@ class Vocabulary(Item, Persistent, Implicit,
return str(result) return str(result)
def manage_insert(self, word='', URL1=None, RESPONSE=None): def manage_insert(self, word='', URL1=None, RESPONSE=None):
""" doc string """ """ doc string """
self.insert(word) self.insert(word)
......
...@@ -7,18 +7,25 @@ ...@@ -7,18 +7,25 @@
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""TextIndex unit tests.
$Id$
"""
import unittest
import Testing
import Zope2
Zope2.startup()
import sys, os, unittest
import zLOG import zLOG
def log_write(subsystem, severity, summary, detail, error): def log_write(subsystem, severity, summary, detail, error):
if severity >= zLOG.PROBLEM: if severity >= zLOG.PROBLEM:
assert 0, "%s(%s): %s" % (subsystem, severity, summary) assert 0, "%s(%s): %s" % (subsystem, severity, summary)
import ZODB import ZODB
from ZODB.MappingStorage import MappingStorage from ZODB.MappingStorage import MappingStorage
import transaction import transaction
...@@ -26,6 +33,7 @@ import transaction ...@@ -26,6 +33,7 @@ import transaction
from Products.PluginIndexes.TextIndex import TextIndex from Products.PluginIndexes.TextIndex import TextIndex
from Products.PluginIndexes.TextIndex import GlobbingLexicon from Products.PluginIndexes.TextIndex import GlobbingLexicon
class Dummy: class Dummy:
def __init__( self, text ): def __init__( self, text ):
...@@ -51,7 +59,6 @@ class Tests(unittest.TestCase): ...@@ -51,7 +59,6 @@ class Tests(unittest.TestCase):
self.old_log_write = zLOG.log_write self.old_log_write = zLOG.log_write
zLOG.log_write=log_write zLOG.log_write=log_write
def dbopen(self): def dbopen(self):
if self.db is None: if self.db is None:
s = MappingStorage() s = MappingStorage()
...@@ -79,14 +86,23 @@ class Tests(unittest.TestCase): ...@@ -79,14 +86,23 @@ class Tests(unittest.TestCase):
self.db = None self.db = None
zLOG.log_write=self.old_log_write zLOG.log_write=self.old_log_write
def checkSimpleAddDelete(self): def test_z3interfaces(self):
from Products.PluginIndexes.interfaces import IPluggableIndex
from Products.PluginIndexes.interfaces import ITextIndex
from Products.PluginIndexes.TextIndex.TextIndex import TextIndex
from zope.interface.verify import verifyClass
verifyClass(IPluggableIndex, TextIndex)
verifyClass(ITextIndex, TextIndex)
def test_SimpleAddDelete(self):
self.index.index_object(0, self.doc) self.index.index_object(0, self.doc)
self.index.index_object(1, self.doc) self.index.index_object(1, self.doc)
self.doc.text='spam is good, spam is fine, span span span' self.doc.text='spam is good, spam is fine, span span span'
self.index.index_object(0, self.doc) self.index.index_object(0, self.doc)
self.index.unindex_object(0) self.index.unindex_object(0)
def checkPersistentUpdate1(self): def test_PersistentUpdate1(self):
# Check simple persistent indexing # Check simple persistent indexing
index=self.dbopen() index=self.dbopen()
...@@ -112,7 +128,7 @@ class Tests(unittest.TestCase): ...@@ -112,7 +128,7 @@ class Tests(unittest.TestCase):
r=list(r[0].keys()) r=list(r[0].keys())
assert r == [0,1], r assert r == [0,1], r
def checkPersistentUpdate2(self): def test_PersistentUpdate2(self):
# Check less simple persistent indexing # Check less simple persistent indexing
index=self.dbopen() index=self.dbopen()
...@@ -146,8 +162,6 @@ class Tests(unittest.TestCase): ...@@ -146,8 +162,6 @@ class Tests(unittest.TestCase):
r=list(r[0].keys()) r=list(r[0].keys())
assert r == [0,1,2], r assert r == [0,1,2], r
sample_texts = [ sample_texts = [
"""This is the time for all good men to come to """This is the time for all good men to come to
the aid of their country""", the aid of their country""",
...@@ -178,95 +192,95 @@ class Tests(unittest.TestCase): ...@@ -178,95 +192,95 @@ class Tests(unittest.TestCase):
assert r == rlist, r assert r == rlist, r
return index._apply_index return index._apply_index
def checkStarQuery(self): def test_StarQuery(self):
self.globTest({'text':'m*n'}, [0,2]) self.globTest({'text':'m*n'}, [0,2])
def checkAndQuery(self): def test_AndQuery(self):
self.globTest({'text':'time and country'}, [0,]) self.globTest({'text':'time and country'}, [0,])
def checkOrQuery(self): def test_OrQuery(self):
self.globTest({'text':'time or country'}, [0,1,6]) self.globTest({'text':'time or country'}, [0,1,6])
def checkDefaultOrQuery(self): def test_DefaultOrQuery(self):
self.globTest({'text':'time country'}, [0,1,6]) self.globTest({'text':'time country'}, [0,1,6])
def checkNearQuery(self): def test_NearQuery(self):
# Check a NEAR query.. (NOTE:ACTUALLY AN 'AND' TEST!!) # Check a NEAR query.. (NOTE:ACTUALLY AN 'AND' TEST!!)
# NEAR never worked, so Zopes post-2.3.1b3 define near to mean AND # NEAR never worked, so Zopes post-2.3.1b3 define near to mean AND
self.globTest({'text':'time ... country'}, [0,]) self.globTest({'text':'time ... country'}, [0,])
def checkQuotesQuery(self): def test_QuotesQuery(self):
ai = self.globTest({'text':'"This is the time"'}, [0,]) ai = self.globTest({'text':'"This is the time"'}, [0,])
r = list(ai({'text':'"now is the time"'})[0].keys()) r = list(ai({'text':'"now is the time"'})[0].keys())
assert r == [], r assert r == [], r
def checkAndNotQuery(self): def test_AndNotQuery(self):
self.globTest({'text':'time and not country'}, [6,]) self.globTest({'text':'time and not country'}, [6,])
def checkParenMatchingQuery(self): def test_ParenMatchingQuery(self):
ai = self.globTest({'text':'(time and country) men'}, [0,]) ai = self.globTest({'text':'(time and country) men'}, [0,])
r = list(ai({'text':'(time and not country) or men'})[0].keys()) r = list(ai({'text':'(time and not country) or men'})[0].keys())
assert r == [0, 6], r assert r == [0, 6], r
def checkTextIndexOperatorQuery(self): def test_TextIndexOperatorQuery(self):
self.globTest({'text': {'query': 'time men', 'operator':'and'}}, [0,]) self.globTest({'text': {'query': 'time men', 'operator':'and'}}, [0,])
def checkNonExistentWord(self): def test_NonExistentWord(self):
self.globTest({'text':'zop'}, []) self.globTest({'text':'zop'}, [])
def checkComplexQuery1(self): def test_ComplexQuery1(self):
self.globTest({'text':'((?ount* or get) and not wait) ' self.globTest({'text':'((?ount* or get) and not wait) '
'"been *ert*"'}, [0, 1, 5, 6]) '"been *ert*"'}, [0, 1, 5, 6])
# same tests, unicode strings # same tests, unicode strings
def checkStarQueryUnicode(self): def test_StarQueryUnicode(self):
self.globTest({'text':u'm*n'}, [0,2]) self.globTest({'text':u'm*n'}, [0,2])
def checkAndQueryUnicode(self): def test_AndQueryUnicode(self):
self.globTest({'text':u'time and country'}, [0,]) self.globTest({'text':u'time and country'}, [0,])
def checkOrQueryUnicode(self): def test_OrQueryUnicode(self):
self.globTest({'text':u'time or country'}, [0,1,6]) self.globTest({'text':u'time or country'}, [0,1,6])
def checkDefaultOrQueryUnicode(self): def test_DefaultOrQueryUnicode(self):
self.globTest({'text':u'time country'}, [0,1,6]) self.globTest({'text':u'time country'}, [0,1,6])
def checkNearQueryUnicode(self): def test_NearQueryUnicode(self):
# Check a NEAR query.. (NOTE:ACTUALLY AN 'AND' TEST!!) (unicode) # Check a NEAR query.. (NOTE:ACTUALLY AN 'AND' TEST!!) (unicode)
# NEAR never worked, so Zopes post-2.3.1b3 define near to mean AND # NEAR never worked, so Zopes post-2.3.1b3 define near to mean AND
self.globTest({'text':u'time ... country'}, [0,]) self.globTest({'text':u'time ... country'}, [0,])
def checkQuotesQueryUnicode(self): def test_QuotesQueryUnicode(self):
ai = self.globTest({'text':u'"This is the time"'}, [0,]) ai = self.globTest({'text':u'"This is the time"'}, [0,])
r = list(ai({'text':'"now is the time"'})[0].keys()) r = list(ai({'text':'"now is the time"'})[0].keys())
assert r == [], r assert r == [], r
def checkAndNotQueryUnicode(self): def test_AndNotQueryUnicode(self):
self.globTest({'text':u'time and not country'}, [6,]) self.globTest({'text':u'time and not country'}, [6,])
def checkParenMatchingQueryUnicode(self): def test_ParenMatchingQueryUnicode(self):
ai = self.globTest({'text':u'(time and country) men'}, [0,]) ai = self.globTest({'text':u'(time and country) men'}, [0,])
r = list(ai({'text':u'(time and not country) or men'})[0].keys()) r = list(ai({'text':u'(time and not country) or men'})[0].keys())
assert r == [0, 6], r assert r == [0, 6], r
def checkTextIndexOperatorQueryUnicode(self): def test_TextIndexOperatorQueryUnicode(self):
self.globTest({'text': {u'query': u'time men', 'operator':'and'}}, self.globTest({'text': {u'query': u'time men', 'operator':'and'}},
[0,]) [0,])
def checkNonExistentWordUnicode(self): def test_NonExistentWordUnicode(self):
self.globTest({'text':u'zop'}, []) self.globTest({'text':u'zop'}, [])
def checkComplexQuery1Unicode(self): def test_ComplexQuery1Unicode(self):
self.globTest({'text':u'((?ount* or get) and not wait) ' self.globTest({'text':u'((?ount* or get) and not wait) '
'"been *ert*"'}, [0, 1, 5, 6]) '"been *ert*"'}, [0, 1, 5, 6])
def test_suite(): def test_suite():
return unittest.makeSuite(Tests, 'check') return unittest.makeSuite(Tests)
if __name__=='__main__': if __name__=='__main__':
unittest.main(defaultTest='test_suite') unittest.main(defaultTest='test_suite')
...@@ -7,11 +7,13 @@ ...@@ -7,11 +7,13 @@
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Filtered set.
__version__ = '$Id$' $Id$
"""
import sys import sys
from logging import getLogger from logging import getLogger
...@@ -19,12 +21,18 @@ from logging import getLogger ...@@ -19,12 +21,18 @@ from logging import getLogger
from ZODB.POSException import ConflictError from ZODB.POSException import ConflictError
from BTrees.IIBTree import IITreeSet from BTrees.IIBTree import IITreeSet
from Persistence import Persistent from Persistence import Persistent
from RestrictedPython.Eval import RestrictionCapableEval from RestrictedPython.Eval import RestrictionCapableEval
from zope.interface import implements
from Products.PluginIndexes.interfaces import IFilteredSet
LOG = getLogger('Zope.TopicIndex.FilteredSet') LOG = getLogger('Zope.TopicIndex.FilteredSet')
class FilteredSetBase(Persistent): class FilteredSetBase(Persistent):
# A pre-calculated result list based on an expression.
implements(IFilteredSet)
def __init__(self, id, expr): def __init__(self, id, expr):
self.id = id self.id = id
...@@ -43,17 +51,21 @@ class FilteredSetBase(Persistent): ...@@ -43,17 +51,21 @@ class FilteredSetBase(Persistent):
def getId(self): def getId(self):
return self.id return self.id
def getExpression(self): def getExpression(self):
# Get the expression.
return self.expr return self.expr
def getIds(self): def getIds(self):
# Get the IDs of all objects for which the expression is True.
return self.ids return self.ids
def getType(self): def getType(self):
return self.meta_type return self.meta_type
def setExpression(self, expr): self.expr = expr def setExpression(self, expr):
# Set the expression.
self.expr = expr
def __repr__(self): def __repr__(self):
return '%s: (%s) %s' % (self.id,self.expr,map(None,self.ids)) return '%s: (%s) %s' % (self.id,self.expr,map(None,self.ids))
...@@ -67,7 +79,7 @@ class PythonFilteredSet(FilteredSetBase): ...@@ -67,7 +79,7 @@ class PythonFilteredSet(FilteredSetBase):
def index_object(self, documentId, o): def index_object(self, documentId, o):
try: try:
if RestrictionCapableEval(self.expr).eval({'o': o}): if RestrictionCapableEval(self.expr).eval({'o': o}):
self.ids.insert(documentId) self.ids.insert(documentId)
else: else:
try: try:
......
...@@ -7,11 +7,13 @@ ...@@ -7,11 +7,13 @@
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Topic index.
__version__ = '$Id$' $Id$
"""
from logging import getLogger from logging import getLogger
...@@ -19,23 +21,29 @@ from Globals import Persistent, DTMLFile ...@@ -19,23 +21,29 @@ from Globals import Persistent, DTMLFile
from OFS.SimpleItem import SimpleItem from OFS.SimpleItem import SimpleItem
from BTrees.OOBTree import OOBTree from BTrees.OOBTree import OOBTree
from BTrees.IIBTree import IITreeSet,intersection,union from BTrees.IIBTree import IITreeSet,intersection,union
from zope.interface import implements
import FilteredSet
from Products.PluginIndexes import PluggableIndex from Products.PluginIndexes import PluggableIndex
from Products.PluginIndexes.common.util import parseIndexRequest from Products.PluginIndexes.common.util import parseIndexRequest
from Products.PluginIndexes.interfaces import IPluggableIndex
from Products.PluginIndexes.interfaces import ITopicIndex
import FilteredSet
_marker = [] _marker = []
LOG = getLogger('Zope.TopicIndex') LOG = getLogger('Zope.TopicIndex')
class TopicIndex(Persistent, SimpleItem): class TopicIndex(Persistent, SimpleItem):
""" A TopicIndex maintains a set of FilteredSet objects. """A TopicIndex maintains a set of FilteredSet objects.
Every FilteredSet object consists of an expression and
and IISet with all Ids of indexed objects that eval with Every FilteredSet object consists of an expression and and IISet with all
this expression to 1. Ids of indexed objects that eval with this expression to 1.
""" """
__implements__ = (PluggableIndex.PluggableIndexInterface,) __implements__ = (PluggableIndex.PluggableIndexInterface,)
implements(ITopicIndex, IPluggableIndex)
meta_type="TopicIndex" meta_type="TopicIndex"
query_options = ('query','operator') query_options = ('query','operator')
...@@ -52,7 +60,8 @@ class TopicIndex(Persistent, SimpleItem): ...@@ -52,7 +60,8 @@ class TopicIndex(Persistent, SimpleItem):
self.operators = ('or','and') self.operators = ('or','and')
self.defaultOperator = 'or' self.defaultOperator = 'or'
def getId(self): return self.id def getId(self):
return self.id
def clear(self): def clear(self):
for fs in self.filteredSets.values(): for fs in self.filteredSets.values():
...@@ -111,25 +120,27 @@ class TopicIndex(Persistent, SimpleItem): ...@@ -111,25 +120,27 @@ class TopicIndex(Persistent, SimpleItem):
def getEntryForObject(self,docid, default=_marker): def getEntryForObject(self,docid, default=_marker):
""" Takes a document ID and returns all the information we have """ Takes a document ID and returns all the information we have
on that specific object. on that specific object.
""" """
return self.filteredSets.keys() return self.filteredSets.keys()
def addFilteredSet(self, filter_id, typeFilteredSet, expr): def addFilteredSet(self, filter_id, typeFilteredSet, expr):
# Add a FilteredSet object.
if self.filteredSets.has_key(filter_id): if self.filteredSets.has_key(filter_id):
raise KeyError,\ raise KeyError,\
'A FilteredSet with this name already exists: %s' % filter_id 'A FilteredSet with this name already exists: %s' % filter_id
self.filteredSets[filter_id] = \ self.filteredSets[filter_id] = \
FilteredSet.factory(filter_id, typeFilteredSet, expr) FilteredSet.factory(filter_id, typeFilteredSet, expr)
def delFilteredSet(self,filter_id): def delFilteredSet(self, filter_id):
# Delete the FilteredSet object specified by 'filter_id'.
if not self.filteredSets.has_key(filter_id): if not self.filteredSets.has_key(filter_id):
raise KeyError,\ raise KeyError,\
'no such FilteredSet: %s' % filter_id 'no such FilteredSet: %s' % filter_id
del self.filteredSets[filter_id] del self.filteredSets[filter_id]
def clearFilteredSet(self,filter_id): def clearFilteredSet(self, filter_id):
# Clear the FilteredSet object specified by 'filter_id'.
if not self.filteredSets.has_key(filter_id): if not self.filteredSets.has_key(filter_id):
raise KeyError,\ raise KeyError,\
'no such FilteredSet: %s' % filter_id 'no such FilteredSet: %s' % filter_id
...@@ -184,7 +195,6 @@ class TopicIndex(Persistent, SimpleItem): ...@@ -184,7 +195,6 @@ class TopicIndex(Persistent, SimpleItem):
RESPONSE.redirect(URL1+'/manage_workspace?' RESPONSE.redirect(URL1+'/manage_workspace?'
'manage_tabs_message=FilteredSet(s)%20cleared') 'manage_tabs_message=FilteredSet(s)%20cleared')
index_html = DTMLFile('dtml/index', globals()) index_html = DTMLFile('dtml/index', globals())
manage_workspace = DTMLFile('dtml/manageTopicIndex',globals()) manage_workspace = DTMLFile('dtml/manageTopicIndex',globals())
editFilteredSet = DTMLFile('dtml/editFilteredSet',globals()) editFilteredSet = DTMLFile('dtml/editFilteredSet',globals())
......
...@@ -7,15 +7,22 @@ ...@@ -7,15 +7,22 @@
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""TopicIndex unit tests.
$Id$
"""
import unittest import unittest
import Testing
import Zope2
Zope2.startup()
import ZODB
from Products.PluginIndexes.TopicIndex.TopicIndex import TopicIndex from Products.PluginIndexes.TopicIndex.TopicIndex import TopicIndex
class Obj: class Obj:
def __init__(self,id,meta_type=''): def __init__(self,id,meta_type=''):
...@@ -25,6 +32,7 @@ class Obj: ...@@ -25,6 +32,7 @@ class Obj:
def getId(self): return self.id def getId(self): return self.id
def getPhysicalPath(self): return self.id def getPhysicalPath(self): return self.id
class TestBase(unittest.TestCase): class TestBase(unittest.TestCase):
def _searchAnd(self,query,expected): def _searchAnd(self,query,expected):
...@@ -41,6 +49,7 @@ class TestBase(unittest.TestCase): ...@@ -41,6 +49,7 @@ class TestBase(unittest.TestCase):
self.assertEqual(rows,expected,query) self.assertEqual(rows,expected,query)
return rows return rows
class TestTopicIndex(TestBase): class TestTopicIndex(TestBase):
def setUp(self): def setUp(self):
...@@ -56,6 +65,13 @@ class TestTopicIndex(TestBase): ...@@ -56,6 +65,13 @@ class TestTopicIndex(TestBase):
self.TI.index_object(5 , Obj('5','doc3')) self.TI.index_object(5 , Obj('5','doc3'))
self.TI.index_object(6 , Obj('6','doc3')) self.TI.index_object(6 , Obj('6','doc3'))
def test_z3interfaces(self):
from Products.PluginIndexes.interfaces import ITopicIndex
from Products.PluginIndexes.interfaces import IPluggableIndex
from zope.interface.verify import verifyClass
verifyClass(ITopicIndex, TopicIndex)
verifyClass(IPluggableIndex, TopicIndex)
def testOr(self): def testOr(self):
self._searchOr('doc1',[1,2]) self._searchOr('doc1',[1,2])
...@@ -64,28 +80,23 @@ class TestTopicIndex(TestBase): ...@@ -64,28 +80,23 @@ class TestTopicIndex(TestBase):
self._searchOr(['doc2'],[3,4]) self._searchOr(['doc2'],[3,4])
self._searchOr(['doc1','doc2'], [1,2,3,4]) self._searchOr(['doc1','doc2'], [1,2,3,4])
def testAnd(self): def testAnd(self):
self._searchAnd('doc1',[1,2]) self._searchAnd('doc1',[1,2])
self._searchAnd(['doc1'],[1,2]) self._searchAnd(['doc1'],[1,2])
self._searchAnd('doc2',[3,4]) self._searchAnd('doc2',[3,4])
self._searchAnd(['doc2'],[3,4]) self._searchAnd(['doc2'],[3,4])
self._searchAnd(['doc1','doc2'],[]) self._searchAnd(['doc1','doc2'],[])
def testRemoval(self): def testRemoval(self):
self.TI.index_object(1, Obj('1','doc2')) self.TI.index_object(1, Obj('1','doc2'))
self._searchOr('doc1',[2]) self._searchOr('doc1',[2])
self._searchOr('doc2', [1,3,4]) self._searchOr('doc2', [1,3,4])
def test_suite():
return unittest.TestSuite( ( def test_suite():
return unittest.TestSuite((
unittest.makeSuite(TestTopicIndex), unittest.makeSuite(TestTopicIndex),
)) ))
def main():
unittest.TextTestRunner().run(test_suite())
if __name__ == '__main__': if __name__ == '__main__':
main() unittest.main(defaultTest='test_suite')
...@@ -4,33 +4,47 @@ ...@@ -4,33 +4,47 @@
# #
# This software is subject to the provisions of the Zope Public License, # 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. # 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 ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Base for bi-directional indexes """Base for bi-directional indexes.
$Id$""" $Id$
"""
import sys import sys
from cgi import escape from cgi import escape
from logging import getLogger from logging import getLogger
from BTrees.OOBTree import OOBTree
from BTrees.IOBTree import IOBTree
from BTrees.IIBTree import IITreeSet, IISet, union, intersection from BTrees.IIBTree import IITreeSet, IISet, union, intersection
from OFS.SimpleItem import SimpleItem from BTrees.IOBTree import IOBTree
import BTrees.Length import BTrees.Length
from BTrees.OOBTree import OOBTree
from OFS.SimpleItem import SimpleItem
from zope.interface import implements
from Products.PluginIndexes.common.util import parseIndexRequest from Products.PluginIndexes import PluggableIndex
from Products.PluginIndexes.common import safe_callable from Products.PluginIndexes.common import safe_callable
from Products.PluginIndexes.common.util import parseIndexRequest
from Products.PluginIndexes.interfaces import IPluggableIndex
from Products.PluginIndexes.interfaces import ISortIndex
from Products.PluginIndexes.interfaces import IUniqueValueIndex
_marker = [] _marker = []
LOG = getLogger('Zope.UnIndex') LOG = getLogger('Zope.UnIndex')
class UnIndex(SimpleItem): class UnIndex(SimpleItem):
"""Simple forward and reverse index"""
"""Simple forward and reverse index.
"""
__implements__ = (PluggableIndex.UniqueValueIndex,
PluggableIndex.SortIndex)
implements(IPluggableIndex, IUniqueValueIndex, ISortIndex)
def __init__( def __init__(
self, id, ignore_ex=None, call_methods=None, extra=None, caller=None): self, id, ignore_ex=None, call_methods=None, extra=None, caller=None):
...@@ -93,7 +107,8 @@ class UnIndex(SimpleItem): ...@@ -93,7 +107,8 @@ class UnIndex(SimpleItem):
self.indexed_attrs = ia.split(',') self.indexed_attrs = ia.split(',')
else: else:
self.indexed_attrs = list(ia) self.indexed_attrs = list(ia)
self.indexed_attrs = [ attr.strip() for attr in self.indexed_attrs if attr ] self.indexed_attrs = [ attr.strip()
for attr in self.indexed_attrs if attr ]
if not self.indexed_attrs: if not self.indexed_attrs:
self.indexed_attrs = [id] self.indexed_attrs = [id]
...@@ -133,7 +148,6 @@ class UnIndex(SimpleItem): ...@@ -133,7 +148,6 @@ class UnIndex(SimpleItem):
"""Generate a list of IDs for which we have referenced objects.""" """Generate a list of IDs for which we have referenced objects."""
return self._unindex.keys() return self._unindex.keys()
def getEntryForObject(self, documentId, default=_marker): def getEntryForObject(self, documentId, default=_marker):
"""Takes a document ID and returns all the information we have """Takes a document ID and returns all the information we have
on that specific object. on that specific object.
...@@ -143,7 +157,6 @@ class UnIndex(SimpleItem): ...@@ -143,7 +157,6 @@ class UnIndex(SimpleItem):
else: else:
return self._unindex.get(documentId, default) return self._unindex.get(documentId, default)
def removeForwardIndexEntry(self, entry, documentId): def removeForwardIndexEntry(self, entry, documentId):
"""Take the entry provided and remove any reference to documentId """Take the entry provided and remove any reference to documentId
in its entry in the index. in its entry in the index.
...@@ -173,7 +186,6 @@ class UnIndex(SimpleItem): ...@@ -173,7 +186,6 @@ class UnIndex(SimpleItem):
'should not happen.' % (self.__class__.__name__, 'should not happen.' % (self.__class__.__name__,
repr(entry), str(self.id))) repr(entry), str(self.id)))
def insertForwardIndexEntry(self, entry, documentId): def insertForwardIndexEntry(self, entry, documentId):
"""Take the entry provided and put it in the correct place """Take the entry provided and put it in the correct place
in the forward index. in the forward index.
...@@ -194,7 +206,6 @@ class UnIndex(SimpleItem): ...@@ -194,7 +206,6 @@ class UnIndex(SimpleItem):
indexRow=IITreeSet((indexRow, documentId)) indexRow=IITreeSet((indexRow, documentId))
self._index[entry] = indexRow self._index[entry] = indexRow
def index_object(self, documentId, obj, threshold=None): def index_object(self, documentId, obj, threshold=None):
""" wrapper to handle indexing of multiple attributes """ """ wrapper to handle indexing of multiple attributes """
...@@ -208,7 +219,6 @@ class UnIndex(SimpleItem): ...@@ -208,7 +219,6 @@ class UnIndex(SimpleItem):
return res > 0 return res > 0
def _index_object(self, documentId, obj, threshold=None, attr=''): def _index_object(self, documentId, obj, threshold=None, attr=''):
""" index and object 'obj' with integer id 'documentId'""" """ index and object 'obj' with integer id 'documentId'"""
returnStatus = 0 returnStatus = 0
...@@ -319,7 +329,6 @@ class UnIndex(SimpleItem): ...@@ -319,7 +329,6 @@ class UnIndex(SimpleItem):
r = None r = None
opr = None opr = None
# experimental code for specifing the operator # experimental code for specifing the operator
operator = record.get('operator',self.useOperator) operator = record.get('operator',self.useOperator)
if not operator in self.operators : if not operator in self.operators :
...@@ -339,13 +348,11 @@ class UnIndex(SimpleItem): ...@@ -339,13 +348,11 @@ class UnIndex(SimpleItem):
if range_parm.find("max")>-1: if range_parm.find("max")>-1:
opr_args.append("max") opr_args.append("max")
if record.get('usage',None): if record.get('usage',None):
# see if any usage params are sent to field # see if any usage params are sent to field
opr = record.usage.lower().split(':') opr = record.usage.lower().split(':')
opr, opr_args=opr[0], opr[1:] opr, opr_args=opr[0], opr[1:]
if opr=="range": # range search if opr=="range": # range search
if 'min' in opr_args: lo = min(record.keys) if 'min' in opr_args: lo = min(record.keys)
else: lo = None else: lo = None
......
##############################################################################
#
# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
#
# 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.
#
##############################################################################
"""PluginIndexes z3 interfaces.
$Id$
"""
from zope.interface import Interface
from zope.schema import Bool
# create IPluggableIndex, IUniqueValueIndex, ISortIndex
from Products.Five.fiveconfigure import createZope2Bridge
from common.PluggableIndex import PluggableIndexInterface
from common.PluggableIndex import SortIndex
from common.PluggableIndex import UniqueValueIndex
import interfaces
createZope2Bridge(PluggableIndexInterface, interfaces, 'IPluggableIndex')
createZope2Bridge(SortIndex, interfaces, 'ISortIndex')
createZope2Bridge(UniqueValueIndex, interfaces, 'IUniqueValueIndex')
del createZope2Bridge
del PluggableIndexInterface
del SortIndex
del UniqueValueIndex
del interfaces
class IDateIndex(Interface):
"""Index for dates.
"""
index_naive_time_as_local = Bool(title=u'Index naive time as local?')
class IDateRangeIndex(Interface):
"""Index for date ranges, such as the "effective-expiration" range in CMF.
Any object may return None for either the start or the end date: for the
start date, this should be the logical equivalent of "since the beginning
of time"; for the end date, "until the end of time".
Therefore, divide the space of indexed objects into four containers:
- Objects which always match (i.e., they returned None for both);
- Objects which match after a given time (i.e., they returned None for the
end date);
- Objects which match until a given time (i.e., they returned None for the
start date);
- Objects which match only during a specific interval.
"""
def getSinceField():
"""Get the name of the attribute indexed as start date.
"""
def getUntilField():
"""Get the name of the attribute indexed as end date.
"""
class IPathIndex(Interface):
"""Index for paths returned by getPhysicalPath.
A path index stores all path components of the physical path of an object.
Internal datastructure:
- a physical path of an object is split into its components
- 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 docids with this path component on this level'
"""
class IVocabulary(Interface):
"""A Vocabulary is a user-managable realization of a Lexicon object.
"""
class ITextIndex(Interface):
"""Full-text index.
There is a ZCatalog UML model that sheds some light on what is
going on here. '_index' is a BTree which maps word ids to mapping
from document id to score. Something like:
{'bob' : {1 : 5, 2 : 3, 42 : 9}}
{'uncle' : {1 : 1}}
The '_unindex' attribute is a mapping from document id to word
ids. This mapping allows the catalog to unindex an object:
{42 : ('bob', 'is', 'your', 'uncle')
This isn't exactly how things are represented in memory, many
optimizations happen along the way.
"""
def getLexicon(vocab_id=None):
"""Get the Lexicon in use.
"""
class IFilteredSet(Interface):
"""A pre-calculated result list based on an expression.
"""
def getExpression():
"""Get the expression.
"""
def getIds():
"""Get the IDs of all objects for which the expression is True.
"""
def setExpression(expr):
"""Set the expression.
"""
class ITopicIndex(Interface):
"""A TopicIndex maintains a set of FilteredSet objects.
Every FilteredSet object consists of an expression and and IISet with all
Ids of indexed objects that eval with this expression to 1.
"""
def addFilteredSet(filter_id, typeFilteredSet, expr):
"""Add a FilteredSet object.
"""
def delFilteredSet(filter_id):
"""Delete the FilteredSet object specified by 'filter_id'.
"""
def clearFilteredSet(filter_id):
"""Clear the FilteredSet object specified by 'filter_id'.
"""
...@@ -8,29 +8,30 @@ ...@@ -8,29 +8,30 @@
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Plug in text index for ZCatalog with relevance ranking.
"""Plug in text index for ZCatalog with relevance ranking.""" $Id$
"""
from cgi import escape from cgi import escape
from types import TupleType
import ZODB
from Persistence import Persistent from Persistence import Persistent
import Acquisition import Acquisition
from Acquisition import aq_base, aq_inner, aq_parent from Acquisition import aq_base, aq_inner, aq_parent
from OFS.SimpleItem import SimpleItem from OFS.SimpleItem import SimpleItem
from Globals import DTMLFile, InitializeClass from Globals import DTMLFile, InitializeClass
from AccessControl.SecurityInfo import ClassSecurityInfo from AccessControl.SecurityInfo import ClassSecurityInfo
from AccessControl.Permissions import manage_zcatalog_indexes, search_zcatalog from AccessControl.Permissions import manage_zcatalog_indexes, search_zcatalog
from zope.interface import implements
from Products.PluginIndexes.common.PluggableIndex import \ from Products.PluginIndexes.common.PluggableIndex import \
PluggableIndexInterface PluggableIndexInterface
from Products.PluginIndexes.common.util import parseIndexRequest from Products.PluginIndexes.common.util import parseIndexRequest
from Products.PluginIndexes.common import safe_callable from Products.PluginIndexes.common import safe_callable
from Products.PluginIndexes.interfaces import IPluggableIndex
from Products.ZCTextIndex.ILexicon import ILexicon from Products.ZCTextIndex.ILexicon import ILexicon
from Products.ZCTextIndex.Lexicon import \ from Products.ZCTextIndex.Lexicon import \
...@@ -38,16 +39,23 @@ from Products.ZCTextIndex.Lexicon import \ ...@@ -38,16 +39,23 @@ from Products.ZCTextIndex.Lexicon import \
from Products.ZCTextIndex.NBest import NBest from Products.ZCTextIndex.NBest import NBest
from Products.ZCTextIndex.QueryParser import QueryParser from Products.ZCTextIndex.QueryParser import QueryParser
from PipelineFactory import element_factory from PipelineFactory import element_factory
from interfaces import IZCLexicon
from interfaces import IZCTextIndex
from Products.ZCTextIndex.CosineIndex import CosineIndex from Products.ZCTextIndex.CosineIndex import CosineIndex
from Products.ZCTextIndex.OkapiIndex import OkapiIndex from Products.ZCTextIndex.OkapiIndex import OkapiIndex
index_types = {'Okapi BM25 Rank':OkapiIndex, index_types = {'Okapi BM25 Rank':OkapiIndex,
'Cosine Measure':CosineIndex} 'Cosine Measure':CosineIndex}
class ZCTextIndex(Persistent, Acquisition.Implicit, SimpleItem): class ZCTextIndex(Persistent, Acquisition.Implicit, SimpleItem):
"""Persistent TextIndex"""
"""Persistent text index.
"""
__implements__ = PluggableIndexInterface __implements__ = PluggableIndexInterface
implements(IZCTextIndex, IPluggableIndex)
## Magic class attributes ## ## Magic class attributes ##
...@@ -72,7 +80,8 @@ class ZCTextIndex(Persistent, Acquisition.Implicit, SimpleItem): ...@@ -72,7 +80,8 @@ class ZCTextIndex(Persistent, Acquisition.Implicit, SimpleItem):
# via the silly "extra" record. # via the silly "extra" record.
self._fieldname = field_name or getattr(extra, 'doc_attr', '') or id self._fieldname = field_name or getattr(extra, 'doc_attr', '') or id
self._indexed_attrs = self._fieldname.split(',') self._indexed_attrs = self._fieldname.split(',')
self._indexed_attrs = [ attr.strip() for attr in self._indexed_attrs if attr ] self._indexed_attrs = [ attr.strip()
for attr in self._indexed_attrs if attr ]
lexicon_id = lexicon_id or getattr(extra, 'lexicon_id', '') lexicon_id = lexicon_id or getattr(extra, 'lexicon_id', '')
lexicon = getattr(caller, lexicon_id, None) lexicon = getattr(caller, lexicon_id, None)
...@@ -254,7 +263,7 @@ class ZCTextIndex(Persistent, Acquisition.Implicit, SimpleItem): ...@@ -254,7 +263,7 @@ class ZCTextIndex(Persistent, Acquisition.Implicit, SimpleItem):
def getIndexSourceNames(self): def getIndexSourceNames(self):
"""Return sequence of names of indexed attributes""" """Return sequence of names of indexed attributes"""
try: try:
return self._indexed_attrs return self._indexed_attrs
except: except:
return [self._fieldname] return [self._fieldname]
...@@ -270,7 +279,6 @@ class ZCTextIndex(Persistent, Acquisition.Implicit, SimpleItem): ...@@ -270,7 +279,6 @@ class ZCTextIndex(Persistent, Acquisition.Implicit, SimpleItem):
return None return None
else: else:
return lex.absolute_url() return lex.absolute_url()
InitializeClass(ZCTextIndex) InitializeClass(ZCTextIndex)
...@@ -314,8 +322,13 @@ def manage_addLexicon(self, id, title='', elements=[], REQUEST=None): ...@@ -314,8 +322,13 @@ def manage_addLexicon(self, id, title='', elements=[], REQUEST=None):
LexiconQueryPerm = 'Query Vocabulary' LexiconQueryPerm = 'Query Vocabulary'
LexiconMgmtPerm = 'Manage Vocabulary' LexiconMgmtPerm = 'Manage Vocabulary'
class PLexicon(Lexicon, Acquisition.Implicit, SimpleItem): class PLexicon(Lexicon, Acquisition.Implicit, SimpleItem):
"""Lexicon for ZCTextIndex"""
"""Lexicon for ZCTextIndex.
"""
implements(IZCLexicon)
meta_type = 'ZCTextIndex Lexicon' meta_type = 'ZCTextIndex Lexicon'
......
##############################################################################
#
# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
#
# 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.
#
##############################################################################
"""ZCTextIndex z3 interfaces.
$Id$
"""
from zope.interface import Interface
class IZCTextIndex(Interface):
"""Persistent text index.
"""
class IZCLexicon(Interface):
"""Lexicon for ZCTextIndex.
"""
...@@ -11,14 +11,21 @@ ...@@ -11,14 +11,21 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""ZCTextIndex unit tests.
$Id$
"""
import unittest
import Testing
import Zope2
Zope2.startup()
import re
from Interface.Verify import verifyClass
import Acquisition import Acquisition
from zExceptions import NotFound from zExceptions import NotFound
from Products.PluginIndexes.common.PluggableIndex import \
PluggableIndexInterface
from Products.ZCTextIndex.ZCTextIndex import ZCTextIndex, PLexicon from Products.ZCTextIndex.ZCTextIndex import ZCTextIndex, PLexicon
from Products.ZCTextIndex.tests import \ from Products.ZCTextIndex.tests import \
testIndex, testQueryEngine, testQueryParser testIndex, testQueryEngine, testQueryParser
...@@ -32,8 +39,6 @@ from Products.ZCTextIndex.QueryParser import QueryParser ...@@ -32,8 +39,6 @@ from Products.ZCTextIndex.QueryParser import QueryParser
from Products.ZCTextIndex.StopDict import get_stopdict from Products.ZCTextIndex.StopDict import get_stopdict
from Products.ZCTextIndex.ParseTree import ParseError from Products.ZCTextIndex.ParseTree import ParseError
import re
import unittest
class Indexable: class Indexable:
def __init__(self, text): def __init__(self, text):
...@@ -250,9 +255,21 @@ class CosineIndexTests(ZCIndexTestsBase, testIndex.CosineIndexTest): ...@@ -250,9 +255,21 @@ class CosineIndexTests(ZCIndexTestsBase, testIndex.CosineIndexTest):
# Gigabytes, pp. 180-188. This test peeks into many internals of the # Gigabytes, pp. 180-188. This test peeks into many internals of the
# cosine indexer. # cosine indexer.
def testInterface(self): def test_z2interfaces(self):
from Interface.Verify import verifyClass
from Products.PluginIndexes.common.PluggableIndex \
import PluggableIndexInterface
verifyClass(PluggableIndexInterface, ZCTextIndex) verifyClass(PluggableIndexInterface, ZCTextIndex)
def test_z3interfaces(self):
from Products.PluginIndexes.interfaces import IPluggableIndex
from Products.ZCTextIndex.interfaces import IZCTextIndex
from zope.interface.verify import verifyClass
verifyClass(IPluggableIndex, ZCTextIndex)
verifyClass(IZCTextIndex, ZCTextIndex)
def testRanking(self): def testRanking(self):
self.words = ["cold", "days", "eat", "hot", "lot", "nine", "old", self.words = ["cold", "days", "eat", "hot", "lot", "nine", "old",
"pease", "porridge", "pot"] "pease", "porridge", "pot"]
...@@ -548,18 +565,28 @@ class QueryTestsBase(testQueryEngine.TestQueryEngine, ...@@ -548,18 +565,28 @@ class QueryTestsBase(testQueryEngine.TestQueryEngine,
dictkeys.sort() dictkeys.sort()
self.assertEqual(setkeys, dictkeys) self.assertEqual(setkeys, dictkeys)
class CosineQueryTests(QueryTestsBase): class CosineQueryTests(QueryTestsBase):
IndexFactory = CosineIndex IndexFactory = CosineIndex
class OkapiQueryTests(QueryTestsBase): class OkapiQueryTests(QueryTestsBase):
IndexFactory = OkapiIndex IndexFactory = OkapiIndex
############################################################################
class PLexiconTests(unittest.TestCase):
def test_z3interfaces(self):
from Products.ZCTextIndex.interfaces import IZCLexicon
from zope.interface.verify import verifyClass
verifyClass(IZCLexicon, PLexicon)
def test_suite(): def test_suite():
s = unittest.TestSuite() s = unittest.TestSuite()
for klass in (CosineIndexTests, OkapiIndexTests, for klass in (CosineIndexTests, OkapiIndexTests,
CosineQueryTests, OkapiQueryTests): CosineQueryTests, OkapiQueryTests, PLexiconTests):
s.addTest(unittest.makeSuite(klass)) s.addTest(unittest.makeSuite(klass))
return s return s
......
...@@ -16,11 +16,10 @@ $Id$ ...@@ -16,11 +16,10 @@ $Id$
""" """
from warnings import warn from warnings import warn
import urllib, time, sys, string,logging import urllib, time, sys, string, logging
from Globals import DTMLFile, MessageDialog from Globals import DTMLFile, MessageDialog
import Globals import Globals
from OFS.Folder import Folder from OFS.Folder import Folder
from OFS.ObjectManager import ObjectManager from OFS.ObjectManager import ObjectManager
from DateTime import DateTime from DateTime import DateTime
...@@ -29,19 +28,23 @@ from Persistence import Persistent ...@@ -29,19 +28,23 @@ from Persistence import Persistent
from DocumentTemplate.DT_Util import InstanceDict, TemplateDict from DocumentTemplate.DT_Util import InstanceDict, TemplateDict
from DocumentTemplate.DT_Util import Eval from DocumentTemplate.DT_Util import Eval
from AccessControl.Permission import name_trans from AccessControl.Permission import name_trans
from Catalog import Catalog, CatalogError
from AccessControl.DTML import RestrictedDTML from AccessControl.DTML import RestrictedDTML
from AccessControl.Permissions import \ from AccessControl.Permissions import \
manage_zcatalog_entries, manage_zcatalog_indexes, search_zcatalog manage_zcatalog_entries, manage_zcatalog_indexes, search_zcatalog
from ZCatalogIndexes import ZCatalogIndexes
from ZODB.POSException import ConflictError from ZODB.POSException import ConflictError
import transaction import transaction
from Products.PluginIndexes.common.PluggableIndex \ from Products.PluginIndexes.common.PluggableIndex \
import PluggableIndexInterface import PluggableIndexInterface
from Products.PluginIndexes.TextIndex import Splitter from Products.PluginIndexes.TextIndex import Splitter
from IZCatalog import IZCatalog from zLOG import LOG
from zope.interface import implements
from Catalog import Catalog, CatalogError
from interfaces import IZCatalog as z3IZCatalog
from IZCatalog import IZCatalog as z2IZCatalog
from ProgressHandler import ZLogHandler from ProgressHandler import ZLogHandler
from zLOG import LOG, INFO from ZCatalogIndexes import ZCatalogIndexes
LOG = logging.getLogger('Zope.ZCatalog') LOG = logging.getLogger('Zope.ZCatalog')
...@@ -79,7 +82,8 @@ class ZCatalog(Folder, Persistent, Implicit): ...@@ -79,7 +82,8 @@ class ZCatalog(Folder, Persistent, Implicit):
Python program to catalog objects. Python program to catalog objects.
""" """
__implements__ = IZCatalog __implements__ = z2IZCatalog
implements(z3IZCatalog)
meta_type = "ZCatalog" meta_type = "ZCatalog"
icon='misc_/ZCatalog/ZCatalog.gif' icon='misc_/ZCatalog/ZCatalog.gif'
......
##############################################################################
#
# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
#
# 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.
#
##############################################################################
"""ZCatalog z3 interfaces.
$Id$
"""
# create IZCatalog
from Products.Five.fiveconfigure import createZope2Bridge
from IZCatalog import IZCatalog as z2IZCatalog
import interfaces
createZope2Bridge(z2IZCatalog, interfaces, 'IZCatalog')
del createZope2Bridge
del z2IZCatalog
del interfaces
...@@ -12,14 +12,13 @@ ...@@ -12,14 +12,13 @@
############################################################################## ##############################################################################
""" Unittests for Catalog. """ Unittests for Catalog.
$Id:$ $Id$
""" """
import unittest import unittest
import Testing import Testing
import Zope2 import Zope2
Zope2.startup() Zope2.startup()
from Interface.Verify import verifyClass
from itertools import chain from itertools import chain
import random import random
...@@ -162,6 +161,7 @@ class zdummyFalse(zdummy): ...@@ -162,6 +161,7 @@ class zdummyFalse(zdummy):
class TestZCatalog(unittest.TestCase): class TestZCatalog(unittest.TestCase):
def setUp(self): def setUp(self):
from Products.ZCatalog.ZCatalog import ZCatalog from Products.ZCatalog.ZCatalog import ZCatalog
self._catalog = ZCatalog('Catalog') self._catalog = ZCatalog('Catalog')
...@@ -181,6 +181,20 @@ class TestZCatalog(unittest.TestCase): ...@@ -181,6 +181,20 @@ class TestZCatalog(unittest.TestCase):
def _resolve_num(self, num): def _resolve_num(self, num):
return self.d[num] return self.d[num]
def test_z2interfaces(self):
from Interface.Verify import verifyClass
from Products.ZCatalog.IZCatalog import IZCatalog
from Products.ZCatalog.ZCatalog import ZCatalog
verifyClass(IZCatalog, ZCatalog)
def test_z3interfaces(self):
from Products.ZCatalog.interfaces import IZCatalog
from Products.ZCatalog.ZCatalog import ZCatalog
from zope.interface.verify import verifyClass
verifyClass(IZCatalog, ZCatalog)
def testGetMetadataForUID(self): def testGetMetadataForUID(self):
testNum = str(self.upper - 3) # as good as any.. testNum = str(self.upper - 3) # as good as any..
data = self._catalog.getMetadataForUID(testNum) data = self._catalog.getMetadataForUID(testNum)
...@@ -232,12 +246,6 @@ class TestZCatalog(unittest.TestCase): ...@@ -232,12 +246,6 @@ class TestZCatalog(unittest.TestCase):
result = self._catalog(title='9999') result = self._catalog(title='9999')
self.assertEquals(1, len(result)) self.assertEquals(1, len(result))
def test_interface(self):
from Products.ZCatalog.IZCatalog import IZCatalog
from Products.ZCatalog.ZCatalog import ZCatalog
verifyClass(IZCatalog, ZCatalog)
class dummy(ExtensionClass.Base): class dummy(ExtensionClass.Base):
att1 = 'att1' att1 = 'att1'
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment