docs: def _build_search(structured_query):
      """Construct search statment for db execution.
  
      Produces the search statement and argument dictionary to be executed
      by the DBAPI v2 execute method.
      For example, ``cursor.execute(*_build_search(query, weights))``
  
      :param query: containing terms, filters, and sorts.
      :type query: Query
      :param weights: weight values to assign to each keyword search field
      :type weights: dictionary of field names to weight integers
      :returns: the build statement and the arguments used against it
      :rtype: a two value tuple of a SQL template and a dictionary of
              arguments to pass into that template
      """
      arguments = {}
  
      # get text terms and filter out common words
      text_terms = [term for ttype, term in structured_query.terms
                    if ttype == 'text']
  
      text_terms_wo_stopwords = [term for term in text_terms
                                 if term.lower() not in STOPWORDS]
      # sql where clauses
      conditions = {'text_terms': '', 'pubYear': '', 'authorID': '', 'type': '',
                    'keyword': '', 'subject': '', 'language': '', 'title': '',
                    'author': '', 'abstract': ''}
  
      # if there are other search terms (not type "text") or if the text
      # terms do not only consist of stopwords, then use the text terms
      # without stopwords
      if arguments or text_terms_wo_stopwords:
          text_terms = text_terms_wo_stopwords
      arguments.update({'text_terms': ' '.join(text_terms)})
      # raise Exception(structured_query.filters, structured_query.terms)
  
      if len(text_terms) > 0:
          conditions['text_terms'] = 'AND module_idx \
                                      @@ plainto_tsquery(%(text_terms)s)'
  
      # build fulltext keys
      fulltext_key = []
      for term in text_terms_wo_stopwords:
          fulltext_key.append(term + '-::-fulltext')
      arguments.update({'fulltext_key':  ';--;'.join(fulltext_key)})
  
      idx = 0
      invalid_filters = []
      filters = _convert(structured_query.filters, [])
  
      while idx < len(filters):
          keyword = filters[idx][0]
          value = filters[idx][1]
          if keyword == 'pubYear':
              conditions['pubYear'] = 'AND extract(year from cm.revised) = \
                                       %(pubYear)s'
              arguments.update({'pubYear': value[0]})
          elif keyword == 'authorID':
              conditions['authorID'] = 'AND ARRAY[%(authorID)s] \
                                        <@ cm.authors'
              arguments.update({'authorID': value[0]})
          elif keyword == 'type':
              value[0] = value[0].lower()
  
              conditions['type'] = 'AND cm.portal_type = %(type)s'
              if value[0] != 'book' and value[0] != 'collection' and \
                      value[0] != 'page' and value[0] != 'module':
                  invalid_filters.append(idx)
              value[0] = 'Collection' if (value[0] == 'book' or
                                          value[0] == 'collection') else 'Module'
              arguments.update({'type': value[0]})
          elif keyword == 'keyword':
              value = _upper(value)
              conditions['keyword'] = 'AND cm.module_ident = \
                                       ANY(WITH target AS ( \
                                       SELECT lm.module_ident AS id, \
                                       array_agg(UPPER(kw.word)) AS akw \
                                       FROM latest_modules lm, \
                                       modulekeywords mk, \
                                       keywords kw \
                                       WHERE lm.module_ident = mk.module_ident \
                                       AND kw.keywordid = mk.keywordid \
                                       GROUP BY id) \
                                       SELECT target.id FROM target WHERE \
                                       target.akw @> %(keyword)s)'
              arguments.update({'keyword': value})
          elif keyword == 'subject':
              conditions['subject'] = 'AND cm.module_ident = \
                                       ANY(WITH sub AS ( \
                                       SELECT module_ident AS id, \
                                       array_agg(tag) AS atag \
                                       FROM latest_modules \
                                       NATURAL JOIN \
                                       moduletags NATURAL JOIN \
                                       tags GROUP BY id) \
                                       SELECT sub.id FROM sub WHERE \
                                       sub.atag @> %(subject)s)'
              arguments.update({'subject': value})
          elif keyword == 'language':
              conditions['language'] = 'AND cm.language = %(language)s'
              arguments.update({'language': value[0]})
          elif keyword == 'title':
              conditions['title'] = 'AND strip_html(cm.name) ~* \
                                     %(title)s'
              arguments.update({'title': value[0]})
          elif keyword == 'author':
              conditions['author'] = 'AND cm.module_ident = \
                                      ANY(WITH name AS ( \
                                      SELECT username FROM users u WHERE \
                                      u.first_name||\' \'||u.last_name \
                                      ~* %(author)s) \
                                      SELECT lm.module_ident \
                                      FROM latest_modules lm \
                                      JOIN name n ON ARRAY[n.username] \
                                      <@ lm.authors)'
              arguments.update({'author': value[0]})
          elif keyword == 'abstract':
              conditions['abstract'] = 'AND cm.module_ident = \
                                        ANY(SELECT lm.module_ident FROM \
                                        latest_modules lm, \
                                        abstracts ab WHERE\
                                        lm.abstractid = ab.abstractid \
                                        AND ab.abstract \
                                        ~* %(abstract)s)'
              arguments.update({'abstract': value[0]})
          else:
              # Invalid filter!
              invalid_filters.append(idx)
          idx += 1
  
      if len(invalid_filters) == len(structured_query.filters) and \
              len(structured_query.terms) == 0:
          # Either query terms are all invalid filters
          # or we received a null query.
          # Clear the filter list in this case.
          structured_query.filters = []
          return None, None
  
      for invalid_filter_idx in invalid_filters:
          # Remove invalid filters.
          del structured_query.filters[invalid_filter_idx]
  
      # Add the arguments for sorting.
      sorts = ['portal_type']
      if structured_query.sorts:
          for sort in structured_query.sorts:
              # These sort values are not the name of the column used
              #   in the database.
              stmt = _transmute_sort(sort)
              sorts.append(stmt)
      sorts.extend(('weight DESC', 'uuid DESC',))
      sorts = ', '.join(sorts)
  
      statement = SQL_QUICK_SELECT_WRAPPER.format(conditions['pubYear'],
                                                  conditions['authorID'],
                                                  conditions['type'],
                                                  conditions['keyword'],
                                                  conditions['subject'],
                                                  conditions['text_terms'],
                                                  conditions['language'],
                                                  conditions['title'],
                                                  conditions['author'],
                                                  conditions['abstract'],
                                                  sorts=sorts)
      return statement, arguments