[Zope-CVS] SVN: zope2/Products/ZemanticIndex/ True first pass
(testing framework only).
Tres Seaver
tseaver at zope.com
Thu Mar 24 18:29:14 EST 2005
Log message for revision 381:
True first pass (testing framework only).
Changed:
A zope2/Products/ZemanticIndex/README.txt
A zope2/Products/ZemanticIndex/__init__.py
A zope2/Products/ZemanticIndex/index.py
A zope2/Products/ZemanticIndex/interfaces.py
A zope2/Products/ZemanticIndex/tests/
A zope2/Products/ZemanticIndex/tests/__init__.py
A zope2/Products/ZemanticIndex/tests/test_cataloguing.py
-=-
Added: zope2/Products/ZemanticIndex/README.txt
===================================================================
--- zope2/Products/ZemanticIndex/README.txt 2005-03-24 23:28:04 UTC (rev 380)
+++ zope2/Products/ZemanticIndex/README.txt 2005-03-24 23:29:14 UTC (rev 381)
@@ -0,0 +1,26 @@
+ZemanticIndex Product README
+
+ Overview
+
+ This product allows a Zemantic triple store to index content within
+ a Zope2 catalog.
+
+ Indexing Content
+
+ The index adapts the object being indexed (the "subject", in RDF terms)
+ to IZemanticInfo, which returns a sequence of two-tuple statement
+ fragments about the subject. Thses statements are in the form,
+ (<predicate>, <object>). The index then constructs three-tuples from
+ these values, in the form (<subject>, <predicate>, <object>), using
+ the catalog's UID as the first first term
+
+ Searching Content
+
+ The index expects its 'query' object to be adaptable to IZemanticInfo.
+ It uses the two-tuples supplied by that adapter to synthesize a query
+ for matching documents, duing an "and" search by default. The 'operator'
+ passed to the index can be either 'and' (perform an intersection), or
+ an 'or' (perform a union).
+
+ As a convenience for HTML-generated queries, the indes will also
+ accept its 'query' term as a sequence of (<predicate>, <object>) tuples.
Added: zope2/Products/ZemanticIndex/__init__.py
===================================================================
--- zope2/Products/ZemanticIndex/__init__.py 2005-03-24 23:28:04 UTC (rev 380)
+++ zope2/Products/ZemanticIndex/__init__.py 2005-03-24 23:29:14 UTC (rev 381)
@@ -0,0 +1,7 @@
+""" Zope2 product wrapping a Zemantic triple-store as a PluginIndex.
+
+$Id$
+"""
+
+def initialize(context):
+ pass
Added: zope2/Products/ZemanticIndex/index.py
===================================================================
--- zope2/Products/ZemanticIndex/index.py 2005-03-24 23:28:04 UTC (rev 380)
+++ zope2/Products/ZemanticIndex/index.py 2005-03-24 23:29:14 UTC (rev 381)
@@ -0,0 +1,129 @@
+""" Classes: ZemanticIndex
+
+$Id$
+"""
+from zope.interface import implements
+
+from Products.ZemanticIndex.interfaces import IZemanticIndex
+from Products.ZemanticIndex.interfaces import IZemanticInfo
+
+class ZemanticIndex:
+ """ Adapt the zemantic triple store to a Zope2 plugin index.
+ """
+ implements(IZemanticIndex)
+
+ def __init__(self, id):
+ self._id = id
+ self._stuff = {} # XXX: Stubbed out to get tests going
+
+ #
+ # IPluginIndex implementation
+ #
+ def getId(self):
+ """ See interfaces.IPluginIndex.
+ """
+ return self._id
+
+ def getEntryForObject(self, documentId, default=None):
+ """ See interfaces.IPluginIndex.
+
+ o Returns a set of triples.
+ """
+ return self._stuff.get(documentId, default)
+
+ def getIndexSourceNames(self):
+ """ See interfaces.IPluginIndex.
+
+ o Note that since we index triples via adaptation to IZemanticInfo,
+ this is not meaningful
+ """
+ return ()
+
+ def index_object(self, documentId, obj, threshold=None):
+ """ See interfaces.IPluginIndex.
+ """
+ triples = []
+ info = IZemanticInfo(obj)
+ subject = info.getSubject()
+
+ for predicate, object in info.listPredicatesAndObjects():
+ triples.append((subject, predicate, object))
+
+ self._stuff[documentId] = tuple(triples)
+
+ def unindex_object(self, documentId):
+ """ See interfaces.IPluginIndex.
+ """
+
+ def _apply_index(self, request, cid=''):
+ """ See interfaces.IPluginIndex.
+ """
+
+ def numObjects(self):
+ """ See interfaces.IPluginIndex.
+
+ o This will be the size of the unique set of subjects.
+ """
+ return len(self._stuff.keys())
+
+ def indexSize(self):
+ """ See interfaces.IPluginIndex.
+
+ o This will be the total number of triples indexed.
+ """
+ count = 0
+ for k, v in self._stuff.items():
+ count += len(v)
+ return count
+
+ def clear(self):
+ """ See interfaces.IPluginIndex.
+ """
+ self._stuff.clear()
+
+ #
+ # IZemanticIndex implementation
+ #
+ def listUniqueSubjects(self):
+ """ See IZemanticIndex.
+ """
+ return self._listUnique(0)
+
+ def listUniquePredicates(self):
+ """ See IZemanticIndex.
+ """
+ return self._listUnique(1)
+
+ def listUniqueObjects(self):
+ """ See IZemanticIndex.
+ """
+ return self._listUnique(2)
+
+ def search(self, subject, predicate, object):
+ """ See IZemanticIndex.
+ """
+ def _passes(triple):
+ if subject is not None and triple[0] != subject:
+ return 0
+ if predicate is not None and triple[1] != predicate:
+ return 0
+ if object is not None and triple[2] != object:
+ return 0
+ return 1
+
+ for triples in self._stuff.values():
+ for triple in triples:
+ if _passes(triple):
+ yield triple
+
+ #
+ # Helper methods
+ #
+ def _listUnique(self, which):
+ seen = {}
+ for triples in self._stuff.values():
+ for triple in triples:
+ seen[triple[which]] = 1
+
+ return seen.keys()
+
Added: zope2/Products/ZemanticIndex/interfaces.py
===================================================================
--- zope2/Products/ZemanticIndex/interfaces.py 2005-03-24 23:28:04 UTC (rev 380)
+++ zope2/Products/ZemanticIndex/interfaces.py 2005-03-24 23:29:14 UTC (rev 381)
@@ -0,0 +1,107 @@
+""" ZemanticIndex interfaces
+
+$Id$
+"""
+
+from zope.interface import Interface
+
+class IZemanticInfo(Interface):
+
+ def getSubject():
+ """ Return the subject ID for the object being indexed.
+
+ o Returned value must be one of the unicode-string-derived
+ statement element types from the zemantic package.
+ """
+
+ def listPredicatesAndObjects():
+ """ Return an iterable containing two-tuples describing the object.
+
+ o Tuples should be in the form (<predicate>, <object>)
+
+ o Each member of the tuple must be one of the unicode-string-derived
+ statement element types from the zemantic package.
+ """
+
+class IPluginIndex(Interface):
+
+ #
+ # Zope2's PluginIndexInterface (could be five:bridge'd).
+ #
+ def getId():
+ """ Return the ID of the index.
+ """
+
+ def getEntryForObject(documentId, default=None):
+ """ Return all information contained for 'documentId'.
+ """
+
+ def getIndexSourceNames():
+ """ Return a sequence of indexed attribute names.
+ """
+
+ def index_object(documentId, obj, threshold=None):
+ """ Index an object.
+
+ o 'documentId' is the integer ID of the document.
+
+ o 'obj' is the object to be indexed.
+
+ o 'threshold' is the number of values to process between committing
+ subtransactions. If None, subtransactions are disabled.
+ """
+
+ def unindex_object(documentId):
+ """ Remove all information about 'documentId' from the index.
+ """
+
+ def _apply_index(request, cid=''):
+ """ Apply the index to query parameters given in 'request'.
+
+ o The 'request' argument should be a mapping object.
+
+ o If the request does not contain the needed parametrs, then
+ return None is returned.
+
+ o If the request contains a parameter with the name of the
+ column and this parameter is either a Record or a class
+ instance then it is assumed that the parameters of this index
+ are passed as attributes.
+
+ o Return a two-tuple, (<resultset>, <argnames>).
+ The first object is a ResultSet containing the 'documentIds'
+ of the matching records. The second object is a tuple
+ containing the names of all data fields used from the request.
+ """
+
+ def numObjects():
+ """ Return the number of indexed objects.
+ """
+
+ def indexSize():
+ """ Return the size of the index in terms of distinct values.
+ """
+
+ def clear():
+ """ Empty the index.
+ """
+
+class IZemanticIndex(IPluginIndex):
+
+ def listUniqueSubjects():
+ """ Return an iterable of the unique subjects in our triple store.
+ """
+
+ def listUniquePredicates():
+ """ Return an iterable of the unique predicates in our triple store.
+ """
+
+ def listUniqueObjects():
+ """ Return an iterable of the unique objects in our triple store.
+ """
+
+ def search(subject, predicate, object):
+ """ Return a set of triples matching the supplied values.
+
+ o For any of the values supplied, None is a wildcard.
+ """
Added: zope2/Products/ZemanticIndex/tests/__init__.py
===================================================================
--- zope2/Products/ZemanticIndex/tests/__init__.py 2005-03-24 23:28:04 UTC (rev 380)
+++ zope2/Products/ZemanticIndex/tests/__init__.py 2005-03-24 23:29:14 UTC (rev 381)
@@ -0,0 +1,4 @@
+""" ZemanticIndex product unit tests
+
+$Id$
+"""
Added: zope2/Products/ZemanticIndex/tests/test_cataloguing.py
===================================================================
--- zope2/Products/ZemanticIndex/tests/test_cataloguing.py 2005-03-24 23:28:04 UTC (rev 380)
+++ zope2/Products/ZemanticIndex/tests/test_cataloguing.py 2005-03-24 23:29:14 UTC (rev 381)
@@ -0,0 +1,109 @@
+""" Test the cataloguing behavior of the index.
+
+$Id$
+"""
+import unittest
+
+class ZemanticIndexCatalogTests(unittest.TestCase):
+
+ def _getTargetClass(self):
+ from Products.ZemanticIndex.index import ZemanticIndex
+ return ZemanticIndex
+
+ def _makeOne(self, *args, **kw):
+ return self._getTargetClass()(*args, **kw)
+
+ def _makePerson(self, id, assertions):
+
+ from zope.interface import implements
+ from Products.ZemanticIndex.interfaces import IZemanticInfo
+
+ class _Person:
+
+ implements(IZemanticInfo)
+
+ def __init__(self, name, assertions):
+ self._name = name
+ self._assertions = tuple(assertions)
+
+ def getSubject(self):
+ return self._name
+
+ def listPredicatesAndObjects(self):
+ return self._assertions
+
+ return _Person(id, assertions)
+
+ def test_IZemanticIndex_conformance(self):
+ from zope.interface.verify import verifyClass
+ from Products.ZemanticIndex.interfaces import IZemanticIndex
+ verifyClass(IZemanticIndex, self._getTargetClass())
+
+ def test_empty(self):
+ index = self._makeOne(id='empty')
+ self.assertEqual(index.getId(), 'empty')
+
+ self.assertEqual(index.getEntryForObject('nonesuch'), None)
+ marker = []
+ self.failUnless(index.getEntryForObject('nonesuch', marker) is marker)
+
+ self.assertEqual(index.getIndexSourceNames(), ())
+ self.assertEqual(index.numObjects(), 0)
+ self.assertEqual(index.indexSize(), 0)
+ self.assertEqual(len(list(index.listUniqueSubjects())), 0)
+ self.assertEqual(len(list(index.listUniquePredicates())), 0)
+ self.assertEqual(len(list(index.listUniqueObjects())), 0)
+
+ self.assertEqual(len(list(index.search(None, None, None))), 0)
+
+ def test_index_object_simple(self):
+ index = self._makeOne(id='simple')
+
+ _PERSON_ID = 'TRES'
+ _NAME = 'Tres'
+ _ASSERTIONS = (('likes', 'cheese'),
+ ('hates', 'liver'),
+ ('loves', 'beer'),
+ ('likes', 'baseball'),
+ )
+
+ person = self._makePerson(_NAME, _ASSERTIONS)
+ index.index_object(_PERSON_ID, person)
+
+ info = index.getEntryForObject(_PERSON_ID)
+ self.assertEqual(type(info), type(()))
+ self.assertEqual(len(info), len(_ASSERTIONS))
+
+ for item in info:
+ self.assertEqual(item[0], _NAME)
+ self.failUnless((item[1], item[2]) in _ASSERTIONS)
+
+ self.assertEqual(index.numObjects(), 1)
+ self.assertEqual(index.indexSize(), len(_ASSERTIONS))
+
+ subjects = list(index.listUniqueSubjects())
+ self.assertEqual(len(subjects), 1)
+ self.assertEqual(subjects[0], _NAME)
+
+ predicates = list(index.listUniquePredicates())
+ self.assertEqual(len(predicates), len(_ASSERTIONS)-1)
+ for predicate in [x[0] for x in _ASSERTIONS]:
+ self.failUnless(predicate in predicates)
+
+ objects = list(index.listUniqueObjects())
+ self.assertEqual(len(objects), len(_ASSERTIONS))
+ for object in [x[1] for x in _ASSERTIONS]:
+ self.failUnless(object in objects)
+
+ results = list(index.search(None, None, None))
+ self.assertEqual(len(results), len(_ASSERTIONS))
+ for assertion in _ASSERTIONS:
+ self.failUnless((_NAME, assertion[0], assertion[1]) in results)
+
+def test_suite():
+ return unittest.TestSuite((
+ unittest.makeSuite(ZemanticIndexCatalogTests),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
More information about the Zope-CVS
mailing list