X-Git-Url: https://svn.cri.ensmp.fr/git/Plinn.git/blobdiff_plain/e9f249dc773d6b5b9648310e7bfcc5a2d3cbb330..b6872cf96bf90c20a97563f6bdc63823f0698613:/catalog.py diff --git a/catalog.py b/catalog.py index 382fc57..69bb701 100644 --- a/catalog.py +++ b/catalog.py @@ -5,94 +5,134 @@ from Products.CMFCore.interfaces import IIndexableObject from Products.CMFCore.CatalogTool import CatalogTool as BaseCatalogTool from Products.CMFCore.CatalogTool import IndexableObjectWrapper from Products.PageTemplates.PageTemplateFile import PageTemplateFile -from Products.CMFCore.permissions import ModifyPortalContent +from Products.CMFCore.permissions import ModifyPortalContent, ManagePortal from zope.component import queryMultiAdapter from Products.ZCatalog.Catalog import Catalog import transaction from solr import * +# imports for Catalog class +from Products.PluginIndexes.interfaces import ILimitedResultIndex +from Products.ZCatalog.Lazy import LazyMap, LazyCat, LazyValues +from BTrees.IIBTree import intersection, IISet +from BTrees.IIBTree import weightedIntersection +import warnings + +_VOLATILE_SOLR_NAME = '_v_solrConnection' + class SolrTransactionHook : ''' commit solr couplé sur le commit de la ZODB ''' - def __init__(self, connection) : - self.connection = connection + def __init__(self, context, con) : + self.context = context + self.con = con def __call__(self, status) : if status : - self.connection.commit() - self.connection.close() + self.con.commit() + self.con.close() else : - self.connection.close() + self.con.close() + try : + delattr(self.context, _VOLATILE_SOLR_NAME) + except AttributeError : + pass class CatalogTool(BaseCatalogTool) : - meta_type = 'Legivoc Catalog' + meta_type = 'Plinn Catalog' security = ClassSecurityInfo() manage_options = (BaseCatalogTool.manage_options[:5] + ({'label' : 'Solr', 'action' : 'manage_solr'},) + BaseCatalogTool.manage_options[5:]) - manage_solr = PageTemplateFile('www/manage_solr', globals()) + manage_solr = PageTemplateFile('www/manage_solr.pt', globals(), __name__='manage_solr') + def __init__(self, idxs=[]) : super(CatalogTool, self).__init__() - self._catalog = DelegatedCatalog() + self._catalog = DelegatedCatalog(self) self.solr_url = 'http://localhost:8983/solr' self.delegatedIndexes = ('Title', 'Description', 'SearchableText') + security.declarePublic('getDelegatedIndexes') + def getDelegatedIndexes(self) : + """ read the method name """ + return self.delegatedIndexes + + security.declareProtected(ManagePortal, 'setDelegatedIndexes') + def setDelegatedIndexes(self, indexes, REQUEST=None) : + """setDelegatedIndexes documentation""" + self.delegatedIndexes = tuple([i.strip() for i in indexes if i.strip()]) + if REQUEST : + REQUEST.RESPONSE.redirect(self.absolute_url() + '/manage_solr?manage_tabs_message=Saved changes.') + + def _getSolrConnection(self) : + if not hasattr(self, _VOLATILE_SOLR_NAME) : + con = SolrConnection(self.solr_url) + setattr(self, _VOLATILE_SOLR_NAME, con) + txn = transaction.get() + txn.addAfterCommitHook(SolrTransactionHook(self, con)) + return getattr(self, _VOLATILE_SOLR_NAME) + security.declarePrivate('solrAdd') - def solrAdd(self, object, idxs=[], uid=None) : - if IIndexableObject.providedBy(object): - w = object - else: - w = queryMultiAdapter( (object, self), IIndexableObject ) - if w is None: - # BBB - w = IndexableObjectWrapper(object, self) - - uid = uid if uid else self.__url(object) - idxs = idxs if idxs !=[] else self.delegatedIndexes + def solrAdd(self, w, uid, idxs) : + idxs = idxs if idxs else self.delegatedIndexes + # Filter out delegated indexes + idxs = [i for i in idxs if i in self.delegatedIndexes] data = {'id' : uid} for name in idxs : attr = getattr(w, name, '') data[name] = attr() if callable(attr) else attr - c = SolrConnection(self.solr_url) + c = self._getSolrConnection() c.add(**data) - txn = transaction.get() - txn.addAfterCommitHook(SolrTransactionHook(c)) - # PortalCatalog api overloads - security.declareProtected(ModifyPortalContent, 'indexObject') - def indexObject(self, object) : - """ Add to catalog and send to Solr """ - super(CatalogTool, self).indexObject(object) - self.solrAdd(object) - - security.declarePrivate('reindexObject') - def reindexObject(self, object, idxs=[], update_metadata=1, uid=None): - super(CatalogTool, self).reindexObject(object, - idxs=idxs, - update_metadata=update_metadata, - uid=uid) - if idxs != []: + def catalog_object(self, obj, uid=None, idxs=None, update_metadata=1, + pghandler=None): + # Wraps the object with workflow and accessibility + # information just before cataloging. + if IIndexableObject.providedBy(obj): + w = obj + else: + w = queryMultiAdapter( (obj, self), IIndexableObject ) + if w is None: + # BBB + w = IndexableObjectWrapper(obj, self) + + idxs_ = idxs + if idxs: # Filter out invalid indexes. valid_indexes = self._catalog.indexes.keys() - idxs = [i for i in idxs if i in valid_indexes and i in self.delegatedIndexes] - else : - idxs = self.delegatedIndexes + idxs_ = [i for i in idxs if i in valid_indexes] + + super(CatalogTool, self).catalog_object(w, uid, idxs_, update_metadata, pghandler) + self.solrAdd(w, uid, idxs) + + security.declarePrivate('reindexObject') + def reindexObject(self, object, idxs=[], update_metadata=1, uid=None): + """Update catalog after object data has changed. - if idxs : - self.solrAdd(object, idxs=idxs, uid=uid) + The optional idxs argument is a list of specific indexes + to update (all of them by default). + + The update_metadata flag controls whether the object's + metadata record is updated as well. + + If a non-None uid is passed, it will be used as the catalog uid + for the object instead of its physical path. + """ + if uid is None: + uid = self.__url(object) + + self.catalog_object(object, uid, idxs, update_metadata) security.declarePrivate('unindexObject') def unindexObject(self, object): """Remove from catalog. """ super(CatalogTool, self).unindexObject(object) - c = SolrConnection(self.solr_url) + c = self._getSolrConnection() url = self.__url(object) c.delete(id=url) - txn = transaction.get() - txn.addAfterCommitHook(SolrTransactionHook(c)) InitializeClass(CatalogTool) @@ -100,6 +140,30 @@ InitializeClass(CatalogTool) class DelegatedCatalog(Catalog) : '''C'est ici qu'on délègue effectivement à Solr ''' + def __init__(self, zcat, brains=None) : + Catalog.__init__(self, brains=brains) + self.zcat = zcat + + def delegateSearch(self, query, plan) : + ''' + retours faux : + None signifie : pas de délégation, il faut continuer à interroger les autres index. + IISet() vide : pas de résultat lors de la délégation, on peut arrêter la recherche. + ''' + indexes = set(query.keys()).intersection(set(self.zcat.delegatedIndexes)) + if not indexes : + return None + delegatedQuery = {} + for i in indexes : + delegatedQuery[i] = query.pop(i) + try : plan.remove(i) + except ValueError : pass + c = SolrConnection(self.zcat.solr_url) + q =' AND '.join(['%s:"%s"' % item for item in delegatedQuery.items()]) + resp = c.query(q, fields='id', rows=len(self)) + c.close() + return IISet(filter(None, [self.uids.get(r['id']) for r in resp.results])) + def search(self, query, sort_index=None, reverse=0, limit=None, merge=1): """Iterate through the indexes, applying the query to each one. If merge is true then return a lazy result set (sorted if appropriate) @@ -129,6 +193,11 @@ class DelegatedCatalog(Catalog) : plan = cr.plan() if not plan: plan = self._sorted_search_indexes(query) + + # délégation + rs = self.delegateSearch(query, plan) + if rs is not None and not rs : + return LazyCat([]) indexes = self.indexes.keys() for i in plan: