# HG changeset patch # User ymh # Date 1379802481 -7200 # Node ID 4749704f9b40d98dd277f9a5e14fc1e465668fa2 # Parent 93b45b4f423cbeb45991f7c95fcdde8e231e26fa use a custom query parser diff -r 93b45b4f423c -r 4749704f9b40 src/p4l/search/forms.py --- a/src/p4l/search/forms.py Sat Sep 21 23:49:04 2013 +0200 +++ b/src/p4l/search/forms.py Sun Sep 22 00:28:01 2013 +0200 @@ -6,6 +6,8 @@ ''' from haystack.forms import SearchForm +from p4l.models.data import Record +from p4l.search.query_parser import QueryParser from p4l.utils import strip_accents @@ -24,7 +26,8 @@ if not self.cleaned_data.get('q'): return self.no_query_found() - sqs = self.searchqueryset.auto_query(strip_accents(self.cleaned_data['q'])) + qp = QueryParser("text") + sqs = self.searchqueryset.models(Record).filter(qp.parse(strip_accents(self.cleaned_data['q']))) if self.load_all: sqs = sqs.load_all() diff -r 93b45b4f423c -r 4749704f9b40 src/p4l/search/query_parser.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/p4l/search/query_parser.py Sun Sep 22 00:28:01 2013 +0200 @@ -0,0 +1,174 @@ +# -*- coding: utf-8 -*- +''' +Created on Aug 1, 2012 + +@author: ymh +''' + +from django.conf import settings +from haystack.query import SQ +from whoosh.qparser import (SimpleParser, FieldsPlugin, OperatorsPlugin, + PhrasePlugin, SingleQuotePlugin, GroupPlugin, PrefixPlugin, GtLtPlugin, + RangePlugin) +from whoosh.query import (Term, And, AndMaybe, Or, AndNot, Not, Phrase, Prefix, + TermRange) + +HAYSTACK_DEFAULT_OPERATOR = getattr(settings,'HAYSTACK_DEFAULT_OPERATOR','AND') + +class QueryParser(object): + + + def __init__(self, fieldname): + ''' + Constructor + ''' + self.w_parser = SimpleParser(fieldname, None) + self.w_parser.add_plugin(FieldsPlugin()) + self.w_parser.add_plugin(OperatorsPlugin()) + self.w_parser.add_plugin(PhrasePlugin()) + self.w_parser.add_plugin(SingleQuotePlugin()) + self.w_parser.add_plugin(GroupPlugin()) + self.w_parser.add_plugin(PrefixPlugin()) + self.w_parser.add_plugin(GtLtPlugin()) + self.w_parser.add_plugin(RangePlugin()) + self.query = None + self.current_node_stack = [] + + def parse(self, query): + + self.query = SQ() + self.current_node_stack = [(self.query, HAYSTACK_DEFAULT_OPERATOR)] + + wquery = self.w_parser.parse(query) + + self.visit(wquery) + + if len(self.query) == 1 and isinstance(self.query.children[0], SQ): + return self.query.children[0] + else: + return self.query + + + def visit(self, q): + + if isinstance(q, Term): + current_node, current_connector = self.current_node_stack.pop() + current_node.add(SQ(**{q.fieldname:q.text}), current_connector) + self.current_node_stack.append((current_node,current_connector)) + elif isinstance(q, And): + self._add_compound_query(q, SQ.AND) + elif isinstance(q, AndMaybe): + self._add_andmaybe(q) + elif isinstance(q, Or): + self._add_compound_query(q, SQ.OR) + elif isinstance(q, AndNot): + self._add_andnot(q) + elif isinstance(q, Not): + self._add_not(q) + elif isinstance(q, Phrase): + self._add_phrase(q) + elif isinstance(q, Prefix): + self._add_prefix(q) + elif isinstance(q, TermRange): + self._add_range(q) + + def _add_compound_query(self, q, connector): + + new_node = SQ() + self.current_node_stack.append((new_node, connector)) + for subquery in q.subqueries: + self.visit(subquery) + self.current_node_stack.pop() + + if len(new_node)==1 and isinstance(new_node.children[0], SQ) : + new_node = new_node.children[0] + + current_node, current_connector = self.current_node_stack[-1] + current_node.add(new_node, current_connector) + + + def _add_andnot(self, q): + + new_node = SQ() + self.current_node_stack.append((new_node, SQ.AND)) + self.visit(q.a) + self.visit(Not(q.b)) + self.current_node_stack.pop() + + if len(new_node)==1 and isinstance(new_node.children[0], SQ) : + new_node = new_node.children[0] + + current_node, current_connector = self.current_node_stack[-1] + current_node.add(new_node, current_connector) + + def _add_andmaybe(self, q): + + new_node = SQ() + self.current_node_stack.append((new_node, SQ.AND)) + self.visit(q.a) + self.visit(q.b) + self.current_node_stack.pop() + + if len(new_node)==1 and isinstance(new_node.children[0], SQ) : + new_node = new_node.children[0] + + current_node, current_connector = self.current_node_stack[-1] + current_node.add(new_node, current_connector) + + + def _add_not(self, q): + + new_node = SQ() + self.current_node_stack.append((new_node, SQ.AND)) + self.visit(q.query) + self.current_node_stack.pop() + + if len(new_node)==1 and isinstance(new_node.children[0], SQ) : + new_node = new_node.children[0] + + current_node, current_connector = self.current_node_stack[-1] + current_node.add(~new_node, current_connector) + + def _add_phrase(self, q): + new_node = SQ(**{q.fieldname+"__exact":" ".join(q.words)}) + current_node, current_connector = self.current_node_stack[-1] + current_node.add(new_node, current_connector) + + def _add_prefix(self, q): + new_node = SQ(**{q.fieldname+"__startswith":q.text}) + current_node, current_connector = self.current_node_stack[-1] + current_node.add(new_node, current_connector) + + def _add_range(self, q): + + if q.start is None: + if q.endexcl: + postfix = "__lt" + else: + postfix = "__lte" + new_node = SQ(**{q.fieldname+postfix:self.__convert_nb(q.end)}) + elif q.end is None: + if q.startexcl: + postfix = "__gt" + else: + postfix = "__gte" + new_node = SQ(**{q.fieldname+postfix:self.__convert_nb(q.start)}) + else: + new_node = SQ(**{q.fieldname+"__range":[self.__convert_nb(q.start),self.__convert_nb(q.end)]}) + + current_node, current_connector = self.current_node_stack[-1] + current_node.add(new_node, current_connector) + + def __convert_nb(self, str_nb): + try: + res = int(str_nb) + return res + except ValueError: + try: + res = float(str_nb) + return res + except ValueError: + return str_nb + + + \ No newline at end of file