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