Return-Path: X-Original-To: apmail-incubator-bloodhound-commits-archive@minotaur.apache.org Delivered-To: apmail-incubator-bloodhound-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id D5E7CD5E1 for ; Thu, 10 Jan 2013 13:33:21 +0000 (UTC) Received: (qmail 73622 invoked by uid 500); 10 Jan 2013 13:33:21 -0000 Delivered-To: apmail-incubator-bloodhound-commits-archive@incubator.apache.org Received: (qmail 73585 invoked by uid 500); 10 Jan 2013 13:33:21 -0000 Mailing-List: contact bloodhound-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: bloodhound-dev@incubator.apache.org Delivered-To: mailing list bloodhound-commits@incubator.apache.org Received: (qmail 73574 invoked by uid 99); 10 Jan 2013 13:33:21 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 10 Jan 2013 13:33:21 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 10 Jan 2013 13:33:17 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 3018D238896F; Thu, 10 Jan 2013 13:32:58 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: svn commit: r1431350 [1/2] - in /incubator/bloodhound/trunk/bloodhound_search: ./ bhsearch/ bhsearch/templates/ bhsearch/tests/ Date: Thu, 10 Jan 2013 13:32:57 -0000 To: bloodhound-commits@incubator.apache.org From: peter@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20130110133258.3018D238896F@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: peter Date: Thu Jan 10 13:32:56 2013 New Revision: 1431350 URL: http://svn.apache.org/viewvc?rev=1431350&view=rev Log: #BEP-0004: prototype phase 2 patch from Andrej Added: incubator/bloodhound/trunk/bloodhound_search/bhsearch/tests/wiki_search.py incubator/bloodhound/trunk/bloodhound_search/bhsearch/wiki_search.py Modified: incubator/bloodhound/trunk/bloodhound_search/bhsearch/api.py incubator/bloodhound/trunk/bloodhound_search/bhsearch/query_parser.py incubator/bloodhound/trunk/bloodhound_search/bhsearch/templates/bhsearch.html incubator/bloodhound/trunk/bloodhound_search/bhsearch/tests/__init__.py incubator/bloodhound/trunk/bloodhound_search/bhsearch/tests/api.py incubator/bloodhound/trunk/bloodhound_search/bhsearch/tests/index_with_whoosh.py incubator/bloodhound/trunk/bloodhound_search/bhsearch/tests/ticket_search.py incubator/bloodhound/trunk/bloodhound_search/bhsearch/tests/utils.py incubator/bloodhound/trunk/bloodhound_search/bhsearch/tests/web_ui.py incubator/bloodhound/trunk/bloodhound_search/bhsearch/tests/whoosh_backend.py incubator/bloodhound/trunk/bloodhound_search/bhsearch/ticket_search.py incubator/bloodhound/trunk/bloodhound_search/bhsearch/web_ui.py incubator/bloodhound/trunk/bloodhound_search/bhsearch/whoosh_backend.py incubator/bloodhound/trunk/bloodhound_search/setup.py Modified: incubator/bloodhound/trunk/bloodhound_search/bhsearch/api.py URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_search/bhsearch/api.py?rev=1431350&r1=1431349&r2=1431350&view=diff ============================================================================== --- incubator/bloodhound/trunk/bloodhound_search/bhsearch/api.py (original) +++ incubator/bloodhound/trunk/bloodhound_search/bhsearch/api.py Thu Jan 10 13:32:56 2013 @@ -27,6 +27,13 @@ ASC = "asc" DESC = "desc" SCORE = "score" +class IndexFields(object): + TYPE = "type" + ID = "id" + TIME = 'time' + AUTHOR = 'author' + CONTENT = 'content' + class QueryResult(object): def __init__(self): self.hits = 0 @@ -35,48 +42,50 @@ class QueryResult(object): self.offset = 0 self.docs = [] self.facets = None + self.debug = {} class ISearchBackend(Interface): """Extension point interface for search backend systems. """ - def add_doc(self, doc, commit=True): +# def add_doc(self, doc, **kwargs): + def add_doc(doc, **kwargs): """ Called when new document instance must be added - - :param doc: document to add - :param commit: flag if commit should be automatically called """ - def delete_doc(self, doc, commit=True): + def delete_doc(type, id, **kwargs): """ Delete document from index + """ - :param doc: document to delete - :param commit: flag if commit should be automatically called + def optimize(): + """ + Optimize index if needed """ - def commit(self): + def commit(optimize, **kwargs): """ - Commits changes + Commit changes """ - def optimize(self): + def cancel(**kwargs): """ - Optimize index if needed + Cancel changes if possible """ - def recreate_index(self): + def recreate_index(): """ Create a new index, if index exists, it will be deleted """ - def open_or_create_index_if_missing(self): + def open_or_create_index_if_missing(): """ Open existing index, if index does not exist, create new one """ - def query(self, query, sort = None, fields = None, boost = None, filters = None, + + def query(query, sort = None, fields = None, boost = None, filter = None, facets = None, pagenum = 1, pagelen = 20): """ Perform query implementation @@ -85,38 +94,42 @@ class ISearchBackend(Interface): :param sort: :param fields: :param boost: - :param filters: + :param filter: :param facets: :param pagenum: :param pagelen: - :return: TBD!!! + :return: ResultsPage """ - pass + + def start_operation(self): + """Used to get arguments for batch operation withing single commit""" + +class IIndexParticipant(Interface): + """Extension point interface for components that should be searched. + """ + + def get_entries_for_index(): + """List entities for index creation""" class ISearchParticipant(Interface): """Extension point interface for components that should be searched. """ + def format_search_results(contents): + """Called to see if the module wants to format the search results.""" def get_search_filters(req): """Called when we want to build the list of components with search. Passes the request object to do permission checking.""" - pass - def build_search_index(backend): - """Called when we want to rebuild the entire index. - :type backend: ISearchBackend - """ - pass - - def format_search_results(contents): - """Called to see if the module wants to format the search results.""" + def get_title(): + """Return resource title""" class IQueryParser(Interface): """Extension point for Bloodhound Search query parser. """ def parse(query_string, req = None): - pass + """Parse query from string""" class BloodhoundSearchApi(Component): """Implements core indexing functionality, provides methods for @@ -132,10 +145,11 @@ class BloodhoundSearchApi(Component): 'Name of the component implementing Bloodhound Search query \ parser.') - search_participants = ExtensionPoint(ISearchParticipant) + index_participants = ExtensionPoint(IIndexParticipant) - def query(self, query, req = None, sort = None, fields = None, boost = None, filters = None, - facets = None, pagenum = 1, pagelen = 20): + def query(self, query, sort = None, fields = None, + boost = None, filter = None, + facets = None, pagenum = 1, pagelen = 20): """Return query result from an underlying search backend. Arguments: @@ -144,9 +158,9 @@ class BloodhoundSearchApi(Component): :param sort: optional sorting :param boost: optional list of fields with boost values e.g. {“id”: 1000, “subject” :100, “description”:10}. - :param filters: optional list of terms. Usually can be cached by underlying - search framework. For example {“type”: “wiki”} - :param facets: optional list of facet terms, can be field or expression. + :param filter: optional list of terms. Usually can be cached by + underlying search framework. For example {“type”: “wiki”} + :param facets: optional list of facet terms, can be field or expression :param page: paging support :param pagelen: paging support @@ -154,18 +168,17 @@ class BloodhoundSearchApi(Component): """ self.env.log.debug("Receive query request: %s", locals()) + parsed_query = self.parser.parse(query) + # TODO: add query parsers and meta keywords post-parsing # TODO: apply security filters - parsed_query = self.parser.parse(query, req) - - #some backend-independent logic will come here... query_result = self.backend.query( query = parsed_query, sort = sort, fields = fields, - filters = filters, + filter = filter, facets = facets, pagenum = pagenum, pagelen = pagelen, @@ -175,39 +188,64 @@ class BloodhoundSearchApi(Component): def rebuild_index(self): - """Delete the index if it exists. Then create a new full index.""" + """Rebuild underlying index""" self.log.info('Rebuilding the search index.') self.backend.recreate_index() + operation_data = self.backend.start_operation() + try: + for participant in self.index_participants: + docs = participant.get_entries_for_index() + for doc in docs: + self.backend.add_doc(doc, **operation_data) + self.backend.commit(True, **operation_data) + except: + self.backend.cancel(**operation_data) + raise + + def change_doc_id(self, doc, old_id): + operation_data = self.backend.start_operation() + try: + self.backend.delete_doc( + doc[IndexFields.TYPE], + old_id, + **operation_data) + self.backend.add_doc(doc, **operation_data) + self.backend.commit(False, **operation_data) + except: + self.backend.cancel(**operation_data) + raise - for participant in self.search_participants: - participant.build_search_index(self.backend) - self.backend.commit() - self.backend.optimize() - - #Erase the index if it exists. Then create a new index from scratch. - - #erase ticket - #call reindex for each resource - #commit - pass def optimize(self): """Optimize underlying index""" - pass + self.backend.optimize() def add_doc(self, doc): """Add a document to underlying search backend. The doc must be dictionary with obligatory "type" field """ - self.backend.add_doc(doc) + operation_data = self.backend.start_operation() + try: + self.backend.add_doc(doc, **operation_data) + self.backend.commit(False, **operation_data) + except: + self.backend.cancel(**operation_data) + raise + def delete_doc(self, type, id): """Add a document from underlying search backend. The doc must be dictionary with obligatory "type" field """ - pass + operation_data = self.backend.start_operation() + try: + self.backend.delete_doc(type, id, **operation_data) + self.backend.commit(False, **operation_data) + except: + self.backend.cancel(**operation_data) + raise Modified: incubator/bloodhound/trunk/bloodhound_search/bhsearch/query_parser.py URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_search/bhsearch/query_parser.py?rev=1431350&r1=1431349&r2=1431350&view=diff ============================================================================== --- incubator/bloodhound/trunk/bloodhound_search/bhsearch/query_parser.py (original) +++ incubator/bloodhound/trunk/bloodhound_search/bhsearch/query_parser.py Thu Jan 10 13:32:56 2013 @@ -23,12 +23,12 @@ r"""Provides Bloodhound Search query par from bhsearch.api import IQueryParser from bhsearch.whoosh_backend import WhooshBackend from trac.core import Component, implements -from whoosh.qparser import QueryParser, DisMaxParser, MultifieldParser +from whoosh.qparser import MultifieldParser class DefaultQueryParser(Component): implements(IQueryParser) - def parse(self, query_string, req = None): + def parse(self, query_string): #todo: make field boost configurable e.g. read from config setting #this is prototype implementation ,the fields boost must be tuned later field_boosts = dict( Modified: incubator/bloodhound/trunk/bloodhound_search/bhsearch/templates/bhsearch.html URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_search/bhsearch/templates/bhsearch.html?rev=1431350&r1=1431349&r2=1431350&view=diff ============================================================================== --- incubator/bloodhound/trunk/bloodhound_search/bhsearch/templates/bhsearch.html (original) +++ incubator/bloodhound/trunk/bloodhound_search/bhsearch/templates/bhsearch.html Thu Jan 10 13:32:56 2013 @@ -43,16 +43,25 @@