<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def format_query(area=None, date=None, raw=None, area_relation='Intersects', **keywords):
 """Create a OpenSearch API query string. """

        if area_relation.lower() not in {"intersects", "contains", "iswithin"}:
            raise ValueError("Incorrect AOI relation provided ({})".format(area_relation))

        # Check for duplicate keywords
        kw_lower = set(x.lower() for x in keywords)
        if (len(kw_lower) != len(keywords) or
                (date is not None and 'beginposition' in kw_lower) or
                (area is not None and 'footprint' in kw_lower)):
            raise ValueError("Query contains duplicate keywords. Note that query keywords are case-insensitive.")

        query_parts = []

        if date is not None:
            keywords['beginPosition'] = date

        for attr, value in sorted(keywords.items()):
            # Escape spaces, where appropriate
            if isinstance(value, string_types):
                value = value.strip()
                if not any(value.startswith(s[0]) and value.endswith(s[1]) for s in ['[]', '{}', '//', '()']):
                    value = re.sub(r'\s', r'\ ', value, re.M)

            # Handle date keywords
            # Keywords from https://github.com/SentinelDataHub/DataHubSystem/search?q=text/date+iso8601
            date_attrs = ['beginposition', 'endposition', 'date', 'creationdate', 'ingestiondate']
            if attr.lower() in date_attrs:
                # Automatically format date-type attributes
                if isinstance(value, string_types) and ' TO ' in value:
                    # This is a string already formatted as a date interval,
                    # e.g. '[NOW-1DAY TO NOW]'
                    pass
                elif not isinstance(value, string_types) and len(value) == 2:
                    value = (format_query_date(value[0]), format_query_date(value[1]))
                else:
                    raise ValueError("Date-type query parameter '{}' expects a two-element tuple "
                                     "of str or datetime objects. Received {}".format(attr, value))

            # Handle ranged values
            if isinstance(value, (list, tuple)):
                # Handle value ranges
                if len(value) == 2:
                    # Allow None to be used as a unlimited bound
                    value = ['*' if x is None else x for x in value]
                    if all(x == '*' for x in value):
                        continue
                    value = '[{} TO {}]'.format(*value)
                else:
                    raise ValueError("Invalid number of elements in list. Expected 2, received "
                                     "{}".format(len(value)))

            query_parts.append('{}:{}'.format(attr, value))

        if raw:
            query_parts.append(raw)

        if area is not None:
            query_parts.append('footprint:"{}({})"'.format(area_relation, area))

        return ' '.join(query_parts)