Package zinnia :: Module search
[hide private]

Source Code for Module zinnia.search

  1  """Search module with complex query parsing for Zinnia""" 
  2  from pyparsing import Word 
  3  from pyparsing import alphas 
  4  from pyparsing import WordEnd 
  5  from pyparsing import Combine 
  6  from pyparsing import opAssoc 
  7  from pyparsing import Optional 
  8  from pyparsing import OneOrMore 
  9  from pyparsing import StringEnd 
 10  from pyparsing import printables 
 11  from pyparsing import quotedString 
 12  from pyparsing import removeQuotes 
 13  from pyparsing import ParseResults 
 14  from pyparsing import CaselessLiteral 
 15  from pyparsing import operatorPrecedence 
 16   
 17  from django.db.models import Q 
 18   
 19  from zinnia.models import Entry 
 20   
 21   
22 -def createQ(token):
23 """Creates the Q() object""" 24 meta = getattr(token, 'meta', None) 25 query = getattr(token, 'query', '') 26 wildcards = None 27 28 if isinstance(query, basestring): # Unicode -> Quoted string 29 search = query 30 else: # List -> No quoted string (possible wildcards) 31 if len(query) == 1: 32 search = query[0] 33 elif len(query) == 3: 34 wildcards = 'BOTH' 35 search = query[1] 36 elif len(query) == 2: 37 if query[0] == '*': 38 wildcards = 'START' 39 search = query[1] 40 else: 41 wildcards = 'END' 42 search = query[0] 43 44 if not meta: 45 return Q(content__icontains=search) | \ 46 Q(excerpt__icontains=search) | \ 47 Q(title__icontains=search) 48 49 if meta == 'category': 50 if wildcards == 'BOTH': 51 return Q(categories__title__icontains=search) | \ 52 Q(categories__slug__icontains=search) 53 elif wildcards == 'START': 54 return Q(categories__title__iendswith=search) | \ 55 Q(categories__slug__iendswith=search) 56 elif wildcards == 'END': 57 return Q(categories__title__istartswith=search) | \ 58 Q(categories__slug__istartswith=search) 59 else: 60 return Q(categories__title__iexact=search) | \ 61 Q(categories__slug__iexact=search) 62 elif meta == 'author': 63 if wildcards == 'BOTH': 64 return Q(authors__username__icontains=search) 65 elif wildcards == 'START': 66 return Q(authors__username__iendswith=search) 67 elif wildcards == 'END': 68 return Q(authors__username__istartswith=search) 69 else: 70 return Q(authors__username__iexact=search) 71 elif meta == 'tag': # TODO: tags ignore wildcards 72 return Q(tags__icontains=search)
73 74
75 -def unionQ(token):
76 """Appends all the Q() objects""" 77 query = Q() 78 operation = 'and' 79 negation = False 80 81 for t in token: 82 if type(t) is ParseResults: # See tokens recursively 83 query &= unionQ(t) 84 else: 85 if t in ('or', 'and'): # Set the new op and go to next token 86 operation = t 87 elif t == '-': # Next tokens needs to be negated 88 negation = True 89 else: # Append to query the token 90 if negation: 91 t = ~t 92 if operation == 'or': 93 query |= t 94 else: 95 query &= t 96 return query
97 98 99 NO_BRTS = printables.replace('(', '').replace(')', '') 100 SINGLE = Word(NO_BRTS.replace('*', '')) 101 WILDCARDS = Optional('*') + SINGLE + Optional('*') + WordEnd(wordChars=NO_BRTS) 102 QUOTED = quotedString.setParseAction(removeQuotes) 103 104 OPER_AND = CaselessLiteral('and') 105 OPER_OR = CaselessLiteral('or') 106 OPER_NOT = '-' 107 108 TERM = Combine(Optional(Word(alphas).setResultsName('meta') + ':') + 109 (QUOTED.setResultsName('query') | 110 WILDCARDS.setResultsName('query'))) 111 TERM.setParseAction(createQ) 112 113 EXPRESSION = operatorPrecedence(TERM, [ 114 (OPER_NOT, 1, opAssoc.RIGHT), 115 (OPER_OR, 2, opAssoc.LEFT), 116 (Optional(OPER_AND, default='and'), 2, opAssoc.LEFT)]) 117 EXPRESSION.setParseAction(unionQ) 118 119 QUERY = OneOrMore(EXPRESSION) + StringEnd() 120 QUERY.setParseAction(unionQ) 121 122
123 -def advanced_search(pattern):
124 """Parse the grammar of a pattern 125 and build a queryset with it""" 126 query_parsed = QUERY.parseString(pattern) 127 return Entry.published.filter(query_parsed[0]).distinct()
128