From d354466bc240655a38974e4f3e9c3c99aba841c3 Mon Sep 17 00:00:00 2001 From: Simone Mainardi Date: Thu, 27 Feb 2014 12:04:51 +0100 Subject: [PATCH 1/6] 1) added redis capability to handle different databases even on the same host. 2) redis lists that are plain strings are converted back into python types --- lshash/storage.py | 42 +++++++++++++++--------------------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/lshash/storage.py b/lshash/storage.py index deffc9f..7de2136 100644 --- a/lshash/storage.py +++ b/lshash/storage.py @@ -21,8 +21,7 @@ def storage(storage_config, index): if 'dict' in storage_config: return InMemoryStorage(storage_config['dict']) elif 'redis' in storage_config: - storage_config['redis']['db'] = index - return RedisStorage(storage_config['redis']) + return RedisStorage(storage_config['redis'], index) else: raise ValueError("Only in-memory dictionary and Redis are supported.") @@ -36,14 +35,6 @@ def keys(self): """ Returns a list of binary hashes that are used as dict keys. """ raise NotImplementedError - def set_val(self, key, val): - """ Set `val` at `key`, note that the `val` must be a string. """ - raise NotImplementedError - - def get_val(self, key): - """ Return `val` at `key`, note that the `val` must be a string. """ - raise NotImplementedError - def append_val(self, key, val): """ Append `val` to the list stored at `key`. @@ -62,19 +53,13 @@ def get_list(self, key): class InMemoryStorage(BaseStorage): - def __init__(self, config): + def __init__(self, h_index): self.name = 'dict' self.storage = dict() def keys(self): return self.storage.keys() - def set_val(self, key, val): - self.storage[key] = val - - def get_val(self, key): - return self.storage[key] - def append_val(self, key, val): self.storage.setdefault(key, []).append(val) @@ -83,23 +68,26 @@ def get_list(self, key): class RedisStorage(BaseStorage): - def __init__(self, config): + def __init__(self, config, h_index): if not redis: raise ImportError("redis-py is required to use Redis as storage.") self.name = 'redis' self.storage = redis.StrictRedis(**config) + # a single db handles multiple hash tables, each one has prefix ``h[h_index].`` + self.h_index = 'h%.2i.' % int(h_index) - def keys(self, pattern="*"): - return self.storage.keys(pattern) - - def set_val(self, key, val): - self.storage.set(key, val) + def _list(self, key): + return self.h_index + key - def get_val(self, key): - return self.storage.get(key) + def keys(self, pattern='*'): + # return the keys BUT be agnostic with reference to the hash table + return [k.split('.')[1] for k in self.storage.keys(self.h_index + pattern)] def append_val(self, key, val): - self.storage.rpush(key, json.dumps(val)) + self.storage.rpush(self._list(key), json.dumps(val)) def get_list(self, key): - return self.storage.lrange(key, 0, -1) + print "getting list for ", self._list(key) + _list = self.storage.lrange(self._list(key), 0, -1) # list elements are plain strings here + _list = [json.loads(el) for el in _list] # transform strings into python elements + return _list \ No newline at end of file From 27ee1e00a7aa80625ae941e982ca2ded82d88981 Mon Sep 17 00:00:00 2001 From: Simone Mainardi Date: Thu, 27 Feb 2014 12:37:02 +0100 Subject: [PATCH 2/6] added a newline at the end of the file --- lshash/storage.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lshash/storage.py b/lshash/storage.py index 7de2136..637b398 100644 --- a/lshash/storage.py +++ b/lshash/storage.py @@ -87,7 +87,6 @@ def append_val(self, key, val): self.storage.rpush(self._list(key), json.dumps(val)) def get_list(self, key): - print "getting list for ", self._list(key) _list = self.storage.lrange(self._list(key), 0, -1) # list elements are plain strings here _list = [json.loads(el) for el in _list] # transform strings into python elements - return _list \ No newline at end of file + return _list From 11f31a62c39013b60bcb48c8dc8286df6d06fbe5 Mon Sep 17 00:00:00 2001 From: Simone Mainardi Date: Thu, 27 Feb 2014 14:51:38 +0100 Subject: [PATCH 3/6] conversion from python lists to tuples in redis to allow hashing --- lshash/storage.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lshash/storage.py b/lshash/storage.py index 637b398..248637b 100644 --- a/lshash/storage.py +++ b/lshash/storage.py @@ -28,7 +28,7 @@ def storage(storage_config, index): class BaseStorage(object): def __init__(self, config): - """ An abstract class used as an adapter for storages. """ + """ An abstract class used as an adapter printfor storages. """ raise NotImplementedError def keys(self): @@ -88,5 +88,10 @@ def append_val(self, key, val): def get_list(self, key): _list = self.storage.lrange(self._list(key), 0, -1) # list elements are plain strings here - _list = [json.loads(el) for el in _list] # transform strings into python elements + _list = [json.loads(el) for el in _list] # transform strings into python tuples + for el in _list: + # if len(el) is 2, then el[1] is the extra value associated to the element + if len(el) == 2 and type(el[0]) == list: + el[0] = tuple(el[0]) + _list = [tuple(el) for el in _list] return _list From 61f31344166a8b52be905500137e770b82d1a252 Mon Sep 17 00:00:00 2001 From: Simone Mainardi Date: Thu, 27 Feb 2014 14:51:55 +0100 Subject: [PATCH 4/6] added some py.test tests --- tests/test_lsh.py | 115 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 tests/test_lsh.py diff --git a/tests/test_lsh.py b/tests/test_lsh.py new file mode 100644 index 0000000..6456c88 --- /dev/null +++ b/tests/test_lsh.py @@ -0,0 +1,115 @@ +import random +import string +from redis import StrictRedis +from pprint import pprint +import sys +import os + +# add the LSHash package to the current python path +sys.path.insert(0, os.path.abspath('../')) +# now we can use our lshash package and not the standard one +from lshash import LSHash + +num_elements = 100 + +els = [] +el_names = [] +for i in range(num_elements): + el = [random.randint(0, 100) for _ in xrange(8)] + elname = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(10)) + els.append(tuple(el)) + el_names.append(elname) + + +def test_lshash(): + lsh = LSHash(6, 8, 1) + for i in xrange(num_elements): + lsh.index(list(els[i])) + hasht = lsh.hash_tables[0] + itms = [hasht.get_list(k) for k in hasht.keys()] + for itm in itms: + for el in itm: + assert el in els + for el in els: + res = lsh.query(list(el), num_results=1, distance_func='euclidean')[0] + # res is a tuple containing the vector and the distance + el_v, el_dist = res + assert el_v in els + assert el_dist == 0 + del lsh + +def test_lshash_extra_val(): + lsh = LSHash(6, 8, 1) + for i in xrange(num_elements): + lsh.index(list(els[i]), el_names[i]) + hasht = lsh.hash_tables[0] + itms = [hasht.get_list(k) for k in hasht.keys()] + for itm in itms: + for el in itm: + assert el[0] in els + assert el[1] in el_names + for el in els: + # res is a list, so we need to select the first entry only + res = lsh.query(list(el), num_results=1, distance_func='euclidean')[0] + # vector an name are in the first element of the tuple res[0] + el_v, el_name = res[0] + # the distance is in the second element of the tuple + el_dist = res[1] + assert el_v in els + assert el_name in el_names + assert el_dist == 0 + del lsh + +def test_lshash_redis(): + """ + Test external lshash module + """ + config = {"redis": {"host": '192.168.70.26', "port": 6379, "db": 15}} + sr = StrictRedis(**config['redis']) + sr.flushdb() + + lsh = LSHash(6, 8, 1, config) + for i in xrange(num_elements): + lsh.index(list(els[i])) + hasht = lsh.hash_tables[0] + itms = [hasht.get_list(k) for k in hasht.keys()] + for itm in itms: + for el in itm: + assert el in els + for el in els: + res = lsh.query(list(el), num_results=1, distance_func='euclidean')[0] + el_v, el_dist = res + assert el_v in els + assert el_dist == 0 + del lsh + sr.flushdb() + +def test_lshash_redis_extra_val(): + """ + Test external lshash module + """ + config = {"redis": {"host": '192.168.70.26', "port": 6379, "db": 15}} + sr = StrictRedis(**config['redis']) + sr.flushdb() + + lsh = LSHash(6, 8, 1, config) + for i in xrange(num_elements): + lsh.index(list(els[i]), el_names[i]) + hasht = lsh.hash_tables[0] + itms = [hasht.get_list(k) for k in hasht.keys()] + for itm in itms: + for el in itm: + assert el[0] in els + assert el[1] in el_names + for el in els: + res = lsh.query(list(el), num_results=1, distance_func='euclidean')[0] + # vector an name are in the first element of the tuple res[0] + el_v, el_name = res[0] + # the distance is in the second element of the tuple + el_dist = res[1] + assert el_v in els + assert el_name in el_names + assert el_dist == 0 + del lsh + sr.flushdb() + From b07fd60415ab833e29c27d4cb0a7499e2aed5efd Mon Sep 17 00:00:00 2001 From: Simone Mainardi Date: Mon, 31 Mar 2014 14:34:18 +0200 Subject: [PATCH 5/6] Multiple insertions of the same key-value pairs are prevented. This is accomplished using python built-in sets and redis sets --- lshash/storage.py | 8 ++++---- tests/test_lsh.py | 10 ++++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lshash/storage.py b/lshash/storage.py index 248637b..85e4c4a 100644 --- a/lshash/storage.py +++ b/lshash/storage.py @@ -61,10 +61,10 @@ def keys(self): return self.storage.keys() def append_val(self, key, val): - self.storage.setdefault(key, []).append(val) + self.storage.setdefault(key, set()).update([val]) def get_list(self, key): - return self.storage.get(key, []) + return list(self.storage.get(key, [])) class RedisStorage(BaseStorage): @@ -84,10 +84,10 @@ def keys(self, pattern='*'): return [k.split('.')[1] for k in self.storage.keys(self.h_index + pattern)] def append_val(self, key, val): - self.storage.rpush(self._list(key), json.dumps(val)) + self.storage.sadd(self._list(key), json.dumps(val)) def get_list(self, key): - _list = self.storage.lrange(self._list(key), 0, -1) # list elements are plain strings here + _list = list(self.storage.smembers(self._list(key))) # list elements are plain strings here _list = [json.loads(el) for el in _list] # transform strings into python tuples for el in _list: # if len(el) is 2, then el[1] is the extra value associated to the element diff --git a/tests/test_lsh.py b/tests/test_lsh.py index 6456c88..a283bee 100644 --- a/tests/test_lsh.py +++ b/tests/test_lsh.py @@ -25,9 +25,11 @@ def test_lshash(): lsh = LSHash(6, 8, 1) for i in xrange(num_elements): lsh.index(list(els[i])) + lsh.index(list(els[i])) # multiple insertions hasht = lsh.hash_tables[0] itms = [hasht.get_list(k) for k in hasht.keys()] for itm in itms: + assert itms.count(itm) == 1 for el in itm: assert el in els for el in els: @@ -64,17 +66,19 @@ def test_lshash_redis(): """ Test external lshash module """ - config = {"redis": {"host": '192.168.70.26', "port": 6379, "db": 15}} + config = {"redis": {"host": 'localhost', "port": 6379, "db": 15}} sr = StrictRedis(**config['redis']) sr.flushdb() lsh = LSHash(6, 8, 1, config) for i in xrange(num_elements): lsh.index(list(els[i])) + lsh.index(list(els[i])) # multiple insertions should be prevented by the library hasht = lsh.hash_tables[0] itms = [hasht.get_list(k) for k in hasht.keys()] for itm in itms: for el in itm: + assert itms.count(itm) == 1 # have multiple insertions been prevented? assert el in els for el in els: res = lsh.query(list(el), num_results=1, distance_func='euclidean')[0] @@ -88,16 +92,18 @@ def test_lshash_redis_extra_val(): """ Test external lshash module """ - config = {"redis": {"host": '192.168.70.26', "port": 6379, "db": 15}} + config = {"redis": {"host": 'localhost', "port": 6379, "db": 15}} sr = StrictRedis(**config['redis']) sr.flushdb() lsh = LSHash(6, 8, 1, config) for i in xrange(num_elements): lsh.index(list(els[i]), el_names[i]) + lsh.index(list(els[i]), el_names[i]) # multiple insertions hasht = lsh.hash_tables[0] itms = [hasht.get_list(k) for k in hasht.keys()] for itm in itms: + assert itms.count(itm) == 1 for el in itm: assert el[0] in els assert el[1] in el_names From 61eecbfc795107da3726e9f4cc8b88cf0a752cc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Wu=CC=88rl?= Date: Thu, 12 Mar 2015 22:13:39 +0100 Subject: [PATCH 6/6] added python 3 compatibility and travis tests --- .gitignore | 3 + .travis.yml | 12 +++ lshash/__init__.py | 2 +- lshash/lshash.py | 15 +++- lshash/storage.py | 10 +-- requirements.txt | 2 + tests/__init__.py | 0 tests/test_lsh.py | 206 +++++++++++++++++++++++---------------------- 8 files changed, 139 insertions(+), 111 deletions(-) create mode 100644 .travis.yml create mode 100644 requirements.txt create mode 100644 tests/__init__.py diff --git a/.gitignore b/.gitignore index 0d327a6..4ae1e6c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ # Python egg metadata, regenerated from source files by setuptools. /*.egg-info /*.egg + +# PyCharm-Files +/.idea diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..5f1b715 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +services: + - redis-server +language: python +python: + - "2.6" + - "2.7" + - "3.3" + - "3.4" +# command to install dependencies +install: "pip install -r requirements.txt" +# command to run tests +script: nosetests diff --git a/lshash/__init__.py b/lshash/__init__.py index e805896..e4424c7 100644 --- a/lshash/__init__.py +++ b/lshash/__init__.py @@ -9,4 +9,4 @@ __license__ = 'MIT' __version__ = '0.0.4dev' -from lshash import LSHash +from .lshash import LSHash diff --git a/lshash/lshash.py b/lshash/lshash.py index 5c895a6..9f375dd 100644 --- a/lshash/lshash.py +++ b/lshash/lshash.py @@ -4,11 +4,18 @@ # This module is part of lshash and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php +import sys + +if sys.version_info[0] >= 3: + basestring = str +else: + range = xrange + import os import json import numpy as np -from storage import storage +from .storage import storage try: from bitarray import bitarray @@ -92,7 +99,7 @@ def _init_uniform_planes(self): self.uniform_planes = [t[1] for t in npzfiles] else: self.uniform_planes = [self._generate_uniform_planes() - for _ in xrange(self.num_hashtables)] + for _ in range(self.num_hashtables)] try: np.savez_compressed(self.matrices_filename, *self.uniform_planes) @@ -101,14 +108,14 @@ def _init_uniform_planes(self): raise else: self.uniform_planes = [self._generate_uniform_planes() - for _ in xrange(self.num_hashtables)] + for _ in range(self.num_hashtables)] def _init_hashtables(self): """ Initialize the hash tables such that each record will be in the form of "[storage1, storage2, ...]" """ self.hash_tables = [storage(self.storage_config, i) - for i in xrange(self.num_hashtables)] + for i in range(self.num_hashtables)] def _generate_uniform_planes(self): """ Generate uniformly distributed hyperplanes and return it as a 2D diff --git a/lshash/storage.py b/lshash/storage.py index 85e4c4a..6352f9b 100644 --- a/lshash/storage.py +++ b/lshash/storage.py @@ -4,6 +4,8 @@ # This module is part of lshash and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php +from __future__ import unicode_literals + import json try: @@ -27,10 +29,6 @@ def storage(storage_config, index): class BaseStorage(object): - def __init__(self, config): - """ An abstract class used as an adapter printfor storages. """ - raise NotImplementedError - def keys(self): """ Returns a list of binary hashes that are used as dict keys. """ raise NotImplementedError @@ -81,14 +79,14 @@ def _list(self, key): def keys(self, pattern='*'): # return the keys BUT be agnostic with reference to the hash table - return [k.split('.')[1] for k in self.storage.keys(self.h_index + pattern)] + return [k.decode('ascii').split('.')[1] for k in self.storage.keys(self.h_index + pattern)] def append_val(self, key, val): self.storage.sadd(self._list(key), json.dumps(val)) def get_list(self, key): _list = list(self.storage.smembers(self._list(key))) # list elements are plain strings here - _list = [json.loads(el) for el in _list] # transform strings into python tuples + _list = [json.loads(el.decode('ascii')) for el in _list] # transform strings into python tuples for el in _list: # if len(el) is 2, then el[1] is the extra value associated to the element if len(el) == 2 and type(el[0]) == list: diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..576fa0e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +numpy>=1.9.1 +redis==2.10.3 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_lsh.py b/tests/test_lsh.py index a283bee..5a7f830 100644 --- a/tests/test_lsh.py +++ b/tests/test_lsh.py @@ -1,5 +1,6 @@ import random import string +from unittest import TestCase from redis import StrictRedis from pprint import pprint import sys @@ -10,112 +11,117 @@ # now we can use our lshash package and not the standard one from lshash import LSHash -num_elements = 100 -els = [] -el_names = [] -for i in range(num_elements): - el = [random.randint(0, 100) for _ in xrange(8)] - elname = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(10)) - els.append(tuple(el)) - el_names.append(elname) +class TestLSHash(TestCase): + num_elements = 100 + def setUp(self): + self.els = [] + self.el_names = [] + for i in range(self.num_elements): + el = [random.randint(0, 100) for _ in range(8)] + elname = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(10)) + self.els.append(tuple(el)) + self.el_names.append(elname) -def test_lshash(): - lsh = LSHash(6, 8, 1) - for i in xrange(num_elements): - lsh.index(list(els[i])) - lsh.index(list(els[i])) # multiple insertions - hasht = lsh.hash_tables[0] - itms = [hasht.get_list(k) for k in hasht.keys()] - for itm in itms: - assert itms.count(itm) == 1 - for el in itm: - assert el in els - for el in els: - res = lsh.query(list(el), num_results=1, distance_func='euclidean')[0] - # res is a tuple containing the vector and the distance - el_v, el_dist = res - assert el_v in els - assert el_dist == 0 - del lsh + def test_lshash(self): + lsh = LSHash(6, 8, 1) + for i in range(self.num_elements): + lsh.index(list(self.els[i])) + lsh.index(list(self.els[i])) # multiple insertions + hasht = lsh.hash_tables[0] + itms = [hasht.get_list(k) for k in hasht.keys()] + for itm in itms: + assert itms.count(itm) == 1 + for el in itm: + assert el in self.els + for el in self.els: + res = lsh.query(list(el), num_results=1, distance_func='euclidean')[0] + # res is a tuple containing the vector and the distance + el_v, el_dist = res + assert el_v in self.els + assert el_dist == 0 + del lsh -def test_lshash_extra_val(): - lsh = LSHash(6, 8, 1) - for i in xrange(num_elements): - lsh.index(list(els[i]), el_names[i]) - hasht = lsh.hash_tables[0] - itms = [hasht.get_list(k) for k in hasht.keys()] - for itm in itms: - for el in itm: - assert el[0] in els - assert el[1] in el_names - for el in els: - # res is a list, so we need to select the first entry only - res = lsh.query(list(el), num_results=1, distance_func='euclidean')[0] - # vector an name are in the first element of the tuple res[0] - el_v, el_name = res[0] - # the distance is in the second element of the tuple - el_dist = res[1] - assert el_v in els - assert el_name in el_names - assert el_dist == 0 - del lsh + def test_lshash_extra_val(self): + lsh = LSHash(6, 8, 1) + for i in range(self.num_elements): + lsh.index(list(self.els[i]), self.el_names[i]) + hasht = lsh.hash_tables[0] + itms = [hasht.get_list(k) for k in hasht.keys()] + for itm in itms: + for el in itm: + assert el[0] in self.els + assert el[1] in self.el_names + for el in self.els: + # res is a list, so we need to select the first entry only + res = lsh.query(list(el), num_results=1, distance_func='euclidean')[0] + # vector an name are in the first element of the tuple res[0] + el_v, el_name = res[0] + # the distance is in the second element of the tuple + el_dist = res[1] + assert el_v in self.els + assert el_name in self.el_names + assert el_dist == 0 + del lsh -def test_lshash_redis(): - """ - Test external lshash module - """ - config = {"redis": {"host": 'localhost', "port": 6379, "db": 15}} - sr = StrictRedis(**config['redis']) - sr.flushdb() + def test_lshash_redis(self): + """ + Test external lshash module + """ + config = {"redis": {"host": 'localhost', "port": 6379, "db": 15}} + sr = StrictRedis(**config['redis']) + sr.flushdb() - lsh = LSHash(6, 8, 1, config) - for i in xrange(num_elements): - lsh.index(list(els[i])) - lsh.index(list(els[i])) # multiple insertions should be prevented by the library - hasht = lsh.hash_tables[0] - itms = [hasht.get_list(k) for k in hasht.keys()] - for itm in itms: - for el in itm: - assert itms.count(itm) == 1 # have multiple insertions been prevented? - assert el in els - for el in els: - res = lsh.query(list(el), num_results=1, distance_func='euclidean')[0] - el_v, el_dist = res - assert el_v in els - assert el_dist == 0 - del lsh - sr.flushdb() + lsh = LSHash(6, 8, 1, config) + for i in range(self.num_elements): + lsh.index(list(self.els[i])) + lsh.index(list(self.els[i])) # multiple insertions should be prevented by the library -def test_lshash_redis_extra_val(): - """ - Test external lshash module - """ - config = {"redis": {"host": 'localhost', "port": 6379, "db": 15}} - sr = StrictRedis(**config['redis']) - sr.flushdb() + hasht = lsh.hash_tables[0] + itms = [hasht.get_list(k) for k in hasht.keys()] - lsh = LSHash(6, 8, 1, config) - for i in xrange(num_elements): - lsh.index(list(els[i]), el_names[i]) - lsh.index(list(els[i]), el_names[i]) # multiple insertions - hasht = lsh.hash_tables[0] - itms = [hasht.get_list(k) for k in hasht.keys()] - for itm in itms: - assert itms.count(itm) == 1 - for el in itm: - assert el[0] in els - assert el[1] in el_names - for el in els: - res = lsh.query(list(el), num_results=1, distance_func='euclidean')[0] - # vector an name are in the first element of the tuple res[0] - el_v, el_name = res[0] - # the distance is in the second element of the tuple - el_dist = res[1] - assert el_v in els - assert el_name in el_names - assert el_dist == 0 - del lsh - sr.flushdb() + for itm in itms: + for el in itm: + assert itms.count(itm) == 1 # have multiple insertions been prevented? + assert el in self.els + + for el in self.els: + res = lsh.query(list(el), num_results=1, distance_func='euclidean')[0] + el_v, el_dist = res + assert el_v in self.els + assert el_dist == 0 + del lsh + sr.flushdb() + + def test_lshash_redis_extra_val(self): + """ + Test external lshash module + """ + config = {"redis": {"host": 'localhost', "port": 6379, "db": 15}} + sr = StrictRedis(**config['redis']) + sr.flushdb() + + lsh = LSHash(6, 8, 1, config) + for i in range(self.num_elements): + lsh.index(list(self.els[i]), self.el_names[i]) + lsh.index(list(self.els[i]), self.el_names[i]) # multiple insertions + hasht = lsh.hash_tables[0] + itms = [hasht.get_list(k) for k in hasht.keys()] + for itm in itms: + assert itms.count(itm) == 1 + for el in itm: + assert el[0] in self.els + assert el[1] in self.el_names + for el in self.els: + res = lsh.query(list(el), num_results=1, distance_func='euclidean')[0] + # vector an name are in the first element of the tuple res[0] + el_v, el_name = res[0] + # the distance is in the second element of the tuple + el_dist = res[1] + assert el_v in self.els + assert el_name in self.el_names + assert el_dist == 0 + del lsh + sr.flushdb()