1# -*- coding: utf-8 -*-
2
3from common import *
4
5def test_1282(env):
6  env.expect('FT.CREATE idx ON HASH SCHEMA txt1 TEXT').equal('OK')
7  env.expect('FT.ADD idx doc1 1.0 FIELDS txt1 foo').equal('OK')
8
9  # optional search for new word would crash server
10  env.expect('FT.SEARCH idx', '~foo').equal([1L, 'doc1', ['txt1', 'foo']])
11  env.expect('FT.SEARCH idx', '~bar ~foo').equal([1L, 'doc1', ['txt1', 'foo']])
12
13def test_1304(env):
14  env.expect('FT.CREATE idx SCHEMA txt1 TEXT').equal('OK')
15  env.expect('FT.EXPLAIN idx -20*').equal('PREFIX{-20*}\n')
16  env.expect('FT.EXPLAIN idx -\\20*').equal('NOT{\n  PREFIX{20*}\n}\n')
17
18def test_1414(env):
19  env.skipOnCluster()
20  env.expect('FT.CREATE idx SCHEMA txt1 TEXT').equal('OK')
21  env.expect('ft.add idx doc 1 fields foo hello bar world').ok()
22  env.expect('ft.search idx * limit 0 1234567').error().contains('LIMIT exceeds maximum of 1000000')
23  env.expect('FT.CONFIG set MAXSEARCHRESULTS -1').equal('OK')
24  env.assertEqual(toSortedFlatList(env.cmd('ft.search idx * limit 0 1234567')), toSortedFlatList([1L, 'doc', ['foo', 'hello', 'bar', 'world']]))
25  env.expect('FT.CONFIG set MAXSEARCHRESULTS 1000000').equal('OK')
26
27def test_1502(env):
28  conn = getConnectionByEnv(env)
29  conn.execute_command('HSET', 'a', 'bar', 'hello')
30
31  env.expect('FT.CREATE idx1 SKIPINITIALSCAN SCHEMA foo TEXT').ok()
32  env.expect('FT.CREATE idx2 SKIPINITIALSCAN SCHEMA foo TEXT').ok()
33
34  env.expect('ft.search idx1 *').equal([0L])
35  env.expect('ft.search idx2 *').equal([0L])
36
37  env.expect('FT.ALTER idx1 SKIPINITIALSCAN SCHEMA ADD bar TEXT').ok()
38  env.expect('FT.ALTER idx2 SCHEMA ADD bar TEXT').ok()
39  waitForIndex(env, 'idx2')
40
41  env.expect('ft.search idx1 *').equal([0L])
42  env.expect('ft.search idx2 *').equal([1L, 'a', ['bar', 'hello']])
43
44def test_1601(env):
45  conn = getConnectionByEnv(env)
46  conn.execute_command('FT.CREATE', 'idx:movie', 'SCHEMA', 'title', 'TEXT')
47  conn.execute_command('HSET', 'movie:1', 'title', 'Star Wars: Episode I - The Phantom Menace')
48  conn.execute_command('HSET', 'movie:2', 'title', 'Star Wars: Episodes II - Attack of the Clones')
49  conn.execute_command('HSET', 'movie:3', 'title', 'Star Wars: Episode III - Revenge of the Sith')
50  res = env.cmd('ft.search idx:movie @title:(episode) withscores nocontent')
51  env.assertEqual(res[0], 3L)
52
53def testMultiSortby(env):
54  conn = getConnectionByEnv(env)
55  conn.execute_command('FT.CREATE', 'idx', 'SCHEMA', 't1', 'TEXT', 'SORTABLE', 't2', 'TEXT', 'SORTABLE', 't3', 'TEXT', 'SORTABLE')
56  conn.execute_command('FT.ADD', 'idx', '1', '1', 'FIELDS', 't1', 'foo', 't2', 'bar', 't3', 'baz')
57  conn.execute_command('FT.ADD', 'idx', '2', '1', 'FIELDS', 't1', 'bar', 't2', 'foo', 't3', 'baz')
58  sortby_t1 = [2L, '2', '1']
59  sortby_t2 = [2L, '1', '2']
60  env.expect('ft.search idx foo nocontent sortby t1 asc').equal(sortby_t1)
61  env.expect('ft.search idx foo nocontent sortby t2 asc').equal(sortby_t2)
62  env.expect('ft.search idx foo nocontent sortby t1 sortby t3').error()\
63    .contains('Multiple SORTBY steps are not allowed. Sort multiple fields in a single step')
64  #TODO: allow multiple sortby steps
65  #env.expect('ft.search idx foo nocontent sortby t1 sortby t3').equal(sortby_t1)
66  #env.expect('ft.search idx foo nocontent sortby t2 sortby t3').equal(sortby_t2)
67
68def test_1667(env):
69  conn = getConnectionByEnv(env)
70  conn.execute_command('FT.CREATE', 'idx', 'SCHEMA', 'tag', 'TAG', 'text', 'TEXT')
71  env.expect('ft.search idx @tag:{a}').equal([0L])
72  env.expect('ft.search idx @tag:{b}').equal([0L])
73
74  conn.execute_command('HSET', 'doc', 'tag', 'a,b')
75  conn.execute_command('HSET', 'doc1', 'tag', 'abc')
76
77  # test single stopword
78  env.expect('ft.search idx @tag:{a}').equal([1L, 'doc', ['tag', 'a,b']])
79  env.expect('ft.search idx @tag:{b}').equal([1L, 'doc', ['tag', 'a,b']])
80  env.expect('ft.search idx @tag:{c}').equal([0L])
81
82  # test stopword in list
83  env.expect('ft.search idx @tag:{a|c}').equal([1L, 'doc', ['tag', 'a,b']])
84  env.expect('ft.search idx @tag:{c|a}').equal([1L, 'doc', ['tag', 'a,b']])
85  env.expect('ft.search idx @tag:{c|a|c}').equal([1L, 'doc', ['tag', 'a,b']])
86
87  # test stopword with prefix
88  env.expect('ft.search idx @tag:{ab*}').equal([1L, 'doc1', ['tag', 'abc']])
89  env.expect('ft.search idx @tag:{abc*}').equal([1L, 'doc1', ['tag', 'abc']])
90
91  # ensure regular text field
92  conn.execute_command('HSET', 'doc_a', 'text', 'a')
93  conn.execute_command('HSET', 'doc_b', 'text', 'b')
94  env.expect('ft.search idx a').equal([0L])
95  env.expect('ft.search idx b').equal([1L, 'doc_b', ['text', 'b']])
96
97def test_MOD_865(env):
98  conn = getConnectionByEnv(env)
99  args_list = ['FT.CREATE', 'idx', 'SCHEMA']
100  for i in range(1025):
101    args_list.extend([i, 'NUMERIC', 'SORTABLE'])
102  env.expect(*args_list).error().contains('Schema is limited to 1024 fields')
103  env.expect('FT.DROPINDEX', 'idx')
104
105  args_list = ['FT.CREATE', 'idx', 'SCHEMA']
106  for i in range(129):
107    args_list.extend([i, 'TEXT'])
108  env.expect(*args_list).error().contains('Schema is limited to 128 TEXT fields')
109  env.expect('FT.DROPINDEX', 'idx')
110
111  args_list = ['FT.CREATE', 'idx', 'SCHEMA']
112  for i in range(2):
113    args_list.extend(['txt', 'TEXT'])
114  env.expect(*args_list).error().contains('Duplicate field in schema - txt')
115  env.expect('FT.DROPINDEX', 'idx')
116
117def test_issue1826(env):
118  # Stopword query is case sensitive.
119  conn = getConnectionByEnv(env)
120  conn.execute_command('FT.CREATE', 'idx', 'SCHEMA', 't', 'TEXT')
121  conn.execute_command('HSET', 'doc', 't', 'boy with glasses')
122
123  env.expect('FT.SEARCH', 'idx', 'boy with glasses').equal([1L, 'doc', ['t', 'boy with glasses']])
124  env.expect('FT.SEARCH', 'idx', 'boy With glasses').equal([1L, 'doc', ['t', 'boy with glasses']])
125
126def test_issue1834(env):
127  # Stopword query is case sensitive.
128  conn = getConnectionByEnv(env)
129  conn.execute_command('FT.CREATE', 'idx', 'SCHEMA', 't', 'TEXT')
130  conn.execute_command('HSET', 'doc', 't', 'hell hello')
131
132  env.expect('FT.SEARCH', 'idx', 'hell|hello', 'HIGHLIGHT').equal([1L, 'doc', ['t', '<b>hell</b> <b>hello</b>']])
133
134def test_issue1880(env):
135  # order of iterator in intersect is optimized by function
136  env.skipOnCluster()
137  conn = getConnectionByEnv(env)
138  env.cmd('FT.CONFIG', 'SET', '_PRINT_PROFILE_CLOCK', 'false')
139  conn.execute_command('FT.CREATE', 'idx', 'SCHEMA', 't', 'TEXT')
140  conn.execute_command('HSET', 'doc1', 't', 'hello world')
141  conn.execute_command('HSET', 'doc2', 't', 'hello')
142
143  excepted_res = ['Type', 'INTERSECT', 'Counter', 1L, 'Child iterators',
144                    ['Type', 'TEXT', 'Term', 'world', 'Counter', 1L, 'Size', 1L],
145                    ['Type', 'TEXT', 'Term', 'hello', 'Counter', 1L, 'Size', 2L]]
146  res1 = env.cmd('FT.PROFILE', 'idx', 'SEARCH', 'QUERY', 'hello world')
147  res2 = env.cmd('FT.PROFILE', 'idx', 'SEARCH', 'QUERY', 'world hello')
148  # both queries return `world` iterator before `hello`
149  env.assertEqual(res1[1][3][1], excepted_res)
150  env.assertEqual(res2[1][3][1], excepted_res)
151
152  # test with a term which does not exist
153  excepted_res = ['Type', 'INTERSECT', 'Counter', 0L, 'Child iterators',
154                    None,
155                    ['Type', 'TEXT', 'Term', 'world', 'Counter', 0L, 'Size', 1L],
156                    ['Type', 'TEXT', 'Term', 'hello', 'Counter', 0L, 'Size', 2L]]
157  res3 = env.cmd('FT.PROFILE', 'idx', 'SEARCH', 'QUERY', 'hello new world')
158
159  env.assertEqual(res3[1][3][1], excepted_res)
160
161def test_issue1932(env):
162    conn = getConnectionByEnv(env)
163    conn.execute_command('FT.CREATE', 'idx', 'SCHEMA', 't', 'TEXT')
164    env.expect('FT.AGGREGATE', 'idx', '*', 'LIMIT', '100000000000000000', '100000000000', 'SORTBY', '1', '@t').error() \
165      .contains('OFFSET exceeds maximum of 1000000')
166
167def test_issue1988(env):
168    conn = getConnectionByEnv(env)
169    conn.execute_command('FT.CREATE', 'idx', 'SCHEMA', 't', 'TEXT')
170    conn.execute_command('HSET', 'doc1', 't', 'foo')
171    env.expect('FT.SEARCH', 'idx', 'foo').equal([1L, 'doc1', ['t', 'foo']])
172    env.expect('FT.SEARCH', 'idx', 'foo', 'WITHSCORES').equal([1L, 'doc1', '1', ['t', 'foo']])
173    env.expect('FT.SEARCH', 'idx', 'foo', 'SORTBY' , 't').equal([1L, 'doc1', ['t', 'foo']])
174    env.expect('FT.SEARCH', 'idx', 'foo', 'WITHSCORES', 'SORTBY' , 't').equal([1L, 'doc1', '1', ['t', 'foo']])
175
176@no_msan
177def testIssue2104(env):
178  # 'AS' attribute does not work in functions
179  conn = getConnectionByEnv(env)
180
181  # hash
182  conn.execute_command('FT.CREATE', 'hash_idx', 'SCHEMA', 'name', 'TEXT', 'SORTABLE', 'subj1', 'NUMERIC', 'SORTABLE')
183  conn.execute_command('FT.ADD', 'hash_idx', 'data1', '1.0', 'FIELDS', 'name', 'abc', 'subj1', '20')
184  # load a single field
185  env.expect('FT.AGGREGATE', 'hash_idx', '*', 'LOAD', '1', '@subj1')    \
186      .equal([1L, ['subj1', '20']])
187  # load a field with an attribute
188  env.expect('FT.AGGREGATE', 'hash_idx', '*', 'LOAD', '3', '@subj1', 'AS', 'a')    \
189      .equal([1L, ['a', '20']])
190  # load field and use `APPLY`
191  env.expect('FT.AGGREGATE', 'hash_idx', '*', 'LOAD', '3', '@subj1', 'AS', 'a', 'APPLY', '(@a+@a)/2', 'AS', 'avg')   \
192      .equal([1L, ['a', '20', 'avg', '20']])
193  # load a field implicitly with `APPLY`
194  res = env.cmd('FT.AGGREGATE', 'hash_idx', '*', 'APPLY', '(@subj1+@subj1)/2', 'AS', 'avg')
195  env.assertEqual(toSortedFlatList([1L, ['subj1', '20', 'avg', '20']]), toSortedFlatList(res))
196  env.expect('FT.AGGREGATE', 'hash_idx', '*', 'LOAD', '3', '@subj1', 'AS', 'a', 'APPLY', '(@subj1+@subj1)/2', 'AS', 'avg')   \
197      .equal([1L, ['a', '20', 'avg', '20']])
198
199  # json
200  conn.execute_command('FT.CREATE', 'json_idx', 'ON', 'JSON', 'SCHEMA', '$.name', 'AS', 'name', 'TEXT', 'SORTABLE',
201                                                                        '$.subj1', 'AS', 'subj2', 'NUMERIC', 'SORTABLE')
202  env.execute_command('JSON.SET', 'doc:1', '$', r'{"name":"Redis", "subj1":3.14}')
203  env.expect('json.get', 'doc:1', '$').equal('[{"name":"Redis","subj1":3.14}]')
204  # load a single field
205  env.expect('FT.AGGREGATE', 'json_idx', '*', 'LOAD', '1', '@subj2')    \
206      .equal([1L, ['subj2', '3.14']])
207  # load a field with an attribute
208  env.expect('FT.AGGREGATE', 'json_idx', '*', 'LOAD', '3', '@subj2', 'AS', 'a')    \
209      .equal([1L, ['a', '3.14']])
210  # load field and use `APPLY`
211  env.expect('FT.AGGREGATE', 'json_idx', '*', 'LOAD', '3', '@subj2', 'AS', 'a', 'APPLY', '(@a+@a)/2', 'AS', 'avg')   \
212      .equal([1L, ['a', '3.14', 'avg', '3.14']])
213  # load a field implicitly with `APPLY`
214  res = env.cmd('FT.AGGREGATE', 'json_idx', '*', 'APPLY', '(@subj2+@subj2)/2', 'AS', 'avg')
215  env.assertEqual(toSortedFlatList([1L, ['subj2', '3.14', 'avg', '3.14']]), toSortedFlatList(res))
216
217  # load a field with an attribute
218  env.expect('FT.AGGREGATE', 'json_idx', '*', 'LOAD', '3', '@$.subj1', 'AS', 'a')    \
219      .equal([1L, ['a', '3.14']])
220  # In this example we get both `a` and `subj1` since
221  env.expect('FT.AGGREGATE', 'json_idx', '*', 'LOAD', '3', '@$.subj1', 'AS', 'a', 'APPLY', '(@a+@a)/2', 'AS', 'avg')   \
222      .equal([1L, ['a', '3.14', 'avg', '3.14']])
223
224@no_msan
225def test_MOD1266(env):
226  # Test parsing failure
227  conn = getConnectionByEnv(env)
228  conn.execute_command('FT.CREATE', 'idx', 'SCHEMA', 'n1', 'NUMERIC', 'SORTABLE', 'n2', 'NUMERIC', 'SORTABLE')
229  conn.execute_command('HSET', 'doc1', 'n1', '1', 'n2', '1')
230  conn.execute_command('HSET', 'doc2', 'n1', '2', 'n2', '2')
231  conn.execute_command('HSET', 'doc2', 'n1', 'foo', 'n2', '-999')
232  conn.execute_command('HSET', 'doc3', 'n1', '3', 'n2', '3')
233
234  env.expect('FT.SEARCH', 'idx', '*', 'sortby', 'n2', 'DESC', 'RETURN', '1', 'n2')  \
235    .equal([2L, 'doc3', ['n2', '3'], 'doc1', ['n2', '1']])
236
237  # Test fetching failure. An object cannot be indexed
238  conn.execute_command('FT.CREATE', 'jsonidx', 'ON', 'JSON', 'SCHEMA', '$.t', 'TEXT')
239  conn.execute_command('JSON.SET', '1', '$', r'{"t":"Redis"}')
240  env.expect('FT.SEARCH', 'jsonidx', '*').equal([1L, '1', ['$', '{"t":"Redis"}']])
241  env.expect('FT.SEARCH', 'jsonidx', 'redis').equal([1L, '1', ['$', '{"t":"Redis"}']])
242  conn.execute_command('JSON.SET', '1', '$.t', r'{"inner_t":"Redis"}')
243  env.expect('FT.SEARCH', 'jsonidx', '*').equal([0L])
244
245def testMemAllocated(env):
246  conn = getConnectionByEnv(env)
247  # sanity
248  conn.execute_command('FT.CREATE', 'idx1', 'SCHEMA', 't', 'TEXT')
249  assertInfoField(env, 'idx1', 'key_table_size_mb', '0')
250  conn.execute_command('HSET', 'doc1', 't', 'foo bar baz')
251  assertInfoField(env, 'idx1', 'key_table_size_mb', '2.765655517578125e-05')
252  conn.execute_command('HSET', 'doc2', 't', 'hello world')
253  assertInfoField(env, 'idx1', 'key_table_size_mb', '8.296966552734375e-05')
254  conn.execute_command('HSET', 'd3', 't', 'help')
255  assertInfoField(env, 'idx1', 'key_table_size_mb', '0.00013828277587890625')
256
257  conn.execute_command('DEL', 'd3')
258  assertInfoField(env, 'idx1', 'key_table_size_mb', '8.296966552734375e-05')
259  conn.execute_command('DEL', 'doc1')
260  assertInfoField(env, 'idx1', 'key_table_size_mb', '2.765655517578125e-05')
261  conn.execute_command('DEL', 'doc2')
262  assertInfoField(env, 'idx1', 'key_table_size_mb', '0')
263
264  # mass
265  conn.execute_command('FT.CREATE', 'idx2', 'SCHEMA', 't', 'TEXT')
266  for i in range(1000):
267    conn.execute_command('HSET', 'doc%d' % i, 't', 'text%d' % i)
268  assertInfoField(env, 'idx2', 'key_table_size_mb', '0.027684211730957031')
269
270  for i in range(1000):
271    conn.execute_command('DEL', 'doc%d' % i)
272  assertInfoField(env, 'idx2', 'key_table_size_mb', '0')
273
274def testUNF(env):
275  conn = getConnectionByEnv(env)
276
277  conn.execute_command('FT.CREATE', 'idx', 'SCHEMA', 'txt', 'TEXT', 'SORTABLE',
278                                                      'txt_unf', 'TEXT', 'SORTABLE', 'UNF',
279                                                      'tag', 'TAG', 'SORTABLE',
280                                                      'tag_unf', 'TAG', 'SORTABLE', 'UNF')
281  conn.execute_command('HSET', 'doc1', 'txt', 'FOO', 'txt_unf', 'FOO',
282                                       'tag', 'FOO', 'tag_unf', 'FOO')
283
284  # test `FOO`
285  env.expect('FT.AGGREGATE', 'idx', '*', 'GROUPBY', '4', '@txt', '@txt_unf', '@tag', '@tag_unf')  \
286    .equal([1L, ['txt', 'foo', 'txt_unf', 'FOO', 'tag', 'foo', 'tag_unf', 'FOO']])
287
288  # test `Maße`
289  conn.execute_command('HSET', 'doc1', 'txt', 'Maße', 'txt_unf', 'Maße',
290                                       'tag', 'Maße', 'tag_unf', 'Maße')
291  env.expect('FT.AGGREGATE', 'idx', '*', 'GROUPBY', '4', '@txt', '@txt_unf', '@tag', '@tag_unf')  \
292    .equal([1L, ['txt', 'masse', 'txt_unf', 'Ma\xc3\x9fe', 'tag', 'masse', 'tag_unf', 'Ma\xc3\x9fe']])
293
294  # test `Maße` with LOAD
295  conn.execute_command('HSET', 'doc1', 'txt', 'Maße', 'txt_unf', 'Maße',
296                                       'tag', 'Maße', 'tag_unf', 'Maße')
297  env.expect('FT.AGGREGATE', 'idx', '*',                              \
298             'LOAD',    '4', '@txt', '@txt_unf', '@tag', '@tag_unf',  \
299             'GROUPBY', '4', '@txt', '@txt_unf', '@tag', '@tag_unf')  \
300    .equal([1L, ['txt', 'Ma\xc3\x9fe', 'txt_unf', 'Ma\xc3\x9fe', 'tag', 'Ma\xc3\x9fe', 'tag_unf', 'Ma\xc3\x9fe']])
301
302def test_MOD_1517(env):
303  conn = getConnectionByEnv(env)
304
305  conn.execute_command('FT.CREATE', 'idx', 'SCHEMA', 'field1', 'TAG', 'SORTABLE',
306                                                     'field2', 'TAG', 'SORTABLE')
307  # both fields exist
308  conn.execute_command('HSET', 'doc1', 'field1', 'val1', 'field2', 'val2', 'amount1', '1', 'amount2', '1')
309  # first tag is nil
310  conn.execute_command('HSET', 'doc2', 'field2', 'val2', 'amount1', '1', 'amount2', '1')
311  # second tag is nil
312  conn.execute_command('HSET', 'doc3', 'field1', 'val1', 'amount1', '1', 'amount2', '1')
313  # both tags are nil
314  conn.execute_command('HSET', 'doc4', 'amount1', '1', 'amount2', '1')
315
316  res = [4L, ['field1', None, 'field2', None, 'amount1Sum', '1', 'amount2Sum', '1'],
317             ['field1', 'val1', 'field2', 'val2', 'amount1Sum', '1', 'amount2Sum', '1'],
318             ['field1', None, 'field2', 'val2', 'amount1Sum', '1', 'amount2Sum', '1'],
319             ['field1', 'val1', 'field2', None, 'amount1Sum', '1', 'amount2Sum', '1']]
320
321  env.expect('FT.AGGREGATE', 'idx', '*',
322             'LOAD', '2', '@amount1', '@amount2',
323             'GROUPBY', '2', '@field1', '@field2',
324             'REDUCE', 'SUM', '1', '@amount1', 'AS', 'amount1Sum',
325             'REDUCE', 'SUM', '1', '@amount2', 'as', 'amount2Sum').equal(res)
326
327@no_msan
328def test_MOD1544(env):
329  # Test parsing failure
330  conn = getConnectionByEnv(env)
331  conn.execute_command('FT.CREATE', 'idx', 'ON', 'JSON', 'SCHEMA', '$.name', 'AS', 'name', 'TEXT')
332  conn.execute_command('JSON.SET', '1', '.', '{"name": "John Smith"}')
333  res = [1L, '1', ['name', '<b>John</b> Smith']]
334  env.expect('FT.SEARCH', 'idx', '@name:(John)', 'RETURN', '1', 'name', 'HIGHLIGHT').equal(res)
335  env.expect('FT.SEARCH', 'idx', '@name:(John)', 'RETURN', '1', 'name', 'HIGHLIGHT', 'FIELDS', '1', 'name').equal(res)
336
337def test_MOD_1808(env):
338  conn = getConnectionByEnv(env)
339  env.expect('FT.CREATE', 'idx', 'SCHEMA', 't', 'TEXT').ok()
340  conn.execute_command('hset', 'doc0', 't', 'world0')
341  conn.execute_command('hset', 'doc1', 't', 'world1')
342  conn.execute_command('hset', 'doc2', 't', 'world2')
343  conn.execute_command('hset', 'doc3', 't', 'world3')
344  res = env.cmd('FT.SEARCH', 'idx', '(~@t:world2) (~@t:world1) (~@fawdfa:wada)', 'SUMMARIZE', 'FRAGS', '1', 'LEN', '25', 'HIGHLIGHT', 'TAGS', "<span style='background-color:yellow'>", '</span>')
345  env.assertEqual(toSortedFlatList(res), toSortedFlatList([4L, 'doc2', ['t', "<span style='background-color:yellow'>world2</span>... "], 'doc1', ['t', "<span style='background-color:yellow'>world1</span>... "], 'doc0', ['t', 'world0'], 'doc3', ['t', 'world3']]))
346
347def test_2370(env):
348  # Test limit offset great than number of results
349  conn = getConnectionByEnv(env)
350  conn.execute_command('FT.CREATE', 'idx', 'SCHEMA', 't1', 'TEXT', 't2', 'TEXT')
351  conn.execute_command('HSET', 'doc1', 't1', 'foo', 't2', 'bar')
352  conn.execute_command('HSET', 'doc2', 't1', 'baz')
353
354  # number of results is lower than LIMIT
355  env.expect('FT.SEARCH', 'idx', '*', 'LIMIT', '10', '10').equal([2L])
356  # missing fields
357  env.expect('FT.SEARCH', 'idx', '*').equal([2L, 'doc1', ['t1', 'foo', 't2', 'bar'], 'doc2', ['t1', 'baz']])
358