From a0b8e4f39bd8e9372a54f108e4c6be40f0791956 Mon Sep 17 00:00:00 2001 From: helmutm Date: Sat, 26 Apr 2008 12:49:12 +0000 Subject: [PATCH] use catalog's apply() method for querying (instead of searchResults()) - do not loose score (weight) information git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2547 fd906abe-77d9-0310-91a1-e0d9ade77398 --- query.py | 35 +++++++++++++++++++++++++++++------ search/README.txt | 38 ++++++++++++-------------------------- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/query.py b/query.py index 5ec2377..d20523f 100644 --- a/query.py +++ b/query.py @@ -22,9 +22,12 @@ Query management stuff. $Id$ """ +from BTrees.IOBTree import IOBTree +from BTrees.IFBTree import weightedIntersection, weightedUnion, IFBucket from zope import schema, component from zope.interface import Interface, Attribute, implements from zope.app.catalog.interfaces import ICatalog +from zope.app.intid.interfaces import IIntIds from zope.cachedescriptors.property import Lazy from cybertools.typology.interfaces import IType @@ -37,6 +40,16 @@ from loops import util from loops.util import _ +class ScoredSet(set): + + def __init__(self, data=set(), scores={}): + super(ScoredSet, self).__init__(data) + self.scores = scores + + def getScore(self, obj): + return self.scores.get(obj, -1) + + class IQuery(Interface): """ The basic query interface. """ @@ -112,10 +125,12 @@ class FullQuery(BaseQuery): def query(self, text=None, type=None, useTitle=True, useFull=False, conceptTitle=None, conceptUid=None, conceptType=None, **kw): result = set() + scores = {} + intids = component.getUtility(IIntIds) rc = self.queryConceptsWithChildren(title=conceptTitle, uid=conceptUid, type=conceptType) if not rc and not text and '*' in type: # there should be some sort of selection... - return result + return ScoredSet(result, scores) if text or type != 'loops:*': # TODO: this may be highly inefficient! cat = self.catalog if type.endswith('*'): @@ -126,13 +141,21 @@ class FullQuery(BaseQuery): criteria = {'loops_type': (start, end),} if useFull and text: criteria['loops_text'] = text - r1 = set(cat.searchResults(**criteria)) + r1 = cat.apply(criteria) #r1 = set(cat.searchResults(**criteria)) else: - r1 = set() + r1 = IFBucket() #r1 = set() if useTitle and text: + if 'loops_text' in criteria: + del criteria['loops_text'] criteria['loops_title'] = text - r2 = set(cat.searchResults(**criteria)) - result = r1.union(r2) + r2 = cat.apply(criteria) #r2 = set(cat.searchResults(**criteria)) + else: + r2 = IFBucket() #r2 = set() + x, uids = weightedUnion(r1, r2) #result = r1.union(r2) + for r, score in uids.items(): + obj = intids.getObject(r) + result.add(obj) + scores[obj] = score if rc is not None: if result: result = result.intersection(rc) @@ -142,7 +165,7 @@ class FullQuery(BaseQuery): if r.getLoopsRoot() == self.loopsRoot and canListObject(r) and getVersion(r) == r) - return result + return ScoredSet(result, scores) class ConceptQuery(BaseQuery): diff --git a/search/README.txt b/search/README.txt index c50d64e..cb9dad4 100755 --- a/search/README.txt +++ b/search/README.txt @@ -99,33 +99,17 @@ Basic (text/title) search The searchresults.html view, i.e. the SearchResults view class provides the result set of the search via its `results` property. -Before accessing the `results` property we have to prepare a (for testing -purposes fairly primitive) catalog and a resource we can search for: - - >>> from zope.app.catalog.interfaces import ICatalog - >>> class DummyCat(object): - ... implements(ICatalog) - ... def searchResults(self, **criteria): - ... result = [] - ... name = criteria.get('loops_title') - ... if name and name.endswith('*'): name = name[:-1] - ... typeToken = criteria.get('loops_type', ('resource',)) - ... if name or typeToken: - ... if 'concept' in typeToken[0]: - ... if name: - ... result = concepts.get(name) - ... else: - ... tp = concepts[typeToken[0].split(':')[-1]] - ... result = list(tp.getChildren()) - ... else: - ... result = resources.get(name) - ... if not result: return [] - ... return type(result) is list and result or [result] - >>> component.provideUtility(DummyCat()) +Before accessing the `results` property we have to prepare a +resource we can search for and index it in the catalog. >>> from loops.resource import Resource >>> rplone = resources['plone'] = Resource() + >>> from zope.app.catalog.interfaces import ICatalog + >>> from loops import util + >>> catalog = component.getUtility(ICatalog) + >>> catalog.index_doc(int(util.getUidForObject(rplone)), rplone) + >>> from loops.search.browser import SearchResults >>> form = {'search.2.title': True, 'search.2.text': u'plone'} >>> request = TestRequest(form=form) @@ -155,6 +139,7 @@ resource (rplone) from above to one of the topics: >>> cplone = concepts['plone'] = Concept(u'Plone') >>> for c in (czope, czope2, czope3, cplone): ... c.conceptType = topic + ... catalog.index_doc(int(util.getUidForObject(c)), c) >>> czope.assignChild(czope2) >>> czope.assignChild(czope3) >>> czope2.assignChild(cplone) @@ -164,7 +149,6 @@ Now we can fill our search form and execute the query; note that all concepts found are listed, plus all their children and all resources associated with them: - >>> from loops import util >>> uid = util.getUidForObject(concepts['zope']) >>> form = {'search.3.type': 'loops:concept:topic', 'search.3.text': uid} >>> request = TestRequest(form=form) @@ -193,7 +177,7 @@ of the concepts' titles: >>> request = TestRequest(form=form) >>> view = Search(page, request) >>> view.listConcepts() - u"{identifier: 'id', items: [{label: 'Zope (Topic)', name: 'Zope', id: '33'}]}" + u"{identifier: 'id', items: [{label: 'Zope (Topic)', name: 'Zope', id: '34'}, {label: 'Zope 2 (Topic)', name: 'Zope 2', id: '37'}, {label: 'Zope 3 (Topic)', name: 'Zope 3', id: '39'}]}" Preset Concept Types on Search Forms ------------------------------------ @@ -211,7 +195,9 @@ Let's start with a new type, the customer type. >>> cust1 = concepts['cust1'] = Concept(u'Zope Corporation') >>> cust2 = concepts['cust2'] = Concept(u'cyberconcepts') - >>> for c in (cust1, cust2): c.conceptType = customer + >>> for c in (cust1, cust2): + ... c.conceptType = customer + ... catalog.index_doc(int(util.getUidForObject(c)), c) >>> from cybertools.typology.interfaces import IType >>> IType(cust1).qualifiers