def test_special_wildcard(self): data = { "a": {"key": 8, "other": 8}, "b": {"key": 4}, "c": {"value": 5}, "d": 0, "e": "daf", "f": None, "g": ["john", "susan", "carl"], "h": True } self.assertEqual( [data["a"]["key"], data["b"]["key"]], Query('$wildcard("key")').single(data) ) self.assertEqual( [data["a"], data["b"]], Query('$wildcard("key", false)').single(data) ) self.assertEqual( [data["e"][0], data["g"][0]], Query('$wildcard(0)').single(data) ) self.assertEqual( [data["e"], data["g"]], Query('$wildcard(0, false)').single(data) )
def test_filter_special(self): self.assertEqual( Filter(Key('latitude') < Key('longitude')).many(small_data), Query('$filter("latitude", "<", { "query": "longitude" }, single=false)').single(small_data) ) self.assertEqual( Filter(Key('latitude') < Key('longitude')).many(small_data), Query( '$filter({ "field": "latitude", "operator": "<", "value": { "query": "longitude" }}, single=false)' ).single(small_data) ) self.assertEqual( Filter(Key('latitude') < Key('longitude')).many(small_data), Query( '$filter([{ "field": "latitude", "operator": "<", "value": { "query": "longitude" }}], single=false)' ).single(small_data) ) self.assertEqual( Filter(Key('latitude') < Key('longitude')).single(small_data[1]), Query( '$filter([{ "field": "latitude", "operator": "<", "value": { "query": "longitude" }}])' ).single(small_data[1]) )
def test_make_dict_special(self): Query.register_special('make_dict', lambda value, *args, context, **kwargs: { **kwargs, **{i: arg for i, arg in enumerate(args)} }) self.assertEqual( {0: True, 1: False, 2: None, 3: 3, 'bob': 'chill', 'jill': [1, 2]}, Query('$make_dict(true, false, null, 3, bob="chill", jill=[1, 2])').single({}) )
def test_special_lookup(self): data = { "a": "query", "b": "filter" } self.assertEqual( data["a"], Query(f"field.$lookup({json.dumps(data)})").single({"field": "a"}) ) self.assertEqual( "missing", Query(f'field.$lookup({json.dumps(data)}, "missing")').single({"field": "c"}) )
def test_special_string(self): self.assertEqual( f"Age: {small_data[0]['age']}", Query('age.$prefix("Age: ")').single(small_data[0]) ) self.assertEqual( f"{len(small_data[0]['tags'])} tags found", Query('tags.$length.$suffix(" tags found")').single(small_data[0]) ) self.assertEqual( "Name: John Smith M.D.", Query('name.$wrap("Name: ", " M.D.")').single({"name": "John Smith"}) ) self.assertEqual("test", Query("a.$strip").single({"a": " test "})) self.assertEqual( small_data[10]["balance"].replace(",", ""), Query('balance.$replace(",", "")').single(small_data[10]) ) self.assertEqual( small_data[0]["about"][:47] + "...", Query("about.$trim").single(small_data[0]) ) self.assertEqual( small_data[0]["about"][:25], Query('about.$trim(25, "")').single(small_data[0]) ) self.assertEqual( small_data[0]["guid"].split("-"), Query('guid.$split("-")').single(small_data[0]) )
def test_math_args(self): data = EZDict({"a": 4, "b": -4, "c": 2.5, "d": [3, 4], "e": 0, "pi": 3.1415926}) self.assertEqual( data.a / (data.b + data.c) - data.pi, Query("$inject(@a / (@b + @c) - @pi)").single(data) ) self.assertEqual( data.a + data.b * data.c ** data.e, Query("$inject(@a + @b * @c ** @e)").single(data) ) self.assertEqual( (data.a + data.b) * data.c ** data.e, Query("$inject((@a + @b) * @c ** @e)").single(data) )
def test_time_part(self): dt = "2020-05-23T10:11:12.123+00:00" self.assertEqual(2020, Query("$strptime.$time_part('year')").single(dt)) self.assertEqual(5, Query("$strptime.$time_part('month')").single(dt)) self.assertEqual(23, Query("$strptime.$time_part('day')").single(dt)) self.assertEqual(10, Query("$strptime.$time_part('hour')").single(dt)) self.assertEqual(11, Query("$strptime.$time_part('minute')").single(dt)) self.assertEqual(12, Query("$strptime.$time_part('second')").single(dt)) self.assertEqual(123, Query("$strptime.$time_part('millisecond')").single(dt)) self.assertEqual(5, Query("$strptime.$time_part('dayOfWeek')").single(dt)) self.assertEqual(144, Query("$strptime.$time_part('dayOfYear')").single(dt))
def test_context(self): self.assertEqual( 5, Query("context").single({}, {"context": 5}) ) self.assertEqual( 5, Query("context.nested").single({}, {"context": {"nested": 5}}) ) self.assertEqual( 5, Query("overlapping").single({"overlapping": 5}, {"overlapping": "nope"}) )
def test_group_by_flat(self): l = [i % 3 for i in range(12)] ez = EZDict() for item in l: ez.appender(item, item) self.assertEqual(ez, Query("$group_by").single(l))
def test_store_as_with_later_use(self): d = small_data[0] self.assertEqual( f"{d['name']} is {d['age']} and has 5 messages", Query( "greeting.$split('You have ').1.$split(' unread').0.$int.$store_as('unread')" + ".$join_arg([@name, 'is', @age, 'and has', @unread, 'messages'], ' ')" ).single(small_data[0]) )
def test_key_of(self): data = {randint(0, 100): i for i in range(10)} min_, max_ = None, None for k, v in data.items(): if min_ is None: min_ = k elif data[k] < data[min_]: min_ = k if max_ is None: max_ = k elif data[k] > data[max_]: max_ = k self.assertEqual(max_, Query("$key_of_max_value").single(data)) self.assertEqual((max_, data[max_]), Query("$key_of_max_value(just_key=false)").single(data)) self.assertEqual(min_, Query("$key_of_min_value").single(data)) self.assertEqual((min_, data[min_]), Query("$key_of_min_value(just_key=false)").single(data))
def test_group_by_number(self): data = [[i, i] for i in range(10)] ez = EZDict() for item in data: ez.appender(item[0], item) self.assertEqual( ez, Query("$group_by(0)").single(data) )
def test_arith_basic(self): data = EZDict({"a": 4, "b": -4, "c": 2.5, "d": [3, 4], "e": 0, "pi": 3.1415926}) self.assertEqual(data.a + data.b + data.c, Query("a.$arith('+', @b + @c)").single(data)) self.assertEqual(data.a + data.b - data.c, Query("a.$arith('+', @b - @c)").single(data)) self.assertEqual(data.a + data.b * data.c, Query("a.$arith('+', @b * @c)").single(data)) self.assertEqual(data.a + data.b / data.c, Query("a.$arith('+', @b / @c)").single(data)) self.assertEqual(data.a + data.b ** data.c, Query("a.$arith('+', @b ** @c)").single(data)) self.assertEqual(data.a + data.b // data.c, Query("a.$arith('+', @b // @c)").single(data)) self.assertEqual(data.a + data.b % data.c, Query("a.$arith('+', @b % @c)").single(data))
def test_special_list(self): data = { "a": [43.2, -34, 54.2], "b": [3, None, 1, 0, False] } self.assertEqual(sum(data["a"]), Query("a.$sum").single(data)) self.assertEqual(", ".join(str(i) for i in data["a"]), Query("a.$join").single(data)) self.assertEqual(", ".join(str(i) for i in data["a"]), Query("$join_arg(@a)").single(data)) self.assertEqual(data["a"][2], Query("a.$index(2)").single(data)) self.assertEqual("nope", Query("a.$index(5, fallback='nope')").single(data)) self.assertEqual(data["a"][1:], Query("a.$range(1)").single(data)) self.assertEqual(data["a"][1:-1], Query("a.$range(1, -1)").single(data)) self.assertEqual([d for d in data["b"] if d is not None], Query("b.$remove_nulls").single(data))
def test_nested_query(self): params = { "index": "name", "origin": [0, 0], "name": small_data[0]["name"], "company": small_data[0]["company"], "lookup": { small_data[0]["name"]: small_data[0]["company"], } } self.assertEqual( [e["name"] for e in small_data[0]["friends"]], Query("data.friends.$map('index', @params.index)").single({"data": small_data[0], "params": params}) ) lat = small_data[0]["latitude"] lon = small_data[0]["longitude"] self.assertEqual( round(sum([(params["origin"][0] - lat)**2, (params["origin"][1] - lon)**2]) ** 0.5, 2), Query("params.origin.$distance([@data.latitude, @data.longitude]).$round").single( {"data": small_data[0], "params": params} ) ) self.assertEqual( params["lookup"][small_data[0]["name"]], Query("data.name.$lookup(@params.lookup)").single({"data": small_data[0], "params": params}) ) self.assertEqual( params["lookup"][small_data[0]["name"]], Query("data.name.$lookup({@params.name: @params.company})").single( {"data": small_data[0], "params": params} ) ) self.assertEqual( f"Lat={small_data[0]['latitude']} & Lon={small_data[0]['longitude']}", Query('latitude.$wrap("Lat=", @longitude.$prefix(" & Lon="))').single(small_data[0]) )
def test_query_reuse(self): runs = 100 value1, recreate_time = StaticTimer.time_it(lambda: Query( 'email.$split("@").1.$split(".").0').single(large_data[0]), runs=runs, iterations_per_run=1, display=False, log_arguments=False) getter = Query('email.$split("@").1.$split(".").0') value2, reuse_time = StaticTimer.time_it(getter.single, large_data[0], runs=runs, iterations_per_run=1, display=False, log_arguments=False) print(recreate_time / reuse_time, "x faster to reuse Query then recreate") self.assertEqual(value1, value2) self.assertGreater(recreate_time / reuse_time, 5)
def test_keyword_args(self): data = small_data[0] self.assertEqual(data["balance"], Query("$index('balance')").single(data)) self.assertEqual(None, Query("$index('nope')").single(data)) self.assertEqual('nope', Query("$index('nope', 'nope')").single(data)) self.assertEqual('nope', Query("$index('nope', fallback='nope')").single(data)) self.assertEqual(data["tags"][0], Query("$index('tags.0', extended=true, fallback='nope')").single(data)) # Do again to check caching self.assertEqual(data["tags"][0], Query("$index('tags.0', extended=true, fallback='nope')").single(data))
def test_triple_nested(self): data = { "data": "test", "keys": { "key1": "data" }, "key1": "key1" } self.assertEqual( data["data"], Query("$index(@keys.$index(@key1))").single(data) )
def test_pipeline(self): min_ = min([float(item["balance"][1:].replace(",", "")) for item in small_data]) self.assertEqual( min_, Query(""" $map( 'pipeline', [ ['index', 'balance'], ['range', 1], ['replace', {'old': ',', 'new': ''}], 'float' ] ).$min """).single(small_data) )
def test_fallback_single_field_single_nested_many(self): self.assertEqual( ["MISSING"]*3, Query("null.null", fallback="MISSING").many(small_data[:3]) )
def test_fallback_single_field_single_nested_single_number(self): self.assertEqual( "MISSING", Query("null.3", fallback="MISSING").single({"null": [0, 1]}) )
def test_fallback_single_field_single_nested_single(self): self.assertEqual( "MISSING", Query("null.null", fallback="MISSING").single(small_data[0]) )
def test_field_after_special(self): data = {'a': {'b': 35}} self.assertEqual( data['a']['b'], Query('a.$values.0').single(data) )
def test_bad_query(self): result = Query("$length(4*5").single({}) self.assertIsNone(result)
def test_single_field_many(self): self.assertEqual( [i["age"] for i in small_data[:3]], Query("age").many(small_data[:3]) )
def test_dict(self): self.assertEqual( small_data[0]["favoriteFruit"], Query("favoriteFruit.$items.$dict").single(small_data[0]) )
def test_fallback_multiple_fields_some_missing_many(self): self.assertEqual( [["MISSING", i["balance"]] for i in small_data[:3]], Query(["isactive", "balance"], fallback="MISSING").many(small_data[:3]) )
def test_special_not_found_error(self): self.assertRaises( SpecialNotFoundError, lambda: Query("age.$mixedup").single(small_data[0]) )
def test_multiple_fields_single(self): self.assertEqual( [small_data[1]["name"], small_data[1]["email"]], Query(["name", "email"]).single(small_data[1]) )
def test_sort_nested_reverse(self): self.assertEqual( sorted(small_data, key=lambda x: EZDict(x).favoriteFruit.banana * -0.5, reverse=True), Query("$sort('favoriteFruit.banana.$multiply(-0.5)', true)").single(small_data) )