def bool_query(must=None, should=None, must_not=None, filter_expr=None):
    """Create a bool query.

    Parameters
    ----------
    must : |dict|, |list| of |dict|, or |None|
        One or more query clauses that documents must match. For use
        when 'AND' logic is needed to join query terms.

    should : |list| of |dict| or |None|
        A list of query clauses for which documents must match at least
        one. For use when 'OR' logic is needed to join query terms.

    must_not : |dict|, |list| of |dict| or |None|
        One or more query query clauses which documents must not match.
        For use when 'NOT' logic is needed.

    filter_expr : |dict| or |None|
        A query clause that documents must match. For use when 'AND'
        logic is needed to join query terms. Preferred over the `must`
        parameter since we're not using relevance scores and it's more
        efficient.

    Returns
    -------
    dict
        An Elasticsearch bool query.

    """
    query_dict = {}

    if must:
        query_dict.update({'must': must})
    else:
        query_dict.update({'must': {'match_all': {}}})

    if should:
        query_dict.update({'should': should, 'minimum_should_match': 1})

    if must_not:
        query_dict.update({'must_not': must_not})

    if filter_expr:
        query_dict.update(filter_expr)

    return {'bool': query_dict}