incubator-bloodhound-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From and...@apache.org
Subject svn commit: r1437987 [2/2] - in /incubator/bloodhound/trunk/bloodhound_search: ./ bhsearch/ bhsearch/default-pages/ bhsearch/templates/ bhsearch/tests/
Date Thu, 24 Jan 2013 13:12:02 GMT
Modified: incubator/bloodhound/trunk/bloodhound_search/bhsearch/web_ui.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_search/bhsearch/web_ui.py?rev=1437987&r1=1437986&r2=1437987&view=diff
==============================================================================
--- incubator/bloodhound/trunk/bloodhound_search/bhsearch/web_ui.py (original)
+++ incubator/bloodhound/trunk/bloodhound_search/bhsearch/web_ui.py Thu Jan 24 13:12:01 2013
@@ -19,15 +19,16 @@
 #  under the License.
 
 r"""Bloodhound Search user interface."""
+import copy
 
 import pkg_resources
 import re
 
-from trac.core import *
+from trac.core import Component, implements, TracError
 from genshi.builder import tag
 from trac.perm import IPermissionRequestor
 from trac.search import shorten_result
-from trac.config import OrderedExtensionsOption
+from trac.config import OrderedExtensionsOption, ListOption
 from trac.util.presentation import Paginator
 from trac.util.datefmt import format_datetime, user_time
 from trac.web import IRequestHandler
