docs: def build(self, search, raw_query):
          """Build query.
  
          :param search: Search query instance
          :param raw_query: Raw query arguments dictionary
          """
          unmatched_items = {}
          for expression, value in raw_query.items():
              # Parse query expression into tokens.
              tokens = expression.split(TOKEN_SEPARATOR)
  
              field = tokens[0]
              tail = tokens[1:]
  
              if field not in self.fields:
                  unmatched_items[expression] = value
                  continue
  
              # Map field alias to final field.
              field = self.fields_map.get(field, field)
  
              # Parse lookup expression. Currently only no token or a single token is allowed.
              if tail:
                  if len(tail) > 1:
                      raise NotImplementedError("Nested lookup expressions are not supported")
  
                  lookup = self.get_lookup(tail[0])
                  search = lookup.apply(search, field, value)
              else:
                  # Default lookup.
                  custom_filter = getattr(self.custom_filter_object, 'custom_filter_{}'.format(field), None)
                  if custom_filter is not None:
                      search = custom_filter(value, search)
                  elif isinstance(value, list):
                      # Default is 'should' between matches. If you need anything else,
                      # a custom filter for this field should be implemented.
                      filters = [Q('match', **{field: item}) for item in value]
                      search = search.query('bool', should=filters)
                  else:
                      search = search.query('match', **{field: {'query': value, 'operator': 'and'}})
  
          return (search, unmatched_items)