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 testMultiExecFlow(FlowTestsBase): def __init__(self): self.env = Env() global redis_con redis_con = self.env.getConnection() def test_graph_entities(self): # Delete previous graph if exists. redis_con.execute_command("DEL", GRAPH_ID) # Start a multi exec transaction. redis_con.execute_command("MULTI") # Create graph. redis_con.execute_command("GRAPH.QUERY", GRAPH_ID, CREATE_QUERY) # Count outgoing connections from Al, expecting 2 edges. # (Al)-[e]->() count (e) redis_con.execute_command("GRAPH.QUERY", GRAPH_ID, MATCH_QUERY) # Disconnect edge connecting Al to Betty. # (Al)-[e]->(Betty) delete (e) redis_con.execute_command("GRAPH.QUERY", GRAPH_ID, DEL_QUERY) # Count outgoing connections from Al, expecting 1 edges. # (Al)-[e]->() count (e) redis_con.execute_command("GRAPH.QUERY", GRAPH_ID, MATCH_QUERY) # Change Al name from Al to Steve. # (Al) set Al.name = Steve redis_con.execute_command("GRAPH.QUERY", GRAPH_ID, UPDATE_QUERY) # Count outgoing connections from Al, expecting 0 edges. # (Al)-[e]->() count (e) redis_con.execute_command("GRAPH.QUERY", GRAPH_ID, MATCH_QUERY) # Commit transaction. results = redis_con.execute_command("EXEC") # [ # [ # ['al.name', 'count(b)'], # ['Al', '2'] # ], # ['Query internal execution time: 0.143000 milliseconds'] # ] two_edges = results[1] two_edges = two_edges[1][0][1] self.env.assertEquals(two_edges, 2) one_edge = results[3] one_edge = one_edge[1][0][1] self.env.assertEquals(one_edge, 1) no_edges = results[5] no_edges = no_edges[1] self.env.assertEquals(len(no_edges), 0) def test_transaction_failure(self): redis_con_a = self.env.getConnection() redis_con_b = self.env.getConnection() results = redis_con_b.execute_command("INFO", "CLIENTS") self.env.assertGreaterEqual(results['connected_clients'], 2) # Delete previous graph if exists. redis_con_a.execute_command("DEL", GRAPH_ID) redis_con_a.execute_command("GRAPH.QUERY", GRAPH_ID, CREATE_QUERY) redis_con_a.execute_command("WATCH", GRAPH_ID) redis_con_a.execute_command("MULTI") redis_con_a.execute_command("GRAPH.QUERY", GRAPH_ID, MATCH_QUERY) results = redis_con_a.execute_command("EXEC") self.env.assertNotEqual(results, None) # read only query from client B - transaction OK redis_con_a.execute_command("WATCH", GRAPH_ID) redis_con_a.execute_command("MULTI") redis_con_b.execute_command("GRAPH.QUERY", GRAPH_ID, MATCH_QUERY) redis_con_a.execute_command("GRAPH.QUERY", GRAPH_ID, MATCH_QUERY) results = redis_con_a.execute_command("EXEC") self.env.assertNotEqual(results, None) # write query from client B - transaction fails redis_con_a.execute_command("WATCH", GRAPH_ID) redis_con_a.execute_command("MULTI") redis_con_b.execute_command("GRAPH.QUERY", GRAPH_ID, UPDATE_QUERY) redis_con_a.execute_command("GRAPH.QUERY", GRAPH_ID, MATCH_QUERY) results = redis_con_a.execute_command("EXEC") self.env.assertEqual(results, None) # GRAPH.EXPLAIN is read only - no data change - transaction OK redis_con_a.execute_command("WATCH", GRAPH_ID) redis_con_a.execute_command("MULTI") redis_con_b.execute_command("GRAPH.EXPLAIN", GRAPH_ID, UPDATE_QUERY) redis_con_a.execute_command("GRAPH.QUERY", GRAPH_ID, MATCH_QUERY) results = redis_con_a.execute_command("EXEC") self.env.assertNotEqual(results, None)
class testConfig(FlowTestsBase): def __init__(self): self.env = Env(decodeResponses=True) global redis_con global redis_graph redis_con = self.env.getConnection() redis_graph = Graph("config", redis_con) def test01_config_get(self): global redis_graph # Try reading 'QUERY_MEM_CAPACITY' from config config_name = "QUERY_MEM_CAPACITY" response = redis_con.execute_command("GRAPH.CONFIG GET " + config_name) expected_response = [config_name, 0] # capacity=QUERY_MEM_CAPACITY_UNLIMITED self.env.assertEqual(response, expected_response) # Try reading all configurations config_name = "*" response = redis_con.execute_command("GRAPH.CONFIG GET " + config_name) # At least 9 configurations should be reported self.env.assertGreaterEqual(len(response), 9) def test02_config_get_invalid_name(self): global redis_graph # Ensure that getter fails on invalid parameters appropriately fake_config_name = "FAKE_CONFIG_NAME" try: redis_con.execute_command("GRAPH.CONFIG GET " + fake_config_name) assert(False) except redis.exceptions.ResponseError as e: # Expecting an error. assert("Unknown configuration field" in str(e)) pass def test03_config_set(self): global redis_graph config_name = "RESULTSET_SIZE" config_value = 3 # Set configuration response = redis_con.execute_command("GRAPH.CONFIG SET %s %d" % (config_name, config_value)) self.env.assertEqual(response, "OK") # Make sure config been updated. response = redis_con.execute_command("GRAPH.CONFIG GET " + config_name) expected_response = [config_name, config_value] self.env.assertEqual(response, expected_response) config_name = "QUERY_MEM_CAPACITY" config_value = 1<<20 # 1MB # Set configuration response = redis_con.execute_command("GRAPH.CONFIG SET %s %d" % (config_name, config_value)) self.env.assertEqual(response, "OK") # Make sure config been updated. response = redis_con.execute_command("GRAPH.CONFIG GET " + config_name) expected_response = [config_name, config_value] self.env.assertEqual(response, expected_response) def test04_config_set_multi(self): # Set multiple configuration values response = redis_con.execute_command("GRAPH.CONFIG SET RESULTSET_SIZE 3 QUERY_MEM_CAPACITY 100") self.env.assertEqual(response, "OK") # Make sure both values been updated names = ["RESULTSET_SIZE", "QUERY_MEM_CAPACITY"] values = [3, 100] for name, val in zip(names, values): response = redis_con.execute_command("GRAPH.CONFIG GET %s" % name) expected_response = [name, val] self.env.assertEqual(response, expected_response) def test05_config_set_invalid_multi(self): # Get current configuration prev_conf = redis_con.execute_command("GRAPH.CONFIG GET *") try: # Set multiple configuration values, VKEY_MAX_ENTITY_COUNT is NOT # a runtime configuration, expecting this command to fail response = redis_con.execute_command("GRAPH.CONFIG SET QUERY_MEM_CAPACITY 150 VKEY_MAX_ENTITY_COUNT 40") assert(False) except redis.exceptions.ResponseError as e: # Expecting an error. assert("Field can not be re-configured" in str(e)) try: # Set multiple configuration values, FAKE_CONFIG_NAME is NOT a valid # configuration, expecting this command to fail response = redis_con.execute_command("GRAPH.CONFIG SET QUERY_MEM_CAPACITY 150 FAKE_CONFIG_NAME 40") assert(False) except redis.exceptions.ResponseError as e: # Expecting an error. assert("Unknown configuration field" in str(e)) try: # Set multiple configuration values, -1 is not a valid value for # MAX_QUEUED_QUERIES, expecting this command to fail response = redis_con.execute_command("GRAPH.CONFIG SET QUERY_MEM_CAPACITY 150 MAX_QUEUED_QUERIES -1") assert(False) except redis.exceptions.ResponseError as e: # Expecting an error. assert("Failed to set config value" in str(e)) # make sure configuration wasn't modified current_conf = redis_con.execute_command("GRAPH.CONFIG GET *") self.env.assertEqual(prev_conf, current_conf) def test06_config_set_invalid_name(self): # Ensure that setter fails on unknown configuration field fake_config_name = "FAKE_CONFIG_NAME" try: redis_con.execute_command("GRAPH.CONFIG SET " + fake_config_name + " 5") assert(False) except redis.exceptions.ResponseError as e: # Expecting an error. assert("Unknown configuration field" in str(e)) pass def test07_config_invalid_subcommand(self): # Ensure failure on invalid sub-command, e.g. GRAPH.CONFIG DREP... config_name = "RESULTSET_SIZE" try: response = redis_con.execute_command("GRAPH.CONFIG DREP " + config_name + " 3") assert(False) except redis.exceptions.ResponseError as e: assert("Unknown subcommand for GRAPH.CONFIG" in str(e)) pass def test08_config_reset_to_defaults(self): # Revert memory limit to default response = redis_con.execute_command("GRAPH.CONFIG SET QUERY_MEM_CAPACITY 0") self.env.assertEqual(response, "OK") # Change timeout value from default response = redis_con.execute_command("GRAPH.CONFIG SET TIMEOUT 10") self.env.assertEqual(response, "OK") # Make sure config been updated. response = redis_con.execute_command("GRAPH.CONFIG GET TIMEOUT") expected_response = ["TIMEOUT", 10] self.env.assertEqual(response, expected_response) query = """UNWIND range(1,1000000) AS v RETURN COUNT(v)""" # Ensure long-running query triggers a timeout try: result = redis_graph.query(query) assert(False) except redis.exceptions.ResponseError as e: self.env.assertContains("Query timed out", str(e)) # Revert timeout to unlimited response = redis_con.execute_command("GRAPH.CONFIG SET TIMEOUT 0") self.env.assertEqual(response, "OK") # Make sure config been updated. response = redis_con.execute_command("GRAPH.CONFIG GET TIMEOUT") expected_response = ["TIMEOUT", 0] self.env.assertEqual(response, expected_response) # Issue long-running query to validate the reconfiguration result = redis_graph.query(query) self.env.assertEqual(result.result_set[0][0], 1000000) # Change resultset_size from default response = redis_con.execute_command("GRAPH.CONFIG SET RESULTSET_SIZE 2") self.env.assertEqual(response, "OK") # Validate modified resultset_size result = redis_graph.query("UNWIND range(1, 10) AS v RETURN v") self.env.assertEqual(len(result.result_set), 2) # Revert resultset_size to unlimited with a negative argument response = redis_con.execute_command("GRAPH.CONFIG SET RESULTSET_SIZE -100") self.env.assertEqual(response, "OK") # Make sure resultset_size has been updated to unlimited. response = redis_con.execute_command("GRAPH.CONFIG GET RESULTSET_SIZE") expected_response = ["RESULTSET_SIZE", -1] self.env.assertEqual(response, expected_response) def test09_set_invalid_values(self): # The run-time configurations supported by RedisGraph are: # MAX_QUEUED_QUERIES # TIMEOUT # QUERY_MEM_CAPACITY # DELTA_MAX_PENDING_CHANGES # RESULTSET_SIZE # Validate that attempting to set these configurations to # invalid values fails try: # MAX_QUEUED_QUERIES must be a positive value redis_con.execute_command("GRAPH.CONFIG SET MAX_QUEUED_QUERIES 0") assert(False) except redis.exceptions.ResponseError as e: assert("Failed to set config value MAX_QUEUED_QUERIES to 0" in str(e)) pass # TIMEOUT, QUERY_MEM_CAPACITY, and DELTA_MAX_PENDING_CHANGES must be # non-negative values, 0 resets to default for config in ["TIMEOUT", "QUERY_MEM_CAPACITY", "DELTA_MAX_PENDING_CHANGES"]: try: redis_con.execute_command("GRAPH.CONFIG SET %s -1" % config) assert(False) except redis.exceptions.ResponseError as e: assert("Failed to set config value %s to -1" % config in str(e)) pass # No configuration can be set to a string for config in ["MAX_QUEUED_QUERIES", "TIMEOUT", "QUERY_MEM_CAPACITY", "DELTA_MAX_PENDING_CHANGES", "RESULTSET_SIZE"]: try: redis_con.execute_command("GRAPH.CONFIG SET %s invalid" % config) assert(False) except redis.exceptions.ResponseError as e: assert(("Failed to set config value %s to invalid" % config) in str(e))
class testConfig(FlowTestsBase): def __init__(self): self.env = Env(decodeResponses=True) global redis_con global redis_graph redis_con = self.env.getConnection() redis_graph = Graph("config", redis_con) def test01_config_get(self): global redis_graph # Try reading 'QUERY_MEM_CAPACITY' from config config_name = "QUERY_MEM_CAPACITY" response = redis_con.execute_command("GRAPH.CONFIG GET " + config_name) expected_response = [config_name, 0] # capacity=QUERY_MEM_CAPACITY_UNLIMITED self.env.assertEqual(response, expected_response) # Try reading all configurations config_name = "*" response = redis_con.execute_command("GRAPH.CONFIG GET " + config_name) # At least 9 configurations should be reported self.env.assertGreaterEqual(len(response), 9) def test02_config_get_invalid_name(self): global redis_graph # Ensure that getter fails on invalid parameters appropriately fake_config_name = "FAKE_CONFIG_NAME" try: redis_con.execute_command("GRAPH.CONFIG GET " + fake_config_name) assert (False) except redis.exceptions.ResponseError as e: # Expecting an error. assert ("Unknown configuration field" in str(e)) pass def test03_config_set(self): global redis_graph config_name = "RESULTSET_SIZE" config_value = 3 # Set configuration response = redis_con.execute_command("GRAPH.CONFIG SET %s %d" % (config_name, config_value)) self.env.assertEqual(response, "OK") # Make sure config been updated. response = redis_con.execute_command("GRAPH.CONFIG GET " + config_name) expected_response = [config_name, config_value] self.env.assertEqual(response, expected_response) config_name = "QUERY_MEM_CAPACITY" config_value = 1 << 20 # 1MB # Set configuration response = redis_con.execute_command("GRAPH.CONFIG SET %s %d" % (config_name, config_value)) self.env.assertEqual(response, "OK") # Make sure config been updated. response = redis_con.execute_command("GRAPH.CONFIG GET " + config_name) expected_response = [config_name, config_value] self.env.assertEqual(response, expected_response) def test04_config_set_multi(self): # Set multiple configuration values response = redis_con.execute_command( "GRAPH.CONFIG SET RESULTSET_SIZE 3 QUERY_MEM_CAPACITY 100") self.env.assertEqual(response, "OK") # Make sure both values been updated names = ["RESULTSET_SIZE", "QUERY_MEM_CAPACITY"] values = [3, 100] for name, val in zip(names, values): response = redis_con.execute_command("GRAPH.CONFIG GET %s" % name) expected_response = [name, val] self.env.assertEqual(response, expected_response) def test05_config_set_invalid_multi(self): # Get current configuration prev_conf = redis_con.execute_command("GRAPH.CONFIG GET *") try: # Set multiple configuration values, VKEY_MAX_ENTITY_COUNT is NOT # a runtime configuration, expecting this command to fail response = redis_con.execute_command( "GRAPH.CONFIG SET QUERY_MEM_CAPACITY 150 VKEY_MAX_ENTITY_COUNT 40" ) assert (False) except redis.exceptions.ResponseError as e: # Expecting an error. assert ("Field can not be re-configured" in str(e)) try: # Set multiple configuration values, FAKE_CONFIG_NAME is NOT a valid # configuration, expecting this command to fail response = redis_con.execute_command( "GRAPH.CONFIG SET QUERY_MEM_CAPACITY 150 FAKE_CONFIG_NAME 40") assert (False) except redis.exceptions.ResponseError as e: # Expecting an error. assert ("Unknown configuration field" in str(e)) try: # Set multiple configuration values, -1 is not a valid value for # MAX_QUEUED_QUERIES, expecting this command to fail response = redis_con.execute_command( "GRAPH.CONFIG SET QUERY_MEM_CAPACITY 150 MAX_QUEUED_QUERIES -1" ) assert (False) except redis.exceptions.ResponseError as e: # Expecting an error. assert ("Failed to set config value" in str(e)) # make sure configuration wasn't modified current_conf = redis_con.execute_command("GRAPH.CONFIG GET *") self.env.assertEqual(prev_conf, current_conf) def test06_config_set_invalid_name(self): # Ensure that setter fails on unknown configuration field fake_config_name = "FAKE_CONFIG_NAME" try: redis_con.execute_command("GRAPH.CONFIG SET " + fake_config_name + " 5") assert (False) except redis.exceptions.ResponseError as e: # Expecting an error. assert ("Unknown configuration field" in str(e)) pass def test07_config_invalid_subcommand(self): # Ensure failure on invalid sub-command, e.g. GRAPH.CONFIG DREP... config_name = "RESULTSET_SIZE" try: response = redis_con.execute_command("GRAPH.CONFIG DREP " + config_name + " 3") assert (False) except redis.exceptions.ResponseError as e: assert ("Unknown subcommand for GRAPH.CONFIG" in str(e)) pass