Skip to content

Commit b442110

Browse files
authored
Add support for SEARCH commands in cluster (#2042)
* Add support for SEARCH commands in cluster * delete json tests mark & list search commands * linters
1 parent fdf4f1a commit b442110

File tree

3 files changed

+68
-37
lines changed

3 files changed

+68
-37
lines changed

redis/cluster.py

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,42 @@ class RedisCluster(RedisClusterCommands):
320320
),
321321
)
322322

323+
SEARCH_COMMANDS = (
324+
[
325+
"FT.CREATE",
326+
"FT.SEARCH",
327+
"FT.AGGREGATE",
328+
"FT.EXPLAIN",
329+
"FT.EXPLAINCLI",
330+
"FT,PROFILE",
331+
"FT.ALTER",
332+
"FT.DROPINDEX",
333+
"FT.ALIASADD",
334+
"FT.ALIASUPDATE",
335+
"FT.ALIASDEL",
336+
"FT.TAGVALS",
337+
"FT.SUGADD",
338+
"FT.SUGGET",
339+
"FT.SUGDEL",
340+
"FT.SUGLEN",
341+
"FT.SYNUPDATE",
342+
"FT.SYNDUMP",
343+
"FT.SPELLCHECK",
344+
"FT.DICTADD",
345+
"FT.DICTDEL",
346+
"FT.DICTDUMP",
347+
"FT.INFO",
348+
"FT._LIST",
349+
"FT.CONFIG",
350+
"FT.ADD",
351+
"FT.DEL",
352+
"FT.DROP",
353+
"FT.GET",
354+
"FT.MGET",
355+
"FT.SYNADD",
356+
],
357+
)
358+
323359
CLUSTER_COMMANDS_RESPONSE_CALLBACKS = {
324360
"CLUSTER ADDSLOTS": bool,
325361
"CLUSTER ADDSLOTSRANGE": bool,
@@ -854,6 +890,8 @@ def _determine_nodes(self, *args, **kwargs):
854890
elif command_flag == self.__class__.DEFAULT_NODE:
855891
# return the cluster's default node
856892
return [self.nodes_manager.default_node]
893+
elif command in self.__class__.SEARCH_COMMANDS[0]:
894+
return [self.nodes_manager.default_node]
857895
else:
858896
# get the node that holds the key's slot
859897
slot = self.determine_slot(*args)
@@ -1956,17 +1994,14 @@ def _send_cluster_commands(
19561994
# refer to our internal node -> slot table that
19571995
# tells us where a given
19581996
# command should route to.
1959-
slot = self.determine_slot(*c.args)
1960-
node = self.nodes_manager.get_node_from_slot(
1961-
slot, self.read_from_replicas and c.args[0] in READ_COMMANDS
1962-
)
1997+
node = self._determine_nodes(*c.args)
19631998

19641999
# now that we know the name of the node
19652000
# ( it's just a string in the form of host:port )
19662001
# we can build a list of commands for each node.
1967-
node_name = node.name
2002+
node_name = node[0].name
19682003
if node_name not in nodes:
1969-
redis_node = self.get_redis_connection(node)
2004+
redis_node = self.get_redis_connection(node[0])
19702005
connection = get_connection(redis_node, c.args)
19712006
nodes[node_name] = NodeCommands(
19722007
redis_node.parse_response, redis_node.connection_pool, connection

tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ def wait_for_cluster_creation(redis_url, cluster_nodes, timeout=60):
183183
while now < end_time:
184184
try:
185185
client = redis.RedisCluster.from_url(redis_url)
186-
if len(client.get_nodes()) == cluster_nodes:
186+
if len(client.get_nodes()) == int(cluster_nodes):
187187
print("All nodes are available!")
188188
break
189189
except RedisClusterException:

tests/test_search.py

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import redis.commands.search
1111
import redis.commands.search.aggregation as aggregations
1212
import redis.commands.search.reducers as reducers
13-
from redis import Redis
1413
from redis.commands.json.path import Path
1514
from redis.commands.search import Search
1615
from redis.commands.search.field import GeoField, NumericField, TagField, TextField
@@ -19,10 +18,7 @@
1918
from redis.commands.search.result import Result
2019
from redis.commands.search.suggestion import Suggestion
2120

22-
from .conftest import default_redismod_url, skip_ifmodversion_lt
23-
24-
pytestmark = pytest.mark.onlynoncluster
25-
21+
from .conftest import skip_ifmodversion_lt
2622

2723
WILL_PLAY_TEXT = os.path.abspath(
2824
os.path.join(os.path.dirname(__file__), "testdata", "will_play_text.csv.bz2")
@@ -36,7 +32,7 @@
3632
def waitForIndex(env, idx, timeout=None):
3733
delay = 0.1
3834
while True:
39-
res = env.execute_command("ft.info", idx)
35+
res = env.execute_command("FT.INFO", idx)
4036
try:
4137
res.index("indexing")
4238
except ValueError:
@@ -52,13 +48,12 @@ def waitForIndex(env, idx, timeout=None):
5248
break
5349

5450

55-
def getClient():
51+
def getClient(client):
5652
"""
5753
Gets a client client attached to an index name which is ready to be
5854
created
5955
"""
60-
rc = Redis.from_url(default_redismod_url, decode_responses=True)
61-
return rc
56+
return client
6257

6358

6459
def createIndex(client, num_docs=100, definition=None):
@@ -96,12 +91,6 @@ def createIndex(client, num_docs=100, definition=None):
9691
indexer.commit()
9792

9893

99-
# override the default module client, search requires both db=0, and text
100-
@pytest.fixture
101-
def modclient():
102-
return Redis.from_url(default_redismod_url, db=0, decode_responses=True)
103-
104-
10594
@pytest.fixture
10695
def client(modclient):
10796
modclient.flushdb()
@@ -234,6 +223,7 @@ def test_payloads(client):
234223

235224

236225
@pytest.mark.redismod
226+
@pytest.mark.onlynoncluster
237227
def test_scores(client):
238228
client.ft().create_index((TextField("txt"),))
239229

@@ -356,14 +346,14 @@ def test_sort_by(client):
356346

357347
@pytest.mark.redismod
358348
@skip_ifmodversion_lt("2.0.0", "search")
359-
def test_drop_index():
349+
def test_drop_index(client):
360350
"""
361351
Ensure the index gets dropped by data remains by default
362352
"""
363353
for x in range(20):
364354
for keep_docs in [[True, {}], [False, {"name": "haveit"}]]:
365355
idx = "HaveIt"
366-
index = getClient()
356+
index = getClient(client)
367357
index.hset("index:haveit", mapping={"name": "haveit"})
368358
idef = IndexDefinition(prefix=["index:"])
369359
index.ft(idx).create_index((TextField("name"),), definition=idef)
@@ -574,9 +564,9 @@ def test_summarize(client):
574564

575565
@pytest.mark.redismod
576566
@skip_ifmodversion_lt("2.0.0", "search")
577-
def test_alias():
578-
index1 = getClient()
579-
index2 = getClient()
567+
def test_alias(client):
568+
index1 = getClient(client)
569+
index2 = getClient(client)
580570

581571
def1 = IndexDefinition(prefix=["index1:"])
582572
def2 = IndexDefinition(prefix=["index2:"])
@@ -594,7 +584,7 @@ def test_alias():
594584

595585
# create alias and check for results
596586
ftindex1.aliasadd("spaceballs")
597-
alias_client = getClient().ft("spaceballs")
587+
alias_client = getClient(client).ft("spaceballs")
598588
res = alias_client.search("*").docs[0]
599589
assert "index1:lonestar" == res.id
600590

@@ -604,7 +594,7 @@ def test_alias():
604594

605595
# update alias and ensure new results
606596
ftindex2.aliasupdate("spaceballs")
607-
alias_client2 = getClient().ft("spaceballs")
597+
alias_client2 = getClient(client).ft("spaceballs")
608598

609599
res = alias_client2.search("*").docs[0]
610600
assert "index2:yogurt" == res.id
@@ -615,21 +605,21 @@ def test_alias():
615605

616606

617607
@pytest.mark.redismod
618-
def test_alias_basic():
608+
def test_alias_basic(client):
619609
# Creating a client with one index
620-
getClient().flushdb()
621-
index1 = getClient().ft("testAlias")
610+
getClient(client).flushdb()
611+
index1 = getClient(client).ft("testAlias")
622612

623613
index1.create_index((TextField("txt"),))
624614
index1.add_document("doc1", txt="text goes here")
625615

626-
index2 = getClient().ft("testAlias2")
616+
index2 = getClient(client).ft("testAlias2")
627617
index2.create_index((TextField("txt"),))
628618
index2.add_document("doc2", txt="text goes here")
629619

630620
# add the actual alias and check
631621
index1.aliasadd("myalias")
632-
alias_client = getClient().ft("myalias")
622+
alias_client = getClient(client).ft("myalias")
633623
res = sorted(alias_client.search("*").docs, key=lambda x: x.id)
634624
assert "doc1" == res[0].id
635625

@@ -639,7 +629,7 @@ def test_alias_basic():
639629

640630
# update the alias and ensure we get doc2
641631
index2.aliasupdate("myalias")
642-
alias_client2 = getClient().ft("myalias")
632+
alias_client2 = getClient(client).ft("myalias")
643633
res = sorted(alias_client2.search("*").docs, key=lambda x: x.id)
644634
assert "doc1" == res[0].id
645635

@@ -790,6 +780,7 @@ def test_phonetic_matcher(client):
790780

791781

792782
@pytest.mark.redismod
783+
@pytest.mark.onlynoncluster
793784
def test_scorer(client):
794785
client.ft().create_index((TextField("description"),))
795786

@@ -842,6 +833,7 @@ def test_get(client):
842833

843834

844835
@pytest.mark.redismod
836+
@pytest.mark.onlynoncluster
845837
@skip_ifmodversion_lt("2.2.0", "search")
846838
def test_config(client):
847839
assert client.ft().config_set("TIMEOUT", "100")
@@ -854,6 +846,7 @@ def test_config(client):
854846

855847

856848
@pytest.mark.redismod
849+
@pytest.mark.onlynoncluster
857850
def test_aggregations_groupby(client):
858851
# Creating the index definition and schema
859852
client.ft().create_index(
@@ -1085,8 +1078,8 @@ def test_aggregations_apply(client):
10851078
CreatedDateTimeUTC="@CreatedDateTimeUTC * 10"
10861079
)
10871080
res = client.ft().aggregate(req)
1088-
assert res.rows[0] == ["CreatedDateTimeUTC", "6373878785249699840"]
1089-
assert res.rows[1] == ["CreatedDateTimeUTC", "6373878758592700416"]
1081+
res_set = set([res.rows[0][1], res.rows[1][1]])
1082+
assert res_set == set(["6373878785249699840", "6373878758592700416"])
10901083

10911084

10921085
@pytest.mark.redismod
@@ -1158,6 +1151,7 @@ def test_index_definition(client):
11581151

11591152

11601153
@pytest.mark.redismod
1154+
@pytest.mark.onlynoncluster
11611155
def testExpire(client):
11621156
client.ft().create_index((TextField("txt", sortable=True),), temporary=4)
11631157
ttl = client.execute_command("ft.debug", "TTL", "idx")
@@ -1477,6 +1471,7 @@ def test_json_with_jsonpath(client):
14771471

14781472

14791473
@pytest.mark.redismod
1474+
@pytest.mark.onlynoncluster
14801475
def test_profile(client):
14811476
client.ft().create_index((TextField("t"),))
14821477
client.ft().client.hset("1", "t", "hello")
@@ -1505,6 +1500,7 @@ def test_profile(client):
15051500

15061501

15071502
@pytest.mark.redismod
1503+
@pytest.mark.onlynoncluster
15081504
def test_profile_limited(client):
15091505
client.ft().create_index((TextField("t"),))
15101506
client.ft().client.hset("1", "t", "hello")

0 commit comments

Comments
 (0)