[Checkins] SVN: zc.relation/trunk/ - make `bin/test` only test
zc.relation (and not have zc.relationship in the
Gary Poster
gary at zope.com
Wed Aug 8 06:49:50 EDT 2007
Log message for revision 78683:
- make `bin/test` only test zc.relation (and not have zc.relationship in the
path, while `bin/testall` tests zc.relationship also.
- Remove untested feature in a searchindex: YAGNI, or I'll test it when I do
- Remove unused old test
Changed:
U zc.relation/trunk/buildout.cfg
D zc.relation/trunk/src/zc/relation/example.txt
U zc.relation/trunk/src/zc/relation/searchindex.py
-=-
Modified: zc.relation/trunk/buildout.cfg
===================================================================
--- zc.relation/trunk/buildout.cfg 2007-08-08 08:53:01 UTC (rev 78682)
+++ zc.relation/trunk/buildout.cfg 2007-08-08 10:49:49 UTC (rev 78683)
@@ -1,11 +1,16 @@
[buildout]
develop = . zc.relationship
-parts = test py
+parts = test testall py
find-links = http://download.zope.org/distribution/
[test]
recipe = zc.recipe.testrunner
+eggs = zc.relation
+defaults = "--tests-pattern [fn]?tests --exit-with-status".split()
+
+[testall]
+recipe = zc.recipe.testrunner
eggs = zc.relation [test]
zc.relationship
defaults = "--tests-pattern [fn]?tests --exit-with-status".split()
Deleted: zc.relation/trunk/src/zc/relation/example.txt
===================================================================
--- zc.relation/trunk/src/zc/relation/example.txt 2007-08-08 08:53:01 UTC (rev 78682)
+++ zc.relation/trunk/src/zc/relation/example.txt 2007-08-08 10:49:49 UTC (rev 78683)
@@ -1,658 +0,0 @@
-====================================
-zc.relation.Catalog Extended Example
-====================================
-
-.. contents::
- :local:
-
-Introduction and Set Up
-=======================
-
-This document assumes you have read the introductory README.txt and want
-to learn a bit more by example. In it, we will explore a more
-complicated set of relations, and will not explain the basics that the
-README already addressed.
-
-Imagine we are indexing security assertions in a system. In this
-system, users may have roles within an organization. Each organization
-may have multiple child organizations and may have a single parent
-organization. A user with a role in a parent organization will have the
-same role in all transitively connected child relations.
-
-This catalog, then, will be indexing a heterogeneous collection of
-relations. One kind of relation will model the hierarchy of
-organizations. We'll do it with an intrinsic relation of
-organizations to their children: that reflects the fact that parent
-organizations choose and are comprised of their children; children do not
-choose their parents.
-
-The other relation will model the (multiple) roles a (single) user has
-in a (single) organization. This relation will be entirely extrinsic.
-
-Let's define this with interfaces.
-
- >>> import zope.interface
- >>> class IOrganization(zope.interface.Interface):
- ... title = zope.interface.Attribute('the title')
- ... parts = zope.interface.Attribute(
- ... 'the organizations that make up this one')
- ...
- >>> class IRoles(zope.interface.Interface):
- ... organization = zope.interface.Attribute(
- ... 'the organization in which this relation operates')
- ... principal_id = zope.interface.Attribute(
- ... 'the pricipal id whose roles this relation lists')
- ... role_ids = zope.interface.Attribute(
- ... 'the role ids that the principal explicitly has in the '
- ... 'organization. The principal may have other roles via '
- ... 'roles in parent organizations.')
- ...
-
-Now we can create some classes. In the first example, the setup was a bit
-of a toy. This time we will be just a bit more practical. We'll also expect
-to be operating within the ZODB, with a root and transactions. [#ZODB]_
-
-Here's how we will dump and load our relations: use a "registry"
-object, similar to an intid utility. [#faux_intid]_
-
-We use the cache just to show you how you might use it. It probably is
-overkill for this job, and maybe even a loss, but you can see the idea.
-
- >>> def dump(obj, catalog, cache):
- ... reg = cache.get('registry')
- ... if reg is None:
- ... reg = cache['registry'] = catalog._p_jar.root()['registry']
- ... return reg.getId(obj)
- ...
- >>> def load(token, catalog, cache):
- ... reg = cache.get('registry')
- ... if reg is None:
- ... reg = cache['registry'] = catalog._p_jar.root()['registry']
- ... return reg.getObject(token)
- ...
-
-Now we can create a relation catalog to hold these items.
-
- >>> import zc.relation
- >>> catalog = root['catalog'] = zc.relation.Catalog(dump, load)
- >>> transaction.commit()
-
-Now we set up our indexes. We'll start with just the organizations, and
-set up the catalog with them. This part will be similar to the example
-in README.txt, but will introduce more discussions of optimizations and
-tokens. Then we'll add in the part about roles, and explore transitive
-query factories and transitive indexes some more.
-
-Optimizations and Tokens: Setting Up the Organizations
-======================================================
-
-The organization will hold a set of organizations. This is actually not
-inherently easy in the ZODB because this means that we need to compare
-or hash persistent objects, which does not work reliably over time and
-across machines out-of-the-box. To side-step the issue for this example,
-and still do something a bit interesting and real-world, we'll the
-registry tokens introduced above. This will also give us a chance to
-talk a bit more about optimizations and tokens. (If you would like
-to sanely and transparently hold a set of persistent objects, try the
-zc.set package.)
-
- >>> import BTrees
- >>> class Organization(persistent.Persistent):
- ... zope.interface.implements(IOrganization)
- ... def __init__(self, title):
- ... self.title = title
- ... self.parts = BTrees.family32.IF.TreeSet()
- ... # the next parts just make the tests prettier
- ... def __repr__(self):
- ... return '<Organization instance "' + self.title + '">'
- ... def __cmp__(self, other):
- ... # pukes if other doesn't have name
- ... return cmp(self.title, other.title)
- ...
-
-Now we can add the `parts` index to the catalog. This will do a few
-new things from how we added indexes in the README. We'll add the index,
-then talk about what we did.
-
- >>> catalog.addValueIndex(IOrganization['parts'], multiple=True,
- ... name="part")
-
-So, what's different?
-
-First, we are using an interface element to define the value to be indexed.
-It provides an interface to which objects will be adapted, a default name
-for the index, and information as to whether the attribute should be used
-directly or called.
-
-Second, we are not specifying a dump or load. They are None. This
-means that the indexed value can already be treated as a token. This
-can allow a very significant optimization for reindexing if the indexed
-value is a large collection using the same BTree family as the
-index--which leads us to the next difference.
-
-Third, we are specifying that `multiple=True`. This means that the value
-on a given relation that provides or can be adapted to IOrganization will
-have a collection of `parts`. These will always be regarded as a set,
-whether the actual colection is a BTrees set or the keys of a BTree.
-
-Last, we are specifying a name to be used for queries. I find that queries
-read more easily when the query keys are singular, so I often rename plurals.
-
-We are just working with organizations at the moment, so we can add
-another simple transposing transitive query factory, switching between
-'part' and `None`.
-
- >>> factory = zc.relation.TransposingTransitiveQueriesFactory(
- ... 'part', None)
- >>> catalog.defaultTransitiveQueriesFactory = factory
-
-Let's add a transitive index in too, of the hierarchy looking down.
-
- >>> catalog.addTransitiveIndex('part', None)
-
-Let's create and add a few organizations. We'll make a structure like this::
-
- Ynod Corp Mangement Zookd Corp Management
- / | \ / | \
- Ynod Devs Ynod SAs Ynod Admins Zookd Admins Zookd SAs Zookd Devs
- / \ \ / / \
-Y3L4 Proj Bet Proj Ynod Zookd Task Force Zookd hOgnmd Zookd Nbd
-
- >>> orgs = root['organizations'] = BTrees.family32.OO.BTree()
- >>> for nm, parts in (
- ... ('Y3L4 Proj', ()),
- ... ('Bet Proj', ()),
- ... ('Ynod Zookd Task Force', ()),
- ... ('Zookd hOgnmd', ()),
- ... ('Zookd Nbd', ()),
- ... ('Ynod Devs', ('Y3L4 Proj', 'Bet Proj')),
- ... ('Ynod SAs', ()),
- ... ('Ynod Admins', ('Ynod Zookd Task Force',)),
- ... ('Zookd Admins', ('Ynod Zookd Task Force',)),
- ... ('Zookd SAs', ()),
- ... ('Zookd Devs', ('Zookd hOgnmd', 'Zookd Nbd')),
- ... ('Ynod Corp Management', ('Ynod Devs', 'Ynod SAs', 'Ynod Admins')),
- ... ('Zookd Corp Management', ('Zookd Devs', 'Zookd SAs',
- ... 'Zookd Admins'))):
- ... org = Organization(nm)
- ... for part in parts:
- ... ignore = org.parts.insert(registry.getId(orgs[part]))
- ... orgs[nm] = org
- ... catalog.index(org)
- ...
-
-Now we can search. To do this, we can use some of the token methods that
-the catalog provides. The most commonly used is `tokenizeQuery`. It takes a
-query with values that are not tokenized and converts them to values that are
-tokenized.
-
- >>> Ynod_SAs_id = registry.getId(orgs['Ynod SAs'])
- >>> catalog.tokenizeQuery({None: orgs['Ynod SAs']}) == {
- ... None: Ynod_SAs_id}
- True
- >>> Zookd_SAs_id = registry.getId(orgs['Zookd SAs'])
- >>> Zookd_Devs_id = registry.getId(orgs['Zookd Devs'])
- >>> catalog.tokenizeQuery(
- ... {None: zc.relation.any(orgs['Zookd SAs'], orgs['Zookd Devs'])}
- ... ) == {
- ... None: zc.relation.any(Zookd_SAs_id, Zookd_Devs_id)}
- True
-
-Of course, right now doing this with 'part' alone is kind of silly, since it
-does not change within the relation catalog (because we said that dump and
-load were `None`, as discussed above).
-
- >>> catalog.tokenizeQuery({'part': Ynod_SAs_id}) == {
- ... 'part': Ynod_SAs_id}
- True
- >>> catalog.tokenizeQuery(
- ... {'part': zc.relation.any(Zookd_SAs_id, Zookd_Devs_id)}
- ... ) == {'part': zc.relation.any(Zookd_SAs_id, Zookd_Devs_id)}
- True
-
-It is so common that we're going to assign it to a variable in our example.
-Then we'll do a search or two.
-
-So...find the relations that Ynod Devs supervise.
-
- >>> t = catalog.tokenizeQuery
- >>> res = list(catalog.findRelationTokens(t({None: orgs['Ynod Devs']})))
-
-OK...we used `findRelationTokens`, as opposed to `findRelations`, so res
-is a couple of numbers now. How do we convert them back?
-`resolveRelationTokens` will do the trick.
-
- >>> len(res)
- 3
- >>> sorted(catalog.resolveRelationTokens(res))
- ... # doctest: +NORMALIZE_WHITESPACE
- [<Organization instance "Bet Proj">, <Organization instance "Y3L4 Proj">,
- <Organization instance "Ynod Devs">]
-
-`resolveQuery` is the mirror image of `tokenizeQuery`: it converts
-tokenized queries to queries with "loaded" values.
-
- >>> original = {'part': zc.relation.any(Zookd_SAs_id, Zookd_Devs_id),
- ... None: orgs['Zookd Devs']}
- >>> tokenized = catalog.tokenizeQuery(original)
- >>> original == catalog.resolveQuery(tokenized)
- True
-
-Likewise, `tokenizeRelations` is the mirror image of `resolveRelationTokens`.
-
- >>> sorted(catalog.tokenizeRelations(
- ... [orgs["Bet Proj"], orgs["Y3L4 Proj"]])) == sorted(
- ... registry.getId(o) for o in
- ... [orgs["Bet Proj"], orgs["Y3L4 Proj"]])
- True
-
-The other token-related methods are as follows:
-
-- `tokenizeValues`, which returns an iterable of tokens for the values
- of the given index name;
-- `resolveValueTokens`, which returns an iterable of values for the tokens of
- the given index name;
-- `tokenizeRelation`, which returns a token for the given relation; and
-- `resolveRelationToken`, which returns a relation for the given token.
-
-Why do we bother with these tokens, instead of hiding them away and making
-the API prettier? [#show_remaining_token_methods]_ By exposing them, we
-enable efficient joining, and efficient use in other contexts. For instance,
-if you use the same intid utility to tokenize in other catalogs, our results
-can be merged with the results of other catalogs. Similarly, you can use
-the results of queries to other catalogs--or even "joins" from earlier
-results of querying this catalog--as query values here.
-
-Custom Transitive Query Factories and Transitive Indexes: Roles
-===============================================================
-
-We have set up the Organization relations. Now let's set up the roles, and
-actually be able to answer the questions that we described at the beginning
-of the document.
-
-In our Roles object, roles and principals will simply be strings--ids, if
-this were a real system. The organization will be a direct object reference.
-
- >>> class Roles(persistent.Persistent):
- ... zope.interface.implements(IRoles)
- ... def __init__(self, principal_id, role_ids, organization):
- ... self.principal_id = principal_id
- ... self.role_ids = BTrees.family32.OI.TreeSet(role_ids)
- ... self.organization = organization
- ... # the rest is for prettier/easier tests
- ... def __repr__(self):
- ... return "<Roles instance (%s has %s in %s)>" % (
- ... self.principal_id, ', '.join(self.role_ids),
- ... self.organization.title)
- ... def __cmp__(self, other):
- ... return cmp(
- ... (self.principal_id, tuple(self.role_ids),
- ... self.organization.title),
- ... (other.principal_id, tuple(other.role_ids),
- ... other.organization.title))
- ...
-
-Now let's add add the value indexes to the relation catalog.
-
- >>> catalog.addValueIndex(IRoles['principal_id'], btree=BTrees.family32.OI)
- >>> catalog.addValueIndex(IRoles['role_ids'], btree=BTrees.family32.OI,
- ... multiple=True, name='role_id')
- >>> catalog.addValueIndex(IRoles['organization'], dump, load)
-
-Those are some slightly new variations of what we've seen in `addValueIndex`
-before, but all mixing and matching on the same ingredients.
-
-As a reminder, here is our data structure::
-
- Ynod Corp Mangement Zookd Corp Management
- / | \ / | \
- Ynod Devs Ynod SAs Ynod Admins Zookd Admins Zookd SAs Zookd Devs
- / \ \ / / \
-Y3L4 Proj Bet Proj Ynod Zookd Task Force Zookd hOgnmd Zookd Nbd
-
-Now let's create and add some roles.
-
- >>> principal_ids = [
- ... 'abe', 'bran', 'cathy', 'david', 'edgar', 'frank', 'gertrude',
- ... 'harriet', 'ignas', 'jacob', 'karyn', 'lettie', 'molly', 'nancy',
- ... 'ophelia', 'pat']
- >>> role_ids = ['user manager', 'writer', 'reviewer', 'publisher']
- >>> get_role = dict((v[0], v) for v in role_ids).__getitem__
- >>> roles = root['roles'] = BTrees.family32.IO.BTree()
- >>> next = 0
- >>> for prin, org, role_ids in (
- ... ('abe', orgs['Zookd Corp Management'], 'uwrp'),
- ... ('bran', orgs['Ynod Corp Management'], 'uwrp'),
- ... ('cathy', orgs['Ynod Devs'], 'w'),
- ... ('cathy', orgs['Y3L4 Proj'], 'r'),
- ... ('david', orgs['Bet Proj'], 'wrp'),
- ... ('edgar', orgs['Ynod Devs'], 'up'),
- ... ('frank', orgs['Ynod SAs'], 'uwrp'),
- ... ('frank', orgs['Ynod Admins'], 'w'),
- ... ('gertrude', orgs['Ynod Zookd Task Force'], 'uwrp'),
- ... ('harriet', orgs['Ynod Zookd Task Force'], 'w'),
- ... ('harriet', orgs['Ynod Admins'], 'r'),
- ... ('ignas', orgs['Zookd Admins'], 'r'),
- ... ('ignas', orgs['Zookd Corp Management'], 'w'),
- ... ('karyn', orgs['Zookd Corp Management'], 'uwrp'),
- ... ('karyn', orgs['Ynod Corp Management'], 'uwrp'),
- ... ('lettie', orgs['Zookd Corp Management'], 'u'),
- ... ('lettie', orgs['Ynod Zookd Task Force'], 'w'),
- ... ('lettie', orgs['Zookd SAs'], 'w'),
- ... ('molly', orgs['Zookd SAs'], 'uwrp'),
- ... ('nancy', orgs['Zookd Devs'], 'wrp'),
- ... ('nancy', orgs['Zookd hOgnmd'], 'u'),
- ... ('ophelia', orgs['Zookd Corp Management'], 'w'),
- ... ('ophelia', orgs['Zookd Devs'], 'r'),
- ... ('ophelia', orgs['Zookd Nbd'], 'p'),
- ... ('pat', orgs['Zookd Nbd'], 'wrp')):
- ... assert prin in principal_ids
- ... role_ids = [get_role(l) for l in role_ids]
- ... role = roles[next] = Roles(prin, role_ids, org)
- ... role.key = next
- ... next += 1
- ... catalog.index(role)
- ...
-
-Now we can begin to do searches [#real_value_tokens]_. They are not
-transitive yet--we have not yet defined how to deal with these
-transitively, and the default transitive queries factory doesn't know
-how to interpret them.
-
-What are all the role settings for ophelia?
-
- >>> sorted(catalog.findRelations({'principal_id': 'ophelia'}))
- ... # doctest: +NORMALIZE_WHITESPACE
- [<Roles instance (ophelia has publisher in Zookd Nbd)>,
- <Roles instance (ophelia has reviewer in Zookd Devs)>,
- <Roles instance (ophelia has writer in Zookd Corp Management)>]
-
-That answer does not need to be transitive.
-
-Next question. Where does ophelia have the 'writer' role?
-
- >>> list(catalog.findValues(
- ... 'organization', {'principal_id': 'ophelia',
- ... 'role_id': 'writer'}))
- [<Organization instance "Zookd Corp Management">]
-
-Well, that's correct intransitively. Do we need a transitive queries
-factory? No! This is a great chance to look at the token join we talked
-about in the previous section. This should actually be a two-step
-operation: find all of the organizations in which ophelia has writer,
-and then find all of the transitive parts to that organization.
-
- >>> sorted(catalog.findRelations({None: zc.relation.Any(
- ... catalog.findValueTokens('organization',
- ... {'principal_id': 'ophelia',
- ... 'role_id': 'writer'}))}))
- ... # doctest: +NORMALIZE_WHITESPACE
- [<Organization instance "Ynod Zookd Task Force">,
- <Organization instance "Zookd Admins">,
- <Organization instance "Zookd Corp Management">,
- <Organization instance "Zookd Devs">,
- <Organization instance "Zookd Nbd">,
- <Organization instance "Zookd SAs">,
- <Organization instance "Zookd hOgnmd">]
-
-That's more like it.
-
-Next question. What users have roles in the 'Zookd Devs' organization?
-Intransitively, that's pretty easy.
-
- >>> sorted(catalog.findValueTokens(
- ... 'principal_id', t({'organization': orgs['Zookd Devs']})))
- ['nancy', 'ophelia']
-
-Transitively, we should do another join.
-
- >>> org_id = registry.getId(orgs['Zookd Devs'])
- >>> sorted(catalog.findValueTokens(
- ... 'principal_id', {
- ... 'organization': zc.relation.any(
- ... org_id, *catalog.findRelationTokens({'part': org_id}))}))
- ['abe', 'ignas', 'karyn', 'lettie', 'nancy', 'ophelia']
-
-That's a little awkward, but it does the trick.
-
-Last question, and the kind of question that started the entire example.
- What roles does ophelia have in the "Zookd Nbd" organization?
-
- >>> list(catalog.findValueTokens(
- ... 'role_id', t({'organization': orgs['Zookd Nbd'],
- ... 'principal_id': 'ophelia'})))
- ['publisher']
-
-Intransitively, that's correct. But, transitively, ophelia also has
-reviewer and writer, and that's the answer we want to be able to get quickly.
-
-We could ask the question a different way, then, again leveraging a join.
-
- >>> org_id = registry.getId(orgs['Zookd Nbd'])
- >>> sorted(catalog.findValueTokens(
- ... 'role_id', {
- ... 'organization': zc.relation.any(
- ... org_id,
- ... *catalog.findRelationTokens({'part': org_id})),
- ... 'principal_id': 'ophelia'}))
- ['publisher', 'reviewer', 'writer']
-
-It has the same annoyance factor as the previous join, but, again, it
-does the trick. Let's make a function that makes that call so we can
-compare its results with some others.
-
- >>> def getRolesInOrganization(principal_id, org):
- ... org_id = registry.getId(org)
- ... return sorted(catalog.findValueTokens(
- ... 'role_id', {
- ... 'organization': zc.relation.any(
- ... org_id,
- ... *catalog.findRelationTokens({'part': org_id})),
- ... 'principal_id': principal_id}))
- ...
- >>> getRolesInOrganization('ophelia', orgs['Zookd Nbd'])
- ['publisher', 'reviewer', 'writer']
-
-But what if we want to define a custom transitive queries factory to
-make the query transitive in the way we expect?
-
-A transitive query factory is a callable that takes four arguments: a
-chain of relations, a query, the catalog, and a dict cache. The last
-token in the relation chain is the most recent. The query is the one
-that *started* the search (with some exceptions for transitive indexes
-that we'll describe later). The output is expected to be an iterable of
-queries to search further from the given chain of relations.
-
-Here's a flawed approach to this problem.
-
- >>> def flawed_factory(relchain, query, catalog, cache):
- ... if (len(query) != 2 or
- ... 'organization' not in query or
- ... 'principal_id' not in query):
- ... return
- ... current = catalog.findValueTokens( # don't mutate this result!!
- ... 'organization', {None: relchain[-1]}, 1)
- ... if current:
- ... organizations = catalog.findRelationTokens(
- ... {'part': zc.relation.Any(current)})
- ... query['organization'] = zc.relation.Any(organizations)
- ... yield query
- ...
-
-That works for our current example.
-
- >>> sorted(catalog.findValueTokens(
- ... 'role_id', t({'organization': orgs['Zookd Nbd'],
- ... 'principal_id': 'ophelia'}),
- ... transitiveQueriesFactory=flawed_factory))
- ['publisher', 'reviewer', 'writer']
-
-However, it won't work for other similar queries.
-
- >>> getRolesInOrganization('abe', orgs['Zookd Nbd'])
- ['publisher', 'reviewer', 'user manager', 'writer']
- >>> sorted(catalog.findValueTokens(
- ... 'role_id', t({'organization': orgs['Zookd Nbd'],
- ... 'principal_id': 'abe'}),
- ... transitiveQueriesFactory=flawed_factory))
- []
-
-oops.
-
-The flawed_factory is actually a useful pattern for more typical relation
-traversal. It goes from relation to relation to relation, and ophelia has
-connected relations all the way to the top. However, abe only has them at
-the top, so nothing is traversed.
-
-We only need a slight change, then. We need to be able to traverse
-"through thin air".
-
-We need a new transitive queries factory, and we'll index it as well.
-
-First, we want to define our transitive query factory. As described in the
-introduction to the problem, the question to ask is to find
-the roles a user has in one organization and above. Because of the
-arrangement of these relationships, this is not a simple transposition of
-value names, as we saw in the README example (and as is common).
-
-We want a search that, given an organization and a principal_id, searches
-up for the same principal_id in parent organizations. As a new wrinkle,
-we need to be able to search up parent organizations even if there is not a
-match for a given organization.
-
- ##>>> def transitiveFactory(relchain, query, index, cache):
-
-
-XXX make default transitive queries factory able to be multiple
-
-XXX indexing funky transitive query factory will not update correctly
- because the search charcteristics are different than the update
- characteristics
-
-
-
-.. ......... ..
-.. Footnotes ..
-.. ......... ..
-
-.. [#ZODB] Here we will set up a ZODB instance for us to use.
-
- >>> from ZODB.tests.util import DB
- >>> db = DB()
- >>> conn = db.open()
- >>> root = conn.root()
-
-.. [#faux_intid] Here's a simple persistent keyreference. Notice that it is
- not persistent itself: this is important for conflict resolution to be
- able to work (which we don't show here, but we're trying to lean more
- towards real usage for this example).
-
- >>> class Reference(object): # see zope.app.keyreference
- ... def __init__(self, obj):
- ... self.object = obj
- ... def __cmp__(self, other):
- ... # this doesn't work during conflict resolution. See
- ... # zope.app.keyreference.persistent, 3.5 release, for current
- ... # best practice.
- ... if not isinstance(other, Reference):
- ... raise ValueError('can only compare with Reference objects')
- ... if self.object._p_jar is None or other.object._p_jar is None:
- ... raise ValueError(
- ... 'can only compare when both objects have connections')
- ... return cmp(
- ... (self.object._p_jar.db().database_name, self.object._p_oid),
- ... (other.object._p_jar.db().database_name, other.object._p_oid),
- ... )
- ...
-
- Here's a simple integer identifier tool.
-
- >>> import persistent
- >>> import BTrees
- >>> class Registry(persistent.Persistent): # see zope.app.intid
- ... def __init__(self, family=BTrees.family32):
- ... self.family = family
- ... self.ids = self.family.IO.BTree()
- ... self.refs = self.family.OI.BTree()
- ... def getId(self, obj):
- ... if not isinstance(obj, persistent.Persistent):
- ... raise ValueError('not a persistent object', obj)
- ... if obj._p_jar is None:
- ... self._p_jar.add(obj)
- ... ref = Reference(obj)
- ... id = self.refs.get(ref)
- ... if id is None:
- ... # naive for conflict resolution; see zope.app.intid
- ... if self.ids:
- ... id = self.ids.maxKey() + 1
- ... else:
- ... id = self.family.minint
- ... self.ids[id] = ref
- ... self.refs[ref] = id
- ... return id
- ... def __contains__(self, obj):
- ... if (not isinstance(obj, persistent.Persistent) or
- ... obj._p_oid is None):
- ... return False
- ... return Reference(obj) in self.refs
- ... def getObject(self, id, default=None):
- ... res = self.ids.get(id, None)
- ... if res is None:
- ... return default
- ... else:
- ... return res.object
- ... def remove(self, r):
- ... if isinstance(r, (int, long)):
- ... self.refs.pop(self.ids.pop(r))
- ... elif (not isinstance(r, persistent.Persistent) or
- ... r._p_oid is None):
- ... raise LookupError(r)
- ... else:
- ... self.ids.pop(self.refs.pop(Reference(r)))
- ...
- >>> registry = root['registry'] = Registry()
-
- >>> import transaction
- >>> transaction.commit()
-
-.. [#show_remaining_token_methods] For what it's worth, here are some small
- examples of the remaining token-related methods.
-
- These two are the singular versions of `tokenizeRelations` and
- `resolveRelationTokens`.
-
- `tokenizeRelation` returns a token for the given relation.
-
- >>> catalog.tokenizeRelation(orgs['Zookd Corp Management']) == (
- ... registry.getId(orgs['Zookd Corp Management']))
- True
-
- `resolveRelationToken` returns a relation for the given token.
-
- >>> catalog.resolveRelationToken(registry.getId(
- ... orgs['Zookd Corp Management'])) is orgs['Zookd Corp Management']
- True
-
- The "values" ones are a bit lame to show now, since the only value
- we have right now is not tokenized but used straight up. But here
- goes, showing some fascinating no-ops.
-
- `tokenizeValues`, returns an iterable of tokens for the values of
- the given index name.
-
- >>> list(catalog.tokenizeValues((1,2,3), 'part'))
- [1, 2, 3]
-
- `resolveValueTokens` returns an iterable of values for the tokens of
- the given index name.
-
- >>> list(catalog.resolveValueTokens((1,2,3), 'part'))
- [1, 2, 3]
-
-.. [#real_value_tokens] We can also show the values token methods more
- sanely now.
-
- >>> original = sorted((orgs['Zookd Devs'], orgs['Ynod SAs']))
- >>> tokens = list(catalog.tokenizeValues(original, 'organization'))
- >>> original == sorted(catalog.resolveValueTokens(tokens, 'organization'))
- True
Modified: zc.relation/trunk/src/zc/relation/searchindex.py
===================================================================
--- zc.relation/trunk/src/zc/relation/searchindex.py 2007-08-08 08:53:01 UTC (rev 78682)
+++ zc.relation/trunk/src/zc/relation/searchindex.py 2007-08-08 10:49:49 UTC (rev 78683)
@@ -34,16 +34,12 @@
name = index = catalog = None
- def __init__(self, forward, reverse, static=(), names=()):
+ def __init__(self, forward, reverse, names=()):
# normalize
- if getattr(static, 'items', None) is not None:
- static = static.items()
- self.static = tuple(sorted(static))
self.names = BTrees.family32.OO.Bucket([(nm, None) for nm in names])
self.forward = forward
self.reverse = reverse
- self.update = frozenset(
- itertools.chain((k for k, v in static), (forward, reverse)))
+ self.update = frozenset((forward, reverse))
self.factory = zc.relation.queryfactory.TransposingTransitive(
forward, reverse)
@@ -61,7 +57,6 @@
new.forward = self.forward
new.reverse = self.reverse
new.factory = self.factory
- new.static = self.static
if self.index is not None:
if catalog is None:
catalog = self.catalog
@@ -89,24 +84,18 @@
if token not in self.index:
self._index(token)
# name, query_names, static_values, maxDepth, filter, queryFactory
- query_names = (self.forward,) + tuple(k for k, v in self.static)
- res = [(None, query_names, self.static, None, None, self.factory)]
+ res = [(None, (self.forward,), (), None, None, self.factory)]
for nm in self.names:
res.append(
- (nm, query_names, self.static, None, None, self.factory))
+ (nm, (self.forward,), (), None, None, self.factory))
return res
- def _buildQuery(self, dynamic):
- res = BTrees.family32.OO.Bucket(self.static)
- res[dynamic] = None
- return res
-
def _index(self, token, removals=None, remove=False):
starts = set((token,))
if removals and self.forward in removals:
starts.update(t for t in removals[self.forward] if t is not None)
tokens = set()
- reverseQuery = self._buildQuery(self.reverse)
+ reverseQuery = BTrees.family32.OO.Bucket(((self.reverse, None),))
for token in starts:
getQueries = self.factory(dict(reverseQuery), self.catalog)
tokens.update(chain[-1] for chain in
@@ -125,7 +114,7 @@
# now we go back and try to fill them back in again. If there had
# been a cycle, we can see now that we have to work down.
relTools = self.catalog.getRelationModuleTools()
- query = self._buildQuery(self.forward)
+ query = BTrees.family32.OO.Bucket(((self.forward, None),))
getQueries = self.factory(query, self.catalog)
for token in tokens:
if token in self.index: # must have filled it in during a cycle
@@ -222,10 +211,6 @@
# end listener interface
def getResults(self, name, query, maxDepth, filter, queryFactory):
- for k, v in self.static:
- if query[k] != v:
- return
- # TODO: try to use intransitive index, if available
rels = self.catalog.getRelationTokens(query)
if name is None:
tools = self.catalog.getRelationModuleTools()
More information about the Checkins
mailing list