@@ -50,48 +51,65 @@ class RequestParameters(object):
     """
     QUERY = "q"
     PAGE = "page"
-    FILTER = "fl"
     TYPE = "type"
     NO_QUICK_JUMP = "noquickjump"
     PAGELEN = "pagelen"
+    FILTER_QUERY = "fq"
 
     def __init__(self, req):
         self.req = req
 
-        self.query = req.args.get(RequestParameters.QUERY)
+        self.query = req.args.getfirst(RequestParameters.QUERY)
         if self.query == None:
             self.query = ""
 
         #TODO: add quick jump functionality
         self.noquickjump = 1
 
-        #TODO: add filters support
-        self.filters = []
+        self.filter_queries = req.args.getlist(RequestParameters.FILTER_QUERY)
+        self.filter_queries = self._remove_possible_duplications(
+                        self.filter_queries)
 
         #TODO: retrieve sort from query string
         self.sort = DEFAULT_SORT
 
-        self.pagelen = int(req.args.get(
+        self.pagelen = int(req.args.getfirst(
             RequestParameters.PAGELEN,
             DEFAULT_RESULTS_PER_PAGE))
-        self.page = int(req.args.get(RequestParameters.PAGE, '1'))
-        self.type = req.args.get(RequestParameters.TYPE, None)
+        self.page = int(req.args.getfirst(RequestParameters.PAGE, '1'))
+        self.type = req.args.getfirst(RequestParameters.TYPE, None)
 
         self.params = {
             self.NO_QUICK_JUMP: self.noquickjump,
+            RequestParameters.FILTER_QUERY: []
         }
         if self.query:
             self.params[self.QUERY] = self.query
         if self.pagelen != DEFAULT_RESULTS_PER_PAGE:
-            self.params[self.PAGELEN]=self.pagelen
+            self.params[self.PAGELEN] = self.pagelen
         if self.page > 1:
-            self.params[self.PAGE]=self.page
+            self.params[self.PAGE] = self.page
         if self.type:
             self.params[self.TYPE] = self.type
+        if self.filter_queries:
+            self.params[RequestParameters.FILTER_QUERY] = self.filter_queries
 
-    def create_href(self, page = None, type=None, skip_type = False,
-                    skip_page = False):
-        params = dict(self.params)
+    def _remove_possible_duplications(self, parameters_list):
+        seen = set()
+        return [parameter for parameter in parameters_list
+                if parameter not in seen and not seen.add(parameter)]
+
+    def create_href(
+            self,
+            page = None,
+            type=None,
+            skip_type = False,
+            skip_page = False,
+            filter_query = None,
+            skip_filter_query = False,
+            force_filters = None
+            ):
+        params = copy.deepcopy(self.params)
         if page:
             params[self.PAGE] = page
 
@@ -102,11 +120,20 @@ class RequestParameters(object):
             params[self.TYPE] = type
 
         if skip_type and self.TYPE in params:
-            #show all does not require type parameter
             del(params[self.TYPE])
 
+        if skip_filter_query:
+            params[self.FILTER_QUERY] = []
+        elif filter_query and filter_query not in params[self.FILTER_QUERY]:
+            params[self.FILTER_QUERY].append(filter_query)
+        elif force_filters is not None:
+            params[self.FILTER_QUERY] = force_filters
+
         return self.req.href.bhsearch(**params)
 
+    def is_show_all_mode(self):
+        return self.type is None
+
 class BloodhoundSearchModule(Component):
     """Main search page"""
 
@@ -123,6 +150,10 @@ class BloodhoundSearchModule(Component):
     )
 
 
+    default_facets_all = ListOption('bhsearch', 'default_facets_all',
+        doc="""Default facets applied to search through all resources""")
+
+
     # INavigationContributor methods
     def get_active_navigation_item(self, req):
         return 'bhsearch'
@@ -144,33 +175,41 @@ class BloodhoundSearchModule(Component):
     def process_request(self, req):
         req.perm.assert_permission(SEARCH_PERMISSION)
         parameters = RequestParameters(req)
+        query_string = parameters.query
 
         #TODO add quick jump support
 
         allowed_participants = self._get_allowed_participants(req)
         data = {
-            'query': parameters.query,
+            'query': query_string,
             }
+        self._prepare_allowed_types(allowed_participants, parameters, data)
+        self._prepare_active_filter_queries(
+            parameters,
+            data,
+        )
 
-        #todo: filters check, tickets etc
-        if not any((parameters.query, )):
-            return self._return_data(req, data)
+        #TBD: should search return results on empty query?
+#        if not any((
+#            query_string,
+#            parameters.type,
+#            parameters.filter_queries,
+#            )):
+#            return self._return_data(req, data)
 
         query_filter = self._prepare_query_filter(
-            parameters.type,
-            parameters.filters,
+            parameters,
             allowed_participants)
 
-        #todo: add proper facets functionality
-        facets = self._prepare_facets(req)
+        facets = self._prepare_facets(parameters, allowed_participants)
 
-        querySystem = BloodhoundSearchApi(self.env)
-        query_result = querySystem.query(
-            parameters.query,
-            pagenum = parameters.page,
-            pagelen = parameters.pagelen,
-            sort = parameters.sort,
-            facets = facets,
+        query_system = BloodhoundSearchApi(self.env)
+        query_result = query_system.query(
+            query_string,
+            pagenum=parameters.page,
+            pagelen=parameters.pagelen,
+            sort=parameters.sort,
+            facets=facets,
             filter=query_filter)
 
         ui_docs = [self._process_doc(doc, req, allowed_participants)
@@ -199,66 +238,147 @@ class BloodhoundSearchModule(Component):
             prev_href = parameters.create_href(page = parameters.page - 1)
             add_link(req, 'prev', prev_href, _('Previous Page'))
 
+
         data['results'] = results
-        self._prepare_type_grouping(
-            allowed_participants,
+
+        self._prepare_result_facet_counts(
             parameters,
-            data)
+            query_result,
+            data,
+        )
 
-        #TODO:add proper facet links
-        data['facets'] = query_result.facets
         data['page_href'] = parameters.create_href()
         return self._return_data(req, data)
 
-    def _prepare_query_filter(self, type, filters, allowed_participants):
-        query_filters = []
-
-        if type in allowed_participants:
-            query_filters.append((IndexFields.TYPE, type))
-        else:
-            self.log.debug("Unsupported type in web request: %s", type)
-
-        #TODO: handle other filters
-        return query_filters
-
-    def _prepare_type_grouping(self, allowed_participants, parameters, data):
+    def _prepare_allowed_types(self, allowed_participants, parameters, data):
         active_type = parameters.type
         if active_type and active_type not in allowed_participants:
             raise TracError(_("Unsupported resource type: '%(name)s'",
                 name=active_type))
-        all_is_active = (active_type is None)
-        grouping = [
+        allowed_types = [
             dict(
                 label=_("All"),
-                active=all_is_active,
+                active=(active_type is None),
                 href=parameters.create_href(
                     skip_type=True,
-                    skip_page=not all_is_active)
+                    skip_page=True,
+                    force_filters=[],
+                ),
             )
         ]
 
-        #we want to obtain the same order as specified in search_participants
-        # option
+        #we want obtain the same order as in search participants options
         participant_with_type = dict((participant, type)
             for type, participant in allowed_participants.iteritems())
         for participant in self.search_participants:
             if participant in participant_with_type:
                 type = participant_with_type[participant]
-                is_active = (type == active_type)
-                grouping.append(dict(
+                allowed_types.append(dict(
                     label=_(participant.get_title()),
-                    active=is_active,
+                    active=(type ==active_type),
                     href=parameters.create_href(
                         type=type,
-                        skip_page=not is_active
-                    )
+                        skip_page=True,
+                        force_filters=[],
+                    ),
                 ))
-        data["types"] =  grouping
-        data["active_type"] = active_type
+        data["types"] =  allowed_types
+        data["active_type"] =  active_type
+
+
 
-    def _prepare_facets(self, req):
-        facets = [IndexFields.TYPE]
-        #TODO: add type specific default facets
+    def _prepare_active_filter_queries(
+            self,
+            parameters,
+            data):
+        active_filter_queries = []
+        for filter_query in parameters.filter_queries:
+            active_filter_queries.append(dict(
+                href=parameters.create_href(
+                    force_filters=self._cut_filters(
+                        parameters.filter_queries,
+                        filter_query)),
+                label=filter_query,
+                query=filter_query,
+            ))
+        data['active_filter_queries'] = active_filter_queries
+
+    def _cut_filters(self, filter_queries, filer_to_cut_from):
+        return filter_queries[:filter_queries.index(filer_to_cut_from)]
+
+
+    def _prepare_result_facet_counts(self, parameters, query_result, data):
+        """
+
+        Sample query_result.facets content returned by query
+        {
+           'component': {None:2},
+           'milestone': {None:1, 'm1':1},
+        }
+
+        returned facet_count contains href parameters:
+        {
+           'component': {None: {'count':2, href:'...'},
+           'milestone': {
+                            None: {'count':1,, href:'...'},
+                            'm1':{'count':1, href:'...'}
+                        },
+        }
+
+        """
+        result_facets = query_result.facets
+        facet_counts = dict()
+        if result_facets:
+            for field, facets_dict in result_facets.iteritems():
+                per_field_dict = dict()
+                for field_value, count in facets_dict.iteritems():
+                    if field==IndexFields.TYPE:
+                        href = parameters.create_href(
+                            skip_page=True,
+                            skip_filter_query=True,
+                            type=field_value)
+                    else:
+                        href = parameters.create_href(
+                            skip_page=True,
+                            filter_query=self._create_field_term_expression(
+                                field,
+                                field_value)
+                        )
+                    per_field_dict[field_value] = dict(
+                        count=count,
+                        href=href
+                    )
+                facet_counts[_(field)] = per_field_dict
+
+        data['facet_counts'] = facet_counts
+
+    def _create_field_term_expression(self, field, field_value):
+        if field_value is None:
+            query = "NOT (%s:*)" % field
+        elif isinstance(field_value, basestring):
+            query = '%s:"%s"' % (field, field_value)
+        else:
+            query = '%s:%s' % (field, field_value)
+        return query
+
+    def _prepare_query_filter(self, parameters, allowed_participants):
+        query_filters = list(parameters.filter_queries)
+        type = parameters.type
+        if type in allowed_participants:
+            query_filters.append(
+                self._create_field_term_expression(IndexFields.TYPE, type))
+        else:
+            self.log.debug("Unsupported type in web request: %s", type)
+        return query_filters
+
+    def _prepare_facets(self, parameters, allowed_participants):
+        #TODO: add possibility of specifying facets in query parameters
+        if parameters.is_show_all_mode():
+            facets = [IndexFields.TYPE]
+            facets.extend(self.default_facets_all)
+        else:
+            type_participant = allowed_participants[parameters.type]
+            facets = type_participant.get_default_facets()
         return facets
 
     def _get_allowed_participants(self, req):

Modified: incubator/bloodhound/trunk/bloodhound_search/bhsearch/whoosh_backend.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_search/bhsearch/whoosh_backend.py?rev=1437987&r1=1437986&r2=1437987&view=diff
==============================================================================
--- incubator/bloodhound/trunk/bloodhound_search/bhsearch/whoosh_backend.py (original)
+++ incubator/bloodhound/trunk/bloodhound_search/bhsearch/whoosh_backend.py Thu Jan 24 13:12:01
2013
@@ -19,18 +19,20 @@
 #  under the License.
 
 r"""Whoosh specific backend for Bloodhound Search plugin."""
-from bhsearch.api import ISearchBackend, DESC, QueryResult, SCORE
+from bhsearch.api import ISearchBackend, DESC, QueryResult, SCORE, \
+    IDocIndexPreprocessor, IResultPostprocessor, IndexFields, \
+    IQueryPreprocessor
 import os
-from trac.core import *
+from trac.core import Component, implements, TracError
 from trac.config import Option
+from trac.util.text import empty
 from trac.util.datefmt import utc
-from whoosh.fields import *
+from whoosh.fields import Schema, ID, DATETIME, KEYWORD, TEXT
 from whoosh import index, sorting, query
-from whoosh.searching import ResultsPage
 from whoosh.writing import AsyncWriter
 from datetime import datetime
 
-UNIQUE_ID = 'unique_id'
+UNIQUE_ID = "unique_id"
 
 class WhooshBackend(Component):
     """
@@ -43,19 +45,22 @@ class WhooshBackend(Component):
         directory of the environment.""")
 
     #This is schema prototype. It will be changed later
-    #TODO: add other fields support, add dynamic field support
+    #TODO: add other fields support, add dynamic field support.
+    #Schema must be driven by index participants
     SCHEMA = Schema(
         unique_id=ID(stored=True, unique=True),
         id=ID(stored=True),
         type=ID(stored=True),
         product=ID(stored=True),
+        milestone=ID(stored=True),
         time=DATETIME(stored=True),
+        due=DATETIME(stored=True),
+        completed=DATETIME(stored=True),
         author=ID(stored=True),
-        component=KEYWORD(stored=True),
-        status=KEYWORD(stored=True),
-        resolution=KEYWORD(stored=True),
+        component=ID(stored=True),
+        status=ID(stored=True),
+        resolution=ID(stored=True),
         keywords=KEYWORD(scorable=True),
-        milestone=TEXT(spelling=True),
         summary=TEXT(stored=True),
         content=TEXT(stored=True),
         changes=TEXT(),
@@ -65,7 +70,7 @@ class WhooshBackend(Component):
         self.index_dir = self.index_dir_setting
         if not os.path.isabs(self.index_dir):
             self.index_dir = os.path.join(self.env.path, self.index_dir)
-        self.open_or_create_index_if_missing()
+        self.index = self._open_or_create_index_if_missing()
 
     #ISearchBackend methods
     def start_operation(self):
@@ -80,16 +85,13 @@ class WhooshBackend(Component):
         The contents should be a dict with fields matching the search schema.
         The only required fields are type and id, everything else is optional.
         """
-        # Really make sure it's unicode, because Whoosh won't have it any
-        # other way.
         is_local_writer = False
         if writer is None:
             is_local_writer = True
             writer = self._create_writer()
 
-        for key in doc:
-            doc[key] = self._to_whoosh_format(doc[key])
-        doc["unique_id"] = self._create_unique_id(doc["type"], doc["id"])
+        self._reformat_doc(doc)
+        doc[UNIQUE_ID] = self._create_unique_id(doc["type"], doc["id"])
         self.log.debug("Doc to index: %s", doc)
         try:
             writer.update_document(**doc)
@@ -98,9 +100,24 @@ class WhooshBackend(Component):
         except:
             if is_local_writer:
                 writer.cancel()
+            raise
+
+    def _reformat_doc(self, doc):
+        """
+        Strings must be converted unicode format accepted by Whoosh.
+        """
+        for key, value in doc.items():
+            if key is None:
+                del doc[None]
+            elif value is None:
+                del doc[key]
+            elif isinstance(value, basestring) and value == "":
+                del doc[key]
+            else:
+                doc[key] = self._to_whoosh_format(value)
 
-    def delete_doc(self, type, id, writer=None):
-        unique_id = self._create_unique_id(type, id)
+    def delete_doc(self, doc_type, doc_id, writer=None):
+        unique_id = self._create_unique_id(doc_type, doc_id)
         self.log.debug('Removing document from the index: %s', unique_id)
         is_local_writer = False
         if writer is None:
@@ -126,7 +143,7 @@ class WhooshBackend(Component):
     def cancel(self, writer):
         try:
             writer.cancel()
-        except Exception,ex:
+        except Exception, ex:
             self.env.log.error("Error during writer cancellation: %s", ex)
 
     def recreate_index(self):
@@ -134,14 +151,21 @@ class WhooshBackend(Component):
         self._make_dir_if_not_exists()
         return index.create_in(self.index_dir, schema=self.SCHEMA)
 
-    def open_or_create_index_if_missing(self):
+    def _open_or_create_index_if_missing(self):
         if index.exists_in(self.index_dir):
-            self.index = index.open_dir(self.index_dir)
+            return index.open_dir(self.index_dir)
         else:
-            self.index = self.recreate_index()
+            return self.recreate_index()
 
-    def query(self, query, sort = None, fields = None, boost = None, filter = 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):
         """
         Perform query.
 
@@ -157,15 +181,18 @@ class WhooshBackend(Component):
         """
         with self.index.searcher() as searcher:
             sortedby = self._prepare_sortedby(sort)
+
+            #TODO: investigate how faceting is applied to multi-value fields
+            #e.g. keywords. For now, just pass facets lit to Whoosh API
+            #groupedby = self._prepare_groupedby(facets)
             groupedby = facets
-            query_filter = self._prepare_filter(filter)
 
             #workaround of Whoosh bug, read method __doc__
             query = self._workaround_join_query_and_filter(
                 query,
-                query_filter)
+                filter)
 
