[Checkins] SVN: zope.app.catalog/tags/3.6.0/ Tag 3.6.0
Dan Korostelev
nadako at gmail.com
Sat Jan 3 10:41:02 EST 2009
Log message for revision 94479:
Tag 3.6.0
Changed:
A zope.app.catalog/tags/3.6.0/
D zope.app.catalog/tags/3.6.0/CHANGES.txt
A zope.app.catalog/tags/3.6.0/CHANGES.txt
D zope.app.catalog/tags/3.6.0/setup.py
A zope.app.catalog/tags/3.6.0/setup.py
D zope.app.catalog/tags/3.6.0/src/zope/app/catalog/README.txt
A zope.app.catalog/tags/3.6.0/src/zope/app/catalog/README.txt
D zope.app.catalog/tags/3.6.0/src/zope/app/catalog/browser/configure.zcml
A zope.app.catalog/tags/3.6.0/src/zope/app/catalog/browser/configure.zcml
D zope.app.catalog/tags/3.6.0/src/zope/app/catalog/catalog.py
A zope.app.catalog/tags/3.6.0/src/zope/app/catalog/catalog.py
D zope.app.catalog/tags/3.6.0/src/zope/app/catalog/classes.zcml
A zope.app.catalog/tags/3.6.0/src/zope/app/catalog/classes.zcml
D zope.app.catalog/tags/3.6.0/src/zope/app/catalog/interfaces.py
A zope.app.catalog/tags/3.6.0/src/zope/app/catalog/interfaces.py
A zope.app.catalog/tags/3.6.0/src/zope/app/catalog/keyword.py
D zope.app.catalog/tags/3.6.0/src/zope/app/catalog/text.py
A zope.app.catalog/tags/3.6.0/src/zope/app/catalog/text.py
-=-
Deleted: zope.app.catalog/tags/3.6.0/CHANGES.txt
===================================================================
--- zope.app.catalog/trunk/CHANGES.txt 2009-01-03 15:21:36 UTC (rev 94476)
+++ zope.app.catalog/tags/3.6.0/CHANGES.txt 2009-01-03 15:41:02 UTC (rev 94479)
@@ -1,48 +0,0 @@
-=======
-CHANGES
-=======
-
-3.6.0 (unreleased)
-------------------
-
-- ...
-
-3.5.2 (2008-12-28)
-------------------
-
-- Remove testing dependencies from install_requires.
-
-3.5.1 (2007-10-31)
-------------------
-
-- Resolve ``ZopeSecurityPolicy`` deprecation warning.
-
-
-3.5.0 (2007-10-11)
-------------------
-
-- Updated some meta-data.
-
-- Move ``ftests.py`` to ``tests.py``.
-
-
-3.5.0a3 (2007-09-27)
---------------------
-
-- removed some deprecations
-
-
-3.5.0a2 (2007-09-21)
---------------------
-
-- bugfix: passing the context to getAllUtilitiesRegisteredFor in all
- eventhandlers because no catalog was found which was located in a
- sub site and for example the ObjectModifiesEvent get fired from somewhere
- in the root.
-
-
-3.5.0a1 (2007-06-26)
---------------------
-
-- Added marker interfaces to prevent automatic indexing (see: ``event.txt``)
-
Copied: zope.app.catalog/tags/3.6.0/CHANGES.txt (from rev 94477, zope.app.catalog/trunk/CHANGES.txt)
===================================================================
--- zope.app.catalog/tags/3.6.0/CHANGES.txt (rev 0)
+++ zope.app.catalog/tags/3.6.0/CHANGES.txt 2009-01-03 15:41:02 UTC (rev 94479)
@@ -0,0 +1,59 @@
+=======
+CHANGES
+=======
+
+3.6.0 (2009-01-03)
+------------------
+
+- Make TextIndex addform use default values as specified in
+ zope.app.catalog.text.ITextIndex interface. Also, change
+ "searchableText" to "getSearchableText" there, as it's the
+ right value.
+
+- Add Keyword (case-insensitive and case-sensitive) catalog
+ indices. It's now possible to use them, because ones in
+ zope.index now implement IIndexSearch interface.
+
+- Add support for sorting, reversing and limiting result set
+ in the ``searchResults`` method, using new IIndexSort interface
+ features of zope.index.
+
+3.5.2 (2008-12-28)
+------------------
+
+- Remove testing dependencies from install_requires.
+
+3.5.1 (2007-10-31)
+------------------
+
+- Resolve ``ZopeSecurityPolicy`` deprecation warning.
+
+
+3.5.0 (2007-10-11)
+------------------
+
+- Updated some meta-data.
+
+- Move ``ftests.py`` to ``tests.py``.
+
+
+3.5.0a3 (2007-09-27)
+--------------------
+
+- removed some deprecations
+
+
+3.5.0a2 (2007-09-21)
+--------------------
+
+- bugfix: passing the context to getAllUtilitiesRegisteredFor in all
+ eventhandlers because no catalog was found which was located in a
+ sub site and for example the ObjectModifiesEvent get fired from somewhere
+ in the root.
+
+
+3.5.0a1 (2007-06-26)
+--------------------
+
+- Added marker interfaces to prevent automatic indexing (see: ``event.txt``)
+
Deleted: zope.app.catalog/tags/3.6.0/setup.py
===================================================================
--- zope.app.catalog/trunk/setup.py 2009-01-03 15:21:36 UTC (rev 94476)
+++ zope.app.catalog/tags/3.6.0/setup.py 2009-01-03 15:41:02 UTC (rev 94479)
@@ -1,81 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2006 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.
-#
-##############################################################################
-"""Setup for zope.app.catalog package
-
-$Id$
-"""
-import os
-from setuptools import setup, find_packages
-
-def read(*rnames):
- return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
-
-setup(name = 'zope.app.catalog',
- version = '3.6.0dev',
- author='Zope Corporation and Contributors',
- author_email='zope3-dev at zope.org',
- description='Zope Cataloging and Indexing Framework',
- long_description=(
- read('README.txt')
- + '\n\n' +
- 'Detailed Documentation\n'
- '**********************\n'
- + '\n\n' +
- read('src', 'zope', 'app', 'catalog', 'README.txt')
- + '\n\n' +
- read('src', 'zope', 'app', 'catalog', 'event.txt')
- + '\n\n' +
- read('CHANGES.txt')
- ),
- keywords = "zope3 catalog index",
- classifiers = [
- 'Development Status :: 5 - Production/Stable',
- 'Environment :: Web Environment',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: Zope Public License',
- 'Programming Language :: Python',
- 'Natural Language :: English',
- 'Operating System :: OS Independent',
- 'Topic :: Internet :: WWW/HTTP',
- 'Framework :: Zope3'],
- url='http://cheeseshop.python.org/pypi/zope.app.catalog',
- license='ZPL 2.1',
- packages=find_packages('src'),
- package_dir = {'': 'src'},
- namespace_packages=['zope', 'zope.app'],
- extras_require = dict(
- test=['zope.testing',
- 'zope.app.component',
- 'zope.app.testing',
- 'zope.app.securitypolicy',
- 'zope.app.zcmlfiles',
- 'zope.app.zptpage',
- ]),
- install_requires = [
- 'setuptools',
- 'ZODB3',
- 'zope.annotation',
- 'zope.app.container',
- 'zope.app.intid',
- 'zope.component',
- 'zope.index',
- 'zope.interface',
- 'zope.lifecycleevent',
- 'zope.location',
- 'zope.schema',
- 'zope.traversing',
- ],
- include_package_data = True,
- zip_safe = False,
- )
Copied: zope.app.catalog/tags/3.6.0/setup.py (from rev 94477, zope.app.catalog/trunk/setup.py)
===================================================================
--- zope.app.catalog/tags/3.6.0/setup.py (rev 0)
+++ zope.app.catalog/tags/3.6.0/setup.py 2009-01-03 15:41:02 UTC (rev 94479)
@@ -0,0 +1,81 @@
+##############################################################################
+#
+# Copyright (c) 2006 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.
+#
+##############################################################################
+"""Setup for zope.app.catalog package
+
+$Id$
+"""
+import os
+from setuptools import setup, find_packages
+
+def read(*rnames):
+ return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+setup(name = 'zope.app.catalog',
+ version = '3.6.0',
+ author='Zope Corporation and Contributors',
+ author_email='zope-dev at zope.org',
+ description='Zope Cataloging and Indexing Framework',
+ long_description=(
+ read('README.txt')
+ + '\n\n' +
+ 'Detailed Documentation\n'
+ '**********************\n'
+ + '\n\n' +
+ read('src', 'zope', 'app', 'catalog', 'README.txt')
+ + '\n\n' +
+ read('src', 'zope', 'app', 'catalog', 'event.txt')
+ + '\n\n' +
+ read('CHANGES.txt')
+ ),
+ keywords = "zope3 catalog index",
+ classifiers = [
+ 'Development Status :: 5 - Production/Stable',
+ 'Environment :: Web Environment',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: Zope Public License',
+ 'Programming Language :: Python',
+ 'Natural Language :: English',
+ 'Operating System :: OS Independent',
+ 'Topic :: Internet :: WWW/HTTP',
+ 'Framework :: Zope3'],
+ url='http://pypi.python.org/pypi/zope.app.catalog',
+ license='ZPL 2.1',
+ packages=find_packages('src'),
+ package_dir = {'': 'src'},
+ namespace_packages=['zope', 'zope.app'],
+ extras_require = dict(
+ test=['zope.testing',
+ 'zope.app.component',
+ 'zope.app.testing',
+ 'zope.app.securitypolicy',
+ 'zope.app.zcmlfiles',
+ 'zope.app.zptpage',
+ ]),
+ install_requires = [
+ 'setuptools',
+ 'ZODB3',
+ 'zope.annotation',
+ 'zope.app.container',
+ 'zope.app.intid',
+ 'zope.component',
+ 'zope.index>=3.5.0',
+ 'zope.interface',
+ 'zope.lifecycleevent',
+ 'zope.location',
+ 'zope.schema',
+ 'zope.traversing',
+ ],
+ include_package_data = True,
+ zip_safe = False,
+ )
Deleted: zope.app.catalog/tags/3.6.0/src/zope/app/catalog/README.txt
===================================================================
--- zope.app.catalog/trunk/src/zope/app/catalog/README.txt 2009-01-03 15:21:36 UTC (rev 94476)
+++ zope.app.catalog/tags/3.6.0/src/zope/app/catalog/README.txt 2009-01-03 15:41:02 UTC (rev 94479)
@@ -1,305 +0,0 @@
-Catalogs
-========
-
-Catalogs provide management of collections of related indexes with a
-basic search algorithm. Let's look at an example:
-
- >>> from zope.app.catalog.catalog import Catalog
- >>> cat = Catalog()
-
-We can add catalog indexes to catalogs. A catalog index is, among
-other things, an attribute index. It indexes attributes of objects. To
-see how this works, we'll create a demonstration attribute index. Our
-attribute index will simply keep track of objects that have a given
-attribute value. The `catalog` package provides an attribute-index
-mix-in class that is meant to work with a base indexing class. First,
-we'll write the base index class:
-
- >>> import persistent, BTrees.OOBTree, BTrees.IFBTree, BTrees.IOBTree
- >>> import zope.interface, zope.index.interfaces
-
- >>> class BaseIndex(persistent.Persistent):
- ... zope.interface.implements(
- ... zope.index.interfaces.IInjection,
- ... zope.index.interfaces.IIndexSearch,
- ... )
- ...
- ... def clear(self):
- ... self.forward = BTrees.OOBTree.OOBTree()
- ... self.backward = BTrees.IOBTree.IOBTree()
- ...
- ... __init__ = clear
- ...
- ... def index_doc(self, docid, value):
- ... if docid in self.backward:
- ... self.unindex_doc(docid)
- ... self.backward[docid] = value
- ...
- ... set = self.forward.get(value)
- ... if set is None:
- ... set = BTrees.IFBTree.IFTreeSet()
- ... self.forward[value] = set
- ... set.insert(docid)
- ...
- ... def unindex_doc(self, docid):
- ... value = self.backward.get(docid)
- ... if value is None:
- ... return
- ... self.forward[value].remove(docid)
- ... del self.backward[docid]
- ...
- ... def apply(self, value):
- ... set = self.forward.get(value)
- ... if set is None:
- ... set = BTrees.IFBTree.IFTreeSet()
- ... return set
-
-The class implements `IInjection` to allow values to be indexed and
-unindexed and `IIndexSearch` to support searching via the `apply`
-method.
-
-Now, we can use the AttributeIndex mixin to make this an attribute
-index:
-
- >>> import zope.app.catalog.attribute
- >>> import zope.app.container.contained
- >>> import zope.app.catalog.interfaces
-
- >>> class Index(zope.app.catalog.attribute.AttributeIndex,
- ... BaseIndex,
- ... zope.app.container.contained.Contained,
- ... ):
- ... zope.interface.implements(zope.app.catalog.interfaces.ICatalogIndex)
-
-Unfortunately, because of the way we currently handle containment
-constraints, we have to provide `ICatalogIndex`, which extends
-`IContained`. We subclass `Contained` to get an implementation for
-`IContained`.
-
-Now let's add some of these indexes to our catalog. Let's create some
-indexes. First we'll define some interfaces providing data to index:
-
- >>> class IFavoriteColor(zope.interface.Interface):
- ... color = zope.interface.Attribute("Favorite color")
-
- >>> class IPerson(zope.interface.Interface):
- ... def age():
- ... """Return the person's age, in years"""
-
-We'll create color and age indexes:
-
- >>> cat['color'] = Index('color', IFavoriteColor)
- >>> cat['age'] = Index('age', IPerson, True)
- >>> cat['size'] = Index('sz')
-
-The indexes are created with:
-
-- the name of the of the attribute to index
-
-- the interface defining the attribute, and
-
-- a flag indicating whether the attribute should be called, which
- defaults to false.
-
-If an interface is provided, then we'll only be able to index an
-object if it can be adapted to the interface, otherwise, we'll simply
-try to get the attribute from the object. If the attribute isn't
-present, then we'll ignore the object.
-
-Now, let's create some objects and index them:
-
- >>> class Person:
- ... zope.interface.implements(IPerson)
- ... def __init__(self, age):
- ... self._age = age
- ... def age(self):
- ... return self._age
-
- >>> class Discriminating:
- ... zope.interface.implements(IFavoriteColor)
- ... def __init__(self, color):
- ... self.color = color
-
- >>> class DiscriminatingPerson(Discriminating, Person):
- ... def __init__(self, age, color):
- ... Discriminating.__init__(self, color)
- ... Person.__init__(self, age)
-
- >>> class Whatever:
- ... def __init__(self, **kw):
- ... self.__dict__.update(kw)
-
- >>> o1 = Person(10)
- >>> o2 = DiscriminatingPerson(20, 'red')
- >>> o3 = Discriminating('blue')
- >>> o4 = Whatever(a=10, c='blue', sz=5)
- >>> o5 = Whatever(a=20, c='red', sz=6)
- >>> o6 = DiscriminatingPerson(10, 'blue')
-
- >>> cat.index_doc(1, o1)
- >>> cat.index_doc(2, o2)
- >>> cat.index_doc(3, o3)
- >>> cat.index_doc(4, o4)
- >>> cat.index_doc(5, o5)
- >>> cat.index_doc(6, o6)
-
-We search by providing query mapping objects that have a key for every
-index we want to use:
-
- >>> list(cat.apply({'age': 10}))
- [1, 6]
- >>> list(cat.apply({'age': 10, 'color': 'blue'}))
- [6]
- >>> list(cat.apply({'age': 10, 'color': 'blue', 'size': 5}))
- []
- >>> list(cat.apply({'size': 5}))
- [4]
-
-We can unindex objects:
-
- >>> cat.unindex_doc(4)
- >>> list(cat.apply({'size': 5}))
- []
-
-and reindex objects:
-
- >>> o5.sz = 5
- >>> cat.index_doc(5, o5)
- >>> list(cat.apply({'size': 5}))
- [5]
-
-If we clear the catalog, we'll clear all of the indexes:
-
- >>> cat.clear()
- >>> [len(index.forward) for index in cat.values()]
- [0, 0, 0]
-
-Note that you don't have to use the catalog's search methods. You can
-access its indexes directly, since the catalog is a mapping:
-
- >>> [(name, cat[name].field_name) for name in cat]
- [(u'age', 'age'), (u'color', 'color'), (u'size', 'sz')]
-
-Catalogs work with int-id utilities, which are responsible for
-maintaining id <-> object mappings. To see how this works, we'll
-create a utility to work with our catalog:
-
- >>> import zope.app.intid.interfaces
- >>> class Ids:
- ... zope.interface.implements(zope.app.intid.interfaces.IIntIds)
- ... def __init__(self, data):
- ... self.data = data
- ... def getObject(self, id):
- ... return self.data[id]
- ... def __iter__(self):
- ... return self.data.iterkeys()
- >>> ids = Ids({1: o1, 2: o2, 3: o3, 4: o4, 5: o5, 6: o6})
-
- >>> from zope.app.testing import ztapi
- >>> ztapi.provideUtility(zope.app.intid.interfaces.IIntIds, ids)
-
-With this utility in place, catalogs can recompute indexes:
-
- >>> cat.updateIndex(cat['size'])
- >>> list(cat.apply({'size': 5}))
- [4, 5]
-
-Of course, that only updates *that* index:
-
- >>> list(cat.apply({'age': 10}))
- []
-
-We can update all of the indexes:
-
- >>> cat.updateIndexes()
- >>> list(cat.apply({'age': 10}))
- [1, 6]
- >>> list(cat.apply({'color': 'red'}))
- [2]
-
-
-There's an alternate search interface that returns "result sets".
-Result sets provide access to objects, rather than object ids:
-
- >>> result = cat.searchResults(size=5)
- >>> len(result)
- 2
- >>> list(result) == [o4, o5]
- True
-
-The index example we looked at didn't provide document scores. Simple
-indexes normally don't, but more complex indexes might give results
-scores, according to how closely a document matches a query. Let's
-create a new index, a "keyword index" that indexes sequences of
-values:
-
- >>> class BaseKeywordIndex(persistent.Persistent):
- ... zope.interface.implements(
- ... zope.index.interfaces.IInjection,
- ... zope.index.interfaces.IIndexSearch,
- ... )
- ...
- ... def clear(self):
- ... self.forward = BTrees.OOBTree.OOBTree()
- ... self.backward = BTrees.IOBTree.IOBTree()
- ...
- ... __init__ = clear
- ...
- ... def index_doc(self, docid, values):
- ... if docid in self.backward:
- ... self.unindex_doc(docid)
- ... self.backward[docid] = values
- ...
- ... for value in values:
- ... set = self.forward.get(value)
- ... if set is None:
- ... set = BTrees.IFBTree.IFTreeSet()
- ... self.forward[value] = set
- ... set.insert(docid)
- ...
- ... def unindex_doc(self, docid):
- ... values = self.backward.get(docid)
- ... if values is None:
- ... return
- ... for value in values:
- ... self.forward[value].remove(docid)
- ... del self.backward[docid]
- ...
- ... def apply(self, values):
- ... result = BTrees.IFBTree.IFBucket()
- ... for value in values:
- ... set = self.forward.get(value)
- ... if set is not None:
- ... _, result = BTrees.IFBTree.weightedUnion(result, set)
- ... return result
-
- >>> class KeywordIndex(zope.app.catalog.attribute.AttributeIndex,
- ... BaseKeywordIndex,
- ... zope.app.container.contained.Contained,
- ... ):
- ... zope.interface.implements(zope.app.catalog.interfaces.ICatalogIndex)
-
-Now, we'll add a hobbies index:
-
- >>> cat['hobbies'] = KeywordIndex('hobbies')
- >>> o1.hobbies = 'camping', 'music'
- >>> o2.hobbies = 'hacking', 'sailing'
- >>> o3.hobbies = 'music', 'camping', 'sailing'
- >>> o6.hobbies = 'cooking', 'dancing'
- >>> cat.updateIndexes()
-
-When we apply the catalog:
-
- >>> cat.apply({'hobbies': ['music', 'camping', 'sailing']})
- BTrees.IFBTree.IFBucket([(1, 2.0), (2, 1.0), (3, 3.0)])
-
-We found objects 1-3, because they each contained at least some of the
-words in the query. The scores represent the number of words that
-matched. If we also include age:
-
- >>> cat.apply({'hobbies': ['music', 'camping', 'sailing'], 'age': 10})
- BTrees.IFBTree.IFBucket([(1, 3.0)])
-
-The score increased because we used an additional index. If an index
-doesn't provide scores, scores of 1.0 are assumed.
-
Copied: zope.app.catalog/tags/3.6.0/src/zope/app/catalog/README.txt (from rev 94477, zope.app.catalog/trunk/src/zope/app/catalog/README.txt)
===================================================================
--- zope.app.catalog/tags/3.6.0/src/zope/app/catalog/README.txt (rev 0)
+++ zope.app.catalog/tags/3.6.0/src/zope/app/catalog/README.txt 2009-01-03 15:41:02 UTC (rev 94479)
@@ -0,0 +1,360 @@
+Catalogs
+========
+
+Catalogs provide management of collections of related indexes with a
+basic search algorithm. Let's look at an example:
+
+ >>> from zope.app.catalog.catalog import Catalog
+ >>> cat = Catalog()
+
+We can add catalog indexes to catalogs. A catalog index is, among
+other things, an attribute index. It indexes attributes of objects. To
+see how this works, we'll create a demonstration attribute index. Our
+attribute index will simply keep track of objects that have a given
+attribute value. The `catalog` package provides an attribute-index
+mix-in class that is meant to work with a base indexing class. First,
+we'll write the base index class:
+
+ >>> import persistent, BTrees.OOBTree, BTrees.IFBTree, BTrees.IOBTree
+ >>> import zope.interface, zope.index.interfaces
+
+ >>> class BaseIndex(persistent.Persistent):
+ ... zope.interface.implements(
+ ... zope.index.interfaces.IInjection,
+ ... zope.index.interfaces.IIndexSearch,
+ ... zope.index.interfaces.IIndexSort,
+ ... )
+ ...
+ ... def clear(self):
+ ... self.forward = BTrees.OOBTree.OOBTree()
+ ... self.backward = BTrees.IOBTree.IOBTree()
+ ...
+ ... __init__ = clear
+ ...
+ ... def index_doc(self, docid, value):
+ ... if docid in self.backward:
+ ... self.unindex_doc(docid)
+ ... self.backward[docid] = value
+ ...
+ ... set = self.forward.get(value)
+ ... if set is None:
+ ... set = BTrees.IFBTree.IFTreeSet()
+ ... self.forward[value] = set
+ ... set.insert(docid)
+ ...
+ ... def unindex_doc(self, docid):
+ ... value = self.backward.get(docid)
+ ... if value is None:
+ ... return
+ ... self.forward[value].remove(docid)
+ ... del self.backward[docid]
+ ...
+ ... def apply(self, value):
+ ... set = self.forward.get(value)
+ ... if set is None:
+ ... set = BTrees.IFBTree.IFTreeSet()
+ ... return set
+ ...
+ ... def sort(self, docids, limit=None, reverse=False):
+ ... for i, docid in enumerate(sorted(docids, key=self.backward.get, reverse=reverse)):
+ ... yield docid
+ ... if limit and i >= (limit - 1):
+ ... break
+
+The class implements `IInjection` to allow values to be indexed and
+unindexed and `IIndexSearch` to support searching via the `apply`
+method.
+
+Now, we can use the AttributeIndex mixin to make this an attribute
+index:
+
+ >>> import zope.app.catalog.attribute
+ >>> import zope.app.container.contained
+ >>> import zope.app.catalog.interfaces
+
+ >>> class Index(zope.app.catalog.attribute.AttributeIndex,
+ ... BaseIndex,
+ ... zope.app.container.contained.Contained,
+ ... ):
+ ... zope.interface.implements(zope.app.catalog.interfaces.ICatalogIndex)
+
+Unfortunately, because of the way we currently handle containment
+constraints, we have to provide `ICatalogIndex`, which extends
+`IContained`. We subclass `Contained` to get an implementation for
+`IContained`.
+
+Now let's add some of these indexes to our catalog. Let's create some
+indexes. First we'll define some interfaces providing data to index:
+
+ >>> class IFavoriteColor(zope.interface.Interface):
+ ... color = zope.interface.Attribute("Favorite color")
+
+ >>> class IPerson(zope.interface.Interface):
+ ... def age():
+ ... """Return the person's age, in years"""
+
+We'll create color and age indexes:
+
+ >>> cat['color'] = Index('color', IFavoriteColor)
+ >>> cat['age'] = Index('age', IPerson, True)
+ >>> cat['size'] = Index('sz')
+
+The indexes are created with:
+
+- the name of the of the attribute to index
+
+- the interface defining the attribute, and
+
+- a flag indicating whether the attribute should be called, which
+ defaults to false.
+
+If an interface is provided, then we'll only be able to index an
+object if it can be adapted to the interface, otherwise, we'll simply
+try to get the attribute from the object. If the attribute isn't
+present, then we'll ignore the object.
+
+Now, let's create some objects and index them:
+
+ >>> class Person:
+ ... zope.interface.implements(IPerson)
+ ... def __init__(self, age):
+ ... self._age = age
+ ... def age(self):
+ ... return self._age
+
+ >>> class Discriminating:
+ ... zope.interface.implements(IFavoriteColor)
+ ... def __init__(self, color):
+ ... self.color = color
+
+ >>> class DiscriminatingPerson(Discriminating, Person):
+ ... def __init__(self, age, color):
+ ... Discriminating.__init__(self, color)
+ ... Person.__init__(self, age)
+
+ >>> class Whatever:
+ ... def __init__(self, **kw):
+ ... self.__dict__.update(kw)
+
+ >>> o1 = Person(10)
+ >>> o2 = DiscriminatingPerson(20, 'red')
+ >>> o3 = Discriminating('blue')
+ >>> o4 = Whatever(a=10, c='blue', sz=5)
+ >>> o5 = Whatever(a=20, c='red', sz=6)
+ >>> o6 = DiscriminatingPerson(10, 'blue')
+
+ >>> cat.index_doc(1, o1)
+ >>> cat.index_doc(2, o2)
+ >>> cat.index_doc(3, o3)
+ >>> cat.index_doc(4, o4)
+ >>> cat.index_doc(5, o5)
+ >>> cat.index_doc(6, o6)
+
+We search by providing query mapping objects that have a key for every
+index we want to use:
+
+ >>> list(cat.apply({'age': 10}))
+ [1, 6]
+ >>> list(cat.apply({'age': 10, 'color': 'blue'}))
+ [6]
+ >>> list(cat.apply({'age': 10, 'color': 'blue', 'size': 5}))
+ []
+ >>> list(cat.apply({'size': 5}))
+ [4]
+
+We can unindex objects:
+
+ >>> cat.unindex_doc(4)
+ >>> list(cat.apply({'size': 5}))
+ []
+
+and reindex objects:
+
+ >>> o5.sz = 5
+ >>> cat.index_doc(5, o5)
+ >>> list(cat.apply({'size': 5}))
+ [5]
+
+If we clear the catalog, we'll clear all of the indexes:
+
+ >>> cat.clear()
+ >>> [len(index.forward) for index in cat.values()]
+ [0, 0, 0]
+
+Note that you don't have to use the catalog's search methods. You can
+access its indexes directly, since the catalog is a mapping:
+
+ >>> [(name, cat[name].field_name) for name in cat]
+ [(u'age', 'age'), (u'color', 'color'), (u'size', 'sz')]
+
+Catalogs work with int-id utilities, which are responsible for
+maintaining id <-> object mappings. To see how this works, we'll
+create a utility to work with our catalog:
+
+ >>> import zope.app.intid.interfaces
+ >>> class Ids:
+ ... zope.interface.implements(zope.app.intid.interfaces.IIntIds)
+ ... def __init__(self, data):
+ ... self.data = data
+ ... def getObject(self, id):
+ ... return self.data[id]
+ ... def __iter__(self):
+ ... return self.data.iterkeys()
+ >>> ids = Ids({1: o1, 2: o2, 3: o3, 4: o4, 5: o5, 6: o6})
+
+ >>> from zope.app.testing import ztapi
+ >>> ztapi.provideUtility(zope.app.intid.interfaces.IIntIds, ids)
+
+With this utility in place, catalogs can recompute indexes:
+
+ >>> cat.updateIndex(cat['size'])
+ >>> list(cat.apply({'size': 5}))
+ [4, 5]
+
+Of course, that only updates *that* index:
+
+ >>> list(cat.apply({'age': 10}))
+ []
+
+We can update all of the indexes:
+
+ >>> cat.updateIndexes()
+ >>> list(cat.apply({'age': 10}))
+ [1, 6]
+ >>> list(cat.apply({'color': 'red'}))
+ [2]
+
+
+There's an alternate search interface that returns "result sets".
+Result sets provide access to objects, rather than object ids:
+
+ >>> result = cat.searchResults(size=5)
+ >>> len(result)
+ 2
+ >>> list(result) == [o4, o5]
+ True
+
+The searchResults method also provides a way to sort, limit and reverse
+results.
+
+When not using sorting, limiting and reversing are done by simple slicing
+and list reversing.
+
+ >>> list(cat.searchResults(size=5, _reverse=True)) == [o5, o4]
+ True
+
+ >>> list(cat.searchResults(size=5, _limit=1)) == [o4]
+ True
+
+ >>> list(cat.searchResults(size=5, _limit=1, _reverse=True)) == [o5]
+ True
+
+However, when using sorting by index, the limit and reverse parameters
+are passed to the index ``sort`` method so it can do it efficiently.
+
+Let's index more objects to work with:
+
+ >>> o7 = DiscriminatingPerson(7, 'blue')
+ >>> o8 = DiscriminatingPerson(3, 'blue')
+ >>> o9 = DiscriminatingPerson(14, 'blue')
+ >>> o10 = DiscriminatingPerson(1, 'blue')
+ >>> ids.data.update({7: o7, 8: o8, 9: o9, 10: o10})
+ >>> cat.index_doc(7, o7)
+ >>> cat.index_doc(8, o8)
+ >>> cat.index_doc(9, o9)
+ >>> cat.index_doc(10, o10)
+
+Now we can search all people who like blue, ordered by age:
+
+ >>> results = list(cat.searchResults(color='blue', _sort_index='age'))
+ >>> results == [o3, o10, o8, o7, o6, o9]
+ True
+
+ >>> results = list(cat.searchResults(color='blue', _sort_index='age', _limit=3))
+ >>> results == [o3, o10, o8]
+ True
+
+ >>> results = list(cat.searchResults(color='blue', _sort_index='age', _reverse=True))
+ >>> results == [o9, o6, o7, o8, o10, o3]
+ True
+
+ >>> results = list(cat.searchResults(color='blue', _sort_index='age', _reverse=True, _limit=4))
+ >>> results == [o9, o6, o7, o8]
+ True
+
+The index example we looked at didn't provide document scores. Simple
+indexes normally don't, but more complex indexes might give results
+scores, according to how closely a document matches a query. Let's
+create a new index, a "keyword index" that indexes sequences of
+values:
+
+ >>> class BaseKeywordIndex(persistent.Persistent):
+ ... zope.interface.implements(
+ ... zope.index.interfaces.IInjection,
+ ... zope.index.interfaces.IIndexSearch,
+ ... )
+ ...
+ ... def clear(self):
+ ... self.forward = BTrees.OOBTree.OOBTree()
+ ... self.backward = BTrees.IOBTree.IOBTree()
+ ...
+ ... __init__ = clear
+ ...
+ ... def index_doc(self, docid, values):
+ ... if docid in self.backward:
+ ... self.unindex_doc(docid)
+ ... self.backward[docid] = values
+ ...
+ ... for value in values:
+ ... set = self.forward.get(value)
+ ... if set is None:
+ ... set = BTrees.IFBTree.IFTreeSet()
+ ... self.forward[value] = set
+ ... set.insert(docid)
+ ...
+ ... def unindex_doc(self, docid):
+ ... values = self.backward.get(docid)
+ ... if values is None:
+ ... return
+ ... for value in values:
+ ... self.forward[value].remove(docid)
+ ... del self.backward[docid]
+ ...
+ ... def apply(self, values):
+ ... result = BTrees.IFBTree.IFBucket()
+ ... for value in values:
+ ... set = self.forward.get(value)
+ ... if set is not None:
+ ... _, result = BTrees.IFBTree.weightedUnion(result, set)
+ ... return result
+
+ >>> class KeywordIndex(zope.app.catalog.attribute.AttributeIndex,
+ ... BaseKeywordIndex,
+ ... zope.app.container.contained.Contained,
+ ... ):
+ ... zope.interface.implements(zope.app.catalog.interfaces.ICatalogIndex)
+
+Now, we'll add a hobbies index:
+
+ >>> cat['hobbies'] = KeywordIndex('hobbies')
+ >>> o1.hobbies = 'camping', 'music'
+ >>> o2.hobbies = 'hacking', 'sailing'
+ >>> o3.hobbies = 'music', 'camping', 'sailing'
+ >>> o6.hobbies = 'cooking', 'dancing'
+ >>> cat.updateIndexes()
+
+When we apply the catalog:
+
+ >>> cat.apply({'hobbies': ['music', 'camping', 'sailing']})
+ BTrees.IFBTree.IFBucket([(1, 2.0), (2, 1.0), (3, 3.0)])
+
+We found objects 1-3, because they each contained at least some of the
+words in the query. The scores represent the number of words that
+matched. If we also include age:
+
+ >>> cat.apply({'hobbies': ['music', 'camping', 'sailing'], 'age': 10})
+ BTrees.IFBTree.IFBucket([(1, 3.0)])
+
+The score increased because we used an additional index. If an index
+doesn't provide scores, scores of 1.0 are assumed.
+
Deleted: zope.app.catalog/tags/3.6.0/src/zope/app/catalog/browser/configure.zcml
===================================================================
--- zope.app.catalog/trunk/src/zope/app/catalog/browser/configure.zcml 2009-01-03 15:21:36 UTC (rev 94476)
+++ zope.app.catalog/tags/3.6.0/src/zope/app/catalog/browser/configure.zcml 2009-01-03 15:41:02 UTC (rev 94479)
@@ -1,95 +0,0 @@
-<zope:configure
- xmlns:zope="http://namespaces.zope.org/zope"
- xmlns="http://namespaces.zope.org/browser"
- i18n_domain="zope"
- >
-
-<!-- Allow a catalog to be added to content space -->
-<addMenuItem
- title="Catalog"
- description="A Catalog allows indexing and searching of objects"
- class="zope.app.catalog.catalog.Catalog"
- permission="zope.ManageContent"
- />
-
-<icon
- name="zmi_icon"
- for="zope.app.catalog.interfaces.ICatalog"
- file="catalog_icon.gif"
- />
-
-<containerViews
- for="..interfaces.ICatalog"
- contents="zope.ManageServices"
- index="zope.ManageServices"
- add="zope.ManageServices"
- />
-
-<pages
- for="..interfaces.ICatalog"
- class=".catalog.Advanced"
- permission="zope.ManageContent">
-
- <page name="advanced.html" template="advanced.pt"
- menu="zmi_views" title="Advanced"/>
- <page name="reindex.html" attribute="reindex"/>
-</pages>
-
-<!-- Indexes -->
-
-<addform
- name="AddFieldIndex"
- label="Add a field index"
- schema="..interfaces.IAttributeIndex"
- permission="zope.ManageServices"
- content_factory="..field.FieldIndex"
- arguments="field_name"
- keyword_arguments="interface field_callable"
- />
-
-<addMenuItem
- title="Field Index"
- description="Index items based on an orderable field value"
- class="..field.FieldIndex"
- permission="zope.ManageServices"
- view="AddFieldIndex"
- />
-
-<schemadisplay
- name="index.html"
- schema="..field.IFieldIndex"
- label="Field Index"
- fields="interface field_name field_callable"
- permission="zope.ManageServices"
- menu="zmi_views" title="Configuration"
- />
-
-<addform
- name="AddTextIndex"
- label="Add a text index"
- schema="..interfaces.IAttributeIndex"
- permission="zope.ManageServices"
- content_factory="..text.TextIndex"
- arguments="field_name"
- keyword_arguments="interface field_callable"
- />
-
-<addMenuItem
- title="Text Index"
- description="Index items based on multi-value fields with
- orderable values"
- class="..text.TextIndex"
- permission="zope.ManageServices"
- view="AddTextIndex"
- />
-
-<schemadisplay
- name="index.html"
- fields="interface field_name field_callable"
- schema="..text.ITextIndex"
- label="Text Index"
- permission="zope.ManageServices"
- menu="zmi_views" title="Configuration"
- />
-
-</zope:configure>
Copied: zope.app.catalog/tags/3.6.0/src/zope/app/catalog/browser/configure.zcml (from rev 94477, zope.app.catalog/trunk/src/zope/app/catalog/browser/configure.zcml)
===================================================================
--- zope.app.catalog/tags/3.6.0/src/zope/app/catalog/browser/configure.zcml (rev 0)
+++ zope.app.catalog/tags/3.6.0/src/zope/app/catalog/browser/configure.zcml 2009-01-03 15:41:02 UTC (rev 94479)
@@ -0,0 +1,141 @@
+<zope:configure
+ xmlns:zope="http://namespaces.zope.org/zope"
+ xmlns="http://namespaces.zope.org/browser"
+ i18n_domain="zope"
+ >
+
+<!-- Allow a catalog to be added to content space -->
+<addMenuItem
+ title="Catalog"
+ description="A Catalog allows indexing and searching of objects"
+ class="zope.app.catalog.catalog.Catalog"
+ permission="zope.ManageContent"
+ />
+
+<icon
+ name="zmi_icon"
+ for="zope.app.catalog.interfaces.ICatalog"
+ file="catalog_icon.gif"
+ />
+
+<containerViews
+ for="..interfaces.ICatalog"
+ contents="zope.ManageServices"
+ index="zope.ManageServices"
+ add="zope.ManageServices"
+ />
+
+<pages
+ for="..interfaces.ICatalog"
+ class=".catalog.Advanced"
+ permission="zope.ManageContent">
+
+ <page name="advanced.html" template="advanced.pt"
+ menu="zmi_views" title="Advanced"/>
+ <page name="reindex.html" attribute="reindex"/>
+</pages>
+
+<!-- Indexes -->
+
+<addform
+ name="AddFieldIndex"
+ label="Add a field index"
+ schema="..interfaces.IAttributeIndex"
+ permission="zope.ManageServices"
+ content_factory="..field.FieldIndex"
+ arguments="field_name"
+ keyword_arguments="interface field_callable"
+ />
+
+<addMenuItem
+ title="Field Index"
+ description="Index items based on an orderable field value"
+ class="..field.FieldIndex"
+ permission="zope.ManageServices"
+ view="AddFieldIndex"
+ />
+
+<schemadisplay
+ name="index.html"
+ schema="..field.IFieldIndex"
+ label="Field Index"
+ fields="interface field_name field_callable"
+ permission="zope.ManageServices"
+ menu="zmi_views" title="Configuration"
+ />
+
+<addform
+ name="AddKeywordIndex"
+ label="Add a keyword index"
+ schema="..interfaces.IAttributeIndex"
+ permission="zope.ManageServices"
+ content_factory="..keyword.KeywordIndex"
+ arguments="field_name"
+ keyword_arguments="interface field_callable"
+ />
+
+<addMenuItem
+ title="Keyword Index"
+ description="Index items based on sequence of keywords"
+ class="..keyword.KeywordIndex"
+ permission="zope.ManageServices"
+ view="AddKeywordIndex"
+ />
+
+<addform
+ name="AddCaseInsensitiveKeywordIndex"
+ label="Add a keyword index (case-omsensitive)"
+ schema="..interfaces.IAttributeIndex"
+ permission="zope.ManageServices"
+ content_factory="..keyword.CaseInsensitiveKeywordIndex"
+ arguments="field_name"
+ keyword_arguments="interface field_callable"
+ />
+
+<addMenuItem
+ title="Keyword Index (case-insensitive)"
+ description="Index items based on sequence of keywords"
+ class="..keyword.CaseInsensitiveKeywordIndex"
+ permission="zope.ManageServices"
+ view="AddCaseInsensitiveKeywordIndex"
+ />
+
+<schemadisplay
+ name="index.html"
+ schema="..keyword.IKeywordIndex"
+ label="Keyword Index"
+ fields="interface field_name field_callable"
+ permission="zope.ManageServices"
+ menu="zmi_views" title="Configuration"
+ />
+
+<addform
+ name="AddTextIndex"
+ label="Add a text index"
+ schema="..text.ITextIndex"
+ fields="interface field_name field_callable"
+ permission="zope.ManageServices"
+ content_factory="..text.TextIndex"
+ arguments="field_name"
+ keyword_arguments="interface field_callable"
+ />
+
+<addMenuItem
+ title="Text Index"
+ description="Index items based on multi-value fields with
+ orderable values"
+ class="..text.TextIndex"
+ permission="zope.ManageServices"
+ view="AddTextIndex"
+ />
+
+<schemadisplay
+ name="index.html"
+ fields="interface field_name field_callable"
+ schema="..text.ITextIndex"
+ label="Text Index"
+ permission="zope.ManageServices"
+ menu="zmi_views" title="Configuration"
+ />
+
+</zope:configure>
Deleted: zope.app.catalog/tags/3.6.0/src/zope/app/catalog/catalog.py
===================================================================
--- zope.app.catalog/trunk/src/zope/app/catalog/catalog.py 2009-01-03 15:21:36 UTC (rev 94476)
+++ zope.app.catalog/tags/3.6.0/src/zope/app/catalog/catalog.py 2009-01-03 15:41:02 UTC (rev 94479)
@@ -1,191 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2003 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Catalog
-
-$Id$
-"""
-__docformat__ = 'restructuredtext'
-
-import BTrees
-
-import zope.index.interfaces
-from zope import component
-from zope.interface import implements
-from zope.annotation.interfaces import IAttributeAnnotatable
-
-from zope.app.container.interfaces import IContainer
-from zope.app.container.btree import BTreeContainer
-from zope.app.catalog.interfaces import ICatalog, INoAutoIndex, INoAutoReindex
-from zope.app.intid.interfaces import IIntIds
-from zope.traversing.interfaces import IPhysicallyLocatable
-from zope.location import location
-
-
-class ResultSet:
- """Lazily accessed set of objects."""
-
- def __init__(self, uids, uidutil):
- self.uids = uids
- self.uidutil = uidutil
-
- def __len__(self):
- return len(self.uids)
-
- def __iter__(self):
- for uid in self.uids:
- obj = self.uidutil.getObject(uid)
- yield obj
-
-
-class Catalog(BTreeContainer):
-
- implements(ICatalog,
- IContainer,
- IAttributeAnnotatable,
- zope.index.interfaces.IIndexSearch,
- )
-
- family = BTrees.family32
-
- def __init__(self, family=None):
- super(Catalog, self).__init__()
- if family is not None:
- self.family = family
-
- def clear(self):
- for index in self.values():
- index.clear()
-
- def index_doc(self, docid, texts):
- """Register the data in indexes of this catalog."""
- for index in self.values():
- index.index_doc(docid, texts)
-
- def unindex_doc(self, docid):
- """Unregister the data from indexes of this catalog."""
- for index in self.values():
- index.unindex_doc(docid)
-
- def _visitSublocations(self) :
- """Restricts the access to the objects that live within
- the nearest site if the catalog itself is locatable.
- """
- uidutil = None
- locatable = IPhysicallyLocatable(self, None)
- if locatable is not None :
- site = locatable.getNearestSite()
- sm = site.getSiteManager()
- uidutil = sm.queryUtility(IIntIds)
- if uidutil not in [c.component for c in sm.registeredUtilities()]:
- # we do not have a local inits utility
- uidutil = component.getUtility(IIntIds, context=self)
- for uid in uidutil:
- obj = uidutil.getObject(uid)
- if location.inside(obj, site) :
- yield uid, obj
- return
- if uidutil is None:
- uidutil = component.getUtility(IIntIds)
- for uid in uidutil:
- yield uid, uidutil.getObject(uid)
-
- def updateIndex(self, index):
- for uid, obj in self._visitSublocations() :
- index.index_doc(uid, obj)
-
- def updateIndexes(self):
- for uid, obj in self._visitSublocations() :
- for index in self.values():
- index.index_doc(uid, obj)
-
- def apply(self, query):
- results = []
- for index_name, index_query in query.items():
- index = self[index_name]
- r = index.apply(index_query)
- if r is None:
- continue
- if not r:
- # empty results
- return r
- results.append((len(r), r))
-
- if not results:
- # no applicable indexes, so catalog was not applicable
- return None
-
- results.sort() # order from smallest to largest
-
- _, result = results.pop(0)
- for _, r in results:
- _, result = self.family.IF.weightedIntersection(result, r)
-
- return result
-
- def searchResults(self, **searchterms):
- results = self.apply(searchterms)
- if results is not None:
- uidutil = component.getUtility(IIntIds)
- results = ResultSet(results, uidutil)
- return results
-
-def indexAdded(index, event):
- """When an index is added to a catalog, we have to index existing objects
-
- When an index is added, we tell it's parent to index it:
-
- >>> class FauxCatalog:
- ... def updateIndex(self, index):
- ... self.updated = index
-
- >>> class FauxIndex:
- ... pass
-
- >>> index = FauxIndex()
- >>> index.__parent__ = FauxCatalog()
-
- >>> indexAdded(index, None)
- >>> index.__parent__.updated is index
- True
- """
- index.__parent__.updateIndex(index)
-
-def indexDocSubscriber(event):
- """A subscriber to IntIdAddedEvent"""
- ob = event.object
- if INoAutoIndex.providedBy(ob):
- return
- for cat in component.getAllUtilitiesRegisteredFor(ICatalog, context=ob):
- id = component.getUtility(IIntIds, context=cat).getId(ob)
- cat.index_doc(id, ob)
-
-
-def reindexDocSubscriber(event):
- """A subscriber to ObjectModifiedEvent"""
- ob = event.object
- if INoAutoReindex.providedBy(ob):
- return
- for cat in component.getAllUtilitiesRegisteredFor(ICatalog, context=ob):
- id = component.getUtility(IIntIds, context=cat).queryId(ob)
- if id is not None:
- cat.index_doc(id, ob)
-
-
-def unindexDocSubscriber(event):
- """A subscriber to IntIdRemovedEvent"""
- ob = event.object
- for cat in component.getAllUtilitiesRegisteredFor(ICatalog, context=ob):
- id = component.getUtility(IIntIds, context=cat).queryId(ob)
- if id is not None:
- cat.unindex_doc(id)
Copied: zope.app.catalog/tags/3.6.0/src/zope/app/catalog/catalog.py (from rev 94477, zope.app.catalog/trunk/src/zope/app/catalog/catalog.py)
===================================================================
--- zope.app.catalog/tags/3.6.0/src/zope/app/catalog/catalog.py (rev 0)
+++ zope.app.catalog/tags/3.6.0/src/zope/app/catalog/catalog.py 2009-01-03 15:41:02 UTC (rev 94479)
@@ -0,0 +1,206 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Catalog
+
+$Id$
+"""
+__docformat__ = 'restructuredtext'
+
+import BTrees
+
+import zope.index.interfaces
+from zope import component
+from zope.interface import implements
+from zope.annotation.interfaces import IAttributeAnnotatable
+
+from zope.app.container.interfaces import IContainer
+from zope.app.container.btree import BTreeContainer
+from zope.app.catalog.interfaces import ICatalog, INoAutoIndex, INoAutoReindex
+from zope.app.intid.interfaces import IIntIds
+from zope.traversing.interfaces import IPhysicallyLocatable
+from zope.location import location
+
+
+class ResultSet:
+ """Lazily accessed set of objects."""
+
+ def __init__(self, uids, uidutil):
+ self.uids = uids
+ self.uidutil = uidutil
+
+ def __len__(self):
+ return len(self.uids)
+
+ def __iter__(self):
+ for uid in self.uids:
+ obj = self.uidutil.getObject(uid)
+ yield obj
+
+
+class Catalog(BTreeContainer):
+
+ implements(ICatalog,
+ IContainer,
+ IAttributeAnnotatable,
+ zope.index.interfaces.IIndexSearch,
+ )
+
+ family = BTrees.family32
+
+ def __init__(self, family=None):
+ super(Catalog, self).__init__()
+ if family is not None:
+ self.family = family
+
+ def clear(self):
+ for index in self.values():
+ index.clear()
+
+ def index_doc(self, docid, texts):
+ """Register the data in indexes of this catalog."""
+ for index in self.values():
+ index.index_doc(docid, texts)
+
+ def unindex_doc(self, docid):
+ """Unregister the data from indexes of this catalog."""
+ for index in self.values():
+ index.unindex_doc(docid)
+
+ def _visitSublocations(self) :
+ """Restricts the access to the objects that live within
+ the nearest site if the catalog itself is locatable.
+ """
+ uidutil = None
+ locatable = IPhysicallyLocatable(self, None)
+ if locatable is not None :
+ site = locatable.getNearestSite()
+ sm = site.getSiteManager()
+ uidutil = sm.queryUtility(IIntIds)
+ if uidutil not in [c.component for c in sm.registeredUtilities()]:
+ # we do not have a local inits utility
+ uidutil = component.getUtility(IIntIds, context=self)
+ for uid in uidutil:
+ obj = uidutil.getObject(uid)
+ if location.inside(obj, site) :
+ yield uid, obj
+ return
+ if uidutil is None:
+ uidutil = component.getUtility(IIntIds)
+ for uid in uidutil:
+ yield uid, uidutil.getObject(uid)
+
+ def updateIndex(self, index):
+ for uid, obj in self._visitSublocations() :
+ index.index_doc(uid, obj)
+
+ def updateIndexes(self):
+ for uid, obj in self._visitSublocations() :
+ for index in self.values():
+ index.index_doc(uid, obj)
+
+ def apply(self, query):
+ results = []
+ for index_name, index_query in query.items():
+ index = self[index_name]
+ r = index.apply(index_query)
+ if r is None:
+ continue
+ if not r:
+ # empty results
+ return r
+ results.append((len(r), r))
+
+ if not results:
+ # no applicable indexes, so catalog was not applicable
+ return None
+
+ results.sort() # order from smallest to largest
+
+ _, result = results.pop(0)
+ for _, r in results:
+ _, result = self.family.IF.weightedIntersection(result, r)
+
+ return result
+
+ def searchResults(self, **searchterms):
+ sort_index = searchterms.pop('_sort_index', None)
+ limit = searchterms.pop('_limit', None)
+ reverse = searchterms.pop('_reverse', False)
+ results = self.apply(searchterms)
+ if results is not None:
+ if sort_index is not None:
+ index = self[sort_index]
+ if not zope.index.interfaces.IIndexSort.providedBy(index):
+ raise ValueError('Index %s does not support sorting.' % sort_index)
+ results = list(index.sort(results, limit=limit, reverse=reverse))
+ else:
+ if reverse or limit:
+ results = list(results)
+ if reverse:
+ results.reverse()
+ if limit:
+ del results[limit:]
+ uidutil = component.getUtility(IIntIds)
+ results = ResultSet(results, uidutil)
+ return results
+
+def indexAdded(index, event):
+ """When an index is added to a catalog, we have to index existing objects
+
+ When an index is added, we tell it's parent to index it:
+
+ >>> class FauxCatalog:
+ ... def updateIndex(self, index):
+ ... self.updated = index
+
+ >>> class FauxIndex:
+ ... pass
+
+ >>> index = FauxIndex()
+ >>> index.__parent__ = FauxCatalog()
+
+ >>> indexAdded(index, None)
+ >>> index.__parent__.updated is index
+ True
+ """
+ index.__parent__.updateIndex(index)
+
+def indexDocSubscriber(event):
+ """A subscriber to IntIdAddedEvent"""
+ ob = event.object
+ if INoAutoIndex.providedBy(ob):
+ return
+ for cat in component.getAllUtilitiesRegisteredFor(ICatalog, context=ob):
+ id = component.getUtility(IIntIds, context=cat).getId(ob)
+ cat.index_doc(id, ob)
+
+
+def reindexDocSubscriber(event):
+ """A subscriber to ObjectModifiedEvent"""
+ ob = event.object
+ if INoAutoReindex.providedBy(ob):
+ return
+ for cat in component.getAllUtilitiesRegisteredFor(ICatalog, context=ob):
+ id = component.getUtility(IIntIds, context=cat).queryId(ob)
+ if id is not None:
+ cat.index_doc(id, ob)
+
+
+def unindexDocSubscriber(event):
+ """A subscriber to IntIdRemovedEvent"""
+ ob = event.object
+ for cat in component.getAllUtilitiesRegisteredFor(ICatalog, context=ob):
+ id = component.getUtility(IIntIds, context=cat).queryId(ob)
+ if id is not None:
+ cat.unindex_doc(id)
Deleted: zope.app.catalog/tags/3.6.0/src/zope/app/catalog/classes.zcml
===================================================================
--- zope.app.catalog/trunk/src/zope/app/catalog/classes.zcml 2009-01-03 15:21:36 UTC (rev 94476)
+++ zope.app.catalog/tags/3.6.0/src/zope/app/catalog/classes.zcml 2009-01-03 15:41:02 UTC (rev 94479)
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<configure
- xmlns="http://namespaces.zope.org/zope"
- xmlns:browser="http://namespaces.zope.org/browser"
- >
-
- <class class=".catalog.Catalog">
- <factory
- id="zope.app.catalog"
- />
- <implements
- interface="zope.annotation.interfaces.IAttributeAnnotatable"
- />
- <require
- interface="zope.app.catalog.interfaces.ICatalogQuery"
- permission="zope.Public"
- />
- <require
- interface="zope.app.catalog.interfaces.ICatalogEdit"
- permission="zope.ManageServices"
- />
- <require
- interface="zope.app.container.interfaces.IContainer"
- permission="zope.ManageServices"
- />
- </class>
-
- <class class=".catalog.ResultSet">
- <require
- attributes="__iter__ __len__"
- permission="zope.Public"
- />
- </class>
-
- <class class=".field.FieldIndex">
- <require
- permission="zope.ManageServices"
- interface=".interfaces.IAttributeIndex
- zope.index.interfaces.IStatistics
- "
- set_schema=".interfaces.IAttributeIndex"
- />
- </class>
-
- <class class=".text.TextIndex">
- <require
- permission="zope.ManageServices"
- interface=".interfaces.IAttributeIndex
- zope.index.interfaces.IStatistics
- "
- set_schema=".interfaces.IAttributeIndex"
- />
- </class>
-
- <interface interface="zope.index.text.interfaces.ISearchableText" />
-
-</configure>
Copied: zope.app.catalog/tags/3.6.0/src/zope/app/catalog/classes.zcml (from rev 94477, zope.app.catalog/trunk/src/zope/app/catalog/classes.zcml)
===================================================================
--- zope.app.catalog/tags/3.6.0/src/zope/app/catalog/classes.zcml (rev 0)
+++ zope.app.catalog/tags/3.6.0/src/zope/app/catalog/classes.zcml 2009-01-03 15:41:02 UTC (rev 94479)
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser"
+ >
+
+ <class class=".catalog.Catalog">
+ <factory
+ id="zope.app.catalog"
+ />
+ <implements
+ interface="zope.annotation.interfaces.IAttributeAnnotatable"
+ />
+ <require
+ interface="zope.app.catalog.interfaces.ICatalogQuery"
+ permission="zope.Public"
+ />
+ <require
+ interface="zope.app.catalog.interfaces.ICatalogEdit"
+ permission="zope.ManageServices"
+ />
+ <require
+ interface="zope.app.container.interfaces.IContainer"
+ permission="zope.ManageServices"
+ />
+ </class>
+
+ <class class=".catalog.ResultSet">
+ <require
+ attributes="__iter__ __len__"
+ permission="zope.Public"
+ />
+ </class>
+
+ <class class=".field.FieldIndex">
+ <require
+ permission="zope.ManageServices"
+ interface=".interfaces.IAttributeIndex
+ zope.index.interfaces.IStatistics
+ "
+ set_schema=".interfaces.IAttributeIndex"
+ />
+ </class>
+
+ <class class=".keyword.KeywordIndex">
+ <require
+ permission="zope.ManageServices"
+ interface=".interfaces.IAttributeIndex
+ zope.index.interfaces.IStatistics"
+ set_schema=".interfaces.IAttributeIndex"
+ />
+ </class>
+
+ <class class=".keyword.CaseInsensitiveKeywordIndex">
+ <require like_class=".keyword.KeywordIndex" />
+ </class>
+
+ <class class=".text.TextIndex">
+ <require
+ permission="zope.ManageServices"
+ interface=".interfaces.IAttributeIndex
+ zope.index.interfaces.IStatistics
+ "
+ set_schema=".interfaces.IAttributeIndex"
+ />
+ </class>
+
+ <interface interface="zope.index.text.interfaces.ISearchableText" />
+
+</configure>
Deleted: zope.app.catalog/tags/3.6.0/src/zope/app/catalog/interfaces.py
===================================================================
--- zope.app.catalog/trunk/src/zope/app/catalog/interfaces.py 2009-01-03 15:21:36 UTC (rev 94476)
+++ zope.app.catalog/tags/3.6.0/src/zope/app/catalog/interfaces.py 2009-01-03 15:41:02 UTC (rev 94479)
@@ -1,88 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Catalog Interfaces
-
-$Id$
-"""
-
-import zope.index.interfaces
-import zope.interface
-import zope.schema
-import zope.app.container.interfaces
-import zope.app.container.constraints
-
-from zope.i18nmessageid import ZopeMessageFactory as _
-
-class ICatalogQuery(zope.interface.Interface):
- """Provides Catalog Queries."""
-
- def searchResults(**kw):
- """Search on the given indexes."""
-
-
-class ICatalogEdit(zope.index.interfaces.IInjection):
- """Allows one to manipulate the Catalog information."""
-
- def updateIndexes():
- """Reindex all objects."""
-
-
-class ICatalogIndex(zope.index.interfaces.IInjection,
- zope.index.interfaces.IIndexSearch,
- ):
- """An index to be used in a catalog
- """
- __parent__ = zope.schema.Field()
-
-
-class ICatalog(ICatalogQuery, ICatalogEdit,
- zope.app.container.interfaces.IContainer):
- """Marker to describe a catalog in content space."""
-
- zope.app.container.constraints.contains(ICatalogIndex)
-
-ICatalogIndex['__parent__'].constraint = (
- zope.app.container.constraints.ContainerTypesConstraint(ICatalog))
-
-class IAttributeIndex(zope.interface.Interface):
- """I index objects by first adapting them to an interface, then
- retrieving a field on the adapted object.
- """
-
- interface = zope.schema.Choice(
- title=_(u"Interface"),
- description=_(u"Objects will be adapted to this interface"),
- vocabulary="Interfaces",
- required=False,
- )
-
- field_name = zope.schema.BytesLine(
- title=_(u"Field Name"),
- description=_(u"Name of the field to index"),
- )
-
- field_callable = zope.schema.Bool(
- title=_(u"Field Callable"),
- description=_(u"If true, then the field should be called to get the "
- u"value to be indexed"),
- )
-
-
-class INoAutoIndex(zope.interface.Interface):
-
- """Marker for objects that should not be automatically indexed"""
-
-class INoAutoReindex(zope.interface.Interface):
-
- """Marker for objects that should not be automatically reindexed"""
Copied: zope.app.catalog/tags/3.6.0/src/zope/app/catalog/interfaces.py (from rev 94477, zope.app.catalog/trunk/src/zope/app/catalog/interfaces.py)
===================================================================
--- zope.app.catalog/tags/3.6.0/src/zope/app/catalog/interfaces.py (rev 0)
+++ zope.app.catalog/tags/3.6.0/src/zope/app/catalog/interfaces.py 2009-01-03 15:41:02 UTC (rev 94479)
@@ -0,0 +1,105 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Catalog Interfaces
+
+$Id$
+"""
+
+import zope.index.interfaces
+import zope.interface
+import zope.schema
+import zope.app.container.interfaces
+import zope.app.container.constraints
+
+from zope.i18nmessageid import ZopeMessageFactory as _
+
+class ICatalogQuery(zope.interface.Interface):
+ """Provides Catalog Queries."""
+
+ def searchResults(**kw):
+ """Search on the given indexes.
+
+ Keyword arguments dictionary keys
+ are index names and values are queries
+ for these indexes.
+
+ Keyword arguments has some special names,
+ used by the catalog itself:
+
+ * _sort_index - The name of index to sort
+ results with. This index must implement
+ zope.index.interfaces.IIndexSort.
+ * _limit - Limit result set by this number,
+ useful when used with sorting.
+ * _reverse - Reverse result set, also
+ useful with sorting.
+
+ """
+
+
+class ICatalogEdit(zope.index.interfaces.IInjection):
+ """Allows one to manipulate the Catalog information."""
+
+ def updateIndexes():
+ """Reindex all objects."""
+
+
+class ICatalogIndex(zope.index.interfaces.IInjection,
+ zope.index.interfaces.IIndexSearch,
+ ):
+ """An index to be used in a catalog
+ """
+ __parent__ = zope.schema.Field()
+
+
+class ICatalog(ICatalogQuery, ICatalogEdit,
+ zope.app.container.interfaces.IContainer):
+ """Marker to describe a catalog in content space."""
+
+ zope.app.container.constraints.contains(ICatalogIndex)
+
+ICatalogIndex['__parent__'].constraint = (
+ zope.app.container.constraints.ContainerTypesConstraint(ICatalog))
+
+class IAttributeIndex(zope.interface.Interface):
+ """I index objects by first adapting them to an interface, then
+ retrieving a field on the adapted object.
+ """
+
+ interface = zope.schema.Choice(
+ title=_(u"Interface"),
+ description=_(u"Objects will be adapted to this interface"),
+ vocabulary="Interfaces",
+ required=False,
+ )
+
+ field_name = zope.schema.BytesLine(
+ title=_(u"Field Name"),
+ description=_(u"Name of the field to index"),
+ )
+
+ field_callable = zope.schema.Bool(
+ title=_(u"Field Callable"),
+ description=_(u"If true, then the field should be called to get the "
+ u"value to be indexed"),
+ )
+
+
+class INoAutoIndex(zope.interface.Interface):
+
+ """Marker for objects that should not be automatically indexed"""
+
+class INoAutoReindex(zope.interface.Interface):
+
+ """Marker for objects that should not be automatically reindexed"""
Copied: zope.app.catalog/tags/3.6.0/src/zope/app/catalog/keyword.py (from rev 94477, zope.app.catalog/trunk/src/zope/app/catalog/keyword.py)
===================================================================
--- zope.app.catalog/tags/3.6.0/src/zope/app/catalog/keyword.py (rev 0)
+++ zope.app.catalog/tags/3.6.0/src/zope/app/catalog/keyword.py 2009-01-03 15:41:02 UTC (rev 94479)
@@ -0,0 +1,39 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Keyword catalog index
+
+$Id:$
+"""
+import zope.index.keyword
+import zope.interface
+
+import zope.app.container.contained
+import zope.app.catalog.attribute
+import zope.app.catalog.interfaces
+
+class IKeywordIndex(zope.app.catalog.interfaces.IAttributeIndex,
+ zope.app.catalog.interfaces.ICatalogIndex):
+ """Interface-based catalog keyword index"""
+
+class KeywordIndex(zope.app.catalog.attribute.AttributeIndex,
+ zope.index.keyword.KeywordIndex,
+ zope.app.container.contained.Contained):
+
+ zope.interface.implements(IKeywordIndex)
+
+class CaseInsensitiveKeywordIndex(zope.app.catalog.attribute.AttributeIndex,
+ zope.index.keyword.CaseInsensitiveKeywordIndex,
+ zope.app.container.contained.Contained):
+
+ zope.interface.implements(IKeywordIndex)
Deleted: zope.app.catalog/tags/3.6.0/src/zope/app/catalog/text.py
===================================================================
--- zope.app.catalog/trunk/src/zope/app/catalog/text.py 2009-01-03 15:21:36 UTC (rev 94476)
+++ zope.app.catalog/tags/3.6.0/src/zope/app/catalog/text.py 2009-01-03 15:41:02 UTC (rev 94479)
@@ -1,57 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2002 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Text catalog indexes
-
-$Id$
-"""
-import zope.index.text
-import zope.index.text.interfaces
-import zope.interface
-
-import zope.app.catalog.attribute
-import zope.app.catalog.interfaces
-import zope.app.container.contained
-from zope.i18nmessageid import ZopeMessageFactory as _
-
-class ITextIndex(zope.app.catalog.interfaces.IAttributeIndex,
- zope.app.catalog.interfaces.ICatalogIndex):
- """Interface-based catalog text index
- """
-
- interface = zope.schema.Choice(
- title=_(u"Interface"),
- description=_(u"Objects will be adapted to this interface"),
- vocabulary=_("Interfaces"),
- required=False,
- default=zope.index.text.interfaces.ISearchableText,
- )
-
- field_name = zope.schema.BytesLine(
- title=_(u"Field Name"),
- description=_(u"Name of the field to index"),
- default="searchableText"
- )
-
- field_callable = zope.schema.Bool(
- title=_(u"Field Callable"),
- description=_(u"If true, then the field should be called to get the "
- u"value to be indexed"),
- default=True,
- )
-
-class TextIndex(zope.app.catalog.attribute.AttributeIndex,
- zope.index.text.TextIndex,
- zope.app.container.contained.Contained):
-
- zope.interface.implements(ITextIndex)
Copied: zope.app.catalog/tags/3.6.0/src/zope/app/catalog/text.py (from rev 94477, zope.app.catalog/trunk/src/zope/app/catalog/text.py)
===================================================================
--- zope.app.catalog/tags/3.6.0/src/zope/app/catalog/text.py (rev 0)
+++ zope.app.catalog/tags/3.6.0/src/zope/app/catalog/text.py 2009-01-03 15:41:02 UTC (rev 94479)
@@ -0,0 +1,57 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Text catalog indexes
+
+$Id$
+"""
+import zope.index.text
+import zope.index.text.interfaces
+import zope.interface
+
+import zope.app.catalog.attribute
+import zope.app.catalog.interfaces
+import zope.app.container.contained
+from zope.i18nmessageid import ZopeMessageFactory as _
+
+class ITextIndex(zope.app.catalog.interfaces.IAttributeIndex,
+ zope.app.catalog.interfaces.ICatalogIndex):
+ """Interface-based catalog text index
+ """
+
+ interface = zope.schema.Choice(
+ title=_(u"Interface"),
+ description=_(u"Objects will be adapted to this interface"),
+ vocabulary=_("Interfaces"),
+ required=False,
+ default=zope.index.text.interfaces.ISearchableText,
+ )
+
+ field_name = zope.schema.BytesLine(
+ title=_(u"Field Name"),
+ description=_(u"Name of the field to index"),
+ default="getSearchableText"
+ )
+
+ field_callable = zope.schema.Bool(
+ title=_(u"Field Callable"),
+ description=_(u"If true, then the field should be called to get the "
+ u"value to be indexed"),
+ default=True,
+ )
+
+class TextIndex(zope.app.catalog.attribute.AttributeIndex,
+ zope.index.text.TextIndex,
+ zope.app.container.contained.Contained):
+
+ zope.interface.implements(ITextIndex)
More information about the Checkins
mailing list