from __future__ import absolute_import, unicode_literals


#
# Base classes
#


class SearchQuery:
    def __and__(self, other):
        return And([self, other])

    def __or__(self, other):
        return Or([self, other])

    def __invert__(self):
        return Not(self)

    def apply(self, func):
        raise NotImplementedError

    def clone(self):
        return self.apply(lambda o: o)

    def get_children(self):
        return ()

    @property
    def children(self):
        return list(self.get_children())

    @property
    def child(self):
        children = self.children
        if len(children) != 1:
            raise IndexError('`%s` object has %d children, not a single child.'
                             % self.__class__.__name__, len(children))
        return children[0]


class SearchQueryOperator(SearchQuery):
    pass


class MultiOperandsSearchQueryOperator(SearchQueryOperator):
    def __init__(self, subqueries):
        self.subqueries = subqueries

    def apply(self, func):
        return func(self.__class__(
            [subquery.apply(func) for subquery in self.subqueries]))

    def get_children(self):
        yield from self.subqueries


#
# Operators
#


class And(MultiOperandsSearchQueryOperator):
    pass


class Or(MultiOperandsSearchQueryOperator):
    pass


class Not(SearchQueryOperator):
    def __init__(self, subquery: SearchQuery):
        self.subquery = subquery

    def apply(self, func):
        return func(self.__class__(self.subquery.apply(func)))

    def get_children(self):
        yield self.subquery


#
# Basic query classes
#


class PlainText(SearchQuery):
    OPERATORS = ['and', 'or']
    DEFAULT_OPERATOR = 'and'

    def __init__(self, query_string: str, operator: str = DEFAULT_OPERATOR,
                 boost: float = 1):
        self.query_string = query_string
        self.operator = operator.lower()
        if self.operator not in self.OPERATORS:
            raise ValueError("`operator` must be either 'or' or 'and'.")
        self.boost = boost

    def apply(self, func):
        return func(self.__class__(self.query_string, self.operator,
                                   self.boost))


class MatchAll(SearchQuery):
    def apply(self, func):
        return self.__class__()


class Boost(SearchQuery):
    def __init__(self, subquery: SearchQuery, boost: float):
        self.subquery = subquery
        self.boost = boost

    def apply(self, func):
        return func(self.__class__(self.subquery.apply(func), self.boost))


MATCH_ALL = MatchAll()

@@ -276,908 +276,183 @@
 f)%0A%0A
-    def apply(self, func):%0A        raise NotImplementedError%0A%0A    def clone(self):%0A        return self.apply(lambda o: o)%0A%0A    def get_children(self):%0A        return ()%0A%0A    @property%0A    def children(self):%0A        return list(self.get_children())%0A%0A    @property%0A    def child(self):%0A        children = self.children%0A        if len(children) != 1:%0A            raise IndexError('%60%25s%60 object has %25d children, not a single child.'%0A                             %25 self.__class__.__name__, len(children))%0A        return children%5B0%5D%0A%0A%0Aclass SearchQueryOperator(SearchQuery):%0A    pass%0A%0A%0Aclass MultiOperandsSearchQueryOperator(SearchQueryOperator):%0A    def __init__(self, subqueries):%0A        self.subqueries = subqueries%0A%0A    def apply(self, func):%0A        return func(self.__class__(%0A            %5Bsubquery.apply(func) for subquery in self.subqueries%5D))%0A%0A    def get_children(self):%0A        yield from self.
+%0Aclass SearchQueryOperator(SearchQuery):%0A    pass%0A%0A%0Aclass MultiOperandsSearchQueryOperator(SearchQueryOperator):%0A    def __init__(self, subqueries):%0A        self.subqueries = 
 subq
@@ -706,156 +706,8 @@
 ry%0A%0A
-    def apply(self, func):%0A        return func(self.__class__(self.subquery.apply(func)))%0A%0A    def get_children(self):%0A        yield self.subquery%0A%0A
 %0A#%0A#
@@ -1168,241 +1168,46 @@
 st%0A%0A
-    def apply(self, func):%0A        return func(self.__class__(self.query_string, self.operator,%0A                                   self.boost))%0A%0A%0Aclass MatchAll(SearchQuery):%0A    def apply(self, func):%0A        return self.__class__()
+%0Aclass MatchAll(SearchQuery):%0A    pass
 %0A%0A%0Ac
@@ -1357,111 +1357,8 @@
 st%0A%0A
-    def apply(self, func):%0A        return func(self.__class__(self.subquery.apply(func), self.boost))%0A%0A
 %0AMAT

Remove unneeded methods on query classes