class testPagerankFlow(FlowTestsBase): def __init__(self): self.env = Env() global redis_graph redis_con = self.env.getConnection() redis_graph = Graph(GRAPH_ID, redis_con) def test_pagerank(self): # Pagerank considers only nodes of given label and # relation of given relationship type # multiple edges between two nodes are considered as a single connection. queries = [ # Single Label, single connection. "CREATE (a:L {v:1})-[:R]->(b:L {v:2})", # Single Label, multi connection. "CREATE (a:L {v:1})-[:R]->(b:L {v:2}), (a)-[:R]->(b)", # Multi Label, single connection. "CREATE (a:L {v:1})-[:R]->(b:L {v:2}), (:X)-[:R]->(:X)", # Multi Label, multi connection. "CREATE (a:L {v:1})-[:R]->(b:L {v:2}), (a)-[:R]->(b), (:X)-[:R]->(:X)" ] for q in queries: self.env.cmd('flushall') redis_graph.query(q) q = """CALL algo.pageRank('L', 'R') YIELD node, score RETURN node.v, score""" resultset = redis_graph.query(q).result_set # 2) 1) 1) (integer) 2 # 2) "0.777813196182251" # 2) 1) (integer) 1 # 2) "0.22218681871891" self.env.assertEqual(len(resultset), 2) self.env.assertEqual(resultset[0][0], 2) self.env.assertAlmostEqual(resultset[0][1], 0.777813196182251, 0.0001) self.env.assertEqual(resultset[1][0], 1) self.env.assertAlmostEqual(resultset[1][1], 0.22218681871891, 0.0001)
class TestAggregate(): def __init__(self): self.env = Env() add_values(self.env) def testGroupBy(self): cmd = [ 'ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'count', '0', 'AS', 'count', 'SORTBY', 2, '@count', 'desc', 'LIMIT', '0', '5' ] res = self.env.cmd(*cmd) self.env.assertIsNotNone(res) self.env.assertEqual([ 292L, ['brand', '', 'count', '1518'], ['brand', 'mad catz', 'count', '43'], ['brand', 'generic', 'count', '40'], ['brand', 'steelseries', 'count', '37'], ['brand', 'logitech', 'count', '35'] ], res) def testMinMax(self): cmd = [ 'ft.aggregate', 'games', 'sony', 'GROUPBY', '1', '@brand', 'REDUCE', 'count', '0', 'REDUCE', 'min', '1', '@price', 'as', 'minPrice', 'SORTBY', '2', '@minPrice', 'DESC' ] res = self.env.cmd(*cmd) self.env.assertIsNotNone(res) row = to_dict(res[1]) self.env.assertEqual(88, int(float(row['minPrice']))) cmd = [ 'ft.aggregate', 'games', 'sony', 'GROUPBY', '1', '@brand', 'REDUCE', 'count', '0', 'REDUCE', 'max', '1', '@price', 'as', 'maxPrice', 'SORTBY', '2', '@maxPrice', 'DESC' ] res = self.env.cmd(*cmd) row = to_dict(res[1]) self.env.assertEqual(695, int(float(row['maxPrice']))) def testAvg(self): cmd = [ 'ft.aggregate', 'games', 'sony', 'GROUPBY', '1', '@brand', 'REDUCE', 'avg', '1', '@price', 'AS', 'avg_price', 'REDUCE', 'count', '0', 'SORTBY', '2', '@avg_price', 'DESC' ] res = self.env.cmd(*cmd) self.env.assertIsNotNone(res) self.env.assertEqual(26, res[0]) # Ensure the formatting actually exists first_row = to_dict(res[1]) self.env.assertEqual(109, int(float(first_row['avg_price']))) for row in res[1:]: row = to_dict(row) self.env.assertIn('avg_price', row) # Test aliasing cmd = [ 'FT.AGGREGATE', 'games', 'sony', 'GROUPBY', '1', '@brand', 'REDUCE', 'avg', '1', '@price', 'AS', 'avgPrice' ] res = self.env.cmd(*cmd) first_row = to_dict(res[1]) self.env.assertEqual(17, int(float(first_row['avgPrice']))) def testCountDistinct(self): cmd = [ 'FT.AGGREGATE', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'COUNT_DISTINCT', '1', '@title', 'AS', 'count_distinct(title)', 'REDUCE', 'COUNT', '0' ] res = self.env.cmd(*cmd)[1:] # print res row = to_dict(res[0]) self.env.assertEqual(1484, int(row['count_distinct(title)'])) cmd = [ 'FT.AGGREGATE', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'COUNT_DISTINCTISH', '1', '@title', 'AS', 'count_distinctish(title)', 'REDUCE', 'COUNT', '0' ] res = self.env.cmd(*cmd)[1:] # print res row = to_dict(res[0]) self.env.assertEqual(1461, int(row['count_distinctish(title)'])) def testQuantile(self): cmd = [ 'FT.AGGREGATE', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'QUANTILE', '2', '@price', '0.50', 'AS', 'q50', 'REDUCE', 'QUANTILE', '2', '@price', '0.90', 'AS', 'q90', 'REDUCE', 'QUANTILE', '2', '@price', '0.95', 'AS', 'q95', 'REDUCE', 'AVG', '1', '@price', 'REDUCE', 'COUNT', '0', 'AS', 'rowcount', 'SORTBY', '2', '@rowcount', 'DESC', 'MAX', '1' ] res = self.env.cmd(*cmd) row = to_dict(res[1]) # TODO: Better samples self.env.assertAlmostEqual(14.99, float(row['q50']), delta=3) self.env.assertAlmostEqual(70, float(row['q90']), delta=50) # This tests the 95th percentile, which is error prone because # so few samples actually exist. I'm disabling it for now so that # there is no breakage in CI # self.env.assertAlmostEqual(110, (float(row['q95'])), delta=50) def testStdDev(self): cmd = [ 'FT.AGGREGATE', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'STDDEV', '1', '@price', 'AS', 'stddev(price)', 'REDUCE', 'AVG', '1', '@price', 'AS', 'avgPrice', 'REDUCE', 'QUANTILE', '2', '@price', '0.50', 'AS', 'q50Price', 'REDUCE', 'COUNT', '0', 'AS', 'rowcount', 'SORTBY', '2', '@rowcount', 'DESC', 'LIMIT', '0', '10' ] res = self.env.cmd(*cmd) row = to_dict(res[1]) self.env.assertTrue(10 <= int(float(row['q50Price'])) <= 20) self.env.assertAlmostEqual(53, int(float(row['stddev(price)'])), delta=50) self.env.assertEqual(29, int(float(row['avgPrice']))) def testParseTime(self): cmd = [ 'FT.AGGREGATE', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'COUNT', '0', 'AS', 'count', 'APPLY', 'timefmt(1517417144)', 'AS', 'dt', 'APPLY', 'parse_time("%FT%TZ", @dt)', 'as', 'parsed_dt', 'LIMIT', '0', '1' ] res = self.env.cmd(*cmd) self.env.assertEqual([ 'brand', '', 'count', '1518', 'dt', '2018-01-31T16:45:44Z', 'parsed_dt', '1517417144' ], res[1]) def testRandomSample(self): cmd = [ 'FT.AGGREGATE', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'COUNT', '0', 'AS', 'num', 'REDUCE', 'RANDOM_SAMPLE', '2', '@price', '10', 'SORTBY', '2', '@num', 'DESC', 'MAX', '10' ] for row in self.env.cmd(*cmd)[1:]: self.env.assertIsInstance(row[5], list) self.env.assertGreater(len(row[5]), 0) self.env.assertGreaterEqual(row[3], len(row[5])) self.env.assertLessEqual(len(row[5]), 10) def testTimeFunctions(self): cmd = [ 'FT.AGGREGATE', 'games', '*', 'APPLY', '1517417144', 'AS', 'dt', 'APPLY', 'timefmt(@dt)', 'AS', 'timefmt', 'APPLY', 'day(@dt)', 'AS', 'day', 'APPLY', 'hour(@dt)', 'AS', 'hour', 'APPLY', 'minute(@dt)', 'AS', 'minute', 'APPLY', 'month(@dt)', 'AS', 'month', 'APPLY', 'dayofweek(@dt)', 'AS', 'dayofweek', 'APPLY', 'dayofmonth(@dt)', 'AS', 'dayofmonth', 'APPLY', 'dayofyear(@dt)', 'AS', 'dayofyear', 'APPLY', 'year(@dt)', 'AS', 'year', 'LIMIT', '0', '1' ] res = self.env.cmd(*cmd) self.env.assertListEqual([ 1L, [ 'dt', '1517417144', 'timefmt', '2018-01-31T16:45:44Z', 'day', '1517356800', 'hour', '1517414400', 'minute', '1517417100', 'month', '1514764800', 'dayofweek', '3', 'dayofmonth', '31', 'dayofyear', '30', 'year', '2018' ] ], res) def testStringFormat(self): cmd = [ 'FT.AGGREGATE', 'games', '@brand:sony', 'GROUPBY', '2', '@title', '@brand', 'REDUCE', 'COUNT', '0', 'REDUCE', 'MAX', '1', '@price', 'AS', 'price', 'APPLY', 'format("%s|%s|%s|%s", @title, @brand, "Mark", @price)', 'as', 'titleBrand', 'LIMIT', '0', '10' ] res = self.env.cmd(*cmd) for row in res[1:]: row = to_dict(row) expected = '%s|%s|%s|%g' % (row['title'], row['brand'], 'Mark', float(row['price'])) self.env.assertEqual(expected, row['titleBrand']) def testSum(self): cmd = [ 'ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'count', '0', 'AS', 'count', 'REDUCE', 'sum', 1, '@price', 'AS', 'sum(price)', 'SORTBY', 2, '@sum(price)', 'desc', 'LIMIT', '0', '5' ] res = self.env.cmd(*cmd) self.env.assertEqual([ 292L, ['brand', '', 'count', '1518', 'sum(price)', '44780.69'], ['brand', 'mad catz', 'count', '43', 'sum(price)', '3973.48'], ['brand', 'razer', 'count', '26', 'sum(price)', '2558.58'], ['brand', 'logitech', 'count', '35', 'sum(price)', '2329.21'], ['brand', 'steelseries', 'count', '37', 'sum(price)', '1851.12'] ], res) def testFilter(self): cmd = [ 'ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'count', '0', 'AS', 'count', 'FILTER', '@count > 5' ] res = self.env.cmd(*cmd) for row in res[1:]: row = to_dict(row) self.env.assertGreater(int(row['count']), 5) cmd = [ 'ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'count', '0', 'AS', 'count', 'FILTER', '@count < 5', 'FILTER', '@count > 2 && @brand != ""' ] res = self.env.cmd(*cmd) for row in res[1:]: row = to_dict(row) self.env.assertLess(int(row['count']), 5) self.env.assertGreater(int(row['count']), 2) def testToList(self): cmd = [ 'ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'count_distinct', '1', '@price', 'as', 'count', 'REDUCE', 'tolist', 1, '@price', 'as', 'prices', 'SORTBY', 2, '@count', 'desc', 'LIMIT', '0', '5' ] res = self.env.cmd(*cmd) for row in res[1:]: row = to_dict(row) self.env.assertEqual(int(row['count']), len(row['prices'])) def testSortBy(self): res = self.env.cmd('ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'sum', 1, '@price', 'as', 'price', 'SORTBY', 2, '@price', 'desc', 'LIMIT', '0', '2') self.env.assertListEqual([ 292L, ['brand', '', 'price', '44780.69'], ['brand', 'mad catz', 'price', '3973.48'] ], res) res = self.env.cmd('ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'sum', 1, '@price', 'as', 'price', 'SORTBY', 2, '@price', 'asc', 'LIMIT', '0', '2') self.env.assertListEqual([ 292L, ['brand', 'myiico', 'price', '0.23'], ['brand', 'crystal dynamics', 'price', '0.25'] ], res) # Test MAX with limit higher than it res = self.env.cmd('ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'sum', 1, '@price', 'as', 'price', 'SORTBY', 2, '@price', 'asc', 'MAX', 2) self.env.assertListEqual([ 292L, ['brand', 'myiico', 'price', '0.23'], ['brand', 'crystal dynamics', 'price', '0.25'] ], res) # Test Sorting by multiple properties res = self.env.cmd( 'ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'sum', 1, '@price', 'as', 'price', 'APPLY', '(@price % 10)', 'AS', 'price', 'SORTBY', 4, '@price', 'asc', '@brand', 'desc', 'MAX', 10, ) self.env.assertListEqual([ 292L, ['brand', 'zps', 'price', '0'], ['brand', 'zalman', 'price', '0'], [ 'brand', 'yoozoo', 'price', '0' ], ['brand', 'white label', 'price', '0'], ['brand', 'stinky', 'price', '0'], ['brand', 'polaroid', 'price', '0'], ['brand', 'plantronics', 'price', '0'], ['brand', 'ozone', 'price', '0'], ['brand', 'oooo', 'price', '0'], ['brand', 'neon', 'price', '0'] ], res) def testExpressions(self): pass def testNoGroup(self): res = self.env.cmd( 'ft.aggregate', 'games', '*', 'LOAD', '2', '@brand', '@price', 'APPLY', 'floor(sqrt(@price)) % 10', 'AS', 'price', 'SORTBY', 4, '@price', 'desc', '@brand', 'desc', 'MAX', 5, ) exp = [ 2265L, ['brand', 'xbox', 'price', '9'], ['brand', 'turtle beach', 'price', '9'], ['brand', 'trust', 'price', '9'], ['brand', 'steelseries', 'price', '9'], ['brand', 'speedlink', 'price', '9'] ] # exp = [2265L, ['brand', 'Xbox', 'price', '9'], ['brand', 'Turtle Beach', 'price', '9'], [ # 'brand', 'Trust', 'price', '9'], ['brand', 'SteelSeries', 'price', '9'], ['brand', 'Speedlink', 'price', '9']] self.env.assertListEqual(exp[1], res[1]) def testLoad(self): res = self.env.cmd('ft.aggregate', 'games', '*', 'LOAD', '3', '@brand', '@price', '@nonexist', 'SORTBY', 2, '@price', 'DESC', 'MAX', 2) exp = [ 3L, ['brand', '', 'price', '759.12'], ['brand', 'Sony', 'price', '695.8'] ] self.env.assertEqual(exp[1], res[1]) def testSplit(self): res = self.env.cmd( 'ft.aggregate', 'games', '*', 'APPLY', 'split("hello world, foo,,,bar,", ",", " ")', 'AS', 'strs', 'APPLY', 'split("hello world, foo,,,bar,", " ", ",")', 'AS', 'strs2', 'APPLY', 'split("hello world, foo,,,bar,", "", "")', 'AS', 'strs3', 'APPLY', 'split("hello world, foo,,,bar,")', 'AS', 'strs4', 'APPLY', 'split("hello world, foo,,,bar,",",")', 'AS', 'strs5', 'APPLY', 'split("")', 'AS', 'empty', 'LIMIT', '0', '1') # print "Got {} results".format(len(res)) # return # pprint.pprint(res) self.env.assertListEqual([ 1L, [ 'strs', ['hello world', 'foo', 'bar'], 'strs2', ['hello', 'world', 'foo,,,bar'], 'strs3', ['hello world, foo,,,bar,'], 'strs4', ['hello world', 'foo', 'bar'], 'strs5', ['hello world', 'foo', 'bar'], 'empty', [] ] ], res) def testFirstValue(self): res = self.env.cmd( 'ft.aggregate', 'games', '@brand:(sony|matias|beyerdynamic|(mad catz))', 'GROUPBY', 1, '@brand', 'REDUCE', 'FIRST_VALUE', 4, '@title', 'BY', '@price', 'DESC', 'AS', 'top_item', 'REDUCE', 'FIRST_VALUE', 4, '@price', 'BY', '@price', 'DESC', 'AS', 'top_price', 'REDUCE', 'FIRST_VALUE', 4, '@title', 'BY', '@price', 'ASC', 'AS', 'bottom_item', 'REDUCE', 'FIRST_VALUE', 4, '@price', 'BY', '@price', 'ASC', 'AS', 'bottom_price', 'SORTBY', 2, '@top_price', 'DESC', 'MAX', 5) expected = [ 4L, [ 'brand', 'sony', 'top_item', 'sony psp slim & lite 2000 console', 'top_price', '695.8', 'bottom_item', 'sony dlchd20p high speed hdmi cable for playstation 3', 'bottom_price', '5.88' ], [ 'brand', 'matias', 'top_item', 'matias halfkeyboard usb', 'top_price', '559.99', 'bottom_item', 'matias halfkeyboard usb', 'bottom_price', '559.99' ], [ 'brand', 'beyerdynamic', 'top_item', 'beyerdynamic mmx300 pc gaming premium digital headset with microphone', 'top_price', '359.74', 'bottom_item', 'beyerdynamic headzone pc gaming digital surround sound system with mmx300 digital headset with microphone', 'bottom_price', '0' ], [ 'brand', 'mad catz', 'top_item', 'mad catz s.t.r.i.k.e.7 gaming keyboard', 'top_price', '295.95', 'bottom_item', 'madcatz mov4545 xbox replacement breakaway cable', 'bottom_price', '3.49' ] ] self.env.assertListEqual(expected, res) def testLoadAfterGroupBy(self): with self.env.assertResponseError(): self.env.cmd('ft.aggregate', 'games', '*', 'GROUPBY', 1, '@brand', 'LOAD', 1, '@brand')
class TestAggregate(): def __init__(self): self.env = Env() add_values(self.env) def testGroupBy(self): cmd = [ 'ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'count', '0', 'AS', 'count', 'SORTBY', 2, '@count', 'desc', 'LIMIT', '0', '5' ] res = self.env.cmd(*cmd) self.env.assertIsNotNone(res) self.env.assertEqual([ 292L, ['brand', '', 'count', '1518'], ['brand', 'mad catz', 'count', '43'], ['brand', 'generic', 'count', '40'], ['brand', 'steelseries', 'count', '37'], ['brand', 'logitech', 'count', '35'] ], res) def testMinMax(self): cmd = [ 'ft.aggregate', 'games', 'sony', 'GROUPBY', '1', '@brand', 'REDUCE', 'count', '0', 'REDUCE', 'min', '1', '@price', 'as', 'minPrice', 'SORTBY', '2', '@minPrice', 'DESC' ] res = self.env.cmd(*cmd) self.env.assertIsNotNone(res) row = to_dict(res[1]) self.env.assertEqual(88, int(float(row['minPrice']))) cmd = [ 'ft.aggregate', 'games', 'sony', 'GROUPBY', '1', '@brand', 'REDUCE', 'count', '0', 'REDUCE', 'max', '1', '@price', 'as', 'maxPrice', 'SORTBY', '2', '@maxPrice', 'DESC' ] res = self.env.cmd(*cmd) row = to_dict(res[1]) self.env.assertEqual(695, int(float(row['maxPrice']))) def testAvg(self): cmd = [ 'ft.aggregate', 'games', 'sony', 'GROUPBY', '1', '@brand', 'REDUCE', 'avg', '1', '@price', 'AS', 'avg_price', 'REDUCE', 'count', '0', 'SORTBY', '2', '@avg_price', 'DESC' ] res = self.env.cmd(*cmd) self.env.assertIsNotNone(res) self.env.assertEqual(26, res[0]) # Ensure the formatting actually exists first_row = to_dict(res[1]) self.env.assertEqual(109, int(float(first_row['avg_price']))) for row in res[1:]: row = to_dict(row) self.env.assertIn('avg_price', row) # Test aliasing cmd = [ 'FT.AGGREGATE', 'games', 'sony', 'GROUPBY', '1', '@brand', 'REDUCE', 'avg', '1', '@price', 'AS', 'avgPrice' ] res = self.env.cmd(*cmd) first_row = to_dict(res[1]) self.env.assertEqual(17, int(float(first_row['avgPrice']))) def testCountDistinct(self): cmd = [ 'FT.AGGREGATE', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'COUNT_DISTINCT', '1', '@title', 'AS', 'count_distinct(title)', 'REDUCE', 'COUNT', '0' ] res = self.env.cmd(*cmd)[1:] # print res row = to_dict(res[0]) self.env.assertEqual(1484, int(row['count_distinct(title)'])) cmd = [ 'FT.AGGREGATE', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'COUNT_DISTINCTISH', '1', '@title', 'AS', 'count_distinctish(title)', 'REDUCE', 'COUNT', '0' ] res = self.env.cmd(*cmd)[1:] # print res row = to_dict(res[0]) self.env.assertEqual(1461, int(row['count_distinctish(title)'])) def testQuantile(self): cmd = [ 'FT.AGGREGATE', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'QUANTILE', '2', '@price', '0.50', 'AS', 'q50', 'REDUCE', 'QUANTILE', '2', '@price', '0.90', 'AS', 'q90', 'REDUCE', 'QUANTILE', '2', '@price', '0.95', 'AS', 'q95', 'REDUCE', 'AVG', '1', '@price', 'REDUCE', 'COUNT', '0', 'AS', 'rowcount', 'SORTBY', '2', '@rowcount', 'DESC', 'MAX', '1' ] res = self.env.cmd(*cmd) row = to_dict(res[1]) # TODO: Better samples self.env.assertAlmostEqual(14.99, float(row['q50']), delta=3) self.env.assertAlmostEqual(70, float(row['q90']), delta=50) self.env.assertAlmostEqual(110, (float(row['q95'])), delta=50) def testStdDev(self): cmd = [ 'FT.AGGREGATE', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'STDDEV', '1', '@price', 'AS', 'stddev(price)', 'REDUCE', 'AVG', '1', '@price', 'AS', 'avgPrice', 'REDUCE', 'QUANTILE', '2', '@price', '0.50', 'AS', 'q50Price', 'REDUCE', 'COUNT', '0', 'AS', 'rowcount', 'SORTBY', '2', '@rowcount', 'DESC', 'LIMIT', '0', '10' ] res = self.env.cmd(*cmd) row = to_dict(res[1]) self.env.assertTrue(10 <= int(float(row['q50Price'])) <= 20) self.env.assertAlmostEqual(53, int(float(row['stddev(price)'])), delta=50) self.env.assertEqual(29, int(float(row['avgPrice']))) def testParseTime(self): cmd = [ 'FT.AGGREGATE', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'COUNT', '0', 'AS', 'count', 'APPLY', 'timefmt(1517417144)', 'AS', 'dt', 'APPLY', 'parse_time("%FT%TZ", @dt)', 'as', 'parsed_dt', 'LIMIT', '0', '1' ] res = self.env.cmd(*cmd) self.env.assertEqual([ 'brand', '', 'count', '1518', 'dt', '2018-01-31T16:45:44Z', 'parsed_dt', '1517417144' ], res[1]) def testRandomSample(self): cmd = [ 'FT.AGGREGATE', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'COUNT', '0', 'AS', 'num', 'REDUCE', 'RANDOM_SAMPLE', '2', '@price', '10', 'SORTBY', '2', '@num', 'DESC', 'MAX', '10' ] for row in self.env.cmd(*cmd)[1:]: self.env.assertIsInstance(row[5], list) self.env.assertGreater(len(row[5]), 0) self.env.assertGreaterEqual(row[3], len(row[5])) self.env.assertLessEqual(len(row[5]), 10) def testTimeFunctions(self): cmd = [ 'FT.AGGREGATE', 'games', '*', 'APPLY', '1517417144', 'AS', 'dt', 'APPLY', 'timefmt(@dt)', 'AS', 'timefmt', 'APPLY', 'day(@dt)', 'AS', 'day', 'APPLY', 'hour(@dt)', 'AS', 'hour', 'APPLY', 'minute(@dt)', 'AS', 'minute', 'APPLY', 'month(@dt)', 'AS', 'month', 'APPLY', 'dayofweek(@dt)', 'AS', 'dayofweek', 'APPLY', 'dayofmonth(@dt)', 'AS', 'dayofmonth', 'APPLY', 'dayofyear(@dt)', 'AS', 'dayofyear', 'APPLY', 'year(@dt)', 'AS', 'year', 'LIMIT', '0', '1' ] res = self.env.cmd(*cmd) self.env.assertListEqual([ 1L, [ 'dt', '1517417144', 'timefmt', '2018-01-31T16:45:44Z', 'day', '1517356800', 'hour', '1517414400', 'minute', '1517417100', 'month', '1514764800', 'dayofweek', '3', 'dayofmonth', '31', 'dayofyear', '30', 'year', '2018' ] ], res) def testStringFormat(self): cmd = [ 'FT.AGGREGATE', 'games', '@brand:sony', 'GROUPBY', '2', '@title', '@brand', 'REDUCE', 'COUNT', '0', 'REDUCE', 'MAX', '1', '@price', 'AS', 'price', 'APPLY', 'format("%s|%s|%s|%s", @title, @brand, "Mark", @price)', 'as', 'titleBrand', 'LIMIT', '0', '10' ] res = self.env.cmd(*cmd) for row in res[1:]: row = to_dict(row) expected = '%s|%s|%s|%g' % (row['title'], row['brand'], 'Mark', float(row['price'])) self.env.assertEqual(expected, row['titleBrand']) def testSum(self): cmd = [ 'ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'count', '0', 'AS', 'count', 'REDUCE', 'sum', 1, '@price', 'AS', 'sum(price)', 'SORTBY', 2, '@sum(price)', 'desc', 'LIMIT', '0', '5' ] res = self.env.cmd(*cmd) self.env.assertEqual([ 292L, ['brand', '', 'count', '1518', 'sum(price)', '44780.69'], ['brand', 'mad catz', 'count', '43', 'sum(price)', '3973.48'], ['brand', 'razer', 'count', '26', 'sum(price)', '2558.58'], ['brand', 'logitech', 'count', '35', 'sum(price)', '2329.21'], ['brand', 'steelseries', 'count', '37', 'sum(price)', '1851.12'] ], res) def testFilter(self): cmd = [ 'ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'count', '0', 'AS', 'count', 'FILTER', '@count > 5' ] res = self.env.cmd(*cmd) for row in res[1:]: row = to_dict(row) self.env.assertGreater(int(row['count']), 5) cmd = [ 'ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'count', '0', 'AS', 'count', 'FILTER', '@count < 5', 'FILTER', '@count > 2 && @brand != ""' ] res = self.env.cmd(*cmd) for row in res[1:]: row = to_dict(row) self.env.assertLess(int(row['count']), 5) self.env.assertGreater(int(row['count']), 2) def testToList(self): cmd = [ 'ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'count_distinct', '1', '@price', 'as', 'count', 'REDUCE', 'tolist', 1, '@price', 'as', 'prices', 'SORTBY', 2, '@count', 'desc', 'LIMIT', '0', '5' ] res = self.env.cmd(*cmd) for row in res[1:]: row = to_dict(row) self.env.assertEqual(int(row['count']), len(row['prices'])) def testSortBy(self): res = self.env.cmd('ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'sum', 1, '@price', 'as', 'price', 'SORTBY', 2, '@price', 'desc', 'LIMIT', '0', '2') self.env.assertListEqual([ 292L, ['brand', '', 'price', '44780.69'], ['brand', 'mad catz', 'price', '3973.48'] ], res) res = self.env.cmd('ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'sum', 1, '@price', 'as', 'price', 'SORTBY', 2, '@price', 'asc', 'LIMIT', '0', '2') self.env.assertListEqual([ 292L, ['brand', 'myiico', 'price', '0.23'], ['brand', 'crystal dynamics', 'price', '0.25'] ], res) # Test MAX with limit higher than it res = self.env.cmd('ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'sum', 1, '@price', 'as', 'price', 'SORTBY', 2, '@price', 'asc', 'MAX', 2) self.env.assertListEqual([ 292L, ['brand', 'myiico', 'price', '0.23'], ['brand', 'crystal dynamics', 'price', '0.25'] ], res) # Test Sorting by multiple properties res = self.env.cmd( 'ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'sum', 1, '@price', 'as', 'price', 'APPLY', '(@price % 10)', 'AS', 'price', 'SORTBY', 4, '@price', 'asc', '@brand', 'desc', 'MAX', 10, ) self.env.assertListEqual([ 292L, ['brand', 'zps', 'price', '0'], ['brand', 'zalman', 'price', '0'], [ 'brand', 'yoozoo', 'price', '0' ], ['brand', 'white label', 'price', '0'], ['brand', 'stinky', 'price', '0'], ['brand', 'polaroid', 'price', '0'], ['brand', 'plantronics', 'price', '0'], ['brand', 'ozone', 'price', '0'], ['brand', 'oooo', 'price', '0'], ['brand', 'neon', 'price', '0'] ], res) def testExpressions(self): pass def testNoGroup(self): res = self.env.cmd( 'ft.aggregate', 'games', '*', 'LOAD', '2', '@brand', '@price', 'APPLY', 'floor(sqrt(@price)) % 10', 'AS', 'price', 'SORTBY', 4, '@price', 'desc', '@brand', 'desc', 'MAX', 5, ) exp = [ 2265L, ['brand', 'Xbox', 'price', '9'], ['brand', 'turtle beach', 'price', '9'], ['brand', 'trust', 'price', '9'], ['brand', 'steelseries', 'price', '9'], ['brand', 'speedlink', 'price', '9'] ] # exp = [2265L, ['brand', 'Xbox', 'price', '9'], ['brand', 'Turtle Beach', 'price', '9'], [ # 'brand', 'Trust', 'price', '9'], ['brand', 'SteelSeries', 'price', '9'], ['brand', 'Speedlink', 'price', '9']] self.env.assertListEqual(exp[1], res[1]) def testLoad(self): res = self.env.cmd('ft.aggregate', 'games', '*', 'LOAD', '3', '@brand', '@price', '@nonexist', 'SORTBY', 2, '@price', 'DESC', 'MAX', 2) exp = [ 3L, ['brand', '', 'price', '759.12'], ['brand', 'Sony', 'price', '695.8'] ] self.env.assertEqual(exp[1], res[1]) self.env.assertEqual(exp[2], res[2]) def testLoadWithDocId(self): res = self.env.cmd('ft.aggregate', 'games', '*', 'LOAD', '3', '@brand', '@price', '@__key', 'SORTBY', 2, '@price', 'DESC', 'MAX', 4) exp = [ 3L, ['brand', '', 'price', '759.12', '__key', 'B00006JJIC'], ['brand', 'Sony', 'price', '695.8', '__key', 'B000F6W1AG'] ] self.env.assertEqual(exp[1], res[1]) self.env.assertEqual(exp[2], res[2]) res = self.env.cmd('ft.aggregate', 'games', '*', 'LOAD', '3', '@brand', '@price', '@__key', 'FILTER', '@__key == "B000F6W1AG"') self.env.assertEqual( res[1], ['brand', 'Sony', 'price', '695.8', '__key', 'B000F6W1AG']) def testLoadImplicit(self): # same as previous res = self.env.cmd('ft.aggregate', 'games', '*', 'LOAD', '1', '@brand', 'SORTBY', 2, '@price', 'DESC') exp = [ 3L, ['brand', '', 'price', '759.12'], ['brand', 'Sony', 'price', '695.8'] ] self.env.assertEqual(exp[1], res[1]) def testSplit(self): res = self.env.cmd( 'ft.aggregate', 'games', '*', 'APPLY', 'split("hello world, foo,,,bar,", ",", " ")', 'AS', 'strs', 'APPLY', 'split("hello world, foo,,,bar,", " ", ",")', 'AS', 'strs2', 'APPLY', 'split("hello world, foo,,,bar,", "", "")', 'AS', 'strs3', 'APPLY', 'split("hello world, foo,,,bar,")', 'AS', 'strs4', 'APPLY', 'split("hello world, foo,,,bar,",",")', 'AS', 'strs5', 'APPLY', 'split("")', 'AS', 'empty', 'LIMIT', '0', '1') # print "Got {} results".format(len(res)) # return # pprint.pprint(res) self.env.assertListEqual([ 1L, [ 'strs', ['hello world', 'foo', 'bar'], 'strs2', ['hello', 'world', 'foo,,,bar'], 'strs3', ['hello world, foo,,,bar,'], 'strs4', ['hello world', 'foo', 'bar'], 'strs5', ['hello world', 'foo', 'bar'], 'empty', [] ] ], res) def testFirstValue(self): res = self.env.cmd( 'ft.aggregate', 'games', '@brand:(sony|matias|beyerdynamic|(mad catz))', 'GROUPBY', 1, '@brand', 'REDUCE', 'FIRST_VALUE', 4, '@title', 'BY', '@price', 'DESC', 'AS', 'top_item', 'REDUCE', 'FIRST_VALUE', 4, '@price', 'BY', '@price', 'DESC', 'AS', 'top_price', 'REDUCE', 'FIRST_VALUE', 4, '@title', 'BY', '@price', 'ASC', 'AS', 'bottom_item', 'REDUCE', 'FIRST_VALUE', 4, '@price', 'BY', '@price', 'ASC', 'AS', 'bottom_price', 'SORTBY', 2, '@top_price', 'DESC', 'MAX', 5) expected = [ 4L, [ 'brand', 'sony', 'top_item', 'sony psp slim & lite 2000 console', 'top_price', '695.8', 'bottom_item', 'sony dlchd20p high speed hdmi cable for playstation 3', 'bottom_price', '5.88' ], [ 'brand', 'matias', 'top_item', 'matias halfkeyboard usb', 'top_price', '559.99', 'bottom_item', 'matias halfkeyboard usb', 'bottom_price', '559.99' ], [ 'brand', 'beyerdynamic', 'top_item', 'beyerdynamic mmx300 pc gaming premium digital headset with microphone', 'top_price', '359.74', 'bottom_item', 'beyerdynamic headzone pc gaming digital surround sound system with mmx300 digital headset with microphone', 'bottom_price', '0' ], [ 'brand', 'mad catz', 'top_item', 'mad catz s.t.r.i.k.e.7 gaming keyboard', 'top_price', '295.95', 'bottom_item', 'madcatz mov4545 xbox replacement breakaway cable', 'bottom_price', '3.49' ] ] # hack :( def mklower(result): for arr in result[1:]: for x in range(len(arr)): arr[x] = arr[x].lower() mklower(expected) mklower(res) self.env.assertListEqual(expected, res) def testLoadAfterGroupBy(self): with self.env.assertResponseError(): self.env.cmd('ft.aggregate', 'games', '*', 'GROUPBY', 1, '@brand', 'LOAD', 1, '@brand') def testReducerGeneratedAliasing(self): rv = self.env.cmd('ft.aggregate', 'games', '*', 'GROUPBY', 1, '@brand', 'REDUCE', 'MIN', 1, '@price', 'LIMIT', 0, 1) self.env.assertEqual( [292L, ['brand', '', '__generated_aliasminprice', '0']], rv) rv = self.env.cmd('ft.aggregate', 'games', '@brand:(sony|matias|beyerdynamic|(mad catz))', 'GROUPBY', 1, '@brand', 'REDUCE', 'FIRST_VALUE', 4, '@title', 'BY', '@price', 'DESC', 'SORTBY', 2, '@brand', 'ASC') self.env.assertEqual('__generated_aliasfirst_valuetitle,by,price,desc', rv[1][2]) def testIssue1125(self): self.env.skipOnCluster() # SEARCH should fail self.env.expect('ft.search', 'games', '*', 'limit', 0, 2000000).error() \ .contains('LIMIT exceeds maximum of 1000000') # SEARCH should succeed self.env.expect('ft.config', 'set', 'MAXSEARCHRESULTS', -1).ok() rv = self.env.cmd('ft.search', 'games', '*', 'LIMIT', 0, 12345678) self.env.assertEqual(4531, len(rv)) # AGGREGATE should succeed rv = self.env.cmd('ft.aggregate', 'games', '*', 'LIMIT', 0, 12345678) self.env.assertEqual(2266, len(rv)) # AGGREGATE should fail self.env.expect('ft.config', 'set', 'MAXAGGREGATERESULTS', 1000000).ok() self.env.expect('ft.aggregate', 'games', '*', 'limit', 0, 2000000).error() \ .contains('LIMIT exceeds maximum of 1000000') # force global limit on aggregate num = 10 self.env.expect('ft.config', 'set', 'MAXAGGREGATERESULTS', num).ok() rv = self.env.cmd('ft.aggregate', 'games', '*') self.env.assertEqual(num + 1, len(rv)) def testMultiSortBy(self): self.env.expect('ft.aggregate', 'games', '*', 'LOAD', '2', '@brand', '@price', 'SORTBY', 2, '@brand', 'DESC', 'SORTBY', 2, '@price', 'DESC').error()\ .contains('Multiple SORTBY steps are not allowed. Sort multiple fields in a single step')
class testPath(): def __init__(self): self.env = Env(decodeResponses=True) global redis_graph redis_con = self.env.getConnection() redis_graph = Graph(GRAPH_ID, redis_con) def setUp(self): self.env.flush() def assert_distance(self, a, b, expected_distance): # A is 18.07 km away from B q = """WITH point({latitude:%f, longitude:%f}) AS a, point({latitude:%f, longitude:%f}) AS b RETURN distance(a, b)""" % (a['lat'], a['lon'], b['lat'], b['lon']) distance = redis_graph.query(q).result_set[0][0] error_rate = 0.1 * expected_distance self.env.assertAlmostEqual(distance, expected_distance, error_rate) def test_point_distance(self): # 0 m apart a = {'lat': 32.070794860, 'lon': 34.820751118} expected_distance = 0 self.assert_distance(a, a, expected_distance) # 160 m apart a = {'lat': 32.070794860, 'lon': 34.820751118} b = {'lat': 32.070109656, 'lon': 34.822351298} expected_distance = 160 self.assert_distance(a, b, expected_distance) # 11352 km apart a = {'lat': 32.070794860, 'lon': 34.820751118} b = {'lat': 30.621734079, 'lon': -96.33775507} expected_distance = 11352120 self.assert_distance(a, b, expected_distance) def test_point_values(self): try: # latitude > 90 q = "RETURN point({latitude:90.1, longitude:20}) AS p" res = redis_graph.query(q) assert (False) except Exception as e: # Expecting an error. self.env.assertIn('latitude should be within', str(e)) try: # latitude < -90 q = "RETURN point({latitude:-90.1, longitude:20}) AS p" res = redis_graph.query(q) assert (False) except Exception as e: # Expecting an error. self.env.assertIn('latitude should be within', str(e)) try: # longitude > 180 q = "RETURN point({latitude:10, longitude:180.1}) AS p" res = redis_graph.query(q) assert (False) except Exception as e: # Expecting an error. self.env.assertIn('longitude should be within', str(e)) try: # longitude < -180 q = "RETURN point({latitude:10, longitude:-180.1}) AS p" res = redis_graph.query(q) assert (False) except Exception as e: # Expecting an error. self.env.assertIn('longitude should be within', str(e)) def test_point_index_lookup(self): home = {'lat': 32.070794860, 'lon': 34.820751118} univ = {'lat': 30.621734079, 'lon': -96.33775507} kiosk = {'lat': 32.07011414663042, 'lon': 34.82235394761603} austin = {'lat': 30.274919961709788, 'lon': -97.7403239617543} miradouro = {'lat': 37.854010999507736, 'lon': -25.775820972037057} # create index over location q = "create index on :N(loc)" redis_graph.query(q) # create 2 points: 'home' and 'univ' q = """create (:N {name:'home', loc:point({ latitude:%f, longitude:%f })})""" % ( home['lat'], home['lon']) redis_graph.query(q) q = """create (:N {name:'univ', loc:point({ latitude:%f, longitude:%f })})""" % ( univ['lat'], univ['lon']) redis_graph.query(q) # validate that the entities were created and can be returned properly q = """match (n:N) RETURN n.name, n.loc ORDER BY n.name""" actual_result = redis_graph.query(q) self.env.assertEquals(actual_result.result_set[0][0], "home") self.env.assertAlmostEqual(actual_result.result_set[0][1]["latitude"], 32.070794860, 1e-5) self.env.assertAlmostEqual(actual_result.result_set[0][1]["longitude"], 34.820751118, 1e-5) self.env.assertEquals(actual_result.result_set[1][0], "univ") self.env.assertAlmostEqual(actual_result.result_set[1][1]["latitude"], 30.621734079, 1e-5) self.env.assertAlmostEqual(actual_result.result_set[1][1]["longitude"], -96.33775507, 1e-5) idx_q = """MATCH (n:N) WHERE distance(n.loc, point({latitude:%f, longitude:%f})) < %d RETURN n.name""" none_idx_q = """MATCH (n) WHERE distance(n.loc, point({latitude:%f, longitude:%f})) < %d RETURN n.name""" # make sure index is being utilized plan = redis_graph.execution_plan(idx_q % (32.12, 43.32, 100)) self.env.assertIn('Index Scan', plan) # near kiosk (200m) distance = 200 idx_res = redis_graph.query( idx_q % (kiosk['lat'], kiosk['lon'], distance)).result_set none_idx_res = redis_graph.query( none_idx_q % (kiosk['lat'], kiosk['lon'], distance)).result_set # expecting just a single result: 'home' self.env.assertEquals(len(idx_res), 1) self.env.assertEquals(idx_res, none_idx_res) self.env.assertEquals(idx_res[0][0], 'home') # near "Miradouro do Cerrado das Freiras" (100km) # expecting no results distance = 100000 idx_res = redis_graph.query( idx_q % (miradouro['lat'], miradouro['lon'], distance)).result_set none_idx_res = redis_graph.query( none_idx_q % (miradouro['lat'], miradouro['lon'], distance)).result_set self.env.assertEquals(idx_res, none_idx_res) self.env.assertEquals(len(idx_res), 0) # near Austin Texas (200km) # expecting just a single result: 'univ' distance = 200000 idx_res = redis_graph.query( idx_q % (austin['lat'], austin['lon'], distance)).result_set none_idx_res = redis_graph.query( none_idx_q % (austin['lat'], austin['lon'], distance)).result_set self.env.assertEquals(idx_res, none_idx_res) self.env.assertEquals(len(idx_res), 1) self.env.assertEquals(idx_res[0][0], 'univ')
class testPagerankFlow(FlowTestsBase): def __init__(self): self.env = Env(decodeResponses=True) global redis_graph redis_con = self.env.getConnection() redis_graph = Graph(GRAPH_ID, redis_con) def test_pagerank_no_label_no_relation(self): self.env.cmd('flushall') q = "CREATE (a:L0 {v:0})-[:R0]->(b:L1 {v:1})-[:R1]->(c:L2 {v:2})" redis_graph.query(q) q = """CALL algo.pageRank(NULL, NULL) YIELD node, score RETURN node.v, score""" resultset = redis_graph.query(q).result_set self.env.assertEqual(len(resultset), 3) self.env.assertEqual(resultset[0][0], 2) self.env.assertAlmostEqual(resultset[0][1], 0.609753012657166, 0.0001) self.env.assertEqual(resultset[1][0], 1) self.env.assertAlmostEqual(resultset[1][1], 0.286585807800293, 0.0001) self.env.assertEqual(resultset[2][0], 0) self.env.assertAlmostEqual(resultset[2][1], 0.103661172091961, 0.0001) def test_pagerank_no_label(self): self.env.cmd('flushall') q = "CREATE (a:L0 {v:0})-[:R]->(b:L1 {v:1})-[:R0]->(c:L2 {v:2})" redis_graph.query(q) q = """CALL algo.pageRank(NULL, 'R') YIELD node, score RETURN node.v, score""" resultset = redis_graph.query(q).result_set self.env.assertEqual(len(resultset), 3) self.env.assertEqual(resultset[0][0], 1) self.env.assertAlmostEqual(resultset[0][1], 0.660703718662262, 0.0001) self.env.assertEqual(resultset[1][0], 0) self.env.assertAlmostEqual(resultset[1][1], 0.169648125767708, 0.0001) self.env.assertEqual(resultset[2][0], 2) self.env.assertAlmostEqual(resultset[2][1], 0.169648125767708, 0.0001) def test_pagerank_no_relation(self): self.env.cmd('flushall') q = "CREATE (a:L {v:0})-[:R]->(b:L {v:1})-[:R0]->(c:L2 {v:2})" redis_graph.query(q) q = """CALL algo.pageRank('L', NULL) YIELD node, score RETURN node.v, score""" resultset = redis_graph.query(q).result_set self.env.assertEqual(len(resultset), 2) self.env.assertEqual(resultset[0][0], 1) self.env.assertAlmostEqual(resultset[0][1], 0.777813196182251, 0.0001) self.env.assertEqual(resultset[1][0], 0) self.env.assertAlmostEqual(resultset[1][1], 0.22218681871891, 0.0001) def test_pagerank_no_connections(self): # Pagerank only considers connections where both ends # are of the same type, as such it might happen that pagerank # is executed against an empty matrix. queries = [ # No data, no edges with both src and dest of type 'L' "CREATE (a {v:1})-[:R]->(b {v:2})", "CREATE (a:L {v:1})-[:R]->(b {v:2})", "CREATE (a {v:1})-[:R]->(b:L {v:2})", "CREATE (a:L {v:1})-[:R]->(b {v:2})-[:R]->(c:L {v:3})", ] for q in queries: self.env.cmd('flushall') redis_graph.query(q) q = """CALL algo.pageRank('L', 'R') YIELD node, score RETURN node.v, score""" resultset = redis_graph.query(q).result_set self.env.assertEqual(len(resultset), 0) def test_pagerank(self): # Pagerank considers only nodes of given label and # relation of given relationship type # multiple edges between two nodes are considered as a single connection. queries = [ # Single Label, single connection. "CREATE (a:L {v:1})-[:R]->(b:L {v:2})", # Single Label, multi connection. "CREATE (a:L {v:1})-[:R]->(b:L {v:2}), (a)-[:R]->(b)", # Multi Label, single connection. "CREATE (a:L {v:1})-[:R]->(b:L {v:2}), (:X)-[:R]->(:X)", # Multi Label, multi connection. "CREATE (a:L {v:1})-[:R]->(b:L {v:2}), (a)-[:R]->(b), (:X)-[:R]->(:X)" ] for q in queries: self.env.cmd('flushall') redis_graph.query(q) q = """CALL algo.pageRank('L', 'R') YIELD node, score RETURN node.v, score""" resultset = redis_graph.query(q).result_set # 2) 1) 1) (integer) 2 # 2) "0.777813196182251" # 2) 1) (integer) 1 # 2) "0.22218681871891" self.env.assertEqual(len(resultset), 2) self.env.assertEqual(resultset[0][0], 2) self.env.assertAlmostEqual(resultset[0][1], 0.777813196182251, 0.0001) self.env.assertEqual(resultset[1][0], 1) self.env.assertAlmostEqual(resultset[1][1], 0.22218681871891, 0.0001)
class TestAggregate(): def __init__(self): self.env = Env() add_values(self.env) def testGroupBy(self): cmd = ['ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'count', '0', 'AS', 'count', 'SORTBY', 2, '@count', 'desc', 'LIMIT', '0', '5' ] res = self.env.cmd(*cmd) self.env.assertIsNotNone(res) self.env.assertEqual([292L, ['brand', '', 'count', '1518'], ['brand', 'mad catz', 'count', '43'], ['brand', 'generic', 'count', '40'], ['brand', 'steelseries', 'count', '37'], ['brand', 'logitech', 'count', '35']], res) def testMinMax(self): cmd = ['ft.aggregate', 'games', 'sony', 'GROUPBY', '1', '@brand', 'REDUCE', 'count', '0', 'REDUCE', 'min', '1', '@price', 'as', 'minPrice', 'SORTBY', '2', '@minPrice', 'DESC'] res = self.env.cmd(*cmd) self.env.assertIsNotNone(res) row = to_dict(res[1]) self.env.assertEqual(88, int(float(row['minPrice']))) cmd = ['ft.aggregate', 'games', 'sony', 'GROUPBY', '1', '@brand', 'REDUCE', 'count', '0', 'REDUCE', 'max', '1', '@price', 'as', 'maxPrice', 'SORTBY', '2', '@maxPrice', 'DESC'] res = self.env.cmd(*cmd) row = to_dict(res[1]) self.env.assertEqual(695, int(float(row['maxPrice']))) def testAvg(self): cmd = ['ft.aggregate', 'games', 'sony', 'GROUPBY', '1', '@brand', 'REDUCE', 'avg', '1', '@price', 'AS', 'avg_price', 'REDUCE', 'count', '0', 'SORTBY', '2', '@avg_price', 'DESC'] res = self.env.cmd(*cmd) self.env.assertIsNotNone(res) self.env.assertEqual(26, res[0]) # Ensure the formatting actually exists first_row = to_dict(res[1]) self.env.assertEqual(109, int(float(first_row['avg_price']))) for row in res[1:]: row = to_dict(row) self.env.assertIn('avg_price', row) # Test aliasing cmd = ['FT.AGGREGATE', 'games', 'sony', 'GROUPBY', '1', '@brand', 'REDUCE', 'avg', '1', '@price', 'AS', 'avgPrice'] res = self.env.cmd(*cmd) first_row = to_dict(res[1]) self.env.assertEqual(17, int(float(first_row['avgPrice']))) def testCountDistinct(self): cmd = ['FT.AGGREGATE', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'COUNT_DISTINCT', '1', '@title', 'AS', 'count_distinct(title)', 'REDUCE', 'COUNT', '0' ] res = self.env.cmd(*cmd)[1:] # print res row = to_dict(res[0]) self.env.assertEqual(1484, int(row['count_distinct(title)'])) cmd = ['FT.AGGREGATE', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'COUNT_DISTINCTISH', '1', '@title', 'AS', 'count_distinctish(title)', 'REDUCE', 'COUNT', '0' ] res = self.env.cmd(*cmd)[1:] # print res row = to_dict(res[0]) self.env.assertEqual(1461, int(row['count_distinctish(title)'])) def testQuantile(self): cmd = ['FT.AGGREGATE', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'QUANTILE', '2', '@price', '0.50', 'AS', 'q50', 'REDUCE', 'QUANTILE', '2', '@price', '0.90', 'AS', 'q90', 'REDUCE', 'QUANTILE', '2', '@price', '0.95', 'AS', 'q95', 'REDUCE', 'AVG', '1', '@price', 'REDUCE', 'COUNT', '0', 'AS', 'rowcount', 'SORTBY', '2', '@rowcount', 'DESC', 'MAX', '1'] res = self.env.cmd(*cmd) row = to_dict(res[1]) # TODO: Better samples self.env.assertAlmostEqual(14.99, float(row['q50']), delta=3) self.env.assertAlmostEqual(70, float(row['q90']), delta=50) # This tests the 95th percentile, which is error prone because # so few samples actually exist. I'm disabling it for now so that # there is no breakage in CI # self.env.assertAlmostEqual(110, (float(row['q95'])), delta=50) def testStdDev(self): cmd = ['FT.AGGREGATE', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'STDDEV', '1', '@price', 'AS', 'stddev(price)', 'REDUCE', 'AVG', '1', '@price', 'AS', 'avgPrice', 'REDUCE', 'QUANTILE', '2', '@price', '0.50', 'AS', 'q50Price', 'REDUCE', 'COUNT', '0', 'AS', 'rowcount', 'SORTBY', '2', '@rowcount', 'DESC', 'LIMIT', '0', '10'] res = self.env.cmd(*cmd) row = to_dict(res[1]) self.env.assertTrue(10 <= int( float(row['q50Price'])) <= 20) self.env.assertAlmostEqual(53, int(float(row['stddev(price)'])), delta=50) self.env.assertEqual(29, int(float(row['avgPrice']))) def testParseTime(self): cmd = ['FT.AGGREGATE', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'COUNT', '0', 'AS', 'count', 'APPLY', 'timefmt(1517417144)', 'AS', 'dt', 'APPLY', 'parse_time("%FT%TZ", @dt)', 'as', 'parsed_dt', 'LIMIT', '0', '1'] res = self.env.cmd(*cmd) self.env.assertEqual(['brand', '', 'count', '1518', 'dt', '2018-01-31T16:45:44Z', 'parsed_dt', '1517417144'], res[1]) def testRandomSample(self): cmd = ['FT.AGGREGATE', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'COUNT', '0', 'AS', 'num', 'REDUCE', 'RANDOM_SAMPLE', '2', '@price', '10', 'SORTBY', '2', '@num', 'DESC', 'MAX', '10'] for row in self.env.cmd(*cmd)[1:]: self.env.assertIsInstance(row[5], list) self.env.assertGreater(len(row[5]), 0) self.env.assertGreaterEqual(row[3], len(row[5])) self.env.assertLessEqual(len(row[5]), 10) def testTimeFunctions(self): cmd = ['FT.AGGREGATE', 'games', '*', 'APPLY', '1517417144', 'AS', 'dt', 'APPLY', 'timefmt(@dt)', 'AS', 'timefmt', 'APPLY', 'day(@dt)', 'AS', 'day', 'APPLY', 'hour(@dt)', 'AS', 'hour', 'APPLY', 'minute(@dt)', 'AS', 'minute', 'APPLY', 'month(@dt)', 'AS', 'month', 'APPLY', 'dayofweek(@dt)', 'AS', 'dayofweek', 'APPLY', 'dayofmonth(@dt)', 'AS', 'dayofmonth', 'APPLY', 'dayofyear(@dt)', 'AS', 'dayofyear', 'APPLY', 'year(@dt)', 'AS', 'year', 'LIMIT', '0', '1'] res = self.env.cmd(*cmd) self.env.assertListEqual([1L, ['dt', '1517417144', 'timefmt', '2018-01-31T16:45:44Z', 'day', '1517356800', 'hour', '1517414400', 'minute', '1517417100', 'month', '1514764800', 'dayofweek', '3', 'dayofmonth', '31', 'dayofyear', '30', 'year', '2018']], res) def testStringFormat(self): cmd = ['FT.AGGREGATE', 'games', '@brand:sony', 'GROUPBY', '2', '@title', '@brand', 'REDUCE', 'COUNT', '0', 'REDUCE', 'MAX', '1', '@price', 'AS', 'price', 'APPLY', 'format("%s|%s|%s|%s", @title, @brand, "Mark", @price)', 'as', 'titleBrand', 'LIMIT', '0', '10'] res = self.env.cmd(*cmd) for row in res[1:]: row = to_dict(row) expected = '%s|%s|%s|%g' % ( row['title'], row['brand'], 'Mark', float(row['price'])) self.env.assertEqual(expected, row['titleBrand']) def testSum(self): cmd = ['ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'count', '0', 'AS', 'count', 'REDUCE', 'sum', 1, '@price', 'AS', 'sum(price)', 'SORTBY', 2, '@sum(price)', 'desc', 'LIMIT', '0', '5' ] res = self.env.cmd(*cmd) self.env.assertEqual([292L, ['brand', '', 'count', '1518', 'sum(price)', '44780.69'], ['brand', 'mad catz', 'count', '43', 'sum(price)', '3973.48'], ['brand', 'razer', 'count', '26', 'sum(price)', '2558.58'], ['brand', 'logitech', 'count', '35', 'sum(price)', '2329.21'], ['brand', 'steelseries', 'count', '37', 'sum(price)', '1851.12']], res) def testFilter(self): cmd = ['ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'count', '0', 'AS', 'count', 'FILTER', '@count > 5' ] res = self.env.cmd(*cmd) for row in res[1:]: row = to_dict(row) self.env.assertGreater(int(row['count']), 5) cmd = ['ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'count', '0', 'AS', 'count', 'FILTER', '@count < 5', 'FILTER', '@count > 2 && @brand != ""' ] res = self.env.cmd(*cmd) for row in res[1:]: row = to_dict(row) self.env.assertLess(int(row['count']), 5) self.env.assertGreater(int(row['count']), 2) def testToList(self): cmd = ['ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'count_distinct', '1', '@price', 'as', 'count', 'REDUCE', 'tolist', 1, '@price', 'as', 'prices', 'SORTBY', 2, '@count', 'desc', 'LIMIT', '0', '5' ] res = self.env.cmd(*cmd) for row in res[1:]: row = to_dict(row) self.env.assertEqual(int(row['count']), len(row['prices'])) def testSortBy(self): res = self.env.cmd('ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'sum', 1, '@price', 'as', 'price', 'SORTBY', 2, '@price', 'desc', 'LIMIT', '0', '2') self.env.assertListEqual([292L, ['brand', '', 'price', '44780.69'], [ 'brand', 'mad catz', 'price', '3973.48']], res) res = self.env.cmd('ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'sum', 1, '@price', 'as', 'price', 'SORTBY', 2, '@price', 'asc', 'LIMIT', '0', '2') self.env.assertListEqual([292L, ['brand', 'myiico', 'price', '0.23'], [ 'brand', 'crystal dynamics', 'price', '0.25']], res) # Test MAX with limit higher than it res = self.env.cmd('ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'sum', 1, '@price', 'as', 'price', 'SORTBY', 2, '@price', 'asc', 'MAX', 2) self.env.assertListEqual([292L, ['brand', 'myiico', 'price', '0.23'], [ 'brand', 'crystal dynamics', 'price', '0.25']], res) # Test Sorting by multiple properties res = self.env.cmd('ft.aggregate', 'games', '*', 'GROUPBY', '1', '@brand', 'REDUCE', 'sum', 1, '@price', 'as', 'price', 'APPLY', '(@price % 10)', 'AS', 'price', 'SORTBY', 4, '@price', 'asc', '@brand', 'desc', 'MAX', 10, ) self.env.assertListEqual([292L, ['brand', 'zps', 'price', '0'], ['brand', 'zalman', 'price', '0'], ['brand', 'yoozoo', 'price', '0'], ['brand', 'white label', 'price', '0'], ['brand', 'stinky', 'price', '0'], [ 'brand', 'polaroid', 'price', '0'], ['brand', 'plantronics', 'price', '0'], ['brand', 'ozone', 'price', '0'], ['brand', 'oooo', 'price', '0'], ['brand', 'neon', 'price', '0']], res) def testExpressions(self): pass def testNoGroup(self): res = self.env.cmd('ft.aggregate', 'games', '*', 'LOAD', '2', '@brand', '@price', 'APPLY', 'floor(sqrt(@price)) % 10', 'AS', 'price', 'SORTBY', 4, '@price', 'desc', '@brand', 'desc', 'MAX', 5, ) exp = [2265L, ['brand', 'xbox', 'price', '9'], ['brand', 'turtle beach', 'price', '9'], ['brand', 'trust', 'price', '9'], ['brand', 'steelseries', 'price', '9'], ['brand', 'speedlink', 'price', '9']] # exp = [2265L, ['brand', 'Xbox', 'price', '9'], ['brand', 'Turtle Beach', 'price', '9'], [ # 'brand', 'Trust', 'price', '9'], ['brand', 'SteelSeries', 'price', '9'], ['brand', 'Speedlink', 'price', '9']] self.env.assertListEqual(exp[1], res[1]) def testLoad(self): res = self.env.cmd('ft.aggregate', 'games', '*', 'LOAD', '3', '@brand', '@price', '@nonexist', 'SORTBY', 2, '@price', 'DESC', 'MAX', 2) exp = [3L, ['brand', '', 'price', '759.12'], ['brand', 'Sony', 'price', '695.8']] self.env.assertEqual(exp[1], res[1]) def testSplit(self): res = self.env.cmd('ft.aggregate', 'games', '*', 'APPLY', 'split("hello world, foo,,,bar,", ",", " ")', 'AS', 'strs', 'APPLY', 'split("hello world, foo,,,bar,", " ", ",")', 'AS', 'strs2', 'APPLY', 'split("hello world, foo,,,bar,", "", "")', 'AS', 'strs3', 'APPLY', 'split("hello world, foo,,,bar,")', 'AS', 'strs4', 'APPLY', 'split("hello world, foo,,,bar,",",")', 'AS', 'strs5', 'APPLY', 'split("")', 'AS', 'empty', 'LIMIT', '0', '1' ) # print "Got {} results".format(len(res)) # return # pprint.pprint(res) self.env.assertListEqual([1L, ['strs', ['hello world', 'foo', 'bar'], 'strs2', ['hello', 'world', 'foo,,,bar'], 'strs3', ['hello world, foo,,,bar,'], 'strs4', ['hello world', 'foo', 'bar'], 'strs5', ['hello world', 'foo', 'bar'], 'empty', []]], res) def testFirstValue(self): res = self.env.cmd('ft.aggregate', 'games', '@brand:(sony|matias|beyerdynamic|(mad catz))', 'GROUPBY', 1, '@brand', 'REDUCE', 'FIRST_VALUE', 4, '@title', 'BY', '@price', 'DESC', 'AS', 'top_item', 'REDUCE', 'FIRST_VALUE', 4, '@price', 'BY', '@price', 'DESC', 'AS', 'top_price', 'REDUCE', 'FIRST_VALUE', 4, '@title', 'BY', '@price', 'ASC', 'AS', 'bottom_item', 'REDUCE', 'FIRST_VALUE', 4, '@price', 'BY', '@price', 'ASC', 'AS', 'bottom_price', 'SORTBY', 2, '@top_price', 'DESC', 'MAX', 5 ) expected = [4L, ['brand', 'sony', 'top_item', 'sony psp slim & lite 2000 console', 'top_price', '695.8', 'bottom_item', 'sony dlchd20p high speed hdmi cable for playstation 3', 'bottom_price', '5.88'], ['brand', 'matias', 'top_item', 'matias halfkeyboard usb', 'top_price', '559.99', 'bottom_item', 'matias halfkeyboard usb', 'bottom_price', '559.99'], ['brand', 'beyerdynamic', 'top_item', 'beyerdynamic mmx300 pc gaming premium digital headset with microphone', 'top_price', '359.74', 'bottom_item', 'beyerdynamic headzone pc gaming digital surround sound system with mmx300 digital headset with microphone', 'bottom_price', '0'], ['brand', 'mad catz', 'top_item', 'mad catz s.t.r.i.k.e.7 gaming keyboard', 'top_price', '295.95', 'bottom_item', 'madcatz mov4545 xbox replacement breakaway cable', 'bottom_price', '3.49']] self.env.assertListEqual(expected, res) def testLoadAfterGroupBy(self): with self.env.assertResponseError(): self.env.cmd('ft.aggregate', 'games', '*', 'GROUPBY', 1, '@brand', 'LOAD', 1, '@brand')
class test_torch_script_extesions: def __init__(self): self.env = Env() if not TEST_PT: self.env.debugPrint("skipping {} since TEST_PT=0".format( sys._getframe().f_code.co_name), force=True) self.env.skip() self.con = self.env.getConnection() test_data_path = os.path.join(os.path.dirname(__file__), 'test_data') script_filename = os.path.join(test_data_path, 'redis_scripts.py') with open(script_filename, 'rb') as f: script = f.read() ret = self.con.execute_command('AI.SCRIPTSET', 'redis_scripts{1}', DEVICE, 'SOURCE', script) self.env.assertEqual(ret, b'OK') # self.env.ensureSlaveSynced(self.con, self.env) def test_redis_error(self): try: self.con.execute_command('AI.SCRIPTEXECUTE', 'redis_scripts', 'test_redis_error', 'KEYS', 1, "x{1}", "INPUTS", 1, "x{1}") self.env.assertTrue(False) except: pass def test_simple_test_set(self): self.con.execute_command('AI.SCRIPTEXECUTE', 'redis_scripts{1}', 'test_set_key', 'KEYS', 1, "x{1}", "INPUTS", 2, "x{1}", 1) self.env.assertEqual(b"1", self.con.get("x{1}")) def test_int_set_get(self): self.con.execute_command('AI.SCRIPTEXECUTE', 'redis_scripts{1}', 'test_int_set_get', 'KEYS', 1, "x{1}", "INPUTS", 2, "x{1}", 1, 'OUTPUTS', 1, 'y{1}') y = self.con.execute_command('AI.TENSORGET', 'y{1}', 'meta', 'VALUES') self.env.assertEqual( y, [b"dtype", b"INT64", b"shape", [], b"values", [1]]) def test_int_set_incr(self): self.con.execute_command('AI.SCRIPTEXECUTE', 'redis_scripts{1}', 'test_int_set_incr', 'KEYS', 1, "x{1}", "INPUTS", 2, "x{1}", 1, 'OUTPUTS', 1, 'y{1}') y = self.con.execute_command('AI.TENSORGET', 'y{1}', 'meta', 'VALUES') self.env.assertEqual( y, [b"dtype", b"INT64", b"shape", [], b"values", [2]]) def test_float_get_set(self): self.con.execute_command('AI.SCRIPTEXECUTE', 'redis_scripts{1}', 'test_float_set_get', 'KEYS', 1, "x{1}", "INPUTS", 2, "x{1}", 1.1, 'OUTPUTS', 1, 'y{1}') y = self.con.execute_command('AI.TENSORGET', 'y{1}', 'meta', 'VALUES') self.env.assertEqual(y[0], b"dtype") self.env.assertEqual(y[1], b"FLOAT") self.env.assertEqual(y[2], b"shape") self.env.assertEqual(y[3], []) self.env.assertEqual(y[4], b"values") self.env.assertAlmostEqual(float(y[5][0]), 1.1, 0.1) def test_int_list(self): self.con.execute_command('AI.SCRIPTEXECUTE', 'redis_scripts{1}', 'test_int_list', 'KEYS', 1, "int_list{1}", 'INPUTS', 1, "int_list{1}", "LIST_INPUTS", 2, "1", "2", 'OUTPUTS', 1, 'y{1}') y = self.con.execute_command('AI.TENSORGET', 'y{1}', 'meta', 'VALUES') self.env.assertEqual( y, [b"dtype", b"INT64", b"shape", [2, 1], b"values", [1, 2]]) def test_str_list(self): self.con.execute_command('AI.SCRIPTEXECUTE', 'redis_scripts{1}', 'test_str_list', 'KEYS', 1, "str_list{1}", 'INPUTS', 1, "str_list{1}", "LIST_INPUTS", 2, "1", "2") res = self.con.execute_command("LRANGE", "str_list{1}", "0", "2") self.env.assertEqual(res, [b"1", b"2"]) def test_hash(self): self.con.execute_command('AI.SCRIPTEXECUTE', 'redis_scripts{1}', 'test_hash', 'KEYS', 1, "hash{1}", 'INPUTS', 1, "hash{1}", "LIST_INPUTS", 4, "field1", 1, "field2", 2, 'OUTPUTS', 1, 'y{1}') y = self.con.execute_command('AI.TENSORGET', 'y{1}', 'meta', 'VALUES') self.env.assertEqual( y, [b"dtype", b"INT64", b"shape", [2, 1], b"values", [1, 2]])