-            search_parameters = dict(
+            query_parameters = dict(
                 query = query,
                 pagenum = pagenum,
                 pagelen = pagelen,
@@ -173,12 +200,12 @@ class WhooshBackend(Component):
                 groupedby = groupedby,
                 maptype=sorting.Count,
                 #workaround of Whoosh bug, read method __doc__
-                #filter = query_filter,
+                #filter = filter,
             )
             self.env.log.debug("Whoosh query to execute: %s",
-                search_parameters)
-            raw_page = searcher.search_page(**search_parameters)
-            results = self._process_results(raw_page, fields, search_parameters)
+                query_parameters)
+            raw_page = searcher.search_page(**query_parameters)
+            results = self._process_results(raw_page, fields, query_parameters)
         return results
 
     def _workaround_join_query_and_filter(
@@ -189,19 +216,8 @@ class WhooshBackend(Component):
             return query_expression
         return query.And((query_expression, query_filter))
 
-    def _prepare_filter(self, filters):
-        if not filters:
-            return None
-        and_filters = []
-        for filter in filters:
-            and_filters.append(query.Term(
-                unicode(filter[0]),
-                unicode(filter[1])))
-        return query.And(and_filters)
-
-
-    def _create_unique_id(self, type, id):
-        return u"%s:%s" % (type, id)
+    def _create_unique_id(self, doc_type, doc_id):
+        return u"%s:%s" % (doc_type, doc_id)
 
     def _to_whoosh_format(self, value):
         if isinstance(value, basestring):
@@ -212,9 +228,9 @@ class WhooshBackend(Component):
 
     def _convert_date_to_tz_naive_utc(self, value):
         """Convert datetime to naive utc datetime
-        Whoosh can not read  from index datetime value with
+        Whoosh can not read  from index datetime values passed from Trac with
         tzinfo=trac.util.datefmt.FixedOffset because of non-empty
-        constructor"""
+        constructor of FixedOffset"""
         if value.tzinfo:
             utc_time = value.astimezone(utc)
             value = utc_time.replace(tzinfo=None)
@@ -225,13 +241,16 @@ class WhooshBackend(Component):
             value = utc.localize(value)
         return value
 
-#    def _prepare_groupedby(self, facets):
-#        if not facets:
-#            return None
-#        groupedby = sorting.Facets()
-#        for facet_name in facets:
-#            groupedby.add_field(facet_name, allow_overlap=True, maptype=sorting.Count)
-#        return groupedby
+    def _prepare_groupedby(self, facets):
+        if not facets:
+            return None
+        groupedby = sorting.Facets()
+        for facet_name in facets:
+            groupedby.add_field(
+                facet_name,
+                allow_overlap=True,
+                maptype=sorting.Count)
+        return groupedby
 
     def _prepare_sortedby(self, sort):
         if not sort:
@@ -240,12 +259,15 @@ class WhooshBackend(Component):
         for (field, order) in sort:
             if field.lower() == SCORE:
                 if self._is_desc(order):
-                    #We can implement later our own ScoreFacet with
+                    #We can implement tis later by our own ScoreFacet with
                     # "score DESC" support
-                    raise TracError("Whoosh does not support DESC score ordering.")
+                    raise TracError(
+                        "Whoosh does not support DESC score ordering.")
                 sort_condition = sorting.ScoreFacet()
             else:
-                sort_condition = sorting.FieldFacet(field, reverse=self._is_desc(order))
+                sort_condition = sorting.FieldFacet(
+                    field,
+                    reverse=self._is_desc(order))
             sortedby.append(sort_condition)
         return sortedby
 
@@ -267,7 +289,7 @@ class WhooshBackend(Component):
         results.facets = self._load_facets(page)
 
         docs = []
-        for doc_offset, retrieved_record in enumerate(page):
+        for retrieved_record in page:
             result_doc = self._process_record(fields, retrieved_record)
             docs.append(result_doc)
         results.docs = docs
@@ -313,3 +335,73 @@ class WhooshBackend(Component):
                  current user."
                 % self.index_dir)
 
+
+class WhooshEmptyFacetErrorWorkaround(Component):
+    """
+        Whoosh 2.4.1 raises "IndexError: list index out of range"
+        when search contains facets on field that is missing in at least one
+        document in the index. The error manifests only when index contains
+        more than one segment.
+
+        The goal of this class is to temporary solve the problem for
+        prototype phase. Fro non-prototype phase, the problem should be solved
+        by the next version of Whoosh.
+
+        Remove this class when fixed version of Whoosh is introduced.
+    """
+    implements(IDocIndexPreprocessor)
+    implements(IResultPostprocessor)
+    implements(IQueryPreprocessor)
+
+    NULL_MARKER = u"empty"
+
+    should_not_be_empty_fields = [
+        IndexFields.STATUS,
+        IndexFields.MILESTONE,
+        IndexFields.COMPONENT,
+    ]
+
+    #IDocIndexPreprocessor methods
+    def pre_process(self, doc):
+        for field in self.should_not_be_empty_fields:
+            if field not in doc or doc[field] is None or doc[field] == empty:
+                doc[field] = self.NULL_MARKER
+
+    #IResultPostprocessor methods
+    def post_process(self, query_result):
+        #fix facets
+        if query_result.facets:
+            for count_dict in query_result.facets.values():
+                for field, count in count_dict.iteritems():
+                    if field == self.NULL_MARKER:
+                        count_dict[None] = count
+                        del count_dict[self.NULL_MARKER]
+        #we can fix query_result.docs later if needed
+
+    #IQueryPreprocessor methods
+    def query_pre_process(self, query_parameters):
+        """
+        Go through filter queries and replace "NOT (field_name:*)" query with
+        "field_name:NULL_MARKER" query.
+
+        This is really quick fix to make prototype working with hope that
+        the next Whoosh version will be released soon.
+        """
+        if "filter" in query_parameters and query_parameters["filter"]:
+            self._find_and_fix_condition(query_parameters["filter"])
+        if "query" in query_parameters and query_parameters["query"]:
+            self._find_and_fix_condition(query_parameters["query"])
+
+    def _find_and_fix_condition(self, filter_condition):
+        if isinstance(filter_condition, query.CompoundQuery):
+            subqueries = list(filter_condition.subqueries)
+            for i, subquery in enumerate(subqueries):
+                term_to_replace =  self._find_and_fix_condition(subquery)
+                if term_to_replace:
+                    filter_condition.subqueries[i] = term_to_replace
+        elif isinstance(filter_condition, query.Not):
+            not_query = filter_condition.query
+            if isinstance(not_query, query.Every) and \
+               not_query.fieldname in self.should_not_be_empty_fields:
+                return query.Term(not_query.fieldname, self.NULL_MARKER)
+        return None

Modified: incubator/bloodhound/trunk/bloodhound_search/bhsearch/wiki_search.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_search/bhsearch/wiki_search.py?rev=1437987&r1=1437986&r2=1437987&view=diff
==============================================================================
--- incubator/bloodhound/trunk/bloodhound_search/bhsearch/wiki_search.py (original)
+++ incubator/bloodhound/trunk/bloodhound_search/bhsearch/wiki_search.py Thu Jan 24 13:12:01
2013
@@ -21,16 +21,15 @@
 r"""Wiki specifics for Bloodhound Search plugin."""
 from bhsearch.api import ISearchParticipant, BloodhoundSearchApi, \
     IIndexParticipant, IndexFields
-from trac.core import *
-from trac.config import Option
+from bhsearch.base import BaseIndexer
+from trac.core import implements, Component
+from trac.config import ListOption
 from trac.wiki import IWikiChangeListener, WikiSystem, WikiPage
 
-WIKI_TYPE = "wiki"
+WIKI_TYPE = u"wiki"
 
-class WikiIndexer(Component):
+class WikiIndexer(BaseIndexer):
     implements(IWikiChangeListener, IIndexParticipant)
-    silence_on_error = Option('bhsearch', 'silence_on_error', "True",
-        """If true, do not throw an exception during indexing a resource""")
 
     #IWikiChangeListener methods
     def wiki_page_added(self, page):
@@ -64,21 +63,26 @@ class WikiIndexer(Component):
             search_api = BloodhoundSearchApi(self.env)
             search_api.change_doc_id(doc, old_name)
         except Exception, e:
-            if self.silence_on_error.lower() == "true":
-                self.log.error("Error occurs during wiki indexing. \
-                    The error will not be propagated. Exception: %s", e)
+            if self.silence_on_error:
+                self.log.error("Error occurs during renaming wiki from %s \
+                    to %s. The error will not be propagated. Exception: %s",
+                old_name, page.name, e)
             else:
                 raise
 
-    def _index_wiki(self, page, raise_exception = False):
+    def _index_wiki(self, page):
         try:
             doc = self.build_doc(page)
             search_api = BloodhoundSearchApi(self.env)
             search_api.add_doc(doc)
         except Exception, e:
-            if (not raise_exception) and self.silence_on_error.lower() == "true":
-                self.log.error("Error occurs during wiki indexing. \
-                    The error will not be propagated. Exception: %s", e)
+            page_name = None
+            if page is not None:
+                page_name = page.name
+            if self.silence_on_error:
+                self.log.error("Error occurs during wiki indexing: %s. \
+                    The error will not be propagated. Exception: %s",
+                    page_name, e)
             else:
                 raise
 
@@ -97,7 +101,6 @@ class WikiIndexer(Component):
         return doc
 
     def get_entries_for_index(self):
-        #is there better way to get all tickets?
         page_names = WikiSystem(self.env).get_pages()
         for page_name in page_names:
             page = WikiPage(self.env, page_name)
@@ -106,6 +109,9 @@ class WikiIndexer(Component):
 class WikiSearchParticipant(Component):
     implements(ISearchParticipant)
 
+    default_facets = ListOption('bhsearch', 'default_facets_wiki',
+        doc="""Default facets applied to search through wiki pages""")
+
     #ISearchParticipant members
     def get_search_filters(self, req=None):
         if not req or 'WIKI_VIEW' in req.perm:
@@ -114,6 +120,11 @@ class WikiSearchParticipant(Component):
     def get_title(self):
         return "Wiki"
 
+    def get_default_facets(self):
+        return self.default_facets
+
     def format_search_results(self, res):
         return u'%s: %s...' % (res['id'], res['content'][:50])
 
+
+

Modified: incubator/bloodhound/trunk/bloodhound_search/setup.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_search/setup.py?rev=1437987&r1=1437986&r2=1437987&view=diff
==============================================================================
--- incubator/bloodhound/trunk/bloodhound_search/setup.py (original)
+++ incubator/bloodhound/trunk/bloodhound_search/setup.py Thu Jan 24 13:12:01 2013
@@ -125,6 +125,7 @@ ENTRY_POINTS = {
             'bhsearch.admin = bhsearch.admin',
             'bhsearch.ticket_search = bhsearch.ticket_search',
             'bhsearch.wiki_search = bhsearch.wiki_search',
+            'bhsearch.milestone_search = bhsearch.milestone_search',
             'bhsearch.query_parser = bhsearch.query_parser',
             'bhsearch.whoosh_backend = bhsearch.whoosh_backend',
         ],



Mime
View raw message