diff --git a/redisearch/client.py b/redisearch/client.py index 93d6452..95972ac 100644 --- a/redisearch/client.py +++ b/redisearch/client.py @@ -25,7 +25,7 @@ class Field(object): def __init__(self, name, *args): self.name = name self.args = args - + def redis_args(self): return [self.name] + list(self.args) @@ -80,7 +80,7 @@ def __init__(self, name): class TagField(Field): """ - TagField is a tag-indexing field with simpler compression and tokenization. + TagField is a tag-indexing field with simpler compression and tokenization. See http://redisearch.io/Tags/ """ def __init__(self, name, separator = ',', no_index=False): @@ -91,12 +91,12 @@ def __init__(self, name, separator = ',', no_index=False): if no_index: self.args.append(Field.NOINDEX) - + class Client(object): """ - A client for the RediSearch module. - It abstracts the API of the module and lets you just use the engine + A client for the RediSearch module. + It abstracts the API of the module and lets you just use the engine """ NUMERIC = 'NUMERIC' @@ -117,8 +117,8 @@ class Client(object): class BatchIndexer(object): """ - A batch indexer allows you to automatically batch - document indexeing in pipelines, flushing it every N documents. + A batch indexer allows you to automatically batch + document indexeing in pipelines, flushing it every N documents. """ def __init__(self, client, chunk_size=1000): @@ -193,7 +193,7 @@ def create_index(self, fields, no_term_offsets=False, args += [self.STOPWORDS, len(stopwords)] if len(stopwords) > 0: args += list(stopwords) - + args.append('SCHEMA') args += list(itertools.chain(*(f.redis_args() for f in fields))) @@ -205,11 +205,11 @@ def drop_index(self): Drop the index if it exists """ return self.redis.execute_command(self.DROP_CMD, self.index_name) - + def _add_document(self, doc_id, conn=None, nosave=False, score=1.0, payload=None, replace=False, partial=False, language=None, **fields): - """ - Internal add_document used for both batch and single doc indexing + """ + Internal add_document used for both batch and single doc indexing """ if conn is None: conn = self.redis @@ -242,17 +242,17 @@ def add_document(self, doc_id, nosave=False, score=1.0, payload=None, - **doc_id**: the id of the saved document. - **nosave**: if set to true, we just index the document, and don't save a copy of it. This means that searches will just return ids. - - **score**: the document ranking, between 0.0 and 1.0 + - **score**: the document ranking, between 0.0 and 1.0 - **payload**: optional inner-index payload we can save for fast access in scoring functions - **replace**: if True, and the document already is in the index, we perform an update and reindex the document - **partial**: if True, the fields specified will be added to the existing document. This has the added benefit that any fields specified with `no_index` will not be reindexed again. Implies `replace` - **language**: Specify the language used for document tokenization. - - **fields** kwargs dictionary of the document fields to be saved and/or indexed. + - **fields** kwargs dictionary of the document fields to be saved and/or indexed. NOTE: Geo points shoule be encoded as strings of "lon,lat" """ - return self._add_document(doc_id, conn=None, nosave=nosave, score=score, + return self._add_document(doc_id, conn=None, nosave=nosave, score=score, payload=payload, replace=replace, partial=partial, language=language, **fields) @@ -367,4 +367,4 @@ def aggregate(self, query): rows = raw[1:] res = AggregateResult(rows, cursor, schema) - return res \ No newline at end of file + return res diff --git a/redisearch/result.py b/redisearch/result.py index 3442295..ce03c6f 100644 --- a/redisearch/result.py +++ b/redisearch/result.py @@ -18,23 +18,28 @@ def __init__(self, res, hascontent, duration=0, has_payload = False): self.docs = [] step = 1 - if hascontent: - step = 3 if has_payload else 2 - else: - # we can't have nocontent and payloads in the same response - has_payload = False + fields_offset = 0 + if hascontent and has_payload: + step = 3 + fields_offset = 2 + elif hascontent and not has_payload: + step = 2 + fields_offset = 1 + elif not hascontent and has_payload: + step = 2 + fields_offset = 0 for i in xrange(1, len(res), step): id = to_string(res[i]) payload = to_string(res[i+1]) if has_payload else None - fields_offset = 2 if has_payload else 1 - fields = {} - if hascontent: + fields = {} + if fields_offset > 0: fields = dict( dict(izip(map(to_string, res[i + fields_offset][::2]), map(to_string, res[i + fields_offset][1::2]))) - ) if hascontent else {} + ) + try: del fields['id'] except KeyError: diff --git a/test/test.py b/test/test.py index d61c30e..4810576 100644 --- a/test/test.py +++ b/test/test.py @@ -24,8 +24,8 @@ def createIndex(self, client, num_docs = 100): #conn.flushdb() #client = Client('test', port=conn.port) try: - client.create_index((TextField('play', weight=5.0), - TextField('txt'), + client.create_index((TextField('play', weight=5.0), + TextField('txt'), NumericField('chapter'))) except redis.ResponseError: client.drop_index() @@ -61,7 +61,7 @@ def createIndex(self, client, num_docs = 100): def testClient(self): conn = self.redis() - + with conn as r: num_docs = 500 r.flushdb() @@ -95,7 +95,7 @@ def testClient(self): self.assertTrue(doc.id) self.assertEqual(doc.play, 'Henry IV') self.assertTrue(len(doc.txt) > 0) - + # test no content res = client.search(Query('king').no_content()) self.assertEqual(194, res.total) @@ -103,11 +103,11 @@ def testClient(self): for doc in res.docs: self.assertNotIn('txt', doc.__dict__) self.assertNotIn('play', doc.__dict__) - + #test verbatim vs no verbatim total = client.search(Query('kings').no_content()).total vtotal = client.search(Query('kings').no_content().verbatim()).total - self.assertGreater(total, vtotal) + self.assertGreater(total, vtotal) # test in fields txt_total = client.search(Query('henry').no_content().limit_fields('txt')).total @@ -124,7 +124,7 @@ def testClient(self): self.assertEqual(doc.play, 'Henry VI Part 3') self.assertTrue(len(doc.txt) > 0) - + # test inkeys ids = [x.id for x in client.search(Query('henry')).docs] @@ -145,12 +145,12 @@ def testClient(self): self.assertEqual(52,client.search(Query('king henry').slop(0).in_order()).total) self.assertEqual(53,client.search(Query('henry king').slop(0)).total) self.assertEqual(167,client.search(Query('henry king').slop(100)).total) - + # test delete document client.add_document('doc-5ghs2', play = 'Death of a Salesman') res = client.search(Query('death of a salesman')) self.assertEqual(1, res.total) - + self.assertEqual(1, client.delete_document('doc-5ghs2')) res = client.search(Query('death of a salesman')) self.assertEqual(0, res.total) @@ -175,7 +175,7 @@ def getCleanClient(self, name): return client def testPayloads(self): - + conn = self.redis() with conn as r: @@ -191,13 +191,36 @@ def testPayloads(self): res = client.search(q) self.assertEqual(2, res.total) self.assertEqual('doc2', res.docs[0].id) - + self.assertEqual('doc1', res.docs[1].id) self.assertEqual('foo baz', res.docs[1].payload) self.assertIsNone(res.docs[0].payload) + def testPayloadsWithNoContent(self): + + conn = self.redis() + + with conn as r: + # Creating a client with a given index name + client = Client('idx', port=conn.port) + client.redis.flushdb() + client.create_index((TextField('txt'),)) + + client.add_document('doc1', payload = 'foo baz', txt = 'foo bar') + client.add_document('doc2', payload = 'foo baz2', txt = 'foo bar') + + q = Query("foo bar").with_payloads().no_content() + res = client.search(q) + + self.assertEqual(2, len(res.docs)) + self.assertEqual('doc2', res.docs[0].id) + self.assertEqual('foo baz2', res.docs[0].payload) + + self.assertEqual('doc1', res.docs[1].id) + self.assertEqual('foo baz', res.docs[1].payload) + def testReplace(self): - + conn = self.redis() with conn as r: @@ -212,19 +235,19 @@ def testReplace(self): res = client.search("foo bar") self.assertEqual(2, res.total) client.add_document('doc1', replace = True, txt = 'this is a replaced doc') - - + + res = client.search("foo bar") self.assertEqual(1, res.total) self.assertEqual('doc2', res.docs[0].id) - + res = client.search("replaced doc") self.assertEqual(1, res.total) self.assertEqual('doc1', res.docs[0].id) - def testStopwords(self): + def testStopwords(self): conn = self.redis() with conn as r: @@ -237,7 +260,7 @@ def testStopwords(self): client.create_index((TextField('txt'),), stopwords = ['foo', 'bar', 'baz']) client.add_document('doc1', txt = 'foo bar') client.add_document('doc2', txt = 'hello world') - + q1 = Query("foo bar").no_content() q2 = Query("foo bar hello world").no_content() res1, res2 = client.search(q1), client.search(q2) @@ -252,7 +275,7 @@ def testFilters(self): # Creating a client with a given index name client = Client('idx', port=conn.port) client.redis.flushdb() - + client.create_index((TextField('txt'), NumericField('num'), GeoField('loc'))) client.add_document('doc1', txt = 'foo bar', num = 3.141, loc = '-0.441,51.458') @@ -274,7 +297,7 @@ def testFilters(self): q1 = Query("foo").add_filter(GeoFilter('loc', -0.44, 51.45, 10)).no_content() q2 = Query("foo").add_filter(GeoFilter('loc', -0.44, 51.45, 100)).no_content() res1, res2 = client.search(q1), client.search(q2) - + self.assertEqual(1, res1.total) self.assertEqual(2, res2.total) self.assertEqual('doc1', res1.docs[0].id) @@ -289,7 +312,7 @@ def testSortby(self): # Creating a client with a given index name client = Client('idx', port=conn.port) client.redis.flushdb() - + client.create_index((TextField('txt'), NumericField('num', sortable=True))) client.add_document('doc1', txt = 'foo bar', num = 1) @@ -300,7 +323,7 @@ def testSortby(self): q1 = Query("foo").sort_by('num', asc=True).no_content() q2 = Query("foo").sort_by('num', asc=False).no_content() res1, res2 = client.search(q1), client.search(q2) - + self.assertEqual(3, res1.total) self.assertEqual('doc1', res1.docs[0].id) self.assertEqual('doc2', res1.docs[1].id) @@ -318,7 +341,7 @@ def testExample(self): # Creating a client with a given index name client = Client('myIndex', port=conn.port) client.redis.flushdb() - + # Creating the index definition and schema client.create_index((TextField('title', weight=5.0), TextField('body'))) @@ -329,15 +352,15 @@ def testExample(self): q = Query("search engine").verbatim().no_content().paging(0,5) res = client.search(q) - - + + self.assertTrue(True) def testAutoComplete(self): with self.redis() as r: self.assertTrue(True) - + ac = AutoCompleter('ac', conn=r) n = 0 with open(TITLES_CSV) as f: @@ -349,7 +372,7 @@ def testAutoComplete(self): #print term, score self.assertEqual(n,ac.add_suggestions(Suggestion(term,score=score))) - + self.assertEqual(n, ac.len()) strs = [] for _ in r.retry_with_rdb_reload(): @@ -372,7 +395,7 @@ def testAutoComplete(self): # make sure a second delete returns 0 for sug in strs: self.assertEqual(0, ac.delete(sug)) - + # make sure they were actually deleted ret2 = ac.get_suggestions('bad', fuzzy=True, num=10) for sug in ret2: @@ -473,11 +496,11 @@ def testTags(self): # Creating a client with a given index name client = Client('idx', port=conn.port) client.redis.flushdb() - + client.create_index((TextField('txt'), TagField('tags'))) client.add_document('doc1', txt = 'fooz barz', tags = 'foo,foo bar,hello;world') - + for i in r.retry_with_rdb_reload(): q = Query("@tags:{foo}")