source: main/waeup.ikoba/trunk/src/waeup/ikoba/doctests/catalog.txt @ 17209

Last change on this file since 17209 was 12993, checked in by Henrik Bettermann, 9 years ago

First adjustments for the upcoming Ikoba User Handbook.

File size: 5.9 KB
Line 
1Cataloging Support
2******************
3
4.. module:: waeup.ikoba.catalog
5
6Components that support cataloging and searching objects inside a
7Ikoba site.
8
9.. :doctest:
10.. :layer: waeup.ikoba.testing.IkobaUnitTestLayer
11
12.. contents::
13
14Classes
15=======
16
17:class:`IkobaQuery`
18------------------
19
20Getting a general query object
21==============================
22
23We can get a IkobaQuery object by asking for an unnamed global utility
24implementing `hurry.query.interfaces.IQuery`:
25
26    >>> from hurry.query.interfaces import IQuery
27    >>> from zope.component import getUtility
28    >>> q = getUtility(IQuery)
29    >>> q
30    <waeup.ikoba.catalog.IkobaQuery object at 0x...>
31
32This query can get 'subqueries' and delivers the objects found or
33their ids. To show this we have to setup a catalog with some entries.
34
35
36Setting up a catalog and feeding it
37===================================
38
39    >>> from zope.catalog.interfaces import ICatalog
40    >>> from zope.catalog.catalog import Catalog
41    >>> mycat = Catalog()
42
43We register this catalog with the component architechture as a utility
44named 'mycatalog':
45
46    >>> from zope.component import provideUtility
47    >>> provideUtility(mycat, ICatalog, 'mycatalog')
48
49We setup a special content type whose instances we will catalog later:
50
51    >>> from zope.interface import Interface, Attribute, implements
52    >>> from zope.container.contained import Contained
53    >>> class IMammoth(Interface):
54    ...   name = Attribute('name')
55    ...   age = Attribute('age')
56
57    >>> class Mammoth(Contained):
58    ...   implements(IMammoth)
59    ...   def __init__(self, name, age):
60    ...     self.name = name
61    ...     self.age = age
62    ...   def __cmp__(self, other):
63    ...     return cmp(self.name, other.name)
64
65By including the __cmp__ method we make sure search results can be
66stably sorted.
67
68We also setup a `zope.intid.interfaces.IIntIds` utility. This is not
69necessary for plain catalogs, but when we want to use IkobaQuery (or
70`hurry.query.query.Query` objects), as to get a unique mapping from
71objects (stored in ZODB) to integer numbers (stored in catalogs),
72these query objects lookup a global IIntIds utiliy:
73
74    >>> from zope import interface
75    >>> import zope.intid.interfaces
76    >>> class DummyIntId(object):
77    ...     interface.implements(zope.intid.interfaces.IIntIds)
78    ...     MARKER = '__dummy_int_id__'
79    ...     def __init__(self):
80    ...         self.counter = 0
81    ...         self.data = {}
82    ...     def register(self, obj):
83    ...         intid = getattr(obj, self.MARKER, None)
84    ...         if intid is None:
85    ...             setattr(obj, self.MARKER, self.counter)
86    ...             self.data[self.counter] = obj
87    ...             intid = self.counter
88    ...             self.counter += 1
89    ...         return intid
90    ...     def getObject(self, intid):
91    ...         return self.data[intid]
92    ...     def __iter__(self):
93    ...         return iter(self.data)
94    >>> intid = DummyIntId()
95    >>> from zope.component import provideUtility
96    >>> provideUtility(intid, zope.intid.interfaces.IIntIds)
97
98Now we can catalog some mammoths. Here we create a herd and catalog
99each item of it:
100
101    >>> from zope.catalog.field import FieldIndex
102    >>> mycat['mammoth_name'] = FieldIndex('name', IMammoth)
103    >>> mycat['mammoth_age'] = FieldIndex('age', IMammoth)
104
105    >>> herd = [
106    ...   Mammoth(name='Fred', age=33),
107    ...   Mammoth(name='Hank', age=30),
108    ...   Mammoth(name='Wilma', age=28),
109    ... ]
110
111    >>> for mammoth in herd:
112    ...   mycat.index_doc(intid.register(mammoth), mammoth)
113
114
115Searching for result sets
116=========================
117
118Finally we can perform queries:
119
120    >>> from hurry.query import Eq
121    >>> from zope.component import getUtility
122    >>> subquery1 = Eq(('mycatalog', 'mammoth_name'), 'Fred')
123
124The latter means: search for objects whose name is ``'Fred'`` in the
125``mammoth_name`` index of a catalog registered as a utility named
126``mycatalog``.
127
128    >>> from hurry.query import Between
129    >>> subquery2 = Between(('mycatalog', 'mammoth_age'), 30, 33)
130
131This means: ask for objects cataloged in an index named 'mammoth_age',
132whose cataloged value is between 30 and 33 (including this values).
133
134    >>> r1 = q.apply(subquery2)
135    >>> r1
136    IFSet([0, 1])
137
138Using ``apply()`` above, we get a set of values stored in an
139``IFBTree``:
140
141    >>> type(r1)
142    <type 'BTrees.IFBTree.IFSet'>
143
144``IFBTree`` objects implement a rather efficient integer to float
145mapping where also integers are allowed as values. For each object
146found (i.e. mammoths whose age is between 30 and 33), we get the
147number of its entry.
148
149To get the real object, we can use intids here, because we setup an
150appropriate IIntIds utility before:
151
152    >>> [intid.getObject(x).name for x in r1]
153    ['Fred', 'Hank']
154
155We can (and should) also use the `searchResults()` method explained
156below to do that.
157
158Retrieving BTree sets can, however, make sense, if you want to know
159only the number of results for a particular query or whether there are
160results at all in a more efficient way:
161
162    >>> len(r1)
163    2
164
165Searching for objects
166=====================
167
168Very often we don't want to know the catalog-internal 'ids' of
169searched objects but the objects themselves.
170
171This can be done by using the ``searchResults`` method of
172``IkobaQuery``:
173
174    >>> r2 = q.searchResults(subquery1)
175    >>> r2
176    <zope.catalog.catalog.ResultSet instance at 0x...>
177
178    >>> list(r2)
179    [<Mammoth object at 0x...>]
180
181We got one result item, we can immediately ask for further infos. To
182access a result item by its index number, we have to turn the
183ResultSet into an ordinary list before:
184
185    >>> entry = list(r2)[0]
186    >>> entry.name, entry.age
187    ('Fred', 33)
188
189We can also use ``subquery2`` as above:
190
191    >>> r3 = q.searchResults(subquery2)
192    >>> [(x.name, x.age) for x in r3]
193    [('Fred', 33), ('Hank', 30)]
194
195or use both queries at once:
196
197    >>> r4 = q.searchResults(subquery1 & subquery2)
198    >>> [(x.name, x.age) for x in r4]
199    [('Fred', 33)]
200
201which will give us, of course, the same result set as with subquery1.
Note: See TracBrowser for help on using the repository browser.