Files
opensearch-pyd/test_opensearchpy/test_helpers/test_query.py
T
Samuel Orji 6f26eb3e8e remove unnecessary utf-8 header in .py files (#615)
* remove unnecessary utf-8 header in .py files

Signed-off-by: samuel orji <awesomeorji@gmail.com>

* review feedback: add link to changelog

Signed-off-by: samuel orji <awesomeorji@gmail.com>

---------

Signed-off-by: samuel orji <awesomeorji@gmail.com>
2023-11-24 16:19:50 -05:00

567 lines
15 KiB
Python

# SPDX-License-Identifier: Apache-2.0
#
# The OpenSearch Contributors require contributions made to
# this file be licensed under the Apache-2.0 license or a
# compatible open source license.
#
# Modifications Copyright OpenSearch Contributors. See
# GitHub history for details.
#
# Licensed to Elasticsearch B.V. under one or more contributor
# license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright
# ownership. Elasticsearch B.V. licenses this file to you under
# the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
from typing import Any
from pytest import raises
from opensearchpy.helpers import function, query
def test_empty_query_is_match_all() -> None:
q = query.Q()
assert isinstance(q, query.MatchAll)
assert query.MatchAll() == q
def test_match_to_dict() -> None:
assert {"match": {"f": "value"}} == query.Match(f="value").to_dict()
def test_match_to_dict_extra() -> None:
assert {"match": {"f": "value", "boost": 2}} == query.Match(
f="value", boost=2
).to_dict()
def test_fuzzy_to_dict() -> None:
assert {"fuzzy": {"f": "value"}} == query.Fuzzy(f="value").to_dict()
def test_prefix_to_dict() -> None:
assert {"prefix": {"f": "value"}} == query.Prefix(f="value").to_dict()
def test_term_to_dict() -> None:
assert {"term": {"_type": "article"}} == query.Term(_type="article").to_dict()
def test_bool_to_dict() -> None:
bool = query.Bool(must=[query.Match(f="value")], should=[])
assert {"bool": {"must": [{"match": {"f": "value"}}]}} == bool.to_dict()
def test_dismax_to_dict() -> None:
assert {"dis_max": {"queries": [{"term": {"_type": "article"}}]}} == query.DisMax(
queries=[query.Term(_type="article")]
).to_dict()
def test_bool_from_dict_issue_318() -> None:
d = {"bool": {"must_not": {"match": {"field": "value"}}}}
q = query.Q(d)
assert q == ~query.Match(field="value")
def test_repr() -> None:
bool = query.Bool(must=[query.Match(f="value")], should=[])
assert "Bool(must=[Match(f='value')])" == repr(bool)
def test_query_clone() -> None:
bool = query.Bool(
must=[query.Match(x=42)],
should=[query.Match(g="v2")],
must_not=[query.Match(title="value")],
)
bool_clone = bool._clone()
assert bool == bool_clone
assert bool is not bool_clone
def test_bool_converts_its_init_args_to_queries() -> None:
q = query.Bool(must=[{"match": {"f": "value"}}])
assert len(q.must) == 1
assert q.must[0] == query.Match(f="value")
def test_two_queries_make_a_bool() -> None:
q1 = query.Match(f="value1")
q2 = query.Match(message={"query": "this is a test", "opeartor": "and"})
q = q1 & q2
assert isinstance(q, query.Bool)
assert [q1, q2] == q.must
def test_other_and_bool_appends_other_to_must() -> None:
q1 = query.Match(f="value1")
qb = query.Bool()
q = q1 & qb
assert q is not qb
assert q.must[0] == q1
def test_bool_and_other_appends_other_to_must() -> None:
q1: Any = query.Match(f="value1")
qb: Any = query.Bool()
q = qb & q1
assert q is not qb
assert q.must[0] == q1
def test_bool_and_other_sets_min_should_match_if_needed() -> None:
q1 = query.Q("term", category=1)
q2 = query.Q(
"bool", should=[query.Q("term", name="aaa"), query.Q("term", name="bbb")]
)
q = q1 & q2
assert q == query.Bool(
must=[q1],
should=[query.Q("term", name="aaa"), query.Q("term", name="bbb")],
minimum_should_match=1,
)
def test_bool_with_different_minimum_should_match_should_not_be_combined() -> None:
q1 = query.Q(
"bool",
minimum_should_match=2,
should=[
query.Q("term", field="aa1"),
query.Q("term", field="aa2"),
query.Q("term", field="aa3"),
query.Q("term", field="aa4"),
],
)
q2 = query.Q(
"bool",
minimum_should_match=3,
should=[
query.Q("term", field="bb1"),
query.Q("term", field="bb2"),
query.Q("term", field="bb3"),
query.Q("term", field="bb4"),
],
)
q3 = query.Q(
"bool",
minimum_should_match=4,
should=[
query.Q("term", field="cc1"),
query.Q("term", field="cc2"),
query.Q("term", field="cc3"),
query.Q("term", field="cc4"),
],
)
q4 = q1 | q2
assert q4 == query.Bool(should=[q1, q2])
q5 = q1 | q2 | q3
assert q5 == query.Bool(should=[q1, q2, q3])
def test_empty_bool_has_min_should_match_0() -> None:
assert 0 == query.Bool()._min_should_match
def test_query_and_query_creates_bool() -> None:
q1 = query.Match(f=42)
q2 = query.Match(g=47)
q = q1 & q2
assert isinstance(q, query.Bool)
assert q.must == [q1, q2]
def test_match_all_and_query_equals_other() -> None:
q1 = query.Match(f=42)
q2 = query.MatchAll()
q = q1 & q2
assert q1 == q
def test_not_match_all_is_match_none() -> None:
q = query.MatchAll()
assert ~q == query.MatchNone()
def test_not_match_none_is_match_all() -> None:
q = query.MatchNone()
assert ~q == query.MatchAll()
def test_invert_empty_bool_is_match_none() -> None:
q = query.Bool()
assert ~q == query.MatchNone()
def test_match_none_or_query_equals_query() -> None:
q1 = query.Match(f=42)
q2 = query.MatchNone()
assert q1 | q2 == query.Match(f=42)
def test_match_none_and_query_equals_match_none() -> None:
q1 = query.Match(f=42)
q2 = query.MatchNone()
assert q1 & q2 == query.MatchNone()
def test_bool_and_bool() -> None:
qt1, qt2, qt3 = query.Match(f=1), query.Match(f=2), query.Match(f=3)
q1 = query.Bool(must=[qt1], should=[qt2])
q2 = query.Bool(must_not=[qt3])
assert q1 & q2 == query.Bool(
must=[qt1], must_not=[qt3], should=[qt2], minimum_should_match=0
)
q1 = query.Bool(must=[qt1], should=[qt1, qt2])
q2 = query.Bool(should=[qt3])
assert q1 & q2 == query.Bool(
must=[qt1, qt3], should=[qt1, qt2], minimum_should_match=0
)
def test_bool_and_bool_with_min_should_match() -> None:
qt1, qt2 = query.Match(f=1), query.Match(f=2)
q1 = query.Q("bool", minimum_should_match=1, should=[qt1])
q2 = query.Q("bool", minimum_should_match=1, should=[qt2])
assert query.Q("bool", must=[qt1, qt2]) == q1 & q2
def test_inverted_query_becomes_bool_with_must_not() -> None:
q = query.Match(f=42)
assert ~q == query.Bool(must_not=[query.Match(f=42)])
def test_inverted_query_with_must_not_become_should() -> None:
q = query.Q("bool", must_not=[query.Q("match", f=1), query.Q("match", f=2)])
assert ~q == query.Q("bool", should=[query.Q("match", f=1), query.Q("match", f=2)])
def test_inverted_query_with_must_and_must_not() -> None:
q = query.Q(
"bool",
must=[query.Q("match", f=3), query.Q("match", f=4)],
must_not=[query.Q("match", f=1), query.Q("match", f=2)],
)
print((~q).to_dict())
assert ~q == query.Q(
"bool",
should=[
# negation of must
query.Q("bool", must_not=[query.Q("match", f=3)]),
query.Q("bool", must_not=[query.Q("match", f=4)]),
# negation of must_not
query.Q("match", f=1),
query.Q("match", f=2),
],
)
def test_double_invert_returns_original_query() -> None:
q = query.Match(f=42)
assert q == ~~q
def test_bool_query_gets_inverted_internally() -> None:
q = query.Bool(must_not=[query.Match(f=42)], must=[query.Match(g="v")])
assert ~q == query.Bool(
should=[
# negating must
query.Bool(must_not=[query.Match(g="v")]),
# negating must_not
query.Match(f=42),
]
)
def test_match_all_or_something_is_match_all() -> None:
q1 = query.MatchAll()
q2 = query.Match(f=42)
assert (q1 | q2) == query.MatchAll()
assert (q2 | q1) == query.MatchAll()
def test_or_produces_bool_with_should() -> None:
q1 = query.Match(f=42)
q2 = query.Match(g="v")
q = q1 | q2
assert q == query.Bool(should=[q1, q2])
def test_or_bool_doesnt_loop_infinitely_issue_37() -> None:
q = query.Match(f=42) | ~query.Match(f=47)
assert q == query.Bool(
should=[query.Bool(must_not=[query.Match(f=47)]), query.Match(f=42)]
)
def test_or_bool_doesnt_loop_infinitely_issue_96() -> None:
q = ~query.Match(f=42) | ~query.Match(f=47)
assert q == query.Bool(
should=[
query.Bool(must_not=[query.Match(f=42)]),
query.Bool(must_not=[query.Match(f=47)]),
]
)
def test_bool_will_append_another_query_with_or() -> None:
qb = query.Bool(should=[query.Match(f="v"), query.Match(f="v2")])
q = query.Match(g=42)
assert (q | qb) == query.Bool(should=[query.Match(f="v"), query.Match(f="v2"), q])
def test_bool_queries_with_only_should_get_concatenated() -> None:
q1 = query.Bool(should=[query.Match(f=1), query.Match(f=2)])
q2 = query.Bool(should=[query.Match(f=3), query.Match(f=4)])
assert (q1 | q2) == query.Bool(
should=[query.Match(f=1), query.Match(f=2), query.Match(f=3), query.Match(f=4)]
)
def test_two_bool_queries_append_one_to_should_if_possible() -> None:
q1 = query.Bool(should=[query.Match(f="v")])
q2 = query.Bool(must=[query.Match(f="v")])
assert (q1 | q2) == query.Bool(
should=[query.Match(f="v"), query.Bool(must=[query.Match(f="v")])]
)
assert (q2 | q1) == query.Bool(
should=[query.Match(f="v"), query.Bool(must=[query.Match(f="v")])]
)
def test_queries_are_registered() -> None:
assert "match" in query.Query._classes
assert query.Query._classes["match"] is query.Match
def test_defining_query_registers_it() -> None:
class MyQuery(query.Query):
name = "my_query"
assert "my_query" in query.Query._classes
assert query.Query._classes["my_query"] is MyQuery
def test_query_passes_query_through() -> None:
q = query.Match(f="value1")
assert query.Q(q) is q
def test_query_constructs_query_by_name() -> None:
q = query.Q("match", f="value")
assert isinstance(q, query.Match)
assert {"f": "value"} == q._params
def test_query_translates_double_underscore_to_dots_in_param_names() -> None:
q = query.Q("match", comment__author="honza")
assert {"comment.author": "honza"} == q._params
def test_query_doesn_translate_double_underscore_to_dots_in_param_names() -> None:
q = query.Q("match", comment__author="honza", _expand__to_dot=False)
assert {"comment__author": "honza"} == q._params
def test_query_constructs_simple_query_from_dict() -> None:
q = query.Q({"match": {"f": "value"}})
assert isinstance(q, query.Match)
assert {"f": "value"} == q._params
def test_query_constructs_compound_query_from_dict() -> None:
q = query.Q({"bool": {"must": [{"match": {"f": "value"}}]}})
assert q == query.Bool(must=[query.Match(f="value")])
def test_query_raises_error_when_passed_in_dict_and_params() -> None:
with raises(Exception):
query.Q({"match": {"f": "value"}}, f="value")
def test_query_raises_error_when_passed_in_query_and_params() -> None:
q = query.Match(f="value1")
with raises(Exception):
query.Q(q, f="value")
def test_query_raises_error_on_unknown_query() -> None:
with raises(Exception):
query.Q("not a query", f="value")
def test_match_all_and_anything_is_anything() -> None:
q = query.MatchAll()
s = query.Match(f=42)
assert q & s == s
assert s & q == s
def test_function_score_with_functions() -> None:
q = query.Q(
"function_score",
functions=[query.SF("script_score", script="doc['comment_count'] * _score")],
)
assert {
"function_score": {
"functions": [{"script_score": {"script": "doc['comment_count'] * _score"}}]
}
} == q.to_dict()
def test_function_score_with_no_function_is_boost_factor() -> None:
q = query.Q(
"function_score",
functions=[query.SF({"weight": 20, "filter": query.Q("term", f=42)})],
)
assert {
"function_score": {"functions": [{"filter": {"term": {"f": 42}}, "weight": 20}]}
} == q.to_dict()
def test_function_score_to_dict() -> None:
q = query.Q(
"function_score",
query=query.Q("match", title="python"),
functions=[
query.SF("random_score"),
query.SF(
"field_value_factor",
field="comment_count",
filter=query.Q("term", tags="python"),
),
],
)
d = {
"function_score": {
"query": {"match": {"title": "python"}},
"functions": [
{"random_score": {}},
{
"filter": {"term": {"tags": "python"}},
"field_value_factor": {"field": "comment_count"},
},
],
}
}
assert d == q.to_dict()
def test_function_score_with_single_function() -> None:
d = {
"function_score": {
"filter": {"term": {"tags": "python"}},
"script_score": {"script": "doc['comment_count'] * _score"},
}
}
q = query.Q(d)
assert isinstance(q, query.FunctionScore)
assert isinstance(q.filter, query.Term)
assert len(q.functions) == 1
sf = q.functions[0]
assert isinstance(sf, function.ScriptScore)
assert "doc['comment_count'] * _score" == sf.script
def test_function_score_from_dict() -> None:
d = {
"function_score": {
"filter": {"term": {"tags": "python"}},
"functions": [
{
"filter": {"terms": {"tags": "python"}},
"script_score": {"script": "doc['comment_count'] * _score"},
},
{"boost_factor": 6},
],
}
}
q = query.Q(d)
assert isinstance(q, query.FunctionScore)
assert isinstance(q.filter, query.Term)
assert len(q.functions) == 2
sf = q.functions[0]
assert isinstance(sf, function.ScriptScore)
assert isinstance(sf.filter, query.Terms)
sf = q.functions[1]
assert isinstance(sf, function.BoostFactor)
assert 6 == sf.value
assert {"boost_factor": 6} == sf.to_dict()
def test_script_score() -> None:
d = {
"script_score": {
"query": {"match_all": {}},
"script": {"source": "...", "params": {}},
}
}
q = query.Q(d)
assert isinstance(q, query.ScriptScore)
assert isinstance(q.query, query.MatchAll)
assert q.script == {"source": "...", "params": {}}
assert q.to_dict() == d