Beispiel #1
0
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 &amp; 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 &amp; 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')
Beispiel #3
0
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)
Beispiel #4
0
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