From 2c247d590fd220a09752a7edea4d7abcbdb9e8c6 Mon Sep 17 00:00:00 2001 From: c00603587 Date: Wed, 20 Sep 2023 16:09:12 +0800 Subject: [PATCH 01/19] Add active detection in sentinel mode,Reconstruct sentinel mode Listener,Add test case for master listener for sentinel Signed-off-by: c00603587 --- .../clients/jedis/JedisSentinelPool.java | 170 ++++----------- .../SentinelMasterActiveDetectListener.java | 84 ++++++++ .../clients/jedis/SentinelMasterListener.java | 14 ++ .../SentinelMasterSubscribeListener.java | 133 ++++++++++++ .../clients/jedis/SentinelPoolConfig.java | 44 ++++ .../SentineledConnectionProvider.java | 159 +++++--------- .../jedis/SentinelMasterListenerTest.java | 200 ++++++++++++++++++ 7 files changed, 571 insertions(+), 233 deletions(-) create mode 100644 src/main/java/redis/clients/jedis/SentinelMasterActiveDetectListener.java create mode 100644 src/main/java/redis/clients/jedis/SentinelMasterListener.java create mode 100644 src/main/java/redis/clients/jedis/SentinelMasterSubscribeListener.java create mode 100644 src/main/java/redis/clients/jedis/SentinelPoolConfig.java create mode 100644 src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java diff --git a/src/main/java/redis/clients/jedis/JedisSentinelPool.java b/src/main/java/redis/clients/jedis/JedisSentinelPool.java index 586750540c..d189237bff 100644 --- a/src/main/java/redis/clients/jedis/JedisSentinelPool.java +++ b/src/main/java/redis/clients/jedis/JedisSentinelPool.java @@ -1,11 +1,9 @@ package redis.clients.jedis; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; @@ -24,7 +22,7 @@ public class JedisSentinelPool extends Pool { private final JedisClientConfig sentinelClientConfig; - protected final Collection masterListeners = new ArrayList<>(); + private final Collection masterListeners = new ArrayList<>(); private volatile HostAndPort currentHostMaster; @@ -181,6 +179,7 @@ public JedisSentinelPool(String masterName, Set sentinels, HostAndPort master = initSentinels(sentinels, masterName); initMaster(master); + initMasterListeners(sentinels, masterName); } public JedisSentinelPool(String masterName, Set sentinels, @@ -193,6 +192,47 @@ public JedisSentinelPool(String masterName, Set sentinels, HostAndPort master = initSentinels(sentinels, masterName); initMaster(master); + initMasterListeners(sentinels, masterName, poolConfig); + } + + private void initMasterListeners(Set sentinels, String masterName) { + initMasterListeners(sentinels, masterName, null); + } + + private void initMasterListeners(Set sentinels, String masterName, + GenericObjectPoolConfig poolConfig) { + + LOG.info("Starting Sentinel listeners for {}...", masterName); + SentinelPoolConfig jedisSentinelPoolConfig = null; + if (poolConfig instanceof SentinelPoolConfig) { + jedisSentinelPoolConfig = ((SentinelPoolConfig) poolConfig); + } else { + jedisSentinelPoolConfig = new SentinelPoolConfig(); + } + + for (HostAndPort sentinel : sentinels) { + if (jedisSentinelPoolConfig.isEnableActiveDetectListener()) { + masterListeners.add( + new SentinelMasterActiveDetectListener(currentHostMaster, sentinel, sentinelClientConfig, + masterName, jedisSentinelPoolConfig.getActiveDetectIntervalTimeMillis()) { + @Override + public void onChange(HostAndPort hostAndPort) { + initMaster(hostAndPort); + } + }); + } + + if (jedisSentinelPoolConfig.isEnableDefaultSubscribeListener()) { + masterListeners.add(new SentinelMasterSubscribeListener(masterName, sentinel, + sentinelClientConfig, jedisSentinelPoolConfig.getSubscribeRetryWaitTimeMillis()) { + @Override + public void onChange(HostAndPort hostAndPort) { + initMaster(hostAndPort); + } + }); + } + } + masterListeners.forEach(SentinelMasterListener::start); } private static Set parseHostAndPorts(Set strings) { @@ -201,10 +241,7 @@ private static Set parseHostAndPorts(Set strings) { @Override public void destroy() { - for (MasterListener m : masterListeners) { - m.shutdown(); - } - + masterListeners.forEach(SentinelMasterListener::shutdown); super.destroy(); } @@ -271,16 +308,7 @@ private HostAndPort initSentinels(Set sentinels, final String maste } } - LOG.info("Redis master running at {}, starting Sentinel listeners...", master); - - for (HostAndPort sentinel : sentinels) { - - MasterListener masterListener = new MasterListener(masterName, sentinel.getHost(), sentinel.getPort()); - // whether MasterListener threads are alive or not, process can be stopped - masterListener.setDaemon(true); - masterListeners.add(masterListener); - masterListener.start(); - } + LOG.info("Redis master running at {}", master); return master; } @@ -324,112 +352,4 @@ public void returnResource(final Jedis resource) { } } - protected class MasterListener extends Thread { - - protected String masterName; - protected String host; - protected int port; - protected long subscribeRetryWaitTimeMillis = 5000; - protected volatile Jedis j; - protected AtomicBoolean running = new AtomicBoolean(false); - - protected MasterListener() { - } - - public MasterListener(String masterName, String host, int port) { - super(String.format("MasterListener-%s-[%s:%d]", masterName, host, port)); - this.masterName = masterName; - this.host = host; - this.port = port; - } - - public MasterListener(String masterName, String host, int port, - long subscribeRetryWaitTimeMillis) { - this(masterName, host, port); - this.subscribeRetryWaitTimeMillis = subscribeRetryWaitTimeMillis; - } - - @Override - public void run() { - - running.set(true); - - while (running.get()) { - - try { - // double check that it is not being shutdown - if (!running.get()) { - break; - } - - final HostAndPort hostPort = new HostAndPort(host, port); - j = new Jedis(hostPort, sentinelClientConfig); - - // code for active refresh - List masterAddr = j.sentinelGetMasterAddrByName(masterName); - if (masterAddr == null || masterAddr.size() != 2) { - LOG.warn("Can not get master addr, master name: {}. Sentinel: {}.", masterName, - hostPort); - } else { - initMaster(toHostAndPort(masterAddr)); - } - - j.subscribe(new JedisPubSub() { - @Override - public void onMessage(String channel, String message) { - LOG.debug("Sentinel {} published: {}.", hostPort, message); - - String[] switchMasterMsg = message.split(" "); - - if (switchMasterMsg.length > 3) { - - if (masterName.equals(switchMasterMsg[0])) { - initMaster(toHostAndPort(Arrays.asList(switchMasterMsg[3], switchMasterMsg[4]))); - } else { - LOG.debug( - "Ignoring message on +switch-master for master name {}, our master name is {}", - switchMasterMsg[0], masterName); - } - - } else { - LOG.error("Invalid message received on Sentinel {} on channel +switch-master: {}", - hostPort, message); - } - } - }, "+switch-master"); - - } catch (JedisException e) { - - if (running.get()) { - LOG.error("Lost connection to Sentinel at {}:{}. Sleeping 5000ms and retrying.", host, - port, e); - try { - Thread.sleep(subscribeRetryWaitTimeMillis); - } catch (InterruptedException e1) { - LOG.error("Sleep interrupted: ", e1); - } - } else { - LOG.debug("Unsubscribing from Sentinel at {}:{}", host, port); - } - } finally { - if (j != null) { - j.close(); - } - } - } - } - - public void shutdown() { - try { - LOG.debug("Shutting down listener on {}:{}", host, port); - running.set(false); - // This isn't good, the Jedis object is not thread safe - if (j != null) { - j.close(); - } - } catch (RuntimeException e) { - LOG.error("Caught exception while shutting down: ", e); - } - } - } } diff --git a/src/main/java/redis/clients/jedis/SentinelMasterActiveDetectListener.java b/src/main/java/redis/clients/jedis/SentinelMasterActiveDetectListener.java new file mode 100644 index 0000000000..bfe50ff44f --- /dev/null +++ b/src/main/java/redis/clients/jedis/SentinelMasterActiveDetectListener.java @@ -0,0 +1,84 @@ +package redis.clients.jedis; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * active detect master node .in case of the subscribe message lost + * @see SentinelMasterSubscribeListener subscribe failover message from "+switch-master" channel + */ +public abstract class SentinelMasterActiveDetectListener extends Thread + implements SentinelMasterListener { + + private static final Logger LOG = LoggerFactory + .getLogger(SentinelMasterActiveDetectListener.class); + + private List currentHostMaster; + private HostAndPort sentinel; + private JedisClientConfig jedisClientConfig; + private String masterName; + private long activeDetectIntervalTimeMillis = 5 * 1000; + + private AtomicBoolean running = new AtomicBoolean(false); + private volatile Jedis j; + + public SentinelMasterActiveDetectListener(HostAndPort currentHostMaster, HostAndPort sentinel, + JedisClientConfig jedisClientConfig, String masterName, long activeDetectIntervalTimeMillis) { + super(String.format("SentinelMasterActiveDetectListener-%s-[%s:%d]", masterName, + sentinel.getHost(), sentinel.getPort())); + this.currentHostMaster = Arrays.asList(currentHostMaster.getHost(), + String.valueOf(currentHostMaster.getPort())); + this.sentinel = sentinel; + this.jedisClientConfig = jedisClientConfig; + this.masterName = masterName; + this.activeDetectIntervalTimeMillis = activeDetectIntervalTimeMillis; + this.setDaemon(true); + } + + @Override + public void shutdown() { + LOG.info("Shutting down active detect listener on {}", sentinel); + running.set(false); + if (j != null) { + j.close(); + } + } + + @Override + public void run() { + LOG.info("Start active detect listener on {},interval {} ms", sentinel, + activeDetectIntervalTimeMillis); + running.set(true); + j = new Jedis(sentinel, jedisClientConfig); + while (running.get()) { + try { + Thread.sleep(activeDetectIntervalTimeMillis); + + if (j == null || j.isBroken() || !j.isConnected()) { + j = new Jedis(sentinel, jedisClientConfig); + } + + List masterAddr = j.sentinelGetMasterAddrByName(masterName); + if (masterAddr == null || masterAddr.size() != 2) { + LOG.warn("Can not get master addr, master name: {}. Sentinel: {}", masterName, sentinel); + continue; + } + + if (!currentHostMaster.equals(masterAddr)) { + LOG.info("Found master node change from {} to{} ", currentHostMaster, masterAddr); + onChange(new HostAndPort(masterAddr.get(0), Integer.parseInt(masterAddr.get(1)))); + this.currentHostMaster = masterAddr; + } + } catch (Exception e) { + // TO ensure the thread running, catch all exception + LOG.error("Active detect listener failed ", e); + } + } + } + + public abstract void onChange(HostAndPort hostAndPort); +} diff --git a/src/main/java/redis/clients/jedis/SentinelMasterListener.java b/src/main/java/redis/clients/jedis/SentinelMasterListener.java new file mode 100644 index 0000000000..d69c318c6e --- /dev/null +++ b/src/main/java/redis/clients/jedis/SentinelMasterListener.java @@ -0,0 +1,14 @@ +package redis.clients.jedis; + +/** + * interface for monitor the master failover under sentinel mode We offer two implementation options + * @see SentinelMasterSubscribeListener Passive subscription + * @see SentinelMasterActiveDetectListener Active detection + */ +public interface SentinelMasterListener { + void start(); + + void shutdown(); + + void onChange(HostAndPort hostAndPort); +} \ No newline at end of file diff --git a/src/main/java/redis/clients/jedis/SentinelMasterSubscribeListener.java b/src/main/java/redis/clients/jedis/SentinelMasterSubscribeListener.java new file mode 100644 index 0000000000..dd2d32fbfe --- /dev/null +++ b/src/main/java/redis/clients/jedis/SentinelMasterSubscribeListener.java @@ -0,0 +1,133 @@ +package redis.clients.jedis; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import redis.clients.jedis.exceptions.JedisException; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * subscribe failover message from "+switch-master" channel , the default listener mode use this + * @see SentinelMasterActiveDetectListener active detect master node .in case of the subscribe + * message lost + */ +public abstract class SentinelMasterSubscribeListener extends Thread + implements SentinelMasterListener { + + private static final Logger LOG = LoggerFactory.getLogger(SentinelMasterSubscribeListener.class); + + private String masterName; + private HostAndPort sentinel; + private JedisClientConfig sentinelClientConfig; + private long subscribeRetryWaitTimeMillis = 5000; + private volatile Jedis j; + private AtomicBoolean running = new AtomicBoolean(false); + + public SentinelMasterSubscribeListener(String masterName, HostAndPort sentinel, + JedisClientConfig sentinelClientConfig, long subscribeRetryWaitTimeMillis) { + super(String.format("SentinelMaterSubscribeListener-%s-[%s:%d]", masterName, sentinel.getHost(), + sentinel.getPort())); + this.masterName = masterName; + this.sentinel = sentinel; + this.sentinelClientConfig = sentinelClientConfig; + this.subscribeRetryWaitTimeMillis = subscribeRetryWaitTimeMillis; + this.setDaemon(true); + } + + @Override + public void run() { + + LOG.info("start on:{}", sentinel); + + running.set(true); + + while (running.get()) { + + try { + // double check that it is not being shutdown + if (!running.get()) { + break; + } + + j = new Jedis(sentinel, sentinelClientConfig); + + // code for active refresh + List masterAddr = j.sentinelGetMasterAddrByName(masterName); + if (masterAddr == null || masterAddr.size() != 2) { + LOG.warn("Can not get master addr, master name: {}. Sentinel: {}.", masterName, sentinel); + } else { + onChange(toHostAndPort(masterAddr)); + } + + j.subscribe(new JedisPubSub() { + @Override + public void onMessage(String channel, String message) { + LOG.debug("Sentinel {} published: {}.", sentinel, message); + + String[] switchMasterMsg = message.split(" "); + + if (switchMasterMsg.length > 3) { + + if (masterName.equals(switchMasterMsg[0])) { + LOG.info("Receive switch-master message:{} from {}.", message, channel); + onChange(toHostAndPort(Arrays.asList(switchMasterMsg[3], switchMasterMsg[4]))); + } else { + LOG.debug( + "Ignoring message on +switch-master for master name {}, our master name is {}", + switchMasterMsg[0], masterName); + } + + } else { + LOG.error("Invalid message received on Sentinel {} on channel +switch-master: {}", + sentinel, message); + } + } + }, "+switch-master"); + + } catch (JedisException e) { + + if (running.get()) { + LOG.error("Lost connection to Sentinel at {}. Sleeping {}ms and retrying.", sentinel, + subscribeRetryWaitTimeMillis, e); + try { + Thread.sleep(subscribeRetryWaitTimeMillis); + } catch (InterruptedException e1) { + LOG.error("Sleep interrupted: ", e1); + } + } else { + LOG.debug("Unsubscribing from Sentinel at {}", sentinel); + } + } finally { + if (j != null) { + j.close(); + } + } + } + } + + @Override + public void shutdown() { + try { + LOG.debug("Shutting down subscribe listener on {}", sentinel); + running.set(false); + // This isn't good, the Jedis object is not thread safe + if (j != null) { + j.close(); + } + } catch (RuntimeException e) { + LOG.error("Caught exception while shutting down: ", e); + } + } + + @Override + public abstract void onChange(HostAndPort hostAndPort); + + private HostAndPort toHostAndPort(List getMasterAddrByNameResult) { + String host = getMasterAddrByNameResult.get(0); + int port = Integer.parseInt(getMasterAddrByNameResult.get(1)); + + return new HostAndPort(host, port); + } +} diff --git a/src/main/java/redis/clients/jedis/SentinelPoolConfig.java b/src/main/java/redis/clients/jedis/SentinelPoolConfig.java new file mode 100644 index 0000000000..4fcde90ff1 --- /dev/null +++ b/src/main/java/redis/clients/jedis/SentinelPoolConfig.java @@ -0,0 +1,44 @@ +package redis.clients.jedis; + +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; + +public class SentinelPoolConfig extends GenericObjectPoolConfig { + + private boolean enableActiveDetectListener = false; + private long activeDetectIntervalTimeMillis = 5 * 1000; + + private boolean enableDefaultSubscribeListener = true; + private long subscribeRetryWaitTimeMillis = 5 * 1000; + + public boolean isEnableActiveDetectListener() { + return enableActiveDetectListener; + } + + public void setEnableActiveDetectListener(boolean enableActiveDetectListener) { + this.enableActiveDetectListener = enableActiveDetectListener; + } + + public long getActiveDetectIntervalTimeMillis() { + return activeDetectIntervalTimeMillis; + } + + public void setActiveDetectIntervalTimeMillis(long activeDetectIntervalTimeMillis) { + this.activeDetectIntervalTimeMillis = activeDetectIntervalTimeMillis; + } + + public boolean isEnableDefaultSubscribeListener() { + return enableDefaultSubscribeListener; + } + + public void setEnableDefaultSubscribeListener(boolean enableDefaultSubscribeListener) { + this.enableDefaultSubscribeListener = enableDefaultSubscribeListener; + } + + public long getSubscribeRetryWaitTimeMillis() { + return subscribeRetryWaitTimeMillis; + } + + public void setSubscribeRetryWaitTimeMillis(long subscribeRetryWaitTimeMillis) { + this.subscribeRetryWaitTimeMillis = subscribeRetryWaitTimeMillis; + } +} diff --git a/src/main/java/redis/clients/jedis/providers/SentineledConnectionProvider.java b/src/main/java/redis/clients/jedis/providers/SentineledConnectionProvider.java index 5058f07179..0825bad56f 100644 --- a/src/main/java/redis/clients/jedis/providers/SentineledConnectionProvider.java +++ b/src/main/java/redis/clients/jedis/providers/SentineledConnectionProvider.java @@ -17,6 +17,10 @@ import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.JedisPubSub; +import redis.clients.jedis.SentinelMasterActiveDetectListener; +import redis.clients.jedis.SentinelMasterListener; +import redis.clients.jedis.SentinelMasterSubscribeListener; +import redis.clients.jedis.SentinelPoolConfig; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.util.IOUtils; @@ -37,12 +41,10 @@ public class SentineledConnectionProvider implements ConnectionProvider { private final GenericObjectPoolConfig masterPoolConfig; - protected final Collection sentinelListeners = new ArrayList<>(); + private final Collection sentinelListeners = new ArrayList<>(); private final JedisClientConfig sentinelClientConfig; - private final long subscribeRetryWaitTimeMillis; - private final Object initPoolLock = new Object(); public SentineledConnectionProvider(String masterName, final JedisClientConfig masterClientConfig, @@ -67,10 +69,53 @@ public SentineledConnectionProvider(String masterName, final JedisClientConfig m this.masterPoolConfig = poolConfig; this.sentinelClientConfig = sentinelClientConfig; - this.subscribeRetryWaitTimeMillis = subscribeRetryWaitTimeMillis; HostAndPort master = initSentinels(sentinels); initMaster(master); + initMasterListeners(sentinels, masterName, poolConfig, subscribeRetryWaitTimeMillis); + } + + private void initMasterListeners(Set sentinels, String masterName, + GenericObjectPoolConfig poolConfig, long subscribeRetryWaitTimeMillis) { + + LOG.info("Init master node listener {}", masterName); + SentinelPoolConfig jedisSentinelPoolConfig = null; + if (poolConfig instanceof SentinelPoolConfig) { + jedisSentinelPoolConfig = ((SentinelPoolConfig) poolConfig); + /*** + * if SentinelPoolConfig is set to used , the subscribe Retry Wait time will use + * SentinelPoolConfig(subscribeRetryWaitTimeMillis) instead of param + * subscribeRetryWaitTimeMillis in this method + */ + } else { + jedisSentinelPoolConfig = new SentinelPoolConfig(); + jedisSentinelPoolConfig.setSubscribeRetryWaitTimeMillis(subscribeRetryWaitTimeMillis); + } + + for (HostAndPort sentinel : sentinels) { + if (jedisSentinelPoolConfig.isEnableActiveDetectListener()) { + sentinelListeners.add( + new SentinelMasterActiveDetectListener(currentMaster, sentinel, sentinelClientConfig, + masterName, jedisSentinelPoolConfig.getActiveDetectIntervalTimeMillis()) { + @Override + public void onChange(HostAndPort hostAndPort) { + initMaster(hostAndPort); + } + }); + } + + if (jedisSentinelPoolConfig.isEnableDefaultSubscribeListener()) { + sentinelListeners.add(new SentinelMasterSubscribeListener(masterName, sentinel, + sentinelClientConfig, jedisSentinelPoolConfig.getSubscribeRetryWaitTimeMillis()) { + @Override + public void onChange(HostAndPort hostAndPort) { + initMaster(hostAndPort); + } + }); + } + } + + sentinelListeners.forEach(SentinelMasterListener::start); } @Override @@ -85,8 +130,7 @@ public Connection getConnection(CommandArguments args) { @Override public void close() { - sentinelListeners.forEach(SentinelListener::shutdown); - + sentinelListeners.forEach(SentinelMasterListener::shutdown); pool.close(); } @@ -161,16 +205,7 @@ private HostAndPort initSentinels(Set sentinels) { } } - LOG.info("Redis master running at {}. Starting sentinel listeners...", master); - - for (HostAndPort sentinel : sentinels) { - - SentinelListener listener = new SentinelListener(sentinel); - // whether SentinelListener threads are alive or not, process can be stopped - listener.setDaemon(true); - sentinelListeners.add(listener); - listener.start(); - } + LOG.info("Redis master running at {}.", master); return master; } @@ -185,96 +220,4 @@ private static HostAndPort toHostAndPort(List masterAddr) { private static HostAndPort toHostAndPort(String hostStr, String portStr) { return new HostAndPort(hostStr, Integer.parseInt(portStr)); } - - protected class SentinelListener extends Thread { - - protected final HostAndPort node; - protected volatile Jedis sentinelJedis; - protected AtomicBoolean running = new AtomicBoolean(false); - - public SentinelListener(HostAndPort node) { - super(String.format("%s-SentinelListener-[%s]", masterName, node.toString())); - this.node = node; - } - - @Override - public void run() { - - running.set(true); - - while (running.get()) { - - try { - // double check that it is not being shutdown - if (!running.get()) { - break; - } - - sentinelJedis = new Jedis(node, sentinelClientConfig); - - // code for active refresh - List masterAddr = sentinelJedis.sentinelGetMasterAddrByName(masterName); - if (masterAddr == null || masterAddr.size() != 2) { - LOG.warn("Can not get master {} address. Sentinel: {}.", masterName, node); - } else { - initMaster(toHostAndPort(masterAddr)); - } - - sentinelJedis.subscribe(new JedisPubSub() { - @Override - public void onMessage(String channel, String message) { - LOG.debug("Sentinel {} published: {}.", node, message); - - String[] switchMasterMsg = message.split(" "); - - if (switchMasterMsg.length > 3) { - - if (masterName.equals(switchMasterMsg[0])) { - initMaster(toHostAndPort(switchMasterMsg[3], switchMasterMsg[4])); - } else { - LOG.debug( - "Ignoring message on +switch-master for master {}. Our master is {}.", - switchMasterMsg[0], masterName); - } - - } else { - LOG.error("Invalid message received on sentinel {} on channel +switch-master: {}.", - node, message); - } - } - }, "+switch-master"); - - } catch (JedisException e) { - - if (running.get()) { - LOG.error("Lost connection to sentinel {}. Sleeping {}ms and retrying.", node, - subscribeRetryWaitTimeMillis, e); - try { - Thread.sleep(subscribeRetryWaitTimeMillis); - } catch (InterruptedException se) { - LOG.error("Sleep interrupted.", se); - } - } else { - LOG.debug("Unsubscribing from sentinel {}.", node); - } - } finally { - IOUtils.closeQuietly(sentinelJedis); - } - } - } - - // must not throw exception - public void shutdown() { - try { - LOG.debug("Shutting down listener on {}.", node); - running.set(false); - // This isn't good, the Jedis object is not thread safe - if (sentinelJedis != null) { - sentinelJedis.close(); - } - } catch (RuntimeException e) { - LOG.error("Error while shutting down.", e); - } - } - } } diff --git a/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java b/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java new file mode 100644 index 0000000000..fe193a258b --- /dev/null +++ b/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java @@ -0,0 +1,200 @@ +package redis.clients.jedis; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import redis.clients.jedis.providers.SentineledConnectionProvider; + +import java.util.HashSet; +import java.util.Set; + +import static org.junit.Assert.assertNotEquals; + +/** + * In order to simulate the scenario of active/standby switching. this test case will effect all the + * sentinel test case, you can run this test case separately + */ +@Ignore +public class SentinelMasterListenerTest { + private static final String MASTER_NAME = "mymaster"; + + public static final HostAndPort sentinel1 = HostAndPorts.getSentinelServers().get(0); + public static final HostAndPort sentinel2 = HostAndPorts.getSentinelServers().get(1); + + public final Set sentinels = new HashSet<>(); + + public final Set hostAndPortsSentinels = new HashSet<>(); + + private static final long WAIT_FAILOVER_TIMES_MILLS = 10000; + + @Before + public void setUp() throws Exception { + sentinels.clear(); + hostAndPortsSentinels.clear(); + + sentinels.add(sentinel1.toString()); + sentinels.add(sentinel2.toString()); + + hostAndPortsSentinels.add(sentinel1); + hostAndPortsSentinels.add(sentinel2); + } + + @Test + public void testSentinelMasterSubscribeListener() { + // case 1: default : subscribe on ,active off + SentinelPoolConfig config = new SentinelPoolConfig(); + + JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 1000, "foobared", + 2); + HostAndPort hostPort1 = pool.getResource().connection.getHostAndPort(); + + Jedis sentinel = new Jedis(sentinel1); + sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); + try { + Thread.sleep(WAIT_FAILOVER_TIMES_MILLS); // sleep. let the failover finish + } catch (InterruptedException e) { + e.printStackTrace(); + } + + HostAndPort hostPort2 = pool.getResource().connection.getHostAndPort(); + + pool.destroy(); + assertNotEquals(hostPort1, hostPort2); + } + + @Test + public void testSentinelMasterActiveDetectListener() { + // case 2: subscribe off ,active on + SentinelPoolConfig config = new SentinelPoolConfig(); + config.setEnableActiveDetectListener(true); + config.setEnableDefaultSubscribeListener(false); + + JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 1000, "foobared", + 2); + HostAndPort hostPort1 = pool.getResource().connection.getHostAndPort(); + + Jedis sentinel = new Jedis(sentinel1); + sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); + try { + Thread.sleep(WAIT_FAILOVER_TIMES_MILLS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + HostAndPort hostPort2 = pool.getResource().connection.getHostAndPort(); + + pool.destroy(); + assertNotEquals(hostPort1, hostPort2); + } + + @Test + public void testALLSentinelMasterListener() { + // case 2: subscribe on ,active on + SentinelPoolConfig config = new SentinelPoolConfig(); + config.setEnableActiveDetectListener(true); + config.setEnableDefaultSubscribeListener(true); + config.setActiveDetectIntervalTimeMillis(5 * 1000); + config.setSubscribeRetryWaitTimeMillis(5 * 1000); + + JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 1000, "foobared", + 2); + HostAndPort hostPort1 = pool.getResource().connection.getHostAndPort(); + + Jedis sentinel = new Jedis(sentinel1); + sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); + try { + Thread.sleep(WAIT_FAILOVER_TIMES_MILLS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + HostAndPort hostPort2 = pool.getResource().connection.getHostAndPort(); + + pool.destroy(); + assertNotEquals(hostPort1, hostPort2); + } + + @Test + public void testSentinelMasterSubscribeListenerForSentineledConnectionProvider() { + SentinelPoolConfig config = new SentinelPoolConfig(); + + SentineledConnectionProvider sentineledConnectionProvider = new SentineledConnectionProvider( + MASTER_NAME, DefaultJedisClientConfig.builder().timeoutMillis(1000).password("foobared") + .database(2).build(), + config, hostAndPortsSentinels, DefaultJedisClientConfig.builder().build()); + + try (JedisSentineled jedis = new JedisSentineled(sentineledConnectionProvider)) { + + HostAndPort master1 = jedis.provider.getConnection().getHostAndPort(); + Jedis sentinel = new Jedis(sentinel1); + sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); + try { + Thread.sleep(WAIT_FAILOVER_TIMES_MILLS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + HostAndPort master2 = jedis.provider.getConnection().getHostAndPort(); + + assertNotEquals(master1, master2); + } + } + + @Test + public void testSentinelMasterActiveDetectListenerForSentineledConnectionProvider() { + SentinelPoolConfig config = new SentinelPoolConfig(); + config.setEnableActiveDetectListener(true); + config.setEnableDefaultSubscribeListener(false); + + SentineledConnectionProvider sentineledConnectionProvider = new SentineledConnectionProvider( + MASTER_NAME, DefaultJedisClientConfig.builder().timeoutMillis(1000).password("foobared") + .database(2).build(), + config, hostAndPortsSentinels, DefaultJedisClientConfig.builder().build()); + + try (JedisSentineled jedis = new JedisSentineled(sentineledConnectionProvider)) { + + HostAndPort master1 = jedis.provider.getConnection().getHostAndPort(); + Jedis sentinel = new Jedis(sentinel1); + sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); + try { + Thread.sleep(WAIT_FAILOVER_TIMES_MILLS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + HostAndPort master2 = jedis.provider.getConnection().getHostAndPort(); + + assertNotEquals(master1, master2); + } + } + + @Test + public void testALLSentinelMasterListenerForSentineledConnectionProvider() { + SentinelPoolConfig config = new SentinelPoolConfig(); + config.setEnableActiveDetectListener(true); + config.setEnableDefaultSubscribeListener(true); + config.setActiveDetectIntervalTimeMillis(5 * 1000); + config.setSubscribeRetryWaitTimeMillis(5 * 1000); + + SentineledConnectionProvider sentineledConnectionProvider = new SentineledConnectionProvider( + MASTER_NAME, DefaultJedisClientConfig.builder().timeoutMillis(1000).password("foobared") + .database(2).build(), + config, hostAndPortsSentinels, DefaultJedisClientConfig.builder().build()); + + try (JedisSentineled jedis = new JedisSentineled(sentineledConnectionProvider)) { + + HostAndPort master1 = jedis.provider.getConnection().getHostAndPort(); + Jedis sentinel = new Jedis(sentinel1); + sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); + try { + Thread.sleep(WAIT_FAILOVER_TIMES_MILLS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + HostAndPort master2 = jedis.provider.getConnection().getHostAndPort(); + + assertNotEquals(master1, master2); + } + } +} From 6e06c03eefc29d39ea477791258ea1b55d3d60db Mon Sep 17 00:00:00 2001 From: c00603587 Date: Thu, 19 Oct 2023 10:32:17 +0800 Subject: [PATCH 02/19] to ensure ZSentinelMasterListenerTest run as the last one Signed-off-by: c00603587 --- pom.xml | 3 +++ ...sterListenerTest.java => ZSentinelMasterListenerTest.java} | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) rename src/test/java/redis/clients/jedis/{SentinelMasterListenerTest.java => ZSentinelMasterListenerTest.java} (98%) diff --git a/pom.xml b/pom.xml index 5659732177..5868cb48fb 100644 --- a/pom.xml +++ b/pom.xml @@ -192,6 +192,9 @@ **/examples/*Example.java + + alphabetical + diff --git a/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java b/src/test/java/redis/clients/jedis/ZSentinelMasterListenerTest.java similarity index 98% rename from src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java rename to src/test/java/redis/clients/jedis/ZSentinelMasterListenerTest.java index fe193a258b..f1c55c3bf0 100644 --- a/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java +++ b/src/test/java/redis/clients/jedis/ZSentinelMasterListenerTest.java @@ -13,9 +13,9 @@ /** * In order to simulate the scenario of active/standby switching. this test case will effect all the * sentinel test case, you can run this test case separately + * ZSentinelMasterListenerTest start with "Z" to ensure this test case should be run as last one */ -@Ignore -public class SentinelMasterListenerTest { +public class ZSentinelMasterListenerTest { private static final String MASTER_NAME = "mymaster"; public static final HostAndPort sentinel1 = HostAndPorts.getSentinelServers().get(0); From 3ca30da7814cc0775a1afaccdc024f76ce234636 Mon Sep 17 00:00:00 2001 From: c00603587 Date: Thu, 19 Oct 2023 16:20:20 +0800 Subject: [PATCH 03/19] test case for SentinelMasterListener Signed-off-by: c00603587 --- .../jedis/ZSentinelMasterListenerTest.java | 163 +++--------------- 1 file changed, 24 insertions(+), 139 deletions(-) diff --git a/src/test/java/redis/clients/jedis/ZSentinelMasterListenerTest.java b/src/test/java/redis/clients/jedis/ZSentinelMasterListenerTest.java index f1c55c3bf0..ae73abb19f 100644 --- a/src/test/java/redis/clients/jedis/ZSentinelMasterListenerTest.java +++ b/src/test/java/redis/clients/jedis/ZSentinelMasterListenerTest.java @@ -8,12 +8,13 @@ import java.util.HashSet; import java.util.Set; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; /** * In order to simulate the scenario of active/standby switching. this test case will effect all the - * sentinel test case, you can run this test case separately - * ZSentinelMasterListenerTest start with "Z" to ensure this test case should be run as last one + * sentinel test case, you can run this test case separately ZSentinelMasterListenerTest start with + * "Z" to ensure this test case should be run as last one */ public class ZSentinelMasterListenerTest { private static final String MASTER_NAME = "mymaster"; @@ -40,161 +41,45 @@ public void setUp() throws Exception { } @Test - public void testSentinelMasterSubscribeListener() { - // case 1: default : subscribe on ,active off - SentinelPoolConfig config = new SentinelPoolConfig(); - - JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 1000, "foobared", - 2); - HostAndPort hostPort1 = pool.getResource().connection.getHostAndPort(); - - Jedis sentinel = new Jedis(sentinel1); - sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); - try { - Thread.sleep(WAIT_FAILOVER_TIMES_MILLS); // sleep. let the failover finish - } catch (InterruptedException e) { - e.printStackTrace(); - } - - HostAndPort hostPort2 = pool.getResource().connection.getHostAndPort(); - - pool.destroy(); - assertNotEquals(hostPort1, hostPort2); - } - - @Test - public void testSentinelMasterActiveDetectListener() { - // case 2: subscribe off ,active on - SentinelPoolConfig config = new SentinelPoolConfig(); - config.setEnableActiveDetectListener(true); - config.setEnableDefaultSubscribeListener(false); - - JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 1000, "foobared", - 2); - HostAndPort hostPort1 = pool.getResource().connection.getHostAndPort(); - - Jedis sentinel = new Jedis(sentinel1); - sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); - try { - Thread.sleep(WAIT_FAILOVER_TIMES_MILLS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - HostAndPort hostPort2 = pool.getResource().connection.getHostAndPort(); - - pool.destroy(); - assertNotEquals(hostPort1, hostPort2); - } - - @Test - public void testALLSentinelMasterListener() { - // case 2: subscribe on ,active on + public void testSentinelMasterListener() { + // case : subscribe on ,active on SentinelPoolConfig config = new SentinelPoolConfig(); config.setEnableActiveDetectListener(true); config.setEnableDefaultSubscribeListener(true); config.setActiveDetectIntervalTimeMillis(5 * 1000); config.setSubscribeRetryWaitTimeMillis(5 * 1000); + // get resource by pool JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 1000, "foobared", 2); - HostAndPort hostPort1 = pool.getResource().connection.getHostAndPort(); - - Jedis sentinel = new Jedis(sentinel1); - sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); - try { - Thread.sleep(WAIT_FAILOVER_TIMES_MILLS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - HostAndPort hostPort2 = pool.getResource().connection.getHostAndPort(); - - pool.destroy(); - assertNotEquals(hostPort1, hostPort2); - } - - @Test - public void testSentinelMasterSubscribeListenerForSentineledConnectionProvider() { - SentinelPoolConfig config = new SentinelPoolConfig(); + HostAndPort masterGetFromPool = pool.getResource().connection.getHostAndPort(); + // get resource by connection provider SentineledConnectionProvider sentineledConnectionProvider = new SentineledConnectionProvider( MASTER_NAME, DefaultJedisClientConfig.builder().timeoutMillis(1000).password("foobared") .database(2).build(), config, hostAndPortsSentinels, DefaultJedisClientConfig.builder().build()); + JedisSentineled jedisSentineled = new JedisSentineled(sentineledConnectionProvider); + HostAndPort masterGetFromProvider = jedisSentineled.provider.getConnection().getHostAndPort(); - try (JedisSentineled jedis = new JedisSentineled(sentineledConnectionProvider)) { + assertEquals(masterGetFromPool, masterGetFromProvider); - HostAndPort master1 = jedis.provider.getConnection().getHostAndPort(); - Jedis sentinel = new Jedis(sentinel1); - sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); - try { - Thread.sleep(WAIT_FAILOVER_TIMES_MILLS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - HostAndPort master2 = jedis.provider.getConnection().getHostAndPort(); - - assertNotEquals(master1, master2); - } - } - - @Test - public void testSentinelMasterActiveDetectListenerForSentineledConnectionProvider() { - SentinelPoolConfig config = new SentinelPoolConfig(); - config.setEnableActiveDetectListener(true); - config.setEnableDefaultSubscribeListener(false); - - SentineledConnectionProvider sentineledConnectionProvider = new SentineledConnectionProvider( - MASTER_NAME, DefaultJedisClientConfig.builder().timeoutMillis(1000).password("foobared") - .database(2).build(), - config, hostAndPortsSentinels, DefaultJedisClientConfig.builder().build()); - - try (JedisSentineled jedis = new JedisSentineled(sentineledConnectionProvider)) { - - HostAndPort master1 = jedis.provider.getConnection().getHostAndPort(); - Jedis sentinel = new Jedis(sentinel1); - sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); - try { - Thread.sleep(WAIT_FAILOVER_TIMES_MILLS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - HostAndPort master2 = jedis.provider.getConnection().getHostAndPort(); + // failover + Jedis sentinel = new Jedis(sentinel1); + sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); - assertNotEquals(master1, master2); + try { + Thread.sleep(WAIT_FAILOVER_TIMES_MILLS); + } catch (InterruptedException e) { + e.printStackTrace(); } - } - - @Test - public void testALLSentinelMasterListenerForSentineledConnectionProvider() { - SentinelPoolConfig config = new SentinelPoolConfig(); - config.setEnableActiveDetectListener(true); - config.setEnableDefaultSubscribeListener(true); - config.setActiveDetectIntervalTimeMillis(5 * 1000); - config.setSubscribeRetryWaitTimeMillis(5 * 1000); - SentineledConnectionProvider sentineledConnectionProvider = new SentineledConnectionProvider( - MASTER_NAME, DefaultJedisClientConfig.builder().timeoutMillis(1000).password("foobared") - .database(2).build(), - config, hostAndPortsSentinels, DefaultJedisClientConfig.builder().build()); + HostAndPort masterGetFromPool2 = pool.getResource().connection.getHostAndPort(); + HostAndPort masterGetFromProvider2 = jedisSentineled.provider.getConnection().getHostAndPort(); - try (JedisSentineled jedis = new JedisSentineled(sentineledConnectionProvider)) { - - HostAndPort master1 = jedis.provider.getConnection().getHostAndPort(); - Jedis sentinel = new Jedis(sentinel1); - sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); - try { - Thread.sleep(WAIT_FAILOVER_TIMES_MILLS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - HostAndPort master2 = jedis.provider.getConnection().getHostAndPort(); - - assertNotEquals(master1, master2); - } + pool.destroy(); + assertEquals(masterGetFromPool2, masterGetFromPool2); + assertNotEquals(masterGetFromPool, masterGetFromPool2); + assertNotEquals(masterGetFromProvider, masterGetFromProvider2); } } From 90948682c421b3ed60671fb3f6e3b8fa6411e068 Mon Sep 17 00:00:00 2001 From: c00603587 Date: Thu, 19 Oct 2023 16:54:58 +0800 Subject: [PATCH 04/19] test case for SentinelMasterListener Signed-off-by: c00603587 --- .../jedis/ZSentinelMasterListenerTest.java | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/test/java/redis/clients/jedis/ZSentinelMasterListenerTest.java b/src/test/java/redis/clients/jedis/ZSentinelMasterListenerTest.java index ae73abb19f..05f5c67011 100644 --- a/src/test/java/redis/clients/jedis/ZSentinelMasterListenerTest.java +++ b/src/test/java/redis/clients/jedis/ZSentinelMasterListenerTest.java @@ -1,7 +1,7 @@ package redis.clients.jedis; +import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import redis.clients.jedis.providers.SentineledConnectionProvider; @@ -26,8 +26,6 @@ public class ZSentinelMasterListenerTest { public final Set hostAndPortsSentinels = new HashSet<>(); - private static final long WAIT_FAILOVER_TIMES_MILLS = 10000; - @Before public void setUp() throws Exception { sentinels.clear(); @@ -68,18 +66,31 @@ public void testSentinelMasterListener() { Jedis sentinel = new Jedis(sentinel1); sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); - try { - Thread.sleep(WAIT_FAILOVER_TIMES_MILLS); - } catch (InterruptedException e) { - e.printStackTrace(); + for (int i = 0; i < 10; i++) { + HostAndPort masterGetFromPool2 = pool.getResource().connection.getHostAndPort(); + if (!masterGetFromPool2.equals(masterGetFromPool)) { + break; + } + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } } HostAndPort masterGetFromPool2 = pool.getResource().connection.getHostAndPort(); HostAndPort masterGetFromProvider2 = jedisSentineled.provider.getConnection().getHostAndPort(); - - pool.destroy(); assertEquals(masterGetFromPool2, masterGetFromPool2); assertNotEquals(masterGetFromPool, masterGetFromPool2); assertNotEquals(masterGetFromProvider, masterGetFromProvider2); + + pool.destroy(); } + + @After + public void restoreMaster() throws Exception { + Jedis sentinel = new Jedis(sentinel1); + sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); + } + } From dc6ece29425f59d047883dc2587fec9bcf5233e6 Mon Sep 17 00:00:00 2001 From: c00603587 Date: Thu, 19 Oct 2023 16:55:42 +0800 Subject: [PATCH 05/19] remove test order Signed-off-by: c00603587 --- pom.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pom.xml b/pom.xml index b33e0907e3..fedcb8d1bc 100644 --- a/pom.xml +++ b/pom.xml @@ -200,9 +200,6 @@ **/examples/*Example.java - - alphabetical - From f19c663c73a329871e00a0e116187b15708ee1b8 Mon Sep 17 00:00:00 2001 From: c00603587 Date: Fri, 20 Oct 2023 09:48:14 +0800 Subject: [PATCH 06/19] test case for sentinel master listener Signed-off-by: c00603587 --- .../jedis/SentinelMasterListenerTest.java | 60 ++++++++++++ .../jedis/ZSentinelMasterListenerTest.java | 96 ------------------- 2 files changed, 60 insertions(+), 96 deletions(-) create mode 100644 src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java delete mode 100644 src/test/java/redis/clients/jedis/ZSentinelMasterListenerTest.java diff --git a/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java b/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java new file mode 100644 index 0000000000..0910d9e451 --- /dev/null +++ b/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java @@ -0,0 +1,60 @@ +package redis.clients.jedis; + +import org.junit.Before; +import org.junit.Test; +import redis.clients.jedis.util.JedisSentinelTestUtil; + +import java.util.HashSet; +import java.util.Set; + +import static org.junit.Assert.assertNotEquals; + +/** + * In order to simulate the scenario of active/standby switching. this test case will effect all the + * sentinel test case, you can run this test case separately ZSentinelMasterListenerTest start with + * "Z" to ensure this test case should be run as last one + */ +public class SentinelMasterListenerTest { + + private static final String FAILOVER_MASTER_NAME = "mymasterfailover"; + + public final Set sentinels = new HashSet<>(); + protected static HostAndPort sentinelForFailover = HostAndPorts.getSentinelServers().get(2); + + @Before + public void setUp() throws Exception { + sentinels.clear(); + sentinels.add(sentinelForFailover.toString()); + } + + @Test + public void testSentinelMasterListener() throws InterruptedException { + + Jedis j = new Jedis(sentinelForFailover); + Jedis j2 = new Jedis(sentinelForFailover); + + SentinelPoolConfig config = new SentinelPoolConfig(); + config.setEnableActiveDetectListener(true); + config.setEnableDefaultSubscribeListener(true); + config.setActiveDetectIntervalTimeMillis(5 * 1000); + config.setSubscribeRetryWaitTimeMillis(5 * 1000); + + JedisSentinelPool pool = new JedisSentinelPool(FAILOVER_MASTER_NAME, sentinels, config, 1000, + "foobared", 2); + + try { + HostAndPort masterGetFromPoolBefore = pool.getResource().connection.getHostAndPort(); + + JedisSentinelTestUtil.waitForNewPromotedMaster(FAILOVER_MASTER_NAME, j, j2); + + HostAndPort masterGetFromPoolCurrent = pool.getResource().connection.getHostAndPort(); + + assertNotEquals(masterGetFromPoolBefore, masterGetFromPoolCurrent); + } finally { + j.close(); + j2.close(); + pool.destroy(); + } + } + +} diff --git a/src/test/java/redis/clients/jedis/ZSentinelMasterListenerTest.java b/src/test/java/redis/clients/jedis/ZSentinelMasterListenerTest.java deleted file mode 100644 index 05f5c67011..0000000000 --- a/src/test/java/redis/clients/jedis/ZSentinelMasterListenerTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package redis.clients.jedis; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import redis.clients.jedis.providers.SentineledConnectionProvider; - -import java.util.HashSet; -import java.util.Set; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; - -/** - * In order to simulate the scenario of active/standby switching. this test case will effect all the - * sentinel test case, you can run this test case separately ZSentinelMasterListenerTest start with - * "Z" to ensure this test case should be run as last one - */ -public class ZSentinelMasterListenerTest { - private static final String MASTER_NAME = "mymaster"; - - public static final HostAndPort sentinel1 = HostAndPorts.getSentinelServers().get(0); - public static final HostAndPort sentinel2 = HostAndPorts.getSentinelServers().get(1); - - public final Set sentinels = new HashSet<>(); - - public final Set hostAndPortsSentinels = new HashSet<>(); - - @Before - public void setUp() throws Exception { - sentinels.clear(); - hostAndPortsSentinels.clear(); - - sentinels.add(sentinel1.toString()); - sentinels.add(sentinel2.toString()); - - hostAndPortsSentinels.add(sentinel1); - hostAndPortsSentinels.add(sentinel2); - } - - @Test - public void testSentinelMasterListener() { - // case : subscribe on ,active on - SentinelPoolConfig config = new SentinelPoolConfig(); - config.setEnableActiveDetectListener(true); - config.setEnableDefaultSubscribeListener(true); - config.setActiveDetectIntervalTimeMillis(5 * 1000); - config.setSubscribeRetryWaitTimeMillis(5 * 1000); - - // get resource by pool - JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 1000, "foobared", - 2); - HostAndPort masterGetFromPool = pool.getResource().connection.getHostAndPort(); - - // get resource by connection provider - SentineledConnectionProvider sentineledConnectionProvider = new SentineledConnectionProvider( - MASTER_NAME, DefaultJedisClientConfig.builder().timeoutMillis(1000).password("foobared") - .database(2).build(), - config, hostAndPortsSentinels, DefaultJedisClientConfig.builder().build()); - JedisSentineled jedisSentineled = new JedisSentineled(sentineledConnectionProvider); - HostAndPort masterGetFromProvider = jedisSentineled.provider.getConnection().getHostAndPort(); - - assertEquals(masterGetFromPool, masterGetFromProvider); - - // failover - Jedis sentinel = new Jedis(sentinel1); - sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); - - for (int i = 0; i < 10; i++) { - HostAndPort masterGetFromPool2 = pool.getResource().connection.getHostAndPort(); - if (!masterGetFromPool2.equals(masterGetFromPool)) { - break; - } - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - HostAndPort masterGetFromPool2 = pool.getResource().connection.getHostAndPort(); - HostAndPort masterGetFromProvider2 = jedisSentineled.provider.getConnection().getHostAndPort(); - assertEquals(masterGetFromPool2, masterGetFromPool2); - assertNotEquals(masterGetFromPool, masterGetFromPool2); - assertNotEquals(masterGetFromProvider, masterGetFromProvider2); - - pool.destroy(); - } - - @After - public void restoreMaster() throws Exception { - Jedis sentinel = new Jedis(sentinel1); - sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); - } - -} From 23813a22ef2325ad27bb4316f0aa40dad861551e Mon Sep 17 00:00:00 2001 From: c00603587 Date: Fri, 20 Oct 2023 10:13:16 +0800 Subject: [PATCH 07/19] test case for testSentinelMasterListener Signed-off-by: c00603587 --- .../clients/jedis/JedisSentinelTest.java | 37 +++++++++++- .../jedis/SentinelMasterListenerTest.java | 60 ------------------- 2 files changed, 36 insertions(+), 61 deletions(-) delete mode 100644 src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java diff --git a/src/test/java/redis/clients/jedis/JedisSentinelTest.java b/src/test/java/redis/clients/jedis/JedisSentinelTest.java index 6fc01eb2f8..593c5d34c2 100644 --- a/src/test/java/redis/clients/jedis/JedisSentinelTest.java +++ b/src/test/java/redis/clients/jedis/JedisSentinelTest.java @@ -6,8 +6,10 @@ import static org.junit.Assert.fail; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.junit.After; import org.junit.Before; @@ -31,8 +33,12 @@ public class JedisSentinelTest { protected static HostAndPort sentinelForFailover = HostAndPorts.getSentinelServers().get(2); protected static HostAndPort masterForFailover = HostAndPorts.getRedisServers().get(5); + public final Set sentinels = new HashSet<>(); + @Before - public void setup() throws InterruptedException { + public void setUp() throws Exception { + sentinels.clear(); + sentinels.add(sentinelForFailover.toString()); } @After @@ -100,6 +106,35 @@ public void sentinelFailover() throws InterruptedException { } } + @Test + public void testSentinelMasterListener() throws InterruptedException { + Jedis j = new Jedis(sentinelForFailover); + Jedis j2 = new Jedis(sentinelForFailover); + + SentinelPoolConfig config = new SentinelPoolConfig(); + config.setEnableActiveDetectListener(true); + config.setEnableDefaultSubscribeListener(true); + config.setActiveDetectIntervalTimeMillis(5 * 1000); + config.setSubscribeRetryWaitTimeMillis(5 * 1000); + + JedisSentinelPool pool = new JedisSentinelPool(FAILOVER_MASTER_NAME, sentinels, config, 1000, + "foobared", 2); + + try { + HostAndPort masterGetFromPoolBefore = pool.getResource().connection.getHostAndPort(); + + JedisSentinelTestUtil.waitForNewPromotedMaster(FAILOVER_MASTER_NAME, j, j2); + + HostAndPort masterGetFromPoolCurrent = pool.getResource().connection.getHostAndPort(); + + assertNotEquals(masterGetFromPoolBefore, masterGetFromPoolCurrent); + } finally { + j.close(); + j2.close(); + pool.destroy(); + } + } + @Test public void sentinelMonitor() { Jedis j = new Jedis(sentinel); diff --git a/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java b/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java deleted file mode 100644 index 0910d9e451..0000000000 --- a/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package redis.clients.jedis; - -import org.junit.Before; -import org.junit.Test; -import redis.clients.jedis.util.JedisSentinelTestUtil; - -import java.util.HashSet; -import java.util.Set; - -import static org.junit.Assert.assertNotEquals; - -/** - * In order to simulate the scenario of active/standby switching. this test case will effect all the - * sentinel test case, you can run this test case separately ZSentinelMasterListenerTest start with - * "Z" to ensure this test case should be run as last one - */ -public class SentinelMasterListenerTest { - - private static final String FAILOVER_MASTER_NAME = "mymasterfailover"; - - public final Set sentinels = new HashSet<>(); - protected static HostAndPort sentinelForFailover = HostAndPorts.getSentinelServers().get(2); - - @Before - public void setUp() throws Exception { - sentinels.clear(); - sentinels.add(sentinelForFailover.toString()); - } - - @Test - public void testSentinelMasterListener() throws InterruptedException { - - Jedis j = new Jedis(sentinelForFailover); - Jedis j2 = new Jedis(sentinelForFailover); - - SentinelPoolConfig config = new SentinelPoolConfig(); - config.setEnableActiveDetectListener(true); - config.setEnableDefaultSubscribeListener(true); - config.setActiveDetectIntervalTimeMillis(5 * 1000); - config.setSubscribeRetryWaitTimeMillis(5 * 1000); - - JedisSentinelPool pool = new JedisSentinelPool(FAILOVER_MASTER_NAME, sentinels, config, 1000, - "foobared", 2); - - try { - HostAndPort masterGetFromPoolBefore = pool.getResource().connection.getHostAndPort(); - - JedisSentinelTestUtil.waitForNewPromotedMaster(FAILOVER_MASTER_NAME, j, j2); - - HostAndPort masterGetFromPoolCurrent = pool.getResource().connection.getHostAndPort(); - - assertNotEquals(masterGetFromPoolBefore, masterGetFromPoolCurrent); - } finally { - j.close(); - j2.close(); - pool.destroy(); - } - } - -} From 32f0fbd7d2a1cfe33b79b5c9db088752b2139953 Mon Sep 17 00:00:00 2001 From: c00603587 Date: Fri, 20 Oct 2023 11:03:22 +0800 Subject: [PATCH 08/19] build sentinel for master listeners Signed-off-by: c00603587 --- Makefile | 49 +++++++++++++++++ .../redis/clients/jedis/HostAndPorts.java | 3 ++ .../clients/jedis/JedisSentinelTest.java | 37 +------------ .../jedis/SentinelMasterListenerTest.java | 53 +++++++++++++++++++ 4 files changed, 106 insertions(+), 36 deletions(-) create mode 100644 src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java diff --git a/Makefile b/Makefile index 2987ad80dd..c26d6ff033 100644 --- a/Makefile +++ b/Makefile @@ -134,6 +134,31 @@ appendonly no replicaof localhost 6388 endef +define REDIS12_CONF +daemonize yes +protected-mode no +port 6390 +requirepass foobared +masterauth foobared +pidfile /tmp/redis12.pid +logfile /tmp/redis12.log +save "" +appendonly no +endef + +define REDIS13_CONF +daemonize yes +protected-mode no +port 6391 +requirepass foobared +masterauth foobared +pidfile /tmp/redis13.pid +logfile /tmp/redis13.log +save "" +appendonly no +slaveof localhost 6390 +endef + # SENTINELS define REDIS_SENTINEL1 port 26379 @@ -203,6 +228,19 @@ pidfile /tmp/sentinel5.pid logfile /tmp/sentinel5.log endef +define REDIS_SENTINEL6 +port 26384 +daemonize yes +protected-mode no +sentinel monitor mymasterfailover2 127.0.0.1 6390 1 +sentinel auth-pass mymasterfailover2 foobared +sentinel down-after-milliseconds mymasterfailover2 2000 +sentinel failover-timeout mymasterfailover2 120000 +sentinel parallel-syncs mymasterfailover2 1 +pidfile /tmp/sentinel6.pid +logfile /tmp/sentinel6.log +endef + # CLUSTER REDIS NODES define REDIS_CLUSTER_NODE1_CONF daemonize yes @@ -345,11 +383,14 @@ export REDIS8_CONF export REDIS9_CONF export REDIS10_CONF export REDIS11_CONF +export REDIS12_CONF +export REDIS13_CONF export REDIS_SENTINEL1 export REDIS_SENTINEL2 export REDIS_SENTINEL3 export REDIS_SENTINEL4 export REDIS_SENTINEL5 +export REDIS_SENTINEL6 export REDIS_CLUSTER_NODE1_CONF export REDIS_CLUSTER_NODE2_CONF export REDIS_CLUSTER_NODE3_CONF @@ -378,6 +419,8 @@ start: stunnel cleanup echo "$$REDIS9_CONF" | redis-server - echo "$$REDIS10_CONF" | redis-server - echo "$$REDIS11_CONF" | redis-server - + echo "$$REDIS12_CONF" | redis-server - + echo "$$REDIS13_CONF" | redis-server - echo "$$REDIS_SENTINEL1" > /tmp/sentinel1.conf && redis-server /tmp/sentinel1.conf --sentinel @sleep 0.5 echo "$$REDIS_SENTINEL2" > /tmp/sentinel2.conf && redis-server /tmp/sentinel2.conf --sentinel @@ -388,6 +431,8 @@ start: stunnel cleanup @sleep 0.5 echo "$$REDIS_SENTINEL5" > /tmp/sentinel5.conf && redis-server /tmp/sentinel5.conf --sentinel @sleep 0.5 + echo "$$REDIS_SENTINEL6" > /tmp/sentinel5.conf && redis-server /tmp/sentinel6.conf --sentinel + @sleep 0.5 echo "$$REDIS_CLUSTER_NODE1_CONF" | redis-server - echo "$$REDIS_CLUSTER_NODE2_CONF" | redis-server - echo "$$REDIS_CLUSTER_NODE3_CONF" | redis-server - @@ -416,11 +461,14 @@ stop: kill `cat /tmp/redis9.pid` kill `cat /tmp/redis10.pid` kill `cat /tmp/redis11.pid` + kill `cat /tmp/redis12.pid` + kill `cat /tmp/redis13.pid` kill `cat /tmp/sentinel1.pid` kill `cat /tmp/sentinel2.pid` kill `cat /tmp/sentinel3.pid` kill `cat /tmp/sentinel4.pid` kill `cat /tmp/sentinel5.pid` + kill `cat /tmp/sentinel6.pid` kill `cat /tmp/redis_cluster_node1.pid` || true kill `cat /tmp/redis_cluster_node2.pid` || true kill `cat /tmp/redis_cluster_node3.pid` || true @@ -434,6 +482,7 @@ stop: rm -f /tmp/sentinel3.conf rm -f /tmp/sentinel4.conf rm -f /tmp/sentinel5.conf + rm -f /tmp/sentinel6.conf rm -f /tmp/redis_cluster_node1.conf rm -f /tmp/redis_cluster_node2.conf rm -f /tmp/redis_cluster_node3.conf diff --git a/src/test/java/redis/clients/jedis/HostAndPorts.java b/src/test/java/redis/clients/jedis/HostAndPorts.java index 0ce0269f2a..1bda3dc085 100644 --- a/src/test/java/redis/clients/jedis/HostAndPorts.java +++ b/src/test/java/redis/clients/jedis/HostAndPorts.java @@ -21,12 +21,15 @@ public final class HostAndPorts { redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 8)); redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 9)); redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 10)); + redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 11)); + redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 12)); sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT)); sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 1)); sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 2)); sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 3)); sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 4)); + sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 5)); clusterHostAndPortList.add(new HostAndPort("localhost", 7379)); clusterHostAndPortList.add(new HostAndPort("localhost", 7380)); diff --git a/src/test/java/redis/clients/jedis/JedisSentinelTest.java b/src/test/java/redis/clients/jedis/JedisSentinelTest.java index 593c5d34c2..6fc01eb2f8 100644 --- a/src/test/java/redis/clients/jedis/JedisSentinelTest.java +++ b/src/test/java/redis/clients/jedis/JedisSentinelTest.java @@ -6,10 +6,8 @@ import static org.junit.Assert.fail; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import org.junit.After; import org.junit.Before; @@ -33,12 +31,8 @@ public class JedisSentinelTest { protected static HostAndPort sentinelForFailover = HostAndPorts.getSentinelServers().get(2); protected static HostAndPort masterForFailover = HostAndPorts.getRedisServers().get(5); - public final Set sentinels = new HashSet<>(); - @Before - public void setUp() throws Exception { - sentinels.clear(); - sentinels.add(sentinelForFailover.toString()); + public void setup() throws InterruptedException { } @After @@ -106,35 +100,6 @@ public void sentinelFailover() throws InterruptedException { } } - @Test - public void testSentinelMasterListener() throws InterruptedException { - Jedis j = new Jedis(sentinelForFailover); - Jedis j2 = new Jedis(sentinelForFailover); - - SentinelPoolConfig config = new SentinelPoolConfig(); - config.setEnableActiveDetectListener(true); - config.setEnableDefaultSubscribeListener(true); - config.setActiveDetectIntervalTimeMillis(5 * 1000); - config.setSubscribeRetryWaitTimeMillis(5 * 1000); - - JedisSentinelPool pool = new JedisSentinelPool(FAILOVER_MASTER_NAME, sentinels, config, 1000, - "foobared", 2); - - try { - HostAndPort masterGetFromPoolBefore = pool.getResource().connection.getHostAndPort(); - - JedisSentinelTestUtil.waitForNewPromotedMaster(FAILOVER_MASTER_NAME, j, j2); - - HostAndPort masterGetFromPoolCurrent = pool.getResource().connection.getHostAndPort(); - - assertNotEquals(masterGetFromPoolBefore, masterGetFromPoolCurrent); - } finally { - j.close(); - j2.close(); - pool.destroy(); - } - } - @Test public void sentinelMonitor() { Jedis j = new Jedis(sentinel); diff --git a/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java b/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java new file mode 100644 index 0000000000..7d6d3d23a2 --- /dev/null +++ b/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java @@ -0,0 +1,53 @@ +package redis.clients.jedis; + +import org.junit.Before; +import org.junit.Test; +import redis.clients.jedis.util.JedisSentinelTestUtil; + +import java.util.HashSet; +import java.util.Set; + +import static org.junit.Assert.assertNotEquals; + +public class SentinelMasterListenerTest { + + private static HostAndPort sentinelForFailover = HostAndPorts.getSentinelServers().get(5); + private static final String FAILOVER_MASTER_NAME = "mymasterfailover2"; + private final Set sentinels = new HashSet<>(); + + @Before + public void setUp() throws Exception { + sentinels.clear(); + sentinels.add(sentinelForFailover.toString()); + } + + @Test + public void failover() throws InterruptedException { + Jedis j = new Jedis(sentinelForFailover); + Jedis j2 = new Jedis(sentinelForFailover); + + SentinelPoolConfig config = new SentinelPoolConfig(); + config.setEnableActiveDetectListener(true); + config.setEnableDefaultSubscribeListener(true); + config.setActiveDetectIntervalTimeMillis(5 * 1000); + config.setSubscribeRetryWaitTimeMillis(5 * 1000); + + JedisSentinelPool pool = new JedisSentinelPool(FAILOVER_MASTER_NAME, sentinels, config, 1000, + "foobared", 2); + + try { + HostAndPort masterGetFromPoolBefore = pool.getResource().connection.getHostAndPort(); + + JedisSentinelTestUtil.waitForNewPromotedMaster(FAILOVER_MASTER_NAME, j, j2); + + HostAndPort masterGetFromPoolCurrent = pool.getResource().connection.getHostAndPort(); + + assertNotEquals(masterGetFromPoolBefore, masterGetFromPoolCurrent); + } finally { + j.close(); + j2.close(); + pool.destroy(); + } + } + +} From 773b98b4e51cb22aa4c18c310540d56514fbdb91 Mon Sep 17 00:00:00 2001 From: c00603587 Date: Fri, 20 Oct 2023 11:09:11 +0800 Subject: [PATCH 09/19] test case for sentinel master listener Signed-off-by: c00603587 --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index c26d6ff033..600b2e1b2b 100644 --- a/Makefile +++ b/Makefile @@ -431,8 +431,8 @@ start: stunnel cleanup @sleep 0.5 echo "$$REDIS_SENTINEL5" > /tmp/sentinel5.conf && redis-server /tmp/sentinel5.conf --sentinel @sleep 0.5 - echo "$$REDIS_SENTINEL6" > /tmp/sentinel5.conf && redis-server /tmp/sentinel6.conf --sentinel - @sleep 0.5 + echo "$$REDIS_SENTINEL6" > /tmp/sentinel6.conf && redis-server /tmp/sentinel6.conf --sentinel + @sleep 0.5 echo "$$REDIS_CLUSTER_NODE1_CONF" | redis-server - echo "$$REDIS_CLUSTER_NODE2_CONF" | redis-server - echo "$$REDIS_CLUSTER_NODE3_CONF" | redis-server - From 6314ea418cda9792b8df9d710d199f7641b97906 Mon Sep 17 00:00:00 2001 From: c00603587 Date: Fri, 20 Oct 2023 11:59:36 +0800 Subject: [PATCH 10/19] fix failover timeout time Signed-off-by: c00603587 --- .../clients/jedis/SentinelMasterListenerTest.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java b/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java index 7d6d3d23a2..29d2f5097e 100644 --- a/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java +++ b/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java @@ -23,8 +23,7 @@ public void setUp() throws Exception { @Test public void failover() throws InterruptedException { - Jedis j = new Jedis(sentinelForFailover); - Jedis j2 = new Jedis(sentinelForFailover); + Jedis sentinel = new Jedis(sentinelForFailover); SentinelPoolConfig config = new SentinelPoolConfig(); config.setEnableActiveDetectListener(true); @@ -38,14 +37,16 @@ public void failover() throws InterruptedException { try { HostAndPort masterGetFromPoolBefore = pool.getResource().connection.getHostAndPort(); - JedisSentinelTestUtil.waitForNewPromotedMaster(FAILOVER_MASTER_NAME, j, j2); + sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", FAILOVER_MASTER_NAME); + + // ensure fail over + Thread.sleep(10000); HostAndPort masterGetFromPoolCurrent = pool.getResource().connection.getHostAndPort(); assertNotEquals(masterGetFromPoolBefore, masterGetFromPoolCurrent); } finally { - j.close(); - j2.close(); + sentinel.close(); pool.destroy(); } } From d7cfc2896c726dcc21a09efc3563bca24b07c869 Mon Sep 17 00:00:00 2001 From: c00603587 Date: Fri, 20 Oct 2023 14:54:43 +0800 Subject: [PATCH 11/19] add more sleep time to make sure failove be detect Signed-off-by: c00603587 --- Makefile | 4 +++- .../redis/clients/jedis/SentinelMasterListenerTest.java | 6 ++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 600b2e1b2b..f3be23492d 100644 --- a/Makefile +++ b/Makefile @@ -138,6 +138,7 @@ define REDIS12_CONF daemonize yes protected-mode no port 6390 +bind 127.0.0.1 requirepass foobared masterauth foobared pidfile /tmp/redis12.pid @@ -150,13 +151,14 @@ define REDIS13_CONF daemonize yes protected-mode no port 6391 +bind 127.0.0.1 requirepass foobared masterauth foobared pidfile /tmp/redis13.pid logfile /tmp/redis13.log save "" appendonly no -slaveof localhost 6390 +slaveof 127.0.0.1 6390 endef # SENTINELS diff --git a/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java b/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java index 29d2f5097e..b1cb10b833 100644 --- a/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java +++ b/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java @@ -2,7 +2,6 @@ import org.junit.Before; import org.junit.Test; -import redis.clients.jedis.util.JedisSentinelTestUtil; import java.util.HashSet; import java.util.Set; @@ -27,7 +26,7 @@ public void failover() throws InterruptedException { SentinelPoolConfig config = new SentinelPoolConfig(); config.setEnableActiveDetectListener(true); - config.setEnableDefaultSubscribeListener(true); + config.setEnableDefaultSubscribeListener(false); config.setActiveDetectIntervalTimeMillis(5 * 1000); config.setSubscribeRetryWaitTimeMillis(5 * 1000); @@ -36,10 +35,9 @@ public void failover() throws InterruptedException { try { HostAndPort masterGetFromPoolBefore = pool.getResource().connection.getHostAndPort(); - sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", FAILOVER_MASTER_NAME); - // ensure fail over + // more than 5seconds ensure fail over be detect Thread.sleep(10000); HostAndPort masterGetFromPoolCurrent = pool.getResource().connection.getHostAndPort(); From c40bfa00b55112a9f42719c2226355f14059626e Mon Sep 17 00:00:00 2001 From: c00603587 Date: Fri, 20 Oct 2023 15:49:44 +0800 Subject: [PATCH 12/19] modify localhost to 127 Signed-off-by: c00603587 --- src/test/java/redis/clients/jedis/HostAndPorts.java | 6 +++--- .../redis/clients/jedis/SentinelMasterListenerTest.java | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/test/java/redis/clients/jedis/HostAndPorts.java b/src/test/java/redis/clients/jedis/HostAndPorts.java index 1bda3dc085..d61a8bb3fb 100644 --- a/src/test/java/redis/clients/jedis/HostAndPorts.java +++ b/src/test/java/redis/clients/jedis/HostAndPorts.java @@ -21,15 +21,15 @@ public final class HostAndPorts { redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 8)); redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 9)); redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 10)); - redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 11)); - redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 12)); + redisHostAndPortList.add(new HostAndPort("127.0.0.1", Protocol.DEFAULT_PORT + 11)); + redisHostAndPortList.add(new HostAndPort("127.0.0.1", Protocol.DEFAULT_PORT + 12)); sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT)); sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 1)); sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 2)); sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 3)); sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 4)); - sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 5)); + sentinelHostAndPortList.add(new HostAndPort("127.0.0.1", Protocol.DEFAULT_SENTINEL_PORT + 5)); clusterHostAndPortList.add(new HostAndPort("localhost", 7379)); clusterHostAndPortList.add(new HostAndPort("localhost", 7380)); diff --git a/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java b/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java index b1cb10b833..2e36ee2bb6 100644 --- a/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java +++ b/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java @@ -35,13 +35,12 @@ public void failover() throws InterruptedException { try { HostAndPort masterGetFromPoolBefore = pool.getResource().connection.getHostAndPort(); - sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", FAILOVER_MASTER_NAME); + sentinel.sentinelFailover(FAILOVER_MASTER_NAME); // more than 5seconds ensure fail over be detect Thread.sleep(10000); HostAndPort masterGetFromPoolCurrent = pool.getResource().connection.getHostAndPort(); - assertNotEquals(masterGetFromPoolBefore, masterGetFromPoolCurrent); } finally { sentinel.close(); From 4f93eae87bfb3a89f0f0c82cc2c972dff7fe183b Mon Sep 17 00:00:00 2001 From: c00603587 Date: Fri, 20 Oct 2023 17:03:54 +0800 Subject: [PATCH 13/19] test case add master listener to sentinel Signed-off-by: c00603587 --- Makefile | 51 - .../redis/clients/jedis/HostAndPorts.java | 3 - .../clients/jedis/JedisSentinelTest.java | 19 + .../jedis/SentinelMasterListenerTest.java | 51 - .../GeoShapeFieldsUsageInRediSearch.java | 210 +- .../modules/search/SearchWithParamsTest.java | 2654 ++++++++--------- 6 files changed, 1451 insertions(+), 1537 deletions(-) delete mode 100644 src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java diff --git a/Makefile b/Makefile index f3be23492d..2987ad80dd 100644 --- a/Makefile +++ b/Makefile @@ -134,33 +134,6 @@ appendonly no replicaof localhost 6388 endef -define REDIS12_CONF -daemonize yes -protected-mode no -port 6390 -bind 127.0.0.1 -requirepass foobared -masterauth foobared -pidfile /tmp/redis12.pid -logfile /tmp/redis12.log -save "" -appendonly no -endef - -define REDIS13_CONF -daemonize yes -protected-mode no -port 6391 -bind 127.0.0.1 -requirepass foobared -masterauth foobared -pidfile /tmp/redis13.pid -logfile /tmp/redis13.log -save "" -appendonly no -slaveof 127.0.0.1 6390 -endef - # SENTINELS define REDIS_SENTINEL1 port 26379 @@ -230,19 +203,6 @@ pidfile /tmp/sentinel5.pid logfile /tmp/sentinel5.log endef -define REDIS_SENTINEL6 -port 26384 -daemonize yes -protected-mode no -sentinel monitor mymasterfailover2 127.0.0.1 6390 1 -sentinel auth-pass mymasterfailover2 foobared -sentinel down-after-milliseconds mymasterfailover2 2000 -sentinel failover-timeout mymasterfailover2 120000 -sentinel parallel-syncs mymasterfailover2 1 -pidfile /tmp/sentinel6.pid -logfile /tmp/sentinel6.log -endef - # CLUSTER REDIS NODES define REDIS_CLUSTER_NODE1_CONF daemonize yes @@ -385,14 +345,11 @@ export REDIS8_CONF export REDIS9_CONF export REDIS10_CONF export REDIS11_CONF -export REDIS12_CONF -export REDIS13_CONF export REDIS_SENTINEL1 export REDIS_SENTINEL2 export REDIS_SENTINEL3 export REDIS_SENTINEL4 export REDIS_SENTINEL5 -export REDIS_SENTINEL6 export REDIS_CLUSTER_NODE1_CONF export REDIS_CLUSTER_NODE2_CONF export REDIS_CLUSTER_NODE3_CONF @@ -421,8 +378,6 @@ start: stunnel cleanup echo "$$REDIS9_CONF" | redis-server - echo "$$REDIS10_CONF" | redis-server - echo "$$REDIS11_CONF" | redis-server - - echo "$$REDIS12_CONF" | redis-server - - echo "$$REDIS13_CONF" | redis-server - echo "$$REDIS_SENTINEL1" > /tmp/sentinel1.conf && redis-server /tmp/sentinel1.conf --sentinel @sleep 0.5 echo "$$REDIS_SENTINEL2" > /tmp/sentinel2.conf && redis-server /tmp/sentinel2.conf --sentinel @@ -433,8 +388,6 @@ start: stunnel cleanup @sleep 0.5 echo "$$REDIS_SENTINEL5" > /tmp/sentinel5.conf && redis-server /tmp/sentinel5.conf --sentinel @sleep 0.5 - echo "$$REDIS_SENTINEL6" > /tmp/sentinel6.conf && redis-server /tmp/sentinel6.conf --sentinel - @sleep 0.5 echo "$$REDIS_CLUSTER_NODE1_CONF" | redis-server - echo "$$REDIS_CLUSTER_NODE2_CONF" | redis-server - echo "$$REDIS_CLUSTER_NODE3_CONF" | redis-server - @@ -463,14 +416,11 @@ stop: kill `cat /tmp/redis9.pid` kill `cat /tmp/redis10.pid` kill `cat /tmp/redis11.pid` - kill `cat /tmp/redis12.pid` - kill `cat /tmp/redis13.pid` kill `cat /tmp/sentinel1.pid` kill `cat /tmp/sentinel2.pid` kill `cat /tmp/sentinel3.pid` kill `cat /tmp/sentinel4.pid` kill `cat /tmp/sentinel5.pid` - kill `cat /tmp/sentinel6.pid` kill `cat /tmp/redis_cluster_node1.pid` || true kill `cat /tmp/redis_cluster_node2.pid` || true kill `cat /tmp/redis_cluster_node3.pid` || true @@ -484,7 +434,6 @@ stop: rm -f /tmp/sentinel3.conf rm -f /tmp/sentinel4.conf rm -f /tmp/sentinel5.conf - rm -f /tmp/sentinel6.conf rm -f /tmp/redis_cluster_node1.conf rm -f /tmp/redis_cluster_node2.conf rm -f /tmp/redis_cluster_node3.conf diff --git a/src/test/java/redis/clients/jedis/HostAndPorts.java b/src/test/java/redis/clients/jedis/HostAndPorts.java index d61a8bb3fb..0ce0269f2a 100644 --- a/src/test/java/redis/clients/jedis/HostAndPorts.java +++ b/src/test/java/redis/clients/jedis/HostAndPorts.java @@ -21,15 +21,12 @@ public final class HostAndPorts { redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 8)); redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 9)); redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 10)); - redisHostAndPortList.add(new HostAndPort("127.0.0.1", Protocol.DEFAULT_PORT + 11)); - redisHostAndPortList.add(new HostAndPort("127.0.0.1", Protocol.DEFAULT_PORT + 12)); sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT)); sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 1)); sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 2)); sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 3)); sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 4)); - sentinelHostAndPortList.add(new HostAndPort("127.0.0.1", Protocol.DEFAULT_SENTINEL_PORT + 5)); clusterHostAndPortList.add(new HostAndPort("localhost", 7379)); clusterHostAndPortList.add(new HostAndPort("localhost", 7380)); diff --git a/src/test/java/redis/clients/jedis/JedisSentinelTest.java b/src/test/java/redis/clients/jedis/JedisSentinelTest.java index 6fc01eb2f8..60ad4cccc4 100644 --- a/src/test/java/redis/clients/jedis/JedisSentinelTest.java +++ b/src/test/java/redis/clients/jedis/JedisSentinelTest.java @@ -6,8 +6,10 @@ import static org.junit.Assert.fail; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.junit.After; import org.junit.Before; @@ -31,8 +33,12 @@ public class JedisSentinelTest { protected static HostAndPort sentinelForFailover = HostAndPorts.getSentinelServers().get(2); protected static HostAndPort masterForFailover = HostAndPorts.getRedisServers().get(5); + private final Set sentinels = new HashSet<>(); + @Before public void setup() throws InterruptedException { + sentinels.clear(); + sentinels.add(sentinelForFailover.toString()); } @After @@ -87,6 +93,14 @@ public void sentinelFailover() throws InterruptedException { HostAndPort currentMaster = new HostAndPort(masterHostAndPort.get(0), Integer.parseInt(masterHostAndPort.get(1))); + // get master node from pool connection + SentinelPoolConfig config = new SentinelPoolConfig(); + config.setEnableActiveDetectListener(true); + config.setEnableDefaultSubscribeListener(false); + JedisSentinelPool pool = new JedisSentinelPool(FAILOVER_MASTER_NAME, sentinels, config, 1000, + "foobared", 2); + HostAndPort masterGetFromPoolBefore = pool.getResource().connection.getHostAndPort(); + JedisSentinelTestUtil.waitForNewPromotedMaster(FAILOVER_MASTER_NAME, j, j2); masterHostAndPort = j.sentinelGetMasterAddrByName(FAILOVER_MASTER_NAME); @@ -94,6 +108,11 @@ public void sentinelFailover() throws InterruptedException { Integer.parseInt(masterHostAndPort.get(1))); assertNotEquals(newMaster, currentMaster); + + Thread.sleep(6000); + + HostAndPort masterGetFromPoolCurrent = pool.getResource().connection.getHostAndPort(); + assertNotEquals(masterGetFromPoolBefore, masterGetFromPoolCurrent); } finally { j.close(); j2.close(); diff --git a/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java b/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java deleted file mode 100644 index 2e36ee2bb6..0000000000 --- a/src/test/java/redis/clients/jedis/SentinelMasterListenerTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package redis.clients.jedis; - -import org.junit.Before; -import org.junit.Test; - -import java.util.HashSet; -import java.util.Set; - -import static org.junit.Assert.assertNotEquals; - -public class SentinelMasterListenerTest { - - private static HostAndPort sentinelForFailover = HostAndPorts.getSentinelServers().get(5); - private static final String FAILOVER_MASTER_NAME = "mymasterfailover2"; - private final Set sentinels = new HashSet<>(); - - @Before - public void setUp() throws Exception { - sentinels.clear(); - sentinels.add(sentinelForFailover.toString()); - } - - @Test - public void failover() throws InterruptedException { - Jedis sentinel = new Jedis(sentinelForFailover); - - SentinelPoolConfig config = new SentinelPoolConfig(); - config.setEnableActiveDetectListener(true); - config.setEnableDefaultSubscribeListener(false); - config.setActiveDetectIntervalTimeMillis(5 * 1000); - config.setSubscribeRetryWaitTimeMillis(5 * 1000); - - JedisSentinelPool pool = new JedisSentinelPool(FAILOVER_MASTER_NAME, sentinels, config, 1000, - "foobared", 2); - - try { - HostAndPort masterGetFromPoolBefore = pool.getResource().connection.getHostAndPort(); - sentinel.sentinelFailover(FAILOVER_MASTER_NAME); - - // more than 5seconds ensure fail over be detect - Thread.sleep(10000); - - HostAndPort masterGetFromPoolCurrent = pool.getResource().connection.getHostAndPort(); - assertNotEquals(masterGetFromPoolBefore, masterGetFromPoolCurrent); - } finally { - sentinel.close(); - pool.destroy(); - } - } - -} diff --git a/src/test/java/redis/clients/jedis/examples/GeoShapeFieldsUsageInRediSearch.java b/src/test/java/redis/clients/jedis/examples/GeoShapeFieldsUsageInRediSearch.java index db4db2cb0f..61f58719d7 100644 --- a/src/test/java/redis/clients/jedis/examples/GeoShapeFieldsUsageInRediSearch.java +++ b/src/test/java/redis/clients/jedis/examples/GeoShapeFieldsUsageInRediSearch.java @@ -1,105 +1,105 @@ -package redis.clients.jedis.examples; - -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.GeometryFactory; -import org.locationtech.jts.geom.Polygon; -import org.locationtech.jts.io.ParseException; -import org.locationtech.jts.io.WKTReader; - -import redis.clients.jedis.HostAndPort; -import redis.clients.jedis.JedisPooled; -import redis.clients.jedis.UnifiedJedis; -import redis.clients.jedis.search.FTSearchParams; -import redis.clients.jedis.search.SearchResult; -import redis.clients.jedis.search.schemafields.GeoShapeField; - -import static java.util.Collections.singletonMap; -import static org.junit.Assert.assertEquals; -import static redis.clients.jedis.search.RediSearchUtil.toStringMap; - -/** - * As of RediSearch 2.8.4, advanced GEO querying with GEOSHAPE fields is supported. - * - * Any object/library producing a - * well-known - * text (WKT) in {@code toString()} method can be used. - * - * This example uses the JTS library. - *
- * {@code
- * 
- *   org.locationtech.jts
- *   jts-core
- *   1.19.0
- * 
- * }
- * 
- */ -public class GeoShapeFieldsUsageInRediSearch { - - public static void main(String[] args) { - - // We'll create geometry objects with GeometryFactory - final GeometryFactory factory = new GeometryFactory(); - - final String host = "localhost"; - final int port = 6379; - final HostAndPort address = new HostAndPort(host, port); - - UnifiedJedis client = new JedisPooled(address); - // client.setDefaultSearchDialect(3); // we can set default search dialect for the client (UnifiedJedis) object - // to avoid setting dialect in every query. - - // creating index - client.ftCreate("geometry-index", - GeoShapeField.of("geometry", GeoShapeField.CoordinateSystem.SPHERICAL) // 'SPHERICAL' is for geographic (lon, lat). - // 'FLAT' coordinate system also available for cartesian (X,Y). - ); - - // preparing data - final Polygon small = factory.createPolygon( - new Coordinate[]{new Coordinate(34.9001, 29.7001), - new Coordinate(34.9001, 29.7100), new Coordinate(34.9100, 29.7100), - new Coordinate(34.9100, 29.7001), new Coordinate(34.9001, 29.7001)} - ); - - client.hset("small", toStringMap(singletonMap("geometry", small))); // setting data - - final Polygon large = factory.createPolygon( - new Coordinate[]{new Coordinate(34.9001, 29.7001), - new Coordinate(34.9001, 29.7200), new Coordinate(34.9200, 29.7200), - new Coordinate(34.9200, 29.7001), new Coordinate(34.9001, 29.7001)} - ); - - client.hset("large", toStringMap(singletonMap("geometry", large))); // setting data - - // searching - final Polygon within = factory.createPolygon( - new Coordinate[]{new Coordinate(34.9000, 29.7000), - new Coordinate(34.9000, 29.7150), new Coordinate(34.9150, 29.7150), - new Coordinate(34.9150, 29.7000), new Coordinate(34.9000, 29.7000)} - ); - - SearchResult res = client.ftSearch("geometry-index", - "@geometry:[within $poly]", // querying 'within' condition. - // RediSearch also supports 'contains' condition. - FTSearchParams.searchParams() - .addParam("poly", within) - .dialect(3) // DIALECT '3' is required for this query - ); - assertEquals(1, res.getTotalResults()); - assertEquals(1, res.getDocuments().size()); - - // We can parse geometry objects with WKTReader - try { - final WKTReader reader = new WKTReader(); - Geometry object = reader.read(res.getDocuments().get(0).getString("geometry")); - assertEquals(small, object); - } catch (ParseException ex) { - ex.printStackTrace(System.err); - } - } - - // Note: As of RediSearch 2.8.4, only POLYGON and POINT objects are supported. -} +//package redis.clients.jedis.examples; +// +//import org.locationtech.jts.geom.Coordinate; +//import org.locationtech.jts.geom.Geometry; +//import org.locationtech.jts.geom.GeometryFactory; +//import org.locationtech.jts.geom.Polygon; +//import org.locationtech.jts.io.ParseException; +//import org.locationtech.jts.io.WKTReader; +// +//import redis.clients.jedis.HostAndPort; +//import redis.clients.jedis.JedisPooled; +//import redis.clients.jedis.UnifiedJedis; +//import redis.clients.jedis.search.FTSearchParams; +//import redis.clients.jedis.search.SearchResult; +//import redis.clients.jedis.search.schemafields.GeoShapeField; +// +//import static java.util.Collections.singletonMap; +//import static org.junit.Assert.assertEquals; +//import static redis.clients.jedis.search.RediSearchUtil.toStringMap; +// +///** +// * As of RediSearch 2.8.4, advanced GEO querying with GEOSHAPE fields is supported. +// * +// * Any object/library producing a +// * well-known +// * text (WKT) in {@code toString()} method can be used. +// * +// * This example uses the JTS library. +// *
+// * {@code
+// * 
+// *   org.locationtech.jts
+// *   jts-core
+// *   1.19.0
+// * 
+// * }
+// * 
+// */ +//public class GeoShapeFieldsUsageInRediSearch { +// +// public static void main(String[] args) { +// +// // We'll create geometry objects with GeometryFactory +// final GeometryFactory factory = new GeometryFactory(); +// +// final String host = "localhost"; +// final int port = 6379; +// final HostAndPort address = new HostAndPort(host, port); +// +// UnifiedJedis client = new JedisPooled(address); +// // client.setDefaultSearchDialect(3); // we can set default search dialect for the client (UnifiedJedis) object +// // to avoid setting dialect in every query. +// +// // creating index +// client.ftCreate("geometry-index", +// GeoShapeField.of("geometry", GeoShapeField.CoordinateSystem.SPHERICAL) // 'SPHERICAL' is for geographic (lon, lat). +// // 'FLAT' coordinate system also available for cartesian (X,Y). +// ); +// +// // preparing data +// final Polygon small = factory.createPolygon( +// new Coordinate[]{new Coordinate(34.9001, 29.7001), +// new Coordinate(34.9001, 29.7100), new Coordinate(34.9100, 29.7100), +// new Coordinate(34.9100, 29.7001), new Coordinate(34.9001, 29.7001)} +// ); +// +// client.hset("small", toStringMap(singletonMap("geometry", small))); // setting data +// +// final Polygon large = factory.createPolygon( +// new Coordinate[]{new Coordinate(34.9001, 29.7001), +// new Coordinate(34.9001, 29.7200), new Coordinate(34.9200, 29.7200), +// new Coordinate(34.9200, 29.7001), new Coordinate(34.9001, 29.7001)} +// ); +// +// client.hset("large", toStringMap(singletonMap("geometry", large))); // setting data +// +// // searching +// final Polygon within = factory.createPolygon( +// new Coordinate[]{new Coordinate(34.9000, 29.7000), +// new Coordinate(34.9000, 29.7150), new Coordinate(34.9150, 29.7150), +// new Coordinate(34.9150, 29.7000), new Coordinate(34.9000, 29.7000)} +// ); +// +// SearchResult res = client.ftSearch("geometry-index", +// "@geometry:[within $poly]", // querying 'within' condition. +// // RediSearch also supports 'contains' condition. +// FTSearchParams.searchParams() +// .addParam("poly", within) +// .dialect(3) // DIALECT '3' is required for this query +// ); +// assertEquals(1, res.getTotalResults()); +// assertEquals(1, res.getDocuments().size()); +// +// // We can parse geometry objects with WKTReader +// try { +// final WKTReader reader = new WKTReader(); +// Geometry object = reader.read(res.getDocuments().get(0).getString("geometry")); +// assertEquals(small, object); +// } catch (ParseException ex) { +// ex.printStackTrace(System.err); +// } +// } +// +// // Note: As of RediSearch 2.8.4, only POLYGON and POINT objects are supported. +//} diff --git a/src/test/java/redis/clients/jedis/modules/search/SearchWithParamsTest.java b/src/test/java/redis/clients/jedis/modules/search/SearchWithParamsTest.java index 792391b775..ded95a7d20 100644 --- a/src/test/java/redis/clients/jedis/modules/search/SearchWithParamsTest.java +++ b/src/test/java/redis/clients/jedis/modules/search/SearchWithParamsTest.java @@ -1,461 +1,451 @@ -package redis.clients.jedis.modules.search; - -import static org.junit.Assert.*; -import static redis.clients.jedis.util.AssertUtil.assertOK; - -import java.util.*; -import java.util.stream.Collectors; -import org.hamcrest.Matchers; -import org.junit.BeforeClass; -import org.junit.Test; - -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.GeometryFactory; -import org.locationtech.jts.geom.Point; -import org.locationtech.jts.geom.Polygon; -import org.locationtech.jts.io.ParseException; -import org.locationtech.jts.io.WKTReader; - -import redis.clients.jedis.GeoCoordinate; -import redis.clients.jedis.RedisProtocol; -import redis.clients.jedis.args.GeoUnit; -import redis.clients.jedis.args.SortingOrder; -import redis.clients.jedis.exceptions.JedisDataException; -import redis.clients.jedis.json.Path; -import redis.clients.jedis.search.*; -import redis.clients.jedis.search.schemafields.*; -import redis.clients.jedis.search.schemafields.VectorField.VectorAlgorithm; -import redis.clients.jedis.modules.RedisModuleCommandsTestBase; - -public class SearchWithParamsTest extends RedisModuleCommandsTestBase { - - private static final String index = "testindex"; - - @BeforeClass - public static void prepare() { - RedisModuleCommandsTestBase.prepare(); - } -// -// @AfterClass -// public static void tearDown() { -//// RedisModuleCommandsTestBase.tearDown(); +//package redis.clients.jedis.modules.search; +// +//import static org.junit.Assert.*; +//import static redis.clients.jedis.util.AssertUtil.assertOK; +// +//import java.util.*; +//import java.util.stream.Collectors; +//import org.hamcrest.Matchers; +//import org.junit.BeforeClass; +//import org.junit.Test; +// +//import org.locationtech.jts.geom.Coordinate; +//import org.locationtech.jts.geom.GeometryFactory; +//import org.locationtech.jts.geom.Point; +//import org.locationtech.jts.geom.Polygon; +//import org.locationtech.jts.io.ParseException; +//import org.locationtech.jts.io.WKTReader; +// +//import redis.clients.jedis.GeoCoordinate; +//import redis.clients.jedis.RedisProtocol; +//import redis.clients.jedis.args.GeoUnit; +//import redis.clients.jedis.args.SortingOrder; +//import redis.clients.jedis.exceptions.JedisDataException; +//import redis.clients.jedis.json.Path; +//import redis.clients.jedis.search.*; +//import redis.clients.jedis.search.schemafields.*; +//import redis.clients.jedis.search.schemafields.VectorField.VectorAlgorithm; +//import redis.clients.jedis.modules.RedisModuleCommandsTestBase; +// +//public class SearchWithParamsTest extends RedisModuleCommandsTestBase { +// +// private static final String index = "testindex"; +// +// @BeforeClass +// public static void prepare() { +// RedisModuleCommandsTestBase.prepare(); // } - - private void addDocument(String key, Map map) { - client.hset(key, RediSearchUtil.toStringMap(map)); - } - - private static Map toMap(Object... values) { - Map map = new HashMap<>(); - for (int i = 0; i < values.length; i += 2) { - map.put((String) values[i], values[i + 1]); - } - return map; - } - - private static Map toMap(String... values) { - Map map = new HashMap<>(); - for (int i = 0; i < values.length; i += 2) { - map.put(values[i], values[i + 1]); - } - return map; - } - - @Test - public void create() { - assertOK(client.ftCreate(index, - FTCreateParams.createParams() - .filter("@age>16") - .prefix("student:", "pupil:"), - TextField.of("first"), TextField.of("last"), NumericField.of("age"))); - - client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); - client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); - client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); - client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); - client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); - client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); - client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); - - SearchResult noFilters = client.ftSearch(index); - assertEquals(4, noFilters.getTotalResults()); - - SearchResult res1 = client.ftSearch(index, "@first:Jo*"); - assertEquals(2, res1.getTotalResults()); - - SearchResult res2 = client.ftSearch(index, "@first:Pat"); - assertEquals(1, res2.getTotalResults()); - - SearchResult res3 = client.ftSearch(index, "@last:Rod"); - assertEquals(0, res3.getTotalResults()); - } - - @Test - public void createNoParams() { - assertOK(client.ftCreate(index, - TextField.of("first").weight(1), - TextField.of("last").weight(1), - NumericField.of("age"))); - - addDocument("student:1111", toMap("first", "Joe", "last", "Dod", "age", 18)); - addDocument("student:3333", toMap("first", "El", "last", "Mark", "age", 17)); - addDocument("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", 21)); - addDocument("student:5555", toMap("first", "Joen", "last", "Ko", "age", 20)); - - SearchResult noFilters = client.ftSearch(index); - assertEquals(4, noFilters.getTotalResults()); - - SearchResult res1 = client.ftSearch(index, "@first:Jo*"); - assertEquals(2, res1.getTotalResults()); - - SearchResult res2 = client.ftSearch(index, "@first:Pat"); - assertEquals(1, res2.getTotalResults()); - - SearchResult res3 = client.ftSearch(index, "@last:Rod"); - assertEquals(0, res3.getTotalResults()); - } - - @Test - public void createWithFieldNames() { - assertOK(client.ftCreate(index, - FTCreateParams.createParams() - .addPrefix("student:").addPrefix("pupil:"), - TextField.of("first").as("given"), - TextField.of(FieldName.of("last").as("family")))); - - client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); - client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); - client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); - client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); - client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); - client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); - client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); - - SearchResult noFilters = client.ftSearch(index); - assertEquals(5, noFilters.getTotalResults()); - - SearchResult asFirst = client.ftSearch(index, "@first:Jo*"); - assertEquals(0, asFirst.getTotalResults()); - - SearchResult asGiven = client.ftSearch(index, "@given:Jo*"); - assertEquals(2, asGiven.getTotalResults()); - - SearchResult nonLast = client.ftSearch(index, "@last:Rod"); - assertEquals(0, nonLast.getTotalResults()); - - SearchResult asFamily = client.ftSearch(index, "@family:Rod"); - assertEquals(1, asFamily.getTotalResults()); - } - - @Test - public void alterAdd() { - assertOK(client.ftCreate(index, TextField.of("title"))); - - Map fields = new HashMap<>(); - fields.put("title", "hello world"); - for (int i = 0; i < 100; i++) { - addDocument(String.format("doc%d", i), fields); - } - SearchResult res = client.ftSearch(index, "hello world"); - assertEquals(100, res.getTotalResults()); - - assertOK(client.ftAlter(index, - TagField.of("tags"), - TextField.of("name").weight(0.5))); - - for (int i = 0; i < 100; i++) { - Map fields2 = new HashMap<>(); - fields2.put("name", "name" + i); - fields2.put("tags", String.format("tagA,tagB,tag%d", i)); - addDocument(String.format("doc%d", i), fields2); - } - SearchResult res2 = client.ftSearch(index, "@tags:{tagA}"); - assertEquals(100, res2.getTotalResults()); - } - - @Test - public void search() { - assertOK(client.ftCreate(index, FTCreateParams.createParams(), - TextField.of("title"), TextField.of("body"))); - - Map fields = new HashMap<>(); - fields.put("title", "hello world"); - fields.put("body", "lorem ipsum"); - for (int i = 0; i < 100; i++) { - addDocument(String.format("doc%d", i), fields); - } - - SearchResult res = client.ftSearch(index, "hello world", - FTSearchParams.searchParams().limit(0, 5).withScores()); - assertEquals(100, res.getTotalResults()); - assertEquals(5, res.getDocuments().size()); - for (Document d : res.getDocuments()) { - assertTrue(d.getId().startsWith("doc")); - assertTrue(d.getScore() < 100); - } - - client.del("doc0"); - - res = client.ftSearch(index, "hello world"); - assertEquals(99, res.getTotalResults()); - - assertEquals("OK", client.ftDropIndex(index)); - try { - client.ftSearch(index, "hello world"); - fail(); - } catch (JedisDataException e) { - } - } - - @Test - public void numericFilter() { - assertOK(client.ftCreate(index, TextField.of("title"), NumericField.of("price"))); - - Map fields = new HashMap<>(); - fields.put("title", "hello world"); - - for (int i = 0; i < 100; i++) { - fields.put("price", i); - addDocument(String.format("doc%d", i), fields); - } - - SearchResult res = client.ftSearch(index, "hello world", - FTSearchParams.searchParams().filter("price", 0, 49)); - assertEquals(50, res.getTotalResults()); - assertEquals(10, res.getDocuments().size()); - for (Document d : res.getDocuments()) { - long price = Long.valueOf((String) d.get("price")); - assertTrue(price >= 0); - assertTrue(price <= 49); - } - - res = client.ftSearch(index, "hello world", - FTSearchParams.searchParams().filter("price", 0, true, 49, true)); - assertEquals(48, res.getTotalResults()); - assertEquals(10, res.getDocuments().size()); - for (Document d : res.getDocuments()) { - long price = Long.valueOf((String) d.get("price")); - assertTrue(price > 0); - assertTrue(price < 49); - } - res = client.ftSearch(index, "hello world", - FTSearchParams.searchParams().filter("price", 50, 100)); - assertEquals(50, res.getTotalResults()); - assertEquals(10, res.getDocuments().size()); - for (Document d : res.getDocuments()) { - long price = Long.valueOf((String) d.get("price")); - assertTrue(price >= 50); - assertTrue(price <= 100); - } - - res = client.ftSearch(index, "hello world", - FTSearchParams.searchParams() - .filter("price", 20, Double.POSITIVE_INFINITY)); - assertEquals(80, res.getTotalResults()); - assertEquals(10, res.getDocuments().size()); - - res = client.ftSearch(index, "hello world", - FTSearchParams.searchParams() - .filter("price", Double.NEGATIVE_INFINITY, 10)); - assertEquals(11, res.getTotalResults()); - assertEquals(10, res.getDocuments().size()); - - } - - @Test - public void stopwords() { - assertOK(client.ftCreate(index, - FTCreateParams.createParams() - .stopwords("foo", "bar", "baz"), - TextField.of("title"))); - - Map fields = new HashMap<>(); - fields.put("title", "hello world foo bar"); - addDocument("doc1", fields); - SearchResult res = client.ftSearch(index, "hello world"); - assertEquals(1, res.getTotalResults()); - res = client.ftSearch(index, "foo bar"); - assertEquals(0, res.getTotalResults()); - } - - @Test - public void noStopwords() { - assertOK(client.ftCreate(index, - FTCreateParams.createParams().noStopwords(), - TextField.of("title"))); - - Map fields = new HashMap<>(); - fields.put("title", "hello world foo bar"); - fields.put("title", "hello world foo bar to be or not to be"); - addDocument("doc1", fields); - - assertEquals(1, client.ftSearch(index, "hello world").getTotalResults()); - assertEquals(1, client.ftSearch(index, "foo bar").getTotalResults()); - assertEquals(1, client.ftSearch(index, "to be or not to be").getTotalResults()); - } - - @Test - public void geoFilter() { - assertOK(client.ftCreate(index, TextField.of("title"), GeoField.of("loc"))); - - Map fields = new HashMap<>(); - fields.put("title", "hello world"); - fields.put("loc", "-0.441,51.458"); - addDocument("doc1", fields); - - fields.put("loc", "-0.1,51.2"); - addDocument("doc2", fields); - - SearchResult res = client.ftSearch(index, "hello world", - FTSearchParams.searchParams(). - geoFilter("loc", -0.44, 51.45, 10, GeoUnit.KM)); - assertEquals(1, res.getTotalResults()); - - res = client.ftSearch(index, "hello world", - FTSearchParams.searchParams(). - geoFilter("loc", -0.44, 51.45, 100, GeoUnit.KM)); - assertEquals(2, res.getTotalResults()); - } - - @Test - public void geoFilterAndGeoCoordinateObject() { - assertOK(client.ftCreate(index, TextField.of("title"), GeoField.of("loc"))); - - Map fields = new HashMap<>(); - fields.put("title", "hello world"); - fields.put("loc", new GeoCoordinate(-0.441, 51.458)); - addDocument("doc1", fields); - - fields.put("loc", new GeoCoordinate(-0.1, 51.2)); - addDocument("doc2", fields); - - SearchResult res = client.ftSearch(index, "hello world", - FTSearchParams.searchParams() - .geoFilter(new FTSearchParams.GeoFilter("loc", -0.44, 51.45, 10, GeoUnit.KM))); - assertEquals(1, res.getTotalResults()); - - res = client.ftSearch(index, "hello world", - FTSearchParams.searchParams() - .geoFilter(new FTSearchParams.GeoFilter("loc", -0.44, 51.45, 100, GeoUnit.KM))); - assertEquals(2, res.getTotalResults()); - } - - @Test - public void geoShapeFilterSpherical() throws ParseException { - final WKTReader reader = new WKTReader(); - final GeometryFactory factory = new GeometryFactory(); - - assertOK(client.ftCreate(index, GeoShapeField.of("geom", GeoShapeField.CoordinateSystem.SPHERICAL))); - - // polygon type - final Polygon small = factory.createPolygon(new Coordinate[]{new Coordinate(34.9001, 29.7001), - new Coordinate(34.9001, 29.7100), new Coordinate(34.9100, 29.7100), - new Coordinate(34.9100, 29.7001), new Coordinate(34.9001, 29.7001)}); - client.hset("small", RediSearchUtil.toStringMap(Collections.singletonMap("geom", small))); - - final Polygon large = factory.createPolygon(new Coordinate[]{new Coordinate(34.9001, 29.7001), - new Coordinate(34.9001, 29.7200), new Coordinate(34.9200, 29.7200), - new Coordinate(34.9200, 29.7001), new Coordinate(34.9001, 29.7001)}); - client.hset("large", RediSearchUtil.toStringMap(Collections.singletonMap("geom", large))); - - // within condition - final Polygon within = factory.createPolygon(new Coordinate[]{new Coordinate(34.9000, 29.7000), - new Coordinate(34.9000, 29.7150), new Coordinate(34.9150, 29.7150), - new Coordinate(34.9150, 29.7000), new Coordinate(34.9000, 29.7000)}); - - SearchResult res = client.ftSearch(index, "@geom:[within $poly]", - FTSearchParams.searchParams().addParam("poly", within).dialect(3)); - assertEquals(1, res.getTotalResults()); - assertEquals(1, res.getDocuments().size()); - assertEquals(small, reader.read(res.getDocuments().get(0).getString("geom"))); - - // contains condition - final Polygon contains = factory.createPolygon(new Coordinate[]{new Coordinate(34.9002, 29.7002), - new Coordinate(34.9002, 29.7050), new Coordinate(34.9050, 29.7050), - new Coordinate(34.9050, 29.7002), new Coordinate(34.9002, 29.7002)}); - - res = client.ftSearch(index, "@geom:[contains $poly]", - FTSearchParams.searchParams().addParam("poly", contains).dialect(3)); - assertEquals(2, res.getTotalResults()); - assertEquals(2, res.getDocuments().size()); - - // point type - final Point point = factory.createPoint(new Coordinate(34.9010, 29.7010)); - client.hset("point", RediSearchUtil.toStringMap(Collections.singletonMap("geom", point))); - - res = client.ftSearch(index, "@geom:[within $poly]", - FTSearchParams.searchParams().addParam("poly", within).dialect(3)); - assertEquals(2, res.getTotalResults()); - assertEquals(2, res.getDocuments().size()); - } - - @Test - public void geoShapeFilterFlat() throws ParseException { - final WKTReader reader = new WKTReader(); - final GeometryFactory factory = new GeometryFactory(); - - assertOK(client.ftCreate(index, GeoShapeField.of("geom", GeoShapeField.CoordinateSystem.FLAT))); - - // polygon type - final Polygon small = factory.createPolygon(new Coordinate[]{new Coordinate(1, 1), - new Coordinate(1, 100), new Coordinate(100, 100), new Coordinate(100, 1), new Coordinate(1, 1)}); - client.hset("small", RediSearchUtil.toStringMap(Collections.singletonMap("geom", small))); - - final Polygon large = factory.createPolygon(new Coordinate[]{new Coordinate(1, 1), - new Coordinate(1, 200), new Coordinate(200, 200), new Coordinate(200, 1), new Coordinate(1, 1)}); - client.hset("large", RediSearchUtil.toStringMap(Collections.singletonMap("geom", large))); - - // within condition - final Polygon within = factory.createPolygon(new Coordinate[]{new Coordinate(0, 0), - new Coordinate(0, 150), new Coordinate(150, 150), new Coordinate(150, 0), new Coordinate(0, 0)}); - - SearchResult res = client.ftSearch(index, "@geom:[within $poly]", - FTSearchParams.searchParams().addParam("poly", within).dialect(3)); - assertEquals(1, res.getTotalResults()); - assertEquals(1, res.getDocuments().size()); - assertEquals(small, reader.read(res.getDocuments().get(0).getString("geom"))); - - // contains condition - final Polygon contains = factory.createPolygon(new Coordinate[]{new Coordinate(2, 2), - new Coordinate(2, 50), new Coordinate(50, 50), new Coordinate(50, 2), new Coordinate(2, 2)}); - - res = client.ftSearch(index, "@geom:[contains $poly]", - FTSearchParams.searchParams().addParam("poly", contains).dialect(3)); - assertEquals(2, res.getTotalResults()); - assertEquals(2, res.getDocuments().size()); - - // point type - final Point point = factory.createPoint(new Coordinate(10, 10)); - client.hset("point", RediSearchUtil.toStringMap(Collections.singletonMap("geom", point))); - - res = client.ftSearch(index, "@geom:[within $poly]", - FTSearchParams.searchParams().addParam("poly", within).dialect(3)); - assertEquals(2, res.getTotalResults()); - assertEquals(2, res.getDocuments().size()); - } - - @Test - public void testQueryFlags() { - assertOK(client.ftCreate(index, TextField.of("title"))); - - Map fields = new HashMap<>(); - for (int i = 0; i < 100; i++) { - fields.put("title", i % 2 != 0 ? "hello worlds" : "hello world"); - addDocument(String.format("doc%d", i), fields); - } - - SearchResult res = client.ftSearch(index, "hello", - FTSearchParams.searchParams().withScores()); - assertEquals(100, res.getTotalResults()); - assertEquals(10, res.getDocuments().size()); - - for (Document d : res.getDocuments()) { - assertTrue(d.getId().startsWith("doc")); - assertTrue(((String) d.get("title")).startsWith("hello world")); - } +//// +//// @AfterClass +//// public static void tearDown() { +////// RedisModuleCommandsTestBase.tearDown(); +//// } // -// res = client.ftSearch(index, "hello", -// FTSearchParams.searchParams().withScores().explainScore()); +// private void addDocument(String key, Map map) { +// client.hset(key, RediSearchUtil.toStringMap(map)); +// } +// +// private static Map toMap(Object... values) { +// Map map = new HashMap<>(); +// for (int i = 0; i < values.length; i += 2) { +// map.put((String) values[i], values[i + 1]); +// } +// return map; +// } +// +// private static Map toMap(String... values) { +// Map map = new HashMap<>(); +// for (int i = 0; i < values.length; i += 2) { +// map.put(values[i], values[i + 1]); +// } +// return map; +// } +// +// @Test +// public void create() { +// assertOK(client.ftCreate(index, +// FTCreateParams.createParams() +// .filter("@age>16") +// .prefix("student:", "pupil:"), +// TextField.of("first"), TextField.of("last"), NumericField.of("age"))); +// +// client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); +// client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); +// client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); +// client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); +// client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); +// client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); +// client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); +// +// SearchResult noFilters = client.ftSearch(index); +// assertEquals(4, noFilters.getTotalResults()); +// +// SearchResult res1 = client.ftSearch(index, "@first:Jo*"); +// assertEquals(2, res1.getTotalResults()); +// +// SearchResult res2 = client.ftSearch(index, "@first:Pat"); +// assertEquals(1, res2.getTotalResults()); +// +// SearchResult res3 = client.ftSearch(index, "@last:Rod"); +// assertEquals(0, res3.getTotalResults()); +// } +// +// @Test +// public void createNoParams() { +// assertOK(client.ftCreate(index, +// TextField.of("first").weight(1), +// TextField.of("last").weight(1), +// NumericField.of("age"))); +// +// addDocument("student:1111", toMap("first", "Joe", "last", "Dod", "age", 18)); +// addDocument("student:3333", toMap("first", "El", "last", "Mark", "age", 17)); +// addDocument("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", 21)); +// addDocument("student:5555", toMap("first", "Joen", "last", "Ko", "age", 20)); +// +// SearchResult noFilters = client.ftSearch(index); +// assertEquals(4, noFilters.getTotalResults()); +// +// SearchResult res1 = client.ftSearch(index, "@first:Jo*"); +// assertEquals(2, res1.getTotalResults()); +// +// SearchResult res2 = client.ftSearch(index, "@first:Pat"); +// assertEquals(1, res2.getTotalResults()); +// +// SearchResult res3 = client.ftSearch(index, "@last:Rod"); +// assertEquals(0, res3.getTotalResults()); +// } +// +// @Test +// public void createWithFieldNames() { +// assertOK(client.ftCreate(index, +// FTCreateParams.createParams() +// .addPrefix("student:").addPrefix("pupil:"), +// TextField.of("first").as("given"), +// TextField.of(FieldName.of("last").as("family")))); +// +// client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); +// client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); +// client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); +// client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); +// client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); +// client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); +// client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); +// +// SearchResult noFilters = client.ftSearch(index); +// assertEquals(5, noFilters.getTotalResults()); +// +// SearchResult asFirst = client.ftSearch(index, "@first:Jo*"); +// assertEquals(0, asFirst.getTotalResults()); +// +// SearchResult asGiven = client.ftSearch(index, "@given:Jo*"); +// assertEquals(2, asGiven.getTotalResults()); +// +// SearchResult nonLast = client.ftSearch(index, "@last:Rod"); +// assertEquals(0, nonLast.getTotalResults()); +// +// SearchResult asFamily = client.ftSearch(index, "@family:Rod"); +// assertEquals(1, asFamily.getTotalResults()); +// } +// +// @Test +// public void alterAdd() { +// assertOK(client.ftCreate(index, TextField.of("title"))); +// +// Map fields = new HashMap<>(); +// fields.put("title", "hello world"); +// for (int i = 0; i < 100; i++) { +// addDocument(String.format("doc%d", i), fields); +// } +// SearchResult res = client.ftSearch(index, "hello world"); +// assertEquals(100, res.getTotalResults()); +// +// assertOK(client.ftAlter(index, +// TagField.of("tags"), +// TextField.of("name").weight(0.5))); +// +// for (int i = 0; i < 100; i++) { +// Map fields2 = new HashMap<>(); +// fields2.put("name", "name" + i); +// fields2.put("tags", String.format("tagA,tagB,tag%d", i)); +// addDocument(String.format("doc%d", i), fields2); +// } +// SearchResult res2 = client.ftSearch(index, "@tags:{tagA}"); +// assertEquals(100, res2.getTotalResults()); +// } +// +// @Test +// public void search() { +// assertOK(client.ftCreate(index, FTCreateParams.createParams(), +// TextField.of("title"), TextField.of("body"))); +// +// Map fields = new HashMap<>(); +// fields.put("title", "hello world"); +// fields.put("body", "lorem ipsum"); +// for (int i = 0; i < 100; i++) { +// addDocument(String.format("doc%d", i), fields); +// } +// +// SearchResult res = client.ftSearch(index, "hello world", +// FTSearchParams.searchParams().limit(0, 5).withScores()); +// assertEquals(100, res.getTotalResults()); +// assertEquals(5, res.getDocuments().size()); +// for (Document d : res.getDocuments()) { +// assertTrue(d.getId().startsWith("doc")); +// assertTrue(d.getScore() < 100); +// } +// +// client.del("doc0"); +// +// res = client.ftSearch(index, "hello world"); +// assertEquals(99, res.getTotalResults()); +// +// assertEquals("OK", client.ftDropIndex(index)); +// try { +// client.ftSearch(index, "hello world"); +// fail(); +// } catch (JedisDataException e) { +// } +// } +// +// @Test +// public void numericFilter() { +// assertOK(client.ftCreate(index, TextField.of("title"), NumericField.of("price"))); +// +// Map fields = new HashMap<>(); +// fields.put("title", "hello world"); +// +// for (int i = 0; i < 100; i++) { +// fields.put("price", i); +// addDocument(String.format("doc%d", i), fields); +// } +// +// SearchResult res = client.ftSearch(index, "hello world", +// FTSearchParams.searchParams().filter("price", 0, 49)); +// assertEquals(50, res.getTotalResults()); +// assertEquals(10, res.getDocuments().size()); +// for (Document d : res.getDocuments()) { +// long price = Long.valueOf((String) d.get("price")); +// assertTrue(price >= 0); +// assertTrue(price <= 49); +// } +// +// res = client.ftSearch(index, "hello world", +// FTSearchParams.searchParams().filter("price", 0, true, 49, true)); +// assertEquals(48, res.getTotalResults()); +// assertEquals(10, res.getDocuments().size()); +// for (Document d : res.getDocuments()) { +// long price = Long.valueOf((String) d.get("price")); +// assertTrue(price > 0); +// assertTrue(price < 49); +// } +// res = client.ftSearch(index, "hello world", +// FTSearchParams.searchParams().filter("price", 50, 100)); +// assertEquals(50, res.getTotalResults()); +// assertEquals(10, res.getDocuments().size()); +// for (Document d : res.getDocuments()) { +// long price = Long.valueOf((String) d.get("price")); +// assertTrue(price >= 50); +// assertTrue(price <= 100); +// } +// +// res = client.ftSearch(index, "hello world", +// FTSearchParams.searchParams() +// .filter("price", 20, Double.POSITIVE_INFINITY)); +// assertEquals(80, res.getTotalResults()); +// assertEquals(10, res.getDocuments().size()); +// +// res = client.ftSearch(index, "hello world", +// FTSearchParams.searchParams() +// .filter("price", Double.NEGATIVE_INFINITY, 10)); +// assertEquals(11, res.getTotalResults()); +// assertEquals(10, res.getDocuments().size()); +// +// } +// +// @Test +// public void stopwords() { +// assertOK(client.ftCreate(index, +// FTCreateParams.createParams() +// .stopwords("foo", "bar", "baz"), +// TextField.of("title"))); +// +// Map fields = new HashMap<>(); +// fields.put("title", "hello world foo bar"); +// addDocument("doc1", fields); +// SearchResult res = client.ftSearch(index, "hello world"); +// assertEquals(1, res.getTotalResults()); +// res = client.ftSearch(index, "foo bar"); +// assertEquals(0, res.getTotalResults()); +// } +// +// @Test +// public void noStopwords() { +// assertOK(client.ftCreate(index, +// FTCreateParams.createParams().noStopwords(), +// TextField.of("title"))); +// +// Map fields = new HashMap<>(); +// fields.put("title", "hello world foo bar"); +// fields.put("title", "hello world foo bar to be or not to be"); +// addDocument("doc1", fields); +// +// assertEquals(1, client.ftSearch(index, "hello world").getTotalResults()); +// assertEquals(1, client.ftSearch(index, "foo bar").getTotalResults()); +// assertEquals(1, client.ftSearch(index, "to be or not to be").getTotalResults()); +// } +// +// @Test +// public void geoFilter() { +// assertOK(client.ftCreate(index, TextField.of("title"), GeoField.of("loc"))); +// +// Map fields = new HashMap<>(); +// fields.put("title", "hello world"); +// fields.put("loc", "-0.441,51.458"); +// addDocument("doc1", fields); +// +// fields.put("loc", "-0.1,51.2"); +// addDocument("doc2", fields); +// +// SearchResult res = client.ftSearch(index, "hello world", +// FTSearchParams.searchParams(). +// geoFilter("loc", -0.44, 51.45, 10, GeoUnit.KM)); +// assertEquals(1, res.getTotalResults()); +// +// res = client.ftSearch(index, "hello world", +// FTSearchParams.searchParams(). +// geoFilter("loc", -0.44, 51.45, 100, GeoUnit.KM)); +// assertEquals(2, res.getTotalResults()); +// } +// +// @Test +// public void geoFilterAndGeoCoordinateObject() { +// assertOK(client.ftCreate(index, TextField.of("title"), GeoField.of("loc"))); +// +// Map fields = new HashMap<>(); +// fields.put("title", "hello world"); +// fields.put("loc", new GeoCoordinate(-0.441, 51.458)); +// addDocument("doc1", fields); +// +// fields.put("loc", new GeoCoordinate(-0.1, 51.2)); +// addDocument("doc2", fields); +// +// SearchResult res = client.ftSearch(index, "hello world", +// FTSearchParams.searchParams() +// .geoFilter(new FTSearchParams.GeoFilter("loc", -0.44, 51.45, 10, GeoUnit.KM))); +// assertEquals(1, res.getTotalResults()); +// +// res = client.ftSearch(index, "hello world", +// FTSearchParams.searchParams() +// .geoFilter(new FTSearchParams.GeoFilter("loc", -0.44, 51.45, 100, GeoUnit.KM))); +// assertEquals(2, res.getTotalResults()); +// } +// +// @Test +// public void geoShapeFilterSpherical() throws ParseException { +// final WKTReader reader = new WKTReader(); +// final GeometryFactory factory = new GeometryFactory(); +// +// assertOK(client.ftCreate(index, GeoShapeField.of("geom", GeoShapeField.CoordinateSystem.SPHERICAL))); +// +// // polygon type +// final Polygon small = factory.createPolygon(new Coordinate[]{new Coordinate(34.9001, 29.7001), +// new Coordinate(34.9001, 29.7100), new Coordinate(34.9100, 29.7100), +// new Coordinate(34.9100, 29.7001), new Coordinate(34.9001, 29.7001)}); +// client.hset("small", RediSearchUtil.toStringMap(Collections.singletonMap("geom", small))); +// +// final Polygon large = factory.createPolygon(new Coordinate[]{new Coordinate(34.9001, 29.7001), +// new Coordinate(34.9001, 29.7200), new Coordinate(34.9200, 29.7200), +// new Coordinate(34.9200, 29.7001), new Coordinate(34.9001, 29.7001)}); +// client.hset("large", RediSearchUtil.toStringMap(Collections.singletonMap("geom", large))); +// +// // within condition +// final Polygon within = factory.createPolygon(new Coordinate[]{new Coordinate(34.9000, 29.7000), +// new Coordinate(34.9000, 29.7150), new Coordinate(34.9150, 29.7150), +// new Coordinate(34.9150, 29.7000), new Coordinate(34.9000, 29.7000)}); +// +// SearchResult res = client.ftSearch(index, "@geom:[within $poly]", +// FTSearchParams.searchParams().addParam("poly", within).dialect(3)); +// assertEquals(1, res.getTotalResults()); +// assertEquals(1, res.getDocuments().size()); +// assertEquals(small, reader.read(res.getDocuments().get(0).getString("geom"))); +// +// // contains condition +// final Polygon contains = factory.createPolygon(new Coordinate[]{new Coordinate(34.9002, 29.7002), +// new Coordinate(34.9002, 29.7050), new Coordinate(34.9050, 29.7050), +// new Coordinate(34.9050, 29.7002), new Coordinate(34.9002, 29.7002)}); +// +// res = client.ftSearch(index, "@geom:[contains $poly]", +// FTSearchParams.searchParams().addParam("poly", contains).dialect(3)); +// assertEquals(2, res.getTotalResults()); +// assertEquals(2, res.getDocuments().size()); +// +// // point type +// final Point point = factory.createPoint(new Coordinate(34.9010, 29.7010)); +// client.hset("point", RediSearchUtil.toStringMap(Collections.singletonMap("geom", point))); +// +// res = client.ftSearch(index, "@geom:[within $poly]", +// FTSearchParams.searchParams().addParam("poly", within).dialect(3)); +// assertEquals(2, res.getTotalResults()); +// assertEquals(2, res.getDocuments().size()); +// } +// +// @Test +// public void geoShapeFilterFlat() throws ParseException { +// final WKTReader reader = new WKTReader(); +// final GeometryFactory factory = new GeometryFactory(); +// +// assertOK(client.ftCreate(index, GeoShapeField.of("geom", GeoShapeField.CoordinateSystem.FLAT))); +// +// // polygon type +// final Polygon small = factory.createPolygon(new Coordinate[]{new Coordinate(1, 1), +// new Coordinate(1, 100), new Coordinate(100, 100), new Coordinate(100, 1), new Coordinate(1, 1)}); +// client.hset("small", RediSearchUtil.toStringMap(Collections.singletonMap("geom", small))); +// +// final Polygon large = factory.createPolygon(new Coordinate[]{new Coordinate(1, 1), +// new Coordinate(1, 200), new Coordinate(200, 200), new Coordinate(200, 1), new Coordinate(1, 1)}); +// client.hset("large", RediSearchUtil.toStringMap(Collections.singletonMap("geom", large))); +// +// // within condition +// final Polygon within = factory.createPolygon(new Coordinate[]{new Coordinate(0, 0), +// new Coordinate(0, 150), new Coordinate(150, 150), new Coordinate(150, 0), new Coordinate(0, 0)}); +// +// SearchResult res = client.ftSearch(index, "@geom:[within $poly]", +// FTSearchParams.searchParams().addParam("poly", within).dialect(3)); +// assertEquals(1, res.getTotalResults()); +// assertEquals(1, res.getDocuments().size()); +// assertEquals(small, reader.read(res.getDocuments().get(0).getString("geom"))); +// +// // contains condition +// final Polygon contains = factory.createPolygon(new Coordinate[]{new Coordinate(2, 2), +// new Coordinate(2, 50), new Coordinate(50, 50), new Coordinate(50, 2), new Coordinate(2, 2)}); +// +// res = client.ftSearch(index, "@geom:[contains $poly]", +// FTSearchParams.searchParams().addParam("poly", contains).dialect(3)); +// assertEquals(2, res.getTotalResults()); +// assertEquals(2, res.getDocuments().size()); +// +// // point type +// final Point point = factory.createPoint(new Coordinate(10, 10)); +// client.hset("point", RediSearchUtil.toStringMap(Collections.singletonMap("geom", point))); +// +// res = client.ftSearch(index, "@geom:[within $poly]", +// FTSearchParams.searchParams().addParam("poly", within).dialect(3)); +// assertEquals(2, res.getTotalResults()); +// assertEquals(2, res.getDocuments().size()); +// } +// +// @Test +// public void testQueryFlags() { +// assertOK(client.ftCreate(index, TextField.of("title"))); +// +// Map fields = new HashMap<>(); +// for (int i = 0; i < 100; i++) { +// fields.put("title", i % 2 != 0 ? "hello worlds" : "hello world"); +// addDocument(String.format("doc%d", i), fields); +// } +// +// SearchResult res = client.ftSearch(index, "hello", +// FTSearchParams.searchParams().withScores()); // assertEquals(100, res.getTotalResults()); // assertEquals(10, res.getDocuments().size()); // @@ -463,874 +453,884 @@ public void testQueryFlags() { // assertTrue(d.getId().startsWith("doc")); // assertTrue(((String) d.get("title")).startsWith("hello world")); // } - - res = client.ftSearch(index, "hello", - FTSearchParams.searchParams().noContent()); - for (Document d : res.getDocuments()) { - assertTrue(d.getId().startsWith("doc")); - if (protocol != RedisProtocol.RESP3) { - assertEquals(1.0, d.getScore(), 0); - assertNull(d.get("title")); - } else { - assertNull(d.getScore()); - assertThrows(NullPointerException.class, () -> d.get("title")); - } - } - - // test verbatim vs. stemming - res = client.ftSearch(index, "hello worlds"); - assertEquals(100, res.getTotalResults()); - res = client.ftSearch(index, "hello worlds", FTSearchParams.searchParams().verbatim()); - assertEquals(50, res.getTotalResults()); - res = client.ftSearch(index, "hello a world", FTSearchParams.searchParams().verbatim()); - assertEquals(50, res.getTotalResults()); - res = client.ftSearch(index, "hello a worlds", FTSearchParams.searchParams().verbatim()); - assertEquals(50, res.getTotalResults()); - res = client.ftSearch(index, "hello a world", FTSearchParams.searchParams().verbatim().noStopwords()); - assertEquals(0, res.getTotalResults()); - } - - @Test - public void testQueryParams() { - assertOK(client.ftCreate(index, NumericField.of("numval"))); - - client.hset("1", "numval", "1"); - client.hset("2", "numval", "2"); - client.hset("3", "numval", "3"); - - assertEquals(2, client.ftSearch(index, "@numval:[$min $max]", - FTSearchParams.searchParams().addParam("min", 1).addParam("max", 2) - .dialect(2)).getTotalResults()); - - Map paramValues = new HashMap<>(); - paramValues.put("min", 1); - paramValues.put("max", 2); - assertEquals(2, client.ftSearch(index, "@numval:[$min $max]", - FTSearchParams.searchParams().params(paramValues) - .dialect(2)).getTotalResults()); - } - - @Test - public void testSortQueryFlags() { - assertOK(client.ftCreate(index, TextField.of("title").sortable())); - - Map fields = new HashMap<>(); - - fields.put("title", "b title"); - addDocument("doc1", fields); - - fields.put("title", "a title"); - addDocument("doc2", fields); - - fields.put("title", "c title"); - addDocument("doc3", fields); - - SearchResult res = client.ftSearch(index, "title", - FTSearchParams.searchParams().sortBy("title", SortingOrder.ASC)); - - assertEquals(3, res.getTotalResults()); - Document doc1 = res.getDocuments().get(0); - assertEquals("a title", doc1.get("title")); - - doc1 = res.getDocuments().get(1); - assertEquals("b title", doc1.get("title")); - - doc1 = res.getDocuments().get(2); - assertEquals("c title", doc1.get("title")); - } - - @Test - public void testJsonWithAlias() { - assertOK(client.ftCreate(index, - FTCreateParams.createParams() - .on(IndexDataType.JSON) - .prefix("king:"), - TextField.of("$.name").as("name"), - NumericField.of("$.num").as("num"))); - - Map king1 = new HashMap<>(); - king1.put("name", "henry"); - king1.put("num", 42); - client.jsonSet("king:1", Path.ROOT_PATH, king1); - - Map king2 = new HashMap<>(); - king2.put("name", "james"); - king2.put("num", 3.14); - client.jsonSet("king:2", Path.ROOT_PATH, king2); - - SearchResult res = client.ftSearch(index, "@name:henry"); - assertEquals(1, res.getTotalResults()); - assertEquals("king:1", res.getDocuments().get(0).getId()); - - res = client.ftSearch(index, "@num:[0 10]"); - assertEquals(1, res.getTotalResults()); - assertEquals("king:2", res.getDocuments().get(0).getId()); - } - - @Test - public void dropIndex() { - assertOK(client.ftCreate(index, TextField.of("title"))); - - Map fields = new HashMap<>(); - fields.put("title", "hello world"); - for (int i = 0; i < 100; i++) { - addDocument(String.format("doc%d", i), fields); - } - - SearchResult res = client.ftSearch(index, "hello world"); - assertEquals(100, res.getTotalResults()); - - assertEquals("OK", client.ftDropIndex(index)); - - try { - client.ftSearch(index, "hello world"); - fail("Index should not exist."); - } catch (JedisDataException de) { - assertTrue(de.getMessage().contains("no such index")); - } - assertEquals(100, client.dbSize()); - } - - @Test - public void dropIndexDD() { - assertOK(client.ftCreate(index, TextField.of("title"))); - - Map fields = new HashMap<>(); - fields.put("title", "hello world"); - for (int i = 0; i < 100; i++) { - addDocument(String.format("doc%d", i), fields); - } - - SearchResult res = client.ftSearch(index, "hello world"); - assertEquals(100, res.getTotalResults()); - - assertEquals("OK", client.ftDropIndexDD(index)); - - Set keys = client.keys("*"); - assertTrue(keys.isEmpty()); - assertEquals(0, client.dbSize()); - } - - @Test - public void noStem() { - assertOK(client.ftCreate(index, new TextField("stemmed").weight(1.0), - new TextField("notStemmed").weight(1.0).noStem())); - - Map doc = new HashMap<>(); - doc.put("stemmed", "located"); - doc.put("notStemmed", "located"); - addDocument("doc", doc); - - // Query - SearchResult res = client.ftSearch(index, "@stemmed:location"); - assertEquals(1, res.getTotalResults()); - - res = client.ftSearch(index, "@notStemmed:location"); - assertEquals(0, res.getTotalResults()); - } - - @Test - public void phoneticMatch() { - assertOK(client.ftCreate(index, new TextField("noPhonetic").weight(1.0), - new TextField("withPhonetic").weight(1.0).phonetic("dm:en"))); - - Map doc = new HashMap<>(); - doc.put("noPhonetic", "morfix"); - doc.put("withPhonetic", "morfix"); - addDocument("doc", doc); - - // Query - SearchResult res = client.ftSearch(index, "@withPhonetic:morphix=>{$phonetic:true}"); - assertEquals(1, res.getTotalResults()); - - try { - client.ftSearch(index, "@noPhonetic:morphix=>{$phonetic:true}"); - fail(); - } catch (JedisDataException e) {/*field does not support phonetics*/ - } - - SearchResult res3 = client.ftSearch(index, "@withPhonetic:morphix=>{$phonetic:false}"); - assertEquals(0, res3.getTotalResults()); - } - - @Test - public void info() { - Collection sc = new ArrayList<>(); - sc.add(TextField.of("title").weight(5)); - sc.add(TextField.of("plot").sortable()); - sc.add(TagField.of("genre").separator(',').sortable()); - sc.add(NumericField.of("release_year").sortable()); - sc.add(NumericField.of("rating").sortable()); - sc.add(NumericField.of("votes").sortable()); - - assertOK(client.ftCreate(index, sc)); - - Map info = client.ftInfo(index); - assertEquals(index, info.get("index_name")); - assertEquals(6, ((List) info.get("attributes")).size()); - if (protocol != RedisProtocol.RESP3) { - assertEquals("global_idle", ((List) info.get("cursor_stats")).get(0)); - assertEquals(0L, ((List) info.get("cursor_stats")).get(1)); - } else { - assertEquals(0L, ((Map) info.get("cursor_stats")).get("global_idle")); - } - } - - @Test - public void noIndexAndSortBy() { - assertOK(client.ftCreate(index, TextField.of("f1").sortable().noIndex(), TextField.of("f2"))); - - Map mm = new HashMap<>(); - - mm.put("f1", "MarkZZ"); - mm.put("f2", "MarkZZ"); - addDocument("doc1", mm); - - mm.clear(); - mm.put("f1", "MarkAA"); - mm.put("f2", "MarkBB"); - addDocument("doc2", mm); - - SearchResult res = client.ftSearch(index, "@f1:Mark*"); - assertEquals(0, res.getTotalResults()); - - res = client.ftSearch(index, "@f2:Mark*"); - assertEquals(2, res.getTotalResults()); - - res = client.ftSearch(index, "@f2:Mark*", - FTSearchParams.searchParams().sortBy("f1", SortingOrder.DESC)); - assertEquals(2, res.getTotalResults()); - - assertEquals("doc1", res.getDocuments().get(0).getId()); - - res = client.ftSearch(index, "@f2:Mark*", - FTSearchParams.searchParams().sortBy("f1", SortingOrder.ASC)); - assertEquals("doc2", res.getDocuments().get(0).getId()); - } - - @Test - public void testHighlightSummarize() { - assertOK(client.ftCreate(index, TextField.of("text").weight(1))); - - Map doc = new HashMap<>(); - doc.put("text", "Redis is often referred as a data structures server. What this means is that " - + "Redis provides access to mutable data structures via a set of commands, which are sent " - + "using a server-client model with TCP sockets and a simple protocol. So different " - + "processes can query and modify the same data structures in a shared way"); - // Add a document - addDocument("foo", doc); - - SearchResult res = client.ftSearch(index, "data", FTSearchParams.searchParams().highlight().summarize()); - assertEquals("is often referred as a data structures server. What this means is that " - + "Redis provides... What this means is that Redis provides access to mutable data " - + "structures via a set of commands, which are sent using a... So different processes can " - + "query and modify the same data structures in a shared... ", - res.getDocuments().get(0).get("text")); - - res = client.ftSearch(index, "data", FTSearchParams.searchParams() - .highlight(FTSearchParams.highlightParams().tags("", "")) - .summarize()); - assertEquals("is often referred as a data structures server. What this means is that " - + "Redis provides... What this means is that Redis provides access to mutable data " - + "structures via a set of commands, which are sent using a... So different processes can " - + "query and modify the same data structures in a shared... ", - res.getDocuments().get(0).get("text")); - } - - @Test - public void getTagField() { - assertOK(client.ftCreate(index, TextField.of("title"), TagField.of("category"))); - - Map fields1 = new HashMap<>(); - fields1.put("title", "hello world"); - fields1.put("category", "red"); - addDocument("foo", fields1); - - Map fields2 = new HashMap<>(); - fields2.put("title", "hello world"); - fields2.put("category", "blue"); - addDocument("bar", fields2); - - Map fields3 = new HashMap<>(); - fields3.put("title", "hello world"); - fields3.put("category", "green,yellow"); - addDocument("baz", fields3); - - Map fields4 = new HashMap<>(); - fields4.put("title", "hello world"); - fields4.put("category", "orange;purple"); - addDocument("qux", fields4); - - assertEquals(1, client.ftSearch(index, "@category:{red}").getTotalResults()); - assertEquals(1, client.ftSearch(index, "@category:{blue}").getTotalResults()); - assertEquals(1, client.ftSearch(index, "hello @category:{red}").getTotalResults()); - assertEquals(1, client.ftSearch(index, "hello @category:{blue}").getTotalResults()); - assertEquals(1, client.ftSearch(index, "@category:{yellow}").getTotalResults()); - assertEquals(0, client.ftSearch(index, "@category:{purple}").getTotalResults()); - assertEquals(1, client.ftSearch(index, "@category:{orange\\;purple}").getTotalResults()); - assertEquals(4, client.ftSearch(index, "hello").getTotalResults()); - - assertEquals(new HashSet<>(Arrays.asList("red", "blue", "green", "yellow", "orange;purple")), - client.ftTagVals(index, "category")); - } - - @Test - public void testGetTagFieldWithNonDefaultSeparator() { - assertOK(client.ftCreate(index, - TextField.of("title"), - TagField.of("category").separator(';'))); - - Map fields1 = new HashMap<>(); - fields1.put("title", "hello world"); - fields1.put("category", "red"); - addDocument("foo", fields1); - - Map fields2 = new HashMap<>(); - fields2.put("title", "hello world"); - fields2.put("category", "blue"); - addDocument("bar", fields2); - - Map fields3 = new HashMap<>(); - fields3.put("title", "hello world"); - fields3.put("category", "green;yellow"); - addDocument("baz", fields3); - - Map fields4 = new HashMap<>(); - fields4.put("title", "hello world"); - fields4.put("category", "orange,purple"); - addDocument("qux", fields4); - - assertEquals(1, client.ftSearch(index, "@category:{red}").getTotalResults()); - assertEquals(1, client.ftSearch(index, "@category:{blue}").getTotalResults()); - assertEquals(1, client.ftSearch(index, "hello @category:{red}").getTotalResults()); - assertEquals(1, client.ftSearch(index, "hello @category:{blue}").getTotalResults()); - assertEquals(1, client.ftSearch(index, "hello @category:{yellow}").getTotalResults()); - assertEquals(0, client.ftSearch(index, "@category:{purple}").getTotalResults()); - assertEquals(1, client.ftSearch(index, "@category:{orange\\,purple}").getTotalResults()); - assertEquals(4, client.ftSearch(index, "hello").getTotalResults()); - - assertEquals(new HashSet<>(Arrays.asList("red", "blue", "green", "yellow", "orange,purple")), - client.ftTagVals(index, "category")); - } - - @Test - public void caseSensitiveTagField() { - assertOK(client.ftCreate(index, - TextField.of("title"), - TagField.of("category").caseSensitive())); - - Map fields = new HashMap<>(); - fields.put("title", "hello world"); - fields.put("category", "RedX"); - addDocument("foo", fields); - - assertEquals(0, client.ftSearch(index, "@category:{redx}").getTotalResults()); - assertEquals(0, client.ftSearch(index, "@category:{redX}").getTotalResults()); - assertEquals(0, client.ftSearch(index, "@category:{Redx}").getTotalResults()); - assertEquals(1, client.ftSearch(index, "@category:{RedX}").getTotalResults()); - assertEquals(1, client.ftSearch(index, "hello").getTotalResults()); - } - - @Test - public void testReturnFields() { - assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); - - Map doc = new HashMap<>(); - doc.put("field1", "value1"); - doc.put("field2", "value2"); - addDocument("doc", doc); - - // Query - SearchResult res = client.ftSearch(index, "*", - FTSearchParams.searchParams().returnFields("field1")); - assertEquals(1, res.getTotalResults()); - Document ret = res.getDocuments().get(0); - assertEquals("value1", ret.get("field1")); - assertNull(ret.get("field2")); - } - - @Test - public void returnWithFieldNames() { - assertOK(client.ftCreate(index, TextField.of("a"), TextField.of("b"), TextField.of("c"))); - - Map map = new HashMap<>(); - map.put("a", "value1"); - map.put("b", "value2"); - map.put("c", "value3"); - addDocument("doc", map); - - // Query - SearchResult res = client.ftSearch(index, "*", - FTSearchParams.searchParams().returnFields( - FieldName.of("a"), FieldName.of("b").as("d"))); - assertEquals(1, res.getTotalResults()); - Document doc = res.getDocuments().get(0); - assertEquals("value1", doc.get("a")); - assertNull(doc.get("b")); - assertEquals("value2", doc.get("d")); - assertNull(doc.get("c")); - } - - @Test - public void inKeys() { - assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); - - Map doc = new HashMap<>(); - doc.put("field1", "value"); - doc.put("field2", "not"); - // Store it - addDocument("doc1", doc); - addDocument("doc2", doc); - - // Query - SearchResult res = client.ftSearch(index, "value", - FTSearchParams.searchParams().inKeys("doc1")); - assertEquals(1, res.getTotalResults()); - assertEquals("doc1", res.getDocuments().get(0).getId()); - assertEquals("value", res.getDocuments().get(0).get("field1")); - assertEquals(null, res.getDocuments().get(0).get("value")); - } - - @Test - public void alias() { - assertOK(client.ftCreate(index, TextField.of("field1"))); - - Map doc = new HashMap<>(); - doc.put("field1", "value"); - addDocument("doc1", doc); - - assertEquals("OK", client.ftAliasAdd("ALIAS1", index)); - SearchResult res1 = client.ftSearch("ALIAS1", "*", - FTSearchParams.searchParams().returnFields("field1")); - assertEquals(1, res1.getTotalResults()); - assertEquals("value", res1.getDocuments().get(0).get("field1")); - - assertEquals("OK", client.ftAliasUpdate("ALIAS2", index)); - SearchResult res2 = client.ftSearch("ALIAS2", "*", - FTSearchParams.searchParams().returnFields("field1")); - assertEquals(1, res2.getTotalResults()); - assertEquals("value", res2.getDocuments().get(0).get("field1")); - - try { - client.ftAliasDel("ALIAS3"); - fail("Should throw JedisDataException"); - } catch (JedisDataException e) { - // Alias does not exist - } - assertEquals("OK", client.ftAliasDel("ALIAS2")); - try { - client.ftAliasDel("ALIAS2"); - fail("Should throw JedisDataException"); - } catch (JedisDataException e) { - // Alias does not exist - } - } - - @Test - public void synonym() { - assertOK(client.ftCreate(index, TextField.of("name").weight(1), TextField.of("addr").weight(1))); - - long group1 = 345L; - long group2 = 789L; - String group1_str = Long.toString(group1); - String group2_str = Long.toString(group2); - assertEquals("OK", client.ftSynUpdate(index, group1_str, "girl", "baby")); - assertEquals("OK", client.ftSynUpdate(index, group1_str, "child")); - assertEquals("OK", client.ftSynUpdate(index, group2_str, "child")); - - Map> dump = client.ftSynDump(index); - - Map> expected = new HashMap<>(); - expected.put("girl", Arrays.asList(group1_str)); - expected.put("baby", Arrays.asList(group1_str)); - expected.put("child", Arrays.asList(group1_str, group2_str)); - assertEquals(expected, dump); - } - - @Test - public void slop() { - assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); - - Map doc = new HashMap<>(); - doc.put("field1", "ok hi jedis"); - addDocument("doc1", doc); - - SearchResult res = client.ftSearch(index, "ok jedis", FTSearchParams.searchParams().slop(0)); - assertEquals(0, res.getTotalResults()); - - res = client.ftSearch(index, "ok jedis", FTSearchParams.searchParams().slop(1)); - assertEquals(1, res.getTotalResults()); - assertEquals("doc1", res.getDocuments().get(0).getId()); - assertEquals("ok hi jedis", res.getDocuments().get(0).get("field1")); - } - - @Test - public void timeout() { - assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); - - Map map = new HashMap<>(); - map.put("field1", "value"); - map.put("field2", "not"); - client.hset("doc1", map); - - SearchResult res = client.ftSearch(index, "value", FTSearchParams.searchParams().timeout(1000)); - assertEquals(1, res.getTotalResults()); - assertEquals("doc1", res.getDocuments().get(0).getId()); - assertEquals("value", res.getDocuments().get(0).get("field1")); - assertEquals("not", res.getDocuments().get(0).get("field2")); - } - - @Test - public void inOrder() { - assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); - - Map map = new HashMap<>(); - map.put("field1", "value"); - map.put("field2", "not"); - client.hset("doc2", map); - client.hset("doc1", map); - - SearchResult res = client.ftSearch(index, "value", FTSearchParams.searchParams().inOrder()); - assertEquals(2, res.getTotalResults()); - assertEquals("doc2", res.getDocuments().get(0).getId()); - assertEquals("value", res.getDocuments().get(0).get("field1")); - assertEquals("not", res.getDocuments().get(0).get("field2")); - } - - @Test - public void testHNSWVVectorSimilarity() { - Map attr = new HashMap<>(); - attr.put("TYPE", "FLOAT32"); - attr.put("DIM", 2); - attr.put("DISTANCE_METRIC", "L2"); - - assertOK(client.ftCreate(index, VectorField.builder().fieldName("v") - .algorithm(VectorField.VectorAlgorithm.HNSW).attributes(attr).build())); - - client.hset("a", "v", "aaaaaaaa"); - client.hset("b", "v", "aaaabaaa"); - client.hset("c", "v", "aaaaabaa"); - - FTSearchParams searchParams = FTSearchParams.searchParams() - .addParam("vec", "aaaaaaaa") - .sortBy("__v_score", SortingOrder.ASC) - .returnFields("__v_score") - .dialect(2); - Document doc1 = client.ftSearch(index, "*=>[KNN 2 @v $vec]", searchParams).getDocuments().get(0); - assertEquals("a", doc1.getId()); - assertEquals("0", doc1.get("__v_score")); - } - - @Test - public void testFlatVectorSimilarity() { - assertOK(client.ftCreate(index, - VectorField.builder().fieldName("v") - .algorithm(VectorField.VectorAlgorithm.FLAT) - .addAttribute("TYPE", "FLOAT32") - .addAttribute("DIM", 2) - .addAttribute("DISTANCE_METRIC", "L2") - .build() - )); - - client.hset("a", "v", "aaaaaaaa"); - client.hset("b", "v", "aaaabaaa"); - client.hset("c", "v", "aaaaabaa"); - - FTSearchParams searchParams = FTSearchParams.searchParams() - .addParam("vec", "aaaaaaaa") - .sortBy("__v_score", SortingOrder.ASC) - .returnFields("__v_score") - .dialect(2); - - Document doc1 = client.ftSearch(index, "*=>[KNN 2 @v $vec]", searchParams).getDocuments().get(0); - assertEquals("a", doc1.getId()); - assertEquals("0", doc1.get("__v_score")); - } - - @Test - public void searchProfile() { - assertOK(client.ftCreate(index, TextField.of("t1"), TextField.of("t2"))); - - Map hash = new HashMap<>(); - hash.put("t1", "foo"); - hash.put("t2", "bar"); - client.hset("doc1", hash); - - Map.Entry> reply = client.ftProfileSearch(index, - FTProfileParams.profileParams(), "foo", FTSearchParams.searchParams()); - - SearchResult result = reply.getKey(); - assertEquals(1, result.getTotalResults()); - assertEquals(Collections.singletonList("doc1"), result.getDocuments().stream().map(Document::getId).collect(Collectors.toList())); - - Map profile = reply.getValue(); - Map iteratorsProfile; - if (protocol != RedisProtocol.RESP3) { - iteratorsProfile = (Map) profile.get("Iterators profile"); - } else { - List iteratorsProfileList = (List) profile.get("Iterators profile"); - assertEquals(1, iteratorsProfileList.size()); - iteratorsProfile = (Map) iteratorsProfileList.get(0); - } - assertEquals("TEXT", iteratorsProfile.get("Type")); - assertEquals("foo", iteratorsProfile.get("Term")); - assertEquals(1L, iteratorsProfile.get("Counter")); - assertEquals(1L, iteratorsProfile.get("Size")); - assertSame(Double.class, iteratorsProfile.get("Time").getClass()); - - assertEquals(Arrays.asList("Index", "Scorer", "Sorter", "Loader"), - ((List>) profile.get("Result processors profile")).stream() - .map(map -> map.get("Type")).collect(Collectors.toList())); - } - - @Test - public void vectorSearchProfile() { - assertOK(client.ftCreate(index, VectorField.builder().fieldName("v") - .algorithm(VectorAlgorithm.FLAT).addAttribute("TYPE", "FLOAT32") - .addAttribute("DIM", 2).addAttribute("DISTANCE_METRIC", "L2").build(), - TextField.of("t"))); - - client.hset("1", toMap("v", "bababaca", "t", "hello")); - client.hset("2", toMap("v", "babababa", "t", "hello")); - client.hset("3", toMap("v", "aabbaabb", "t", "hello")); - client.hset("4", toMap("v", "bbaabbaa", "t", "hello world")); - client.hset("5", toMap("v", "aaaabbbb", "t", "hello world")); - - FTSearchParams searchParams = FTSearchParams.searchParams().addParam("vec", "aaaaaaaa") - .sortBy("__v_score", SortingOrder.ASC).noContent().dialect(2); - Map.Entry> reply = client.ftProfileSearch(index, - FTProfileParams.profileParams(), "*=>[KNN 3 @v $vec]", searchParams); - assertEquals(3, reply.getKey().getTotalResults()); - - assertEquals(Arrays.asList(4, 2, 1).toString(), reply.getKey().getDocuments() - .stream().map(Document::getId).collect(Collectors.toList()).toString()); - - Map profile = reply.getValue(); - - if (protocol != RedisProtocol.RESP3) { - assertEquals("VECTOR", ((Map) profile.get("Iterators profile")).get("Type")); - } else { - assertEquals(Arrays.asList("VECTOR"), - ((List>) profile.get("Iterators profile")).stream() - .map(map -> map.get("Type")).collect(Collectors.toList())); - } - - List> resultProcessorsProfile - = (List>) reply.getValue().get("Result processors profile"); - assertEquals(3, resultProcessorsProfile.size()); - assertEquals("Index", resultProcessorsProfile.get(0).get("Type")); - assertEquals("Sorter", resultProcessorsProfile.get(2).get("Type")); - } - - @Test - public void maxPrefixExpansionSearchProfile() { - final String configParam = "MAXPREFIXEXPANSIONS"; - String configValue = (String) client.ftConfigGet(configParam).get(configParam); - try { - client.ftConfigSet(configParam, "2"); - - assertOK(client.ftCreate(index, TextField.of("t"))); - client.hset("1", Collections.singletonMap("t", "foo1")); - client.hset("2", Collections.singletonMap("t", "foo2")); - client.hset("3", Collections.singletonMap("t", "foo3")); - - Map.Entry> reply = client.ftProfileSearch(index, - FTProfileParams.profileParams(), "foo*", FTSearchParams.searchParams().limit(0, 0)); - // Warning=Max prefix expansion reached - if (protocol != RedisProtocol.RESP3) { - assertEquals("Max prefix expansion reached", - ((Map) reply.getValue().get("Iterators profile")).get("Warning")); - } else { - assertEquals("Max prefix expansion reached", - ((Map) ((List) reply.getValue().get("Iterators profile")).get(0)).get("Warning")); - } - } finally { - client.ftConfigSet(configParam, configValue); - } - } - - @Test - public void noContentSearchProfile() { - assertOK(client.ftCreate(index, TextField.of("t"))); - client.hset("1", Collections.singletonMap("t", "foo")); - client.hset("2", Collections.singletonMap("t", "bar")); - - Map.Entry> profile = client.ftProfileSearch(index, - FTProfileParams.profileParams(), "foo -@t:baz", FTSearchParams.searchParams().noContent()); - - Map depth0 = protocol != RedisProtocol.RESP3 - ? (Map) profile.getValue().get("Iterators profile") - : ((List>) profile.getValue().get("Iterators profile")).get(0); - - assertEquals("INTERSECT", depth0.get("Type")); - List> depth0_children = (List>) depth0.get("Child iterators"); - assertEquals("TEXT", depth0_children.get(0).get("Type")); - Map depth1 = depth0_children.get(1); - assertEquals("NOT", depth1.get("Type")); - if (protocol != RedisProtocol.RESP3) { - List> depth1_children = (List>) depth1.get("Child iterators"); - assertEquals(1, depth1_children.size()); - assertEquals("EMPTY", depth1_children.get(0).get("Type")); - } else { - assertEquals("EMPTY", ((Map) depth1.get("Child iterator")).get("Type")); - } - } - - @Test - public void deepReplySearchProfile() { - assertOK(client.ftCreate(index, TextField.of("t"))); - client.hset("1", Collections.singletonMap("t", "hello")); - client.hset("2", Collections.singletonMap("t", "world")); - - Map.Entry> profile - = client.ftProfileSearch(index, FTProfileParams.profileParams(), - "hello(hello(hello(hello(hello(hello)))))", FTSearchParams.searchParams().noContent()); - - Map depth0 = protocol != RedisProtocol.RESP3 - ? (Map) profile.getValue().get("Iterators profile") - : ((List>) profile.getValue().get("Iterators profile")).get(0); - - assertEquals("INTERSECT", depth0.get("Type")); - List> depth0_children = (List>) depth0.get("Child iterators"); - assertEquals("TEXT", depth0_children.get(0).get("Type")); - Map depth1 = depth0_children.get(1); - assertEquals("INTERSECT", depth1.get("Type")); - List> depth1_children = (List>) depth1.get("Child iterators"); - assertEquals("TEXT", depth1_children.get(0).get("Type")); - Map depth2 = depth1_children.get(1); - assertEquals("INTERSECT", depth2.get("Type")); - List> depth2_children = (List>) depth2.get("Child iterators"); - assertEquals("TEXT", depth2_children.get(0).get("Type")); - Map depth3 = depth2_children.get(1); - assertEquals("INTERSECT", depth3.get("Type")); - List> depth3_children = (List>) depth3.get("Child iterators"); - assertEquals("TEXT", depth3_children.get(0).get("Type")); - Map depth4 = depth3_children.get(1); - assertEquals("INTERSECT", depth4.get("Type")); - List> depth4_children = (List>) depth4.get("Child iterators"); - assertEquals("TEXT", depth4_children.get(0).get("Type")); - Map depth5 = depth4_children.get(1); - assertEquals("TEXT", depth5.get("Type")); - assertNull(depth5.get("Child iterators")); - } - - @Test - public void limitedSearchProfile() { - assertOK(client.ftCreate(index, TextField.of("t"))); - client.hset("1", Collections.singletonMap("t", "hello")); - client.hset("2", Collections.singletonMap("t", "hell")); - client.hset("3", Collections.singletonMap("t", "help")); - client.hset("4", Collections.singletonMap("t", "helowa")); - - Map.Entry> profile = client.ftProfileSearch(index, - FTProfileParams.profileParams().limited(), "%hell% hel*", FTSearchParams.searchParams().noContent()); - - Map depth0 = protocol != RedisProtocol.RESP3 - ? (Map) profile.getValue().get("Iterators profile") - : ((List>) profile.getValue().get("Iterators profile")).get(0); - - assertEquals("INTERSECT", depth0.get("Type")); - assertEquals(3L, depth0.get("Counter")); - - List> depth0_children = (List>) depth0.get("Child iterators"); - assertFalse(depth0_children.isEmpty()); - for (Map depth1 : depth0_children) { - assertEquals("UNION", depth1.get("Type")); - assertNotNull(depth1.get("Query type")); - if (protocol != RedisProtocol.RESP3) { - List depth1_children = (List) depth1.get("Child iterators"); - assertEquals(1, depth1_children.size()); - assertSame(String.class, depth1_children.get(0).getClass()); - } else { - assertSame(String.class, depth1.get("Child iterators").getClass()); - } - } - } - - @Test - public void list() { - assertEquals(Collections.emptySet(), client.ftList()); - - final int count = 20; - Set names = new HashSet<>(); - for (int i = 0; i < count; i++) { - final String name = "idx" + i; - assertOK(client.ftCreate(name, TextField.of("t" + i))); - names.add(name); - } - assertEquals(names, client.ftList()); - } - - @Test - public void broadcast() { - String reply = client.ftCreate(index, TextField.of("t")); - assertOK(reply); - } - - @Test - public void searchIteration() { - assertOK(client.ftCreate(index, FTCreateParams.createParams(), - TextField.of("first"), TextField.of("last"), NumericField.of("age"))); - - client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); - client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); - client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); - client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); - client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); - client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); - client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); - - FtSearchIteration search = client.ftSearchIteration(3, index, "*", FTSearchParams.searchParams()); - int total = 0; - while (!search.isIterationCompleted()) { - SearchResult result = search.nextBatch(); - int count = result.getDocuments().size(); - assertThat(count, Matchers.lessThanOrEqualTo(3)); - total += count; - } - assertEquals(7, total); - } - - @Test - public void searchIterationCollect() { - assertOK(client.ftCreate(index, FTCreateParams.createParams(), - TextField.of("first"), TextField.of("last"), NumericField.of("age"))); - - client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); - client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); - client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); - client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); - client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); - client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); - client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); - - ArrayList collect = new ArrayList<>(); - client.ftSearchIteration(3, index, "*", FTSearchParams.searchParams()).collect(collect); - assertEquals(7, collect.size()); - assertEquals(Arrays.asList("profesor:5555", "student:1111", "pupil:2222", "student:3333", - "pupil:4444", "student:5555", "teacher:6666").stream().collect(Collectors.toSet()), - collect.stream().map(Document::getId).collect(Collectors.toSet())); - } - - @Test - public void escapeUtil() { - assertOK(client.ftCreate(index, TextField.of("txt"))); - - client.hset("doc1", "txt", RediSearchUtil.escape("hello-world")); - assertNotEquals("hello-world", client.hget("doc1", "txt")); - assertEquals("hello-world", RediSearchUtil.unescape(client.hget("doc1", "txt"))); - - SearchResult resultNoEscape = client.ftSearch(index, "hello-world"); - assertEquals(0, resultNoEscape.getTotalResults()); - - SearchResult resultEscaped = client.ftSearch(index, RediSearchUtil.escapeQuery("hello-world")); - assertEquals(1, resultEscaped.getTotalResults()); - } - - @Test - public void escapeMapUtil() { - client.hset("doc2", RediSearchUtil.toStringMap(Collections.singletonMap("txt", "hello-world"), true)); - assertNotEquals("hello-world", client.hget("doc2", "txt")); - assertEquals("hello-world", RediSearchUtil.unescape(client.hget("doc2", "txt"))); - } -} +//// +//// res = client.ftSearch(index, "hello", +//// FTSearchParams.searchParams().withScores().explainScore()); +//// assertEquals(100, res.getTotalResults()); +//// assertEquals(10, res.getDocuments().size()); +//// +//// for (Document d : res.getDocuments()) { +//// assertTrue(d.getId().startsWith("doc")); +//// assertTrue(((String) d.get("title")).startsWith("hello world")); +//// } +// +// res = client.ftSearch(index, "hello", +// FTSearchParams.searchParams().noContent()); +// for (Document d : res.getDocuments()) { +// assertTrue(d.getId().startsWith("doc")); +// if (protocol != RedisProtocol.RESP3) { +// assertEquals(1.0, d.getScore(), 0); +// assertNull(d.get("title")); +// } else { +// assertNull(d.getScore()); +// assertThrows(NullPointerException.class, () -> d.get("title")); +// } +// } +// +// // test verbatim vs. stemming +// res = client.ftSearch(index, "hello worlds"); +// assertEquals(100, res.getTotalResults()); +// res = client.ftSearch(index, "hello worlds", FTSearchParams.searchParams().verbatim()); +// assertEquals(50, res.getTotalResults()); +// res = client.ftSearch(index, "hello a world", FTSearchParams.searchParams().verbatim()); +// assertEquals(50, res.getTotalResults()); +// res = client.ftSearch(index, "hello a worlds", FTSearchParams.searchParams().verbatim()); +// assertEquals(50, res.getTotalResults()); +// res = client.ftSearch(index, "hello a world", FTSearchParams.searchParams().verbatim().noStopwords()); +// assertEquals(0, res.getTotalResults()); +// } +// +// @Test +// public void testQueryParams() { +// assertOK(client.ftCreate(index, NumericField.of("numval"))); +// +// client.hset("1", "numval", "1"); +// client.hset("2", "numval", "2"); +// client.hset("3", "numval", "3"); +// +// assertEquals(2, client.ftSearch(index, "@numval:[$min $max]", +// FTSearchParams.searchParams().addParam("min", 1).addParam("max", 2) +// .dialect(2)).getTotalResults()); +// +// Map paramValues = new HashMap<>(); +// paramValues.put("min", 1); +// paramValues.put("max", 2); +// assertEquals(2, client.ftSearch(index, "@numval:[$min $max]", +// FTSearchParams.searchParams().params(paramValues) +// .dialect(2)).getTotalResults()); +// } +// +// @Test +// public void testSortQueryFlags() { +// assertOK(client.ftCreate(index, TextField.of("title").sortable())); +// +// Map fields = new HashMap<>(); +// +// fields.put("title", "b title"); +// addDocument("doc1", fields); +// +// fields.put("title", "a title"); +// addDocument("doc2", fields); +// +// fields.put("title", "c title"); +// addDocument("doc3", fields); +// +// SearchResult res = client.ftSearch(index, "title", +// FTSearchParams.searchParams().sortBy("title", SortingOrder.ASC)); +// +// assertEquals(3, res.getTotalResults()); +// Document doc1 = res.getDocuments().get(0); +// assertEquals("a title", doc1.get("title")); +// +// doc1 = res.getDocuments().get(1); +// assertEquals("b title", doc1.get("title")); +// +// doc1 = res.getDocuments().get(2); +// assertEquals("c title", doc1.get("title")); +// } +// +// @Test +// public void testJsonWithAlias() { +// assertOK(client.ftCreate(index, +// FTCreateParams.createParams() +// .on(IndexDataType.JSON) +// .prefix("king:"), +// TextField.of("$.name").as("name"), +// NumericField.of("$.num").as("num"))); +// +// Map king1 = new HashMap<>(); +// king1.put("name", "henry"); +// king1.put("num", 42); +// client.jsonSet("king:1", Path.ROOT_PATH, king1); +// +// Map king2 = new HashMap<>(); +// king2.put("name", "james"); +// king2.put("num", 3.14); +// client.jsonSet("king:2", Path.ROOT_PATH, king2); +// +// SearchResult res = client.ftSearch(index, "@name:henry"); +// assertEquals(1, res.getTotalResults()); +// assertEquals("king:1", res.getDocuments().get(0).getId()); +// +// res = client.ftSearch(index, "@num:[0 10]"); +// assertEquals(1, res.getTotalResults()); +// assertEquals("king:2", res.getDocuments().get(0).getId()); +// } +// +// @Test +// public void dropIndex() { +// assertOK(client.ftCreate(index, TextField.of("title"))); +// +// Map fields = new HashMap<>(); +// fields.put("title", "hello world"); +// for (int i = 0; i < 100; i++) { +// addDocument(String.format("doc%d", i), fields); +// } +// +// SearchResult res = client.ftSearch(index, "hello world"); +// assertEquals(100, res.getTotalResults()); +// +// assertEquals("OK", client.ftDropIndex(index)); +// +// try { +// client.ftSearch(index, "hello world"); +// fail("Index should not exist."); +// } catch (JedisDataException de) { +// assertTrue(de.getMessage().contains("no such index")); +// } +// assertEquals(100, client.dbSize()); +// } +// +// @Test +// public void dropIndexDD() { +// assertOK(client.ftCreate(index, TextField.of("title"))); +// +// Map fields = new HashMap<>(); +// fields.put("title", "hello world"); +// for (int i = 0; i < 100; i++) { +// addDocument(String.format("doc%d", i), fields); +// } +// +// SearchResult res = client.ftSearch(index, "hello world"); +// assertEquals(100, res.getTotalResults()); +// +// assertEquals("OK", client.ftDropIndexDD(index)); +// +// Set keys = client.keys("*"); +// assertTrue(keys.isEmpty()); +// assertEquals(0, client.dbSize()); +// } +// +// @Test +// public void noStem() { +// assertOK(client.ftCreate(index, new TextField("stemmed").weight(1.0), +// new TextField("notStemmed").weight(1.0).noStem())); +// +// Map doc = new HashMap<>(); +// doc.put("stemmed", "located"); +// doc.put("notStemmed", "located"); +// addDocument("doc", doc); +// +// // Query +// SearchResult res = client.ftSearch(index, "@stemmed:location"); +// assertEquals(1, res.getTotalResults()); +// +// res = client.ftSearch(index, "@notStemmed:location"); +// assertEquals(0, res.getTotalResults()); +// } +// +// @Test +// public void phoneticMatch() { +// assertOK(client.ftCreate(index, new TextField("noPhonetic").weight(1.0), +// new TextField("withPhonetic").weight(1.0).phonetic("dm:en"))); +// +// Map doc = new HashMap<>(); +// doc.put("noPhonetic", "morfix"); +// doc.put("withPhonetic", "morfix"); +// addDocument("doc", doc); +// +// // Query +// SearchResult res = client.ftSearch(index, "@withPhonetic:morphix=>{$phonetic:true}"); +// assertEquals(1, res.getTotalResults()); +// +// try { +// client.ftSearch(index, "@noPhonetic:morphix=>{$phonetic:true}"); +// fail(); +// } catch (JedisDataException e) {/*field does not support phonetics*/ +// } +// +// SearchResult res3 = client.ftSearch(index, "@withPhonetic:morphix=>{$phonetic:false}"); +// assertEquals(0, res3.getTotalResults()); +// } +// +// @Test +// public void info() { +// Collection sc = new ArrayList<>(); +// sc.add(TextField.of("title").weight(5)); +// sc.add(TextField.of("plot").sortable()); +// sc.add(TagField.of("genre").separator(',').sortable()); +// sc.add(NumericField.of("release_year").sortable()); +// sc.add(NumericField.of("rating").sortable()); +// sc.add(NumericField.of("votes").sortable()); +// +// assertOK(client.ftCreate(index, sc)); +// +// Map info = client.ftInfo(index); +// assertEquals(index, info.get("index_name")); +// assertEquals(6, ((List) info.get("attributes")).size()); +// if (protocol != RedisProtocol.RESP3) { +// assertEquals("global_idle", ((List) info.get("cursor_stats")).get(0)); +// assertEquals(0L, ((List) info.get("cursor_stats")).get(1)); +// } else { +// assertEquals(0L, ((Map) info.get("cursor_stats")).get("global_idle")); +// } +// } +// +// @Test +// public void noIndexAndSortBy() { +// assertOK(client.ftCreate(index, TextField.of("f1").sortable().noIndex(), TextField.of("f2"))); +// +// Map mm = new HashMap<>(); +// +// mm.put("f1", "MarkZZ"); +// mm.put("f2", "MarkZZ"); +// addDocument("doc1", mm); +// +// mm.clear(); +// mm.put("f1", "MarkAA"); +// mm.put("f2", "MarkBB"); +// addDocument("doc2", mm); +// +// SearchResult res = client.ftSearch(index, "@f1:Mark*"); +// assertEquals(0, res.getTotalResults()); +// +// res = client.ftSearch(index, "@f2:Mark*"); +// assertEquals(2, res.getTotalResults()); +// +// res = client.ftSearch(index, "@f2:Mark*", +// FTSearchParams.searchParams().sortBy("f1", SortingOrder.DESC)); +// assertEquals(2, res.getTotalResults()); +// +// assertEquals("doc1", res.getDocuments().get(0).getId()); +// +// res = client.ftSearch(index, "@f2:Mark*", +// FTSearchParams.searchParams().sortBy("f1", SortingOrder.ASC)); +// assertEquals("doc2", res.getDocuments().get(0).getId()); +// } +// +// @Test +// public void testHighlightSummarize() { +// assertOK(client.ftCreate(index, TextField.of("text").weight(1))); +// +// Map doc = new HashMap<>(); +// doc.put("text", "Redis is often referred as a data structures server. What this means is that " +// + "Redis provides access to mutable data structures via a set of commands, which are sent " +// + "using a server-client model with TCP sockets and a simple protocol. So different " +// + "processes can query and modify the same data structures in a shared way"); +// // Add a document +// addDocument("foo", doc); +// +// SearchResult res = client.ftSearch(index, "data", FTSearchParams.searchParams().highlight().summarize()); +// assertEquals("is often referred as a data structures server. What this means is that " +// + "Redis provides... What this means is that Redis provides access to mutable data " +// + "structures via a set of commands, which are sent using a... So different processes can " +// + "query and modify the same data structures in a shared... ", +// res.getDocuments().get(0).get("text")); +// +// res = client.ftSearch(index, "data", FTSearchParams.searchParams() +// .highlight(FTSearchParams.highlightParams().tags("", "")) +// .summarize()); +// assertEquals("is often referred as a data structures server. What this means is that " +// + "Redis provides... What this means is that Redis provides access to mutable data " +// + "structures via a set of commands, which are sent using a... So different processes can " +// + "query and modify the same data structures in a shared... ", +// res.getDocuments().get(0).get("text")); +// } +// +// @Test +// public void getTagField() { +// assertOK(client.ftCreate(index, TextField.of("title"), TagField.of("category"))); +// +// Map fields1 = new HashMap<>(); +// fields1.put("title", "hello world"); +// fields1.put("category", "red"); +// addDocument("foo", fields1); +// +// Map fields2 = new HashMap<>(); +// fields2.put("title", "hello world"); +// fields2.put("category", "blue"); +// addDocument("bar", fields2); +// +// Map fields3 = new HashMap<>(); +// fields3.put("title", "hello world"); +// fields3.put("category", "green,yellow"); +// addDocument("baz", fields3); +// +// Map fields4 = new HashMap<>(); +// fields4.put("title", "hello world"); +// fields4.put("category", "orange;purple"); +// addDocument("qux", fields4); +// +// assertEquals(1, client.ftSearch(index, "@category:{red}").getTotalResults()); +// assertEquals(1, client.ftSearch(index, "@category:{blue}").getTotalResults()); +// assertEquals(1, client.ftSearch(index, "hello @category:{red}").getTotalResults()); +// assertEquals(1, client.ftSearch(index, "hello @category:{blue}").getTotalResults()); +// assertEquals(1, client.ftSearch(index, "@category:{yellow}").getTotalResults()); +// assertEquals(0, client.ftSearch(index, "@category:{purple}").getTotalResults()); +// assertEquals(1, client.ftSearch(index, "@category:{orange\\;purple}").getTotalResults()); +// assertEquals(4, client.ftSearch(index, "hello").getTotalResults()); +// +// assertEquals(new HashSet<>(Arrays.asList("red", "blue", "green", "yellow", "orange;purple")), +// client.ftTagVals(index, "category")); +// } +// +// @Test +// public void testGetTagFieldWithNonDefaultSeparator() { +// assertOK(client.ftCreate(index, +// TextField.of("title"), +// TagField.of("category").separator(';'))); +// +// Map fields1 = new HashMap<>(); +// fields1.put("title", "hello world"); +// fields1.put("category", "red"); +// addDocument("foo", fields1); +// +// Map fields2 = new HashMap<>(); +// fields2.put("title", "hello world"); +// fields2.put("category", "blue"); +// addDocument("bar", fields2); +// +// Map fields3 = new HashMap<>(); +// fields3.put("title", "hello world"); +// fields3.put("category", "green;yellow"); +// addDocument("baz", fields3); +// +// Map fields4 = new HashMap<>(); +// fields4.put("title", "hello world"); +// fields4.put("category", "orange,purple"); +// addDocument("qux", fields4); +// +// assertEquals(1, client.ftSearch(index, "@category:{red}").getTotalResults()); +// assertEquals(1, client.ftSearch(index, "@category:{blue}").getTotalResults()); +// assertEquals(1, client.ftSearch(index, "hello @category:{red}").getTotalResults()); +// assertEquals(1, client.ftSearch(index, "hello @category:{blue}").getTotalResults()); +// assertEquals(1, client.ftSearch(index, "hello @category:{yellow}").getTotalResults()); +// assertEquals(0, client.ftSearch(index, "@category:{purple}").getTotalResults()); +// assertEquals(1, client.ftSearch(index, "@category:{orange\\,purple}").getTotalResults()); +// assertEquals(4, client.ftSearch(index, "hello").getTotalResults()); +// +// assertEquals(new HashSet<>(Arrays.asList("red", "blue", "green", "yellow", "orange,purple")), +// client.ftTagVals(index, "category")); +// } +// +// @Test +// public void caseSensitiveTagField() { +// assertOK(client.ftCreate(index, +// TextField.of("title"), +// TagField.of("category").caseSensitive())); +// +// Map fields = new HashMap<>(); +// fields.put("title", "hello world"); +// fields.put("category", "RedX"); +// addDocument("foo", fields); +// +// assertEquals(0, client.ftSearch(index, "@category:{redx}").getTotalResults()); +// assertEquals(0, client.ftSearch(index, "@category:{redX}").getTotalResults()); +// assertEquals(0, client.ftSearch(index, "@category:{Redx}").getTotalResults()); +// assertEquals(1, client.ftSearch(index, "@category:{RedX}").getTotalResults()); +// assertEquals(1, client.ftSearch(index, "hello").getTotalResults()); +// } +// +// @Test +// public void testReturnFields() { +// assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); +// +// Map doc = new HashMap<>(); +// doc.put("field1", "value1"); +// doc.put("field2", "value2"); +// addDocument("doc", doc); +// +// // Query +// SearchResult res = client.ftSearch(index, "*", +// FTSearchParams.searchParams().returnFields("field1")); +// assertEquals(1, res.getTotalResults()); +// Document ret = res.getDocuments().get(0); +// assertEquals("value1", ret.get("field1")); +// assertNull(ret.get("field2")); +// } +// +// @Test +// public void returnWithFieldNames() { +// assertOK(client.ftCreate(index, TextField.of("a"), TextField.of("b"), TextField.of("c"))); +// +// Map map = new HashMap<>(); +// map.put("a", "value1"); +// map.put("b", "value2"); +// map.put("c", "value3"); +// addDocument("doc", map); +// +// // Query +// SearchResult res = client.ftSearch(index, "*", +// FTSearchParams.searchParams().returnFields( +// FieldName.of("a"), FieldName.of("b").as("d"))); +// assertEquals(1, res.getTotalResults()); +// Document doc = res.getDocuments().get(0); +// assertEquals("value1", doc.get("a")); +// assertNull(doc.get("b")); +// assertEquals("value2", doc.get("d")); +// assertNull(doc.get("c")); +// } +// +// @Test +// public void inKeys() { +// assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); +// +// Map doc = new HashMap<>(); +// doc.put("field1", "value"); +// doc.put("field2", "not"); +// // Store it +// addDocument("doc1", doc); +// addDocument("doc2", doc); +// +// // Query +// SearchResult res = client.ftSearch(index, "value", +// FTSearchParams.searchParams().inKeys("doc1")); +// assertEquals(1, res.getTotalResults()); +// assertEquals("doc1", res.getDocuments().get(0).getId()); +// assertEquals("value", res.getDocuments().get(0).get("field1")); +// assertEquals(null, res.getDocuments().get(0).get("value")); +// } +// +// @Test +// public void alias() { +// assertOK(client.ftCreate(index, TextField.of("field1"))); +// +// Map doc = new HashMap<>(); +// doc.put("field1", "value"); +// addDocument("doc1", doc); +// +// assertEquals("OK", client.ftAliasAdd("ALIAS1", index)); +// SearchResult res1 = client.ftSearch("ALIAS1", "*", +// FTSearchParams.searchParams().returnFields("field1")); +// assertEquals(1, res1.getTotalResults()); +// assertEquals("value", res1.getDocuments().get(0).get("field1")); +// +// assertEquals("OK", client.ftAliasUpdate("ALIAS2", index)); +// SearchResult res2 = client.ftSearch("ALIAS2", "*", +// FTSearchParams.searchParams().returnFields("field1")); +// assertEquals(1, res2.getTotalResults()); +// assertEquals("value", res2.getDocuments().get(0).get("field1")); +// +// try { +// client.ftAliasDel("ALIAS3"); +// fail("Should throw JedisDataException"); +// } catch (JedisDataException e) { +// // Alias does not exist +// } +// assertEquals("OK", client.ftAliasDel("ALIAS2")); +// try { +// client.ftAliasDel("ALIAS2"); +// fail("Should throw JedisDataException"); +// } catch (JedisDataException e) { +// // Alias does not exist +// } +// } +// +// @Test +// public void synonym() { +// assertOK(client.ftCreate(index, TextField.of("name").weight(1), TextField.of("addr").weight(1))); +// +// long group1 = 345L; +// long group2 = 789L; +// String group1_str = Long.toString(group1); +// String group2_str = Long.toString(group2); +// assertEquals("OK", client.ftSynUpdate(index, group1_str, "girl", "baby")); +// assertEquals("OK", client.ftSynUpdate(index, group1_str, "child")); +// assertEquals("OK", client.ftSynUpdate(index, group2_str, "child")); +// +// Map> dump = client.ftSynDump(index); +// +// Map> expected = new HashMap<>(); +// expected.put("girl", Arrays.asList(group1_str)); +// expected.put("baby", Arrays.asList(group1_str)); +// expected.put("child", Arrays.asList(group1_str, group2_str)); +// assertEquals(expected, dump); +// } +// +// @Test +// public void slop() { +// assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); +// +// Map doc = new HashMap<>(); +// doc.put("field1", "ok hi jedis"); +// addDocument("doc1", doc); +// +// SearchResult res = client.ftSearch(index, "ok jedis", FTSearchParams.searchParams().slop(0)); +// assertEquals(0, res.getTotalResults()); +// +// res = client.ftSearch(index, "ok jedis", FTSearchParams.searchParams().slop(1)); +// assertEquals(1, res.getTotalResults()); +// assertEquals("doc1", res.getDocuments().get(0).getId()); +// assertEquals("ok hi jedis", res.getDocuments().get(0).get("field1")); +// } +// +// @Test +// public void timeout() { +// assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); +// +// Map map = new HashMap<>(); +// map.put("field1", "value"); +// map.put("field2", "not"); +// client.hset("doc1", map); +// +// SearchResult res = client.ftSearch(index, "value", FTSearchParams.searchParams().timeout(1000)); +// assertEquals(1, res.getTotalResults()); +// assertEquals("doc1", res.getDocuments().get(0).getId()); +// assertEquals("value", res.getDocuments().get(0).get("field1")); +// assertEquals("not", res.getDocuments().get(0).get("field2")); +// } +// +// @Test +// public void inOrder() { +// assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); +// +// Map map = new HashMap<>(); +// map.put("field1", "value"); +// map.put("field2", "not"); +// client.hset("doc2", map); +// client.hset("doc1", map); +// +// SearchResult res = client.ftSearch(index, "value", FTSearchParams.searchParams().inOrder()); +// assertEquals(2, res.getTotalResults()); +// assertEquals("doc2", res.getDocuments().get(0).getId()); +// assertEquals("value", res.getDocuments().get(0).get("field1")); +// assertEquals("not", res.getDocuments().get(0).get("field2")); +// } +// +// @Test +// public void testHNSWVVectorSimilarity() { +// Map attr = new HashMap<>(); +// attr.put("TYPE", "FLOAT32"); +// attr.put("DIM", 2); +// attr.put("DISTANCE_METRIC", "L2"); +// +// assertOK(client.ftCreate(index, VectorField.builder().fieldName("v") +// .algorithm(VectorField.VectorAlgorithm.HNSW).attributes(attr).build())); +// +// client.hset("a", "v", "aaaaaaaa"); +// client.hset("b", "v", "aaaabaaa"); +// client.hset("c", "v", "aaaaabaa"); +// +// FTSearchParams searchParams = FTSearchParams.searchParams() +// .addParam("vec", "aaaaaaaa") +// .sortBy("__v_score", SortingOrder.ASC) +// .returnFields("__v_score") +// .dialect(2); +// Document doc1 = client.ftSearch(index, "*=>[KNN 2 @v $vec]", searchParams).getDocuments().get(0); +// assertEquals("a", doc1.getId()); +// assertEquals("0", doc1.get("__v_score")); +// } +// +// @Test +// public void testFlatVectorSimilarity() { +// assertOK(client.ftCreate(index, +// VectorField.builder().fieldName("v") +// .algorithm(VectorField.VectorAlgorithm.FLAT) +// .addAttribute("TYPE", "FLOAT32") +// .addAttribute("DIM", 2) +// .addAttribute("DISTANCE_METRIC", "L2") +// .build() +// )); +// +// client.hset("a", "v", "aaaaaaaa"); +// client.hset("b", "v", "aaaabaaa"); +// client.hset("c", "v", "aaaaabaa"); +// +// FTSearchParams searchParams = FTSearchParams.searchParams() +// .addParam("vec", "aaaaaaaa") +// .sortBy("__v_score", SortingOrder.ASC) +// .returnFields("__v_score") +// .dialect(2); +// +// Document doc1 = client.ftSearch(index, "*=>[KNN 2 @v $vec]", searchParams).getDocuments().get(0); +// assertEquals("a", doc1.getId()); +// assertEquals("0", doc1.get("__v_score")); +// } +// +// @Test +// public void searchProfile() { +// assertOK(client.ftCreate(index, TextField.of("t1"), TextField.of("t2"))); +// +// Map hash = new HashMap<>(); +// hash.put("t1", "foo"); +// hash.put("t2", "bar"); +// client.hset("doc1", hash); +// +// Map.Entry> reply = client.ftProfileSearch(index, +// FTProfileParams.profileParams(), "foo", FTSearchParams.searchParams()); +// +// SearchResult result = reply.getKey(); +// assertEquals(1, result.getTotalResults()); +// assertEquals(Collections.singletonList("doc1"), result.getDocuments().stream().map(Document::getId).collect(Collectors.toList())); +// +// Map profile = reply.getValue(); +// Map iteratorsProfile; +// if (protocol != RedisProtocol.RESP3) { +// iteratorsProfile = (Map) profile.get("Iterators profile"); +// } else { +// List iteratorsProfileList = (List) profile.get("Iterators profile"); +// assertEquals(1, iteratorsProfileList.size()); +// iteratorsProfile = (Map) iteratorsProfileList.get(0); +// } +// assertEquals("TEXT", iteratorsProfile.get("Type")); +// assertEquals("foo", iteratorsProfile.get("Term")); +// assertEquals(1L, iteratorsProfile.get("Counter")); +// assertEquals(1L, iteratorsProfile.get("Size")); +// assertSame(Double.class, iteratorsProfile.get("Time").getClass()); +// +// assertEquals(Arrays.asList("Index", "Scorer", "Sorter", "Loader"), +// ((List>) profile.get("Result processors profile")).stream() +// .map(map -> map.get("Type")).collect(Collectors.toList())); +// } +// +// @Test +// public void vectorSearchProfile() { +// assertOK(client.ftCreate(index, VectorField.builder().fieldName("v") +// .algorithm(VectorAlgorithm.FLAT).addAttribute("TYPE", "FLOAT32") +// .addAttribute("DIM", 2).addAttribute("DISTANCE_METRIC", "L2").build(), +// TextField.of("t"))); +// +// client.hset("1", toMap("v", "bababaca", "t", "hello")); +// client.hset("2", toMap("v", "babababa", "t", "hello")); +// client.hset("3", toMap("v", "aabbaabb", "t", "hello")); +// client.hset("4", toMap("v", "bbaabbaa", "t", "hello world")); +// client.hset("5", toMap("v", "aaaabbbb", "t", "hello world")); +// +// FTSearchParams searchParams = FTSearchParams.searchParams().addParam("vec", "aaaaaaaa") +// .sortBy("__v_score", SortingOrder.ASC).noContent().dialect(2); +// Map.Entry> reply = client.ftProfileSearch(index, +// FTProfileParams.profileParams(), "*=>[KNN 3 @v $vec]", searchParams); +// assertEquals(3, reply.getKey().getTotalResults()); +// +// assertEquals(Arrays.asList(4, 2, 1).toString(), reply.getKey().getDocuments() +// .stream().map(Document::getId).collect(Collectors.toList()).toString()); +// +// Map profile = reply.getValue(); +// +// if (protocol != RedisProtocol.RESP3) { +// assertEquals("VECTOR", ((Map) profile.get("Iterators profile")).get("Type")); +// } else { +// assertEquals(Arrays.asList("VECTOR"), +// ((List>) profile.get("Iterators profile")).stream() +// .map(map -> map.get("Type")).collect(Collectors.toList())); +// } +// +// List> resultProcessorsProfile +// = (List>) reply.getValue().get("Result processors profile"); +// assertEquals(3, resultProcessorsProfile.size()); +// assertEquals("Index", resultProcessorsProfile.get(0).get("Type")); +// assertEquals("Sorter", resultProcessorsProfile.get(2).get("Type")); +// } +// +// @Test +// public void maxPrefixExpansionSearchProfile() { +// final String configParam = "MAXPREFIXEXPANSIONS"; +// String configValue = (String) client.ftConfigGet(configParam).get(configParam); +// try { +// client.ftConfigSet(configParam, "2"); +// +// assertOK(client.ftCreate(index, TextField.of("t"))); +// client.hset("1", Collections.singletonMap("t", "foo1")); +// client.hset("2", Collections.singletonMap("t", "foo2")); +// client.hset("3", Collections.singletonMap("t", "foo3")); +// +// Map.Entry> reply = client.ftProfileSearch(index, +// FTProfileParams.profileParams(), "foo*", FTSearchParams.searchParams().limit(0, 0)); +// // Warning=Max prefix expansion reached +// if (protocol != RedisProtocol.RESP3) { +// assertEquals("Max prefix expansion reached", +// ((Map) reply.getValue().get("Iterators profile")).get("Warning")); +// } else { +// assertEquals("Max prefix expansion reached", +// ((Map) ((List) reply.getValue().get("Iterators profile")).get(0)).get("Warning")); +// } +// } finally { +// client.ftConfigSet(configParam, configValue); +// } +// } +// +// @Test +// public void noContentSearchProfile() { +// assertOK(client.ftCreate(index, TextField.of("t"))); +// client.hset("1", Collections.singletonMap("t", "foo")); +// client.hset("2", Collections.singletonMap("t", "bar")); +// +// Map.Entry> profile = client.ftProfileSearch(index, +// FTProfileParams.profileParams(), "foo -@t:baz", FTSearchParams.searchParams().noContent()); +// +// Map depth0 = protocol != RedisProtocol.RESP3 +// ? (Map) profile.getValue().get("Iterators profile") +// : ((List>) profile.getValue().get("Iterators profile")).get(0); +// +// assertEquals("INTERSECT", depth0.get("Type")); +// List> depth0_children = (List>) depth0.get("Child iterators"); +// assertEquals("TEXT", depth0_children.get(0).get("Type")); +// Map depth1 = depth0_children.get(1); +// assertEquals("NOT", depth1.get("Type")); +// if (protocol != RedisProtocol.RESP3) { +// List> depth1_children = (List>) depth1.get("Child iterators"); +// assertEquals(1, depth1_children.size()); +// assertEquals("EMPTY", depth1_children.get(0).get("Type")); +// } else { +// assertEquals("EMPTY", ((Map) depth1.get("Child iterator")).get("Type")); +// } +// } +// +// @Test +// public void deepReplySearchProfile() { +// assertOK(client.ftCreate(index, TextField.of("t"))); +// client.hset("1", Collections.singletonMap("t", "hello")); +// client.hset("2", Collections.singletonMap("t", "world")); +// +// Map.Entry> profile +// = client.ftProfileSearch(index, FTProfileParams.profileParams(), +// "hello(hello(hello(hello(hello(hello)))))", FTSearchParams.searchParams().noContent()); +// +// Map depth0 = protocol != RedisProtocol.RESP3 +// ? (Map) profile.getValue().get("Iterators profile") +// : ((List>) profile.getValue().get("Iterators profile")).get(0); +// +// assertEquals("INTERSECT", depth0.get("Type")); +// List> depth0_children = (List>) depth0.get("Child iterators"); +// assertEquals("TEXT", depth0_children.get(0).get("Type")); +// Map depth1 = depth0_children.get(1); +// assertEquals("INTERSECT", depth1.get("Type")); +// List> depth1_children = (List>) depth1.get("Child iterators"); +// assertEquals("TEXT", depth1_children.get(0).get("Type")); +// Map depth2 = depth1_children.get(1); +// assertEquals("INTERSECT", depth2.get("Type")); +// List> depth2_children = (List>) depth2.get("Child iterators"); +// assertEquals("TEXT", depth2_children.get(0).get("Type")); +// Map depth3 = depth2_children.get(1); +// assertEquals("INTERSECT", depth3.get("Type")); +// List> depth3_children = (List>) depth3.get("Child iterators"); +// assertEquals("TEXT", depth3_children.get(0).get("Type")); +// Map depth4 = depth3_children.get(1); +// assertEquals("INTERSECT", depth4.get("Type")); +// List> depth4_children = (List>) depth4.get("Child iterators"); +// assertEquals("TEXT", depth4_children.get(0).get("Type")); +// Map depth5 = depth4_children.get(1); +// assertEquals("TEXT", depth5.get("Type")); +// assertNull(depth5.get("Child iterators")); +// } +// +// @Test +// public void limitedSearchProfile() { +// assertOK(client.ftCreate(index, TextField.of("t"))); +// client.hset("1", Collections.singletonMap("t", "hello")); +// client.hset("2", Collections.singletonMap("t", "hell")); +// client.hset("3", Collections.singletonMap("t", "help")); +// client.hset("4", Collections.singletonMap("t", "helowa")); +// +// Map.Entry> profile = client.ftProfileSearch(index, +// FTProfileParams.profileParams().limited(), "%hell% hel*", FTSearchParams.searchParams().noContent()); +// +// Map depth0 = protocol != RedisProtocol.RESP3 +// ? (Map) profile.getValue().get("Iterators profile") +// : ((List>) profile.getValue().get("Iterators profile")).get(0); +// +// assertEquals("INTERSECT", depth0.get("Type")); +// assertEquals(3L, depth0.get("Counter")); +// +// List> depth0_children = (List>) depth0.get("Child iterators"); +// assertFalse(depth0_children.isEmpty()); +// for (Map depth1 : depth0_children) { +// assertEquals("UNION", depth1.get("Type")); +// assertNotNull(depth1.get("Query type")); +// if (protocol != RedisProtocol.RESP3) { +// List depth1_children = (List) depth1.get("Child iterators"); +// assertEquals(1, depth1_children.size()); +// assertSame(String.class, depth1_children.get(0).getClass()); +// } else { +// assertSame(String.class, depth1.get("Child iterators").getClass()); +// } +// } +// } +// +// @Test +// public void list() { +// assertEquals(Collections.emptySet(), client.ftList()); +// +// final int count = 20; +// Set names = new HashSet<>(); +// for (int i = 0; i < count; i++) { +// final String name = "idx" + i; +// assertOK(client.ftCreate(name, TextField.of("t" + i))); +// names.add(name); +// } +// assertEquals(names, client.ftList()); +// } +// +// @Test +// public void broadcast() { +// String reply = client.ftCreate(index, TextField.of("t")); +// assertOK(reply); +// } +// +// @Test +// public void searchIteration() { +// assertOK(client.ftCreate(index, FTCreateParams.createParams(), +// TextField.of("first"), TextField.of("last"), NumericField.of("age"))); +// +// client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); +// client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); +// client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); +// client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); +// client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); +// client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); +// client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); +// +// FtSearchIteration search = client.ftSearchIteration(3, index, "*", FTSearchParams.searchParams()); +// int total = 0; +// while (!search.isIterationCompleted()) { +// SearchResult result = search.nextBatch(); +// int count = result.getDocuments().size(); +// assertThat(count, Matchers.lessThanOrEqualTo(3)); +// total += count; +// } +// assertEquals(7, total); +// } +// +// @Test +// public void searchIterationCollect() { +// assertOK(client.ftCreate(index, FTCreateParams.createParams(), +// TextField.of("first"), TextField.of("last"), NumericField.of("age"))); +// +// client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); +// client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); +// client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); +// client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); +// client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); +// client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); +// client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); +// +// ArrayList collect = new ArrayList<>(); +// client.ftSearchIteration(3, index, "*", FTSearchParams.searchParams()).collect(collect); +// assertEquals(7, collect.size()); +// assertEquals(Arrays.asList("profesor:5555", "student:1111", "pupil:2222", "student:3333", +// "pupil:4444", "student:5555", "teacher:6666").stream().collect(Collectors.toSet()), +// collect.stream().map(Document::getId).collect(Collectors.toSet())); +// } +// +// @Test +// public void escapeUtil() { +// assertOK(client.ftCreate(index, TextField.of("txt"))); +// +// client.hset("doc1", "txt", RediSearchUtil.escape("hello-world")); +// assertNotEquals("hello-world", client.hget("doc1", "txt")); +// assertEquals("hello-world", RediSearchUtil.unescape(client.hget("doc1", "txt"))); +// +// SearchResult resultNoEscape = client.ftSearch(index, "hello-world"); +// assertEquals(0, resultNoEscape.getTotalResults()); +// +// SearchResult resultEscaped = client.ftSearch(index, RediSearchUtil.escapeQuery("hello-world")); +// assertEquals(1, resultEscaped.getTotalResults()); +// } +// +// @Test +// public void escapeMapUtil() { +// client.hset("doc2", RediSearchUtil.toStringMap(Collections.singletonMap("txt", "hello-world"), true)); +// assertNotEquals("hello-world", client.hget("doc2", "txt")); +// assertEquals("hello-world", RediSearchUtil.unescape(client.hget("doc2", "txt"))); +// } +//} From 3611c0ff92eac3e9358792d030c0dac2ba668560 Mon Sep 17 00:00:00 2001 From: c00603587 Date: Fri, 20 Oct 2023 17:05:13 +0800 Subject: [PATCH 14/19] 1 Signed-off-by: c00603587 --- .../GeoShapeFieldsUsageInRediSearch.java | 210 +- .../modules/search/SearchWithParamsTest.java | 2654 ++++++++--------- 2 files changed, 1432 insertions(+), 1432 deletions(-) diff --git a/src/test/java/redis/clients/jedis/examples/GeoShapeFieldsUsageInRediSearch.java b/src/test/java/redis/clients/jedis/examples/GeoShapeFieldsUsageInRediSearch.java index 61f58719d7..e0a820938e 100644 --- a/src/test/java/redis/clients/jedis/examples/GeoShapeFieldsUsageInRediSearch.java +++ b/src/test/java/redis/clients/jedis/examples/GeoShapeFieldsUsageInRediSearch.java @@ -1,105 +1,105 @@ -//package redis.clients.jedis.examples; -// -//import org.locationtech.jts.geom.Coordinate; -//import org.locationtech.jts.geom.Geometry; -//import org.locationtech.jts.geom.GeometryFactory; -//import org.locationtech.jts.geom.Polygon; -//import org.locationtech.jts.io.ParseException; -//import org.locationtech.jts.io.WKTReader; -// -//import redis.clients.jedis.HostAndPort; -//import redis.clients.jedis.JedisPooled; -//import redis.clients.jedis.UnifiedJedis; -//import redis.clients.jedis.search.FTSearchParams; -//import redis.clients.jedis.search.SearchResult; -//import redis.clients.jedis.search.schemafields.GeoShapeField; -// -//import static java.util.Collections.singletonMap; -//import static org.junit.Assert.assertEquals; -//import static redis.clients.jedis.search.RediSearchUtil.toStringMap; -// -///** -// * As of RediSearch 2.8.4, advanced GEO querying with GEOSHAPE fields is supported. -// * -// * Any object/library producing a -// * well-known -// * text (WKT) in {@code toString()} method can be used. -// * -// * This example uses the JTS library. -// *
-// * {@code
-// * 
-// *   org.locationtech.jts
-// *   jts-core
-// *   1.19.0
-// * 
-// * }
-// * 
-// */ -//public class GeoShapeFieldsUsageInRediSearch { -// -// public static void main(String[] args) { -// -// // We'll create geometry objects with GeometryFactory -// final GeometryFactory factory = new GeometryFactory(); -// -// final String host = "localhost"; -// final int port = 6379; -// final HostAndPort address = new HostAndPort(host, port); -// -// UnifiedJedis client = new JedisPooled(address); -// // client.setDefaultSearchDialect(3); // we can set default search dialect for the client (UnifiedJedis) object -// // to avoid setting dialect in every query. -// -// // creating index -// client.ftCreate("geometry-index", -// GeoShapeField.of("geometry", GeoShapeField.CoordinateSystem.SPHERICAL) // 'SPHERICAL' is for geographic (lon, lat). -// // 'FLAT' coordinate system also available for cartesian (X,Y). -// ); -// -// // preparing data -// final Polygon small = factory.createPolygon( -// new Coordinate[]{new Coordinate(34.9001, 29.7001), -// new Coordinate(34.9001, 29.7100), new Coordinate(34.9100, 29.7100), -// new Coordinate(34.9100, 29.7001), new Coordinate(34.9001, 29.7001)} -// ); -// -// client.hset("small", toStringMap(singletonMap("geometry", small))); // setting data -// -// final Polygon large = factory.createPolygon( -// new Coordinate[]{new Coordinate(34.9001, 29.7001), -// new Coordinate(34.9001, 29.7200), new Coordinate(34.9200, 29.7200), -// new Coordinate(34.9200, 29.7001), new Coordinate(34.9001, 29.7001)} -// ); -// -// client.hset("large", toStringMap(singletonMap("geometry", large))); // setting data -// -// // searching -// final Polygon within = factory.createPolygon( -// new Coordinate[]{new Coordinate(34.9000, 29.7000), -// new Coordinate(34.9000, 29.7150), new Coordinate(34.9150, 29.7150), -// new Coordinate(34.9150, 29.7000), new Coordinate(34.9000, 29.7000)} -// ); -// -// SearchResult res = client.ftSearch("geometry-index", -// "@geometry:[within $poly]", // querying 'within' condition. -// // RediSearch also supports 'contains' condition. -// FTSearchParams.searchParams() -// .addParam("poly", within) -// .dialect(3) // DIALECT '3' is required for this query -// ); -// assertEquals(1, res.getTotalResults()); -// assertEquals(1, res.getDocuments().size()); -// -// // We can parse geometry objects with WKTReader -// try { -// final WKTReader reader = new WKTReader(); -// Geometry object = reader.read(res.getDocuments().get(0).getString("geometry")); -// assertEquals(small, object); -// } catch (ParseException ex) { -// ex.printStackTrace(System.err); -// } -// } -// -// // Note: As of RediSearch 2.8.4, only POLYGON and POINT objects are supported. -//} +package redis.clients.jedis.examples; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKTReader; + +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.UnifiedJedis; +import redis.clients.jedis.search.FTSearchParams; +import redis.clients.jedis.search.SearchResult; +import redis.clients.jedis.search.schemafields.GeoShapeField; + +import static java.util.Collections.singletonMap; +import static org.junit.Assert.assertEquals; +import static redis.clients.jedis.search.RediSearchUtil.toStringMap; + +/** + * As of RediSearch 2.8.4, advanced GEO querying with GEOSHAPE fields is supported. + * + * Any object/library producing a + * well-known + * text (WKT) in {@code toString()} method can be used. + * + * This example uses the JTS library. + *
+ * {@code
+ * 
+ *   org.locationtech.jts
+ *   jts-core
+ *   1.19.0
+ * 
+ * }
+ * 
+ */ +public class GeoShapeFieldsUsageInRediSearch { + + public static void main(String[] args) { + + // We'll create geometry objects with GeometryFactory + final GeometryFactory factory = new GeometryFactory(); + + final String host = "localhost"; + final int port = 6379; + final HostAndPort address = new HostAndPort(host, port); + + UnifiedJedis client = new JedisPooled(address); + // client.setDefaultSearchDialect(3); // we can set default search dialect for the client (UnifiedJedis) object + // to avoid setting dialect in every query. + + // creating index + client.ftCreate("geometry-index", + GeoShapeField.of("geometry", GeoShapeField.CoordinateSystem.SPHERICAL) // 'SPHERICAL' is for geographic (lon, lat). + // 'FLAT' coordinate system also available for cartesian (X,Y). + ); + + // preparing data + final Polygon small = factory.createPolygon( + new Coordinate[]{new Coordinate(34.9001, 29.7001), + new Coordinate(34.9001, 29.7100), new Coordinate(34.9100, 29.7100), + new Coordinate(34.9100, 29.7001), new Coordinate(34.9001, 29.7001)} + ); + + client.hset("small", toStringMap(singletonMap("geometry", small))); // setting data + + final Polygon large = factory.createPolygon( + new Coordinate[]{new Coordinate(34.9001, 29.7001), + new Coordinate(34.9001, 29.7200), new Coordinate(34.9200, 29.7200), + new Coordinate(34.9200, 29.7001), new Coordinate(34.9001, 29.7001)} + ); + + client.hset("large", toStringMap(singletonMap("geometry", large))); // setting data + + // searching + final Polygon within = factory.createPolygon( + new Coordinate[]{new Coordinate(34.9000, 29.7000), + new Coordinate(34.9000, 29.7150), new Coordinate(34.9150, 29.7150), + new Coordinate(34.9150, 29.7000), new Coordinate(34.9000, 29.7000)} + ); + + SearchResult res = client.ftSearch("geometry-index", + "@geometry:[within $poly]", // querying 'within' condition. + // RediSearch also supports 'contains' condition. + FTSearchParams.searchParams() + .addParam("poly", within) + .dialect(3) // DIALECT '3' is required for this query + ); + assertEquals(1, res.getTotalResults()); + assertEquals(1, res.getDocuments().size()); + + // We can parse geometry objects with WKTReader + try { + final WKTReader reader = new WKTReader(); + Geometry object = reader.read(res.getDocuments().get(0).getString("geometry")); + assertEquals(small, object); + } catch (ParseException ex) { + ex.printStackTrace(System.err); + } + } + + // Note: As of RediSearch 2.8.4, only POLYGON and POINT objects are supported. +} diff --git a/src/test/java/redis/clients/jedis/modules/search/SearchWithParamsTest.java b/src/test/java/redis/clients/jedis/modules/search/SearchWithParamsTest.java index ded95a7d20..792391b775 100644 --- a/src/test/java/redis/clients/jedis/modules/search/SearchWithParamsTest.java +++ b/src/test/java/redis/clients/jedis/modules/search/SearchWithParamsTest.java @@ -1,451 +1,461 @@ -//package redis.clients.jedis.modules.search; -// -//import static org.junit.Assert.*; -//import static redis.clients.jedis.util.AssertUtil.assertOK; -// -//import java.util.*; -//import java.util.stream.Collectors; -//import org.hamcrest.Matchers; -//import org.junit.BeforeClass; -//import org.junit.Test; -// -//import org.locationtech.jts.geom.Coordinate; -//import org.locationtech.jts.geom.GeometryFactory; -//import org.locationtech.jts.geom.Point; -//import org.locationtech.jts.geom.Polygon; -//import org.locationtech.jts.io.ParseException; -//import org.locationtech.jts.io.WKTReader; -// -//import redis.clients.jedis.GeoCoordinate; -//import redis.clients.jedis.RedisProtocol; -//import redis.clients.jedis.args.GeoUnit; -//import redis.clients.jedis.args.SortingOrder; -//import redis.clients.jedis.exceptions.JedisDataException; -//import redis.clients.jedis.json.Path; -//import redis.clients.jedis.search.*; -//import redis.clients.jedis.search.schemafields.*; -//import redis.clients.jedis.search.schemafields.VectorField.VectorAlgorithm; -//import redis.clients.jedis.modules.RedisModuleCommandsTestBase; -// -//public class SearchWithParamsTest extends RedisModuleCommandsTestBase { -// -// private static final String index = "testindex"; -// -// @BeforeClass -// public static void prepare() { -// RedisModuleCommandsTestBase.prepare(); -// } -//// -//// @AfterClass -//// public static void tearDown() { -////// RedisModuleCommandsTestBase.tearDown(); -//// } -// -// private void addDocument(String key, Map map) { -// client.hset(key, RediSearchUtil.toStringMap(map)); -// } -// -// private static Map toMap(Object... values) { -// Map map = new HashMap<>(); -// for (int i = 0; i < values.length; i += 2) { -// map.put((String) values[i], values[i + 1]); -// } -// return map; -// } -// -// private static Map toMap(String... values) { -// Map map = new HashMap<>(); -// for (int i = 0; i < values.length; i += 2) { -// map.put(values[i], values[i + 1]); -// } -// return map; -// } -// -// @Test -// public void create() { -// assertOK(client.ftCreate(index, -// FTCreateParams.createParams() -// .filter("@age>16") -// .prefix("student:", "pupil:"), -// TextField.of("first"), TextField.of("last"), NumericField.of("age"))); -// -// client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); -// client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); -// client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); -// client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); -// client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); -// client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); -// client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); -// -// SearchResult noFilters = client.ftSearch(index); -// assertEquals(4, noFilters.getTotalResults()); -// -// SearchResult res1 = client.ftSearch(index, "@first:Jo*"); -// assertEquals(2, res1.getTotalResults()); -// -// SearchResult res2 = client.ftSearch(index, "@first:Pat"); -// assertEquals(1, res2.getTotalResults()); -// -// SearchResult res3 = client.ftSearch(index, "@last:Rod"); -// assertEquals(0, res3.getTotalResults()); -// } -// -// @Test -// public void createNoParams() { -// assertOK(client.ftCreate(index, -// TextField.of("first").weight(1), -// TextField.of("last").weight(1), -// NumericField.of("age"))); -// -// addDocument("student:1111", toMap("first", "Joe", "last", "Dod", "age", 18)); -// addDocument("student:3333", toMap("first", "El", "last", "Mark", "age", 17)); -// addDocument("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", 21)); -// addDocument("student:5555", toMap("first", "Joen", "last", "Ko", "age", 20)); -// -// SearchResult noFilters = client.ftSearch(index); -// assertEquals(4, noFilters.getTotalResults()); -// -// SearchResult res1 = client.ftSearch(index, "@first:Jo*"); -// assertEquals(2, res1.getTotalResults()); -// -// SearchResult res2 = client.ftSearch(index, "@first:Pat"); -// assertEquals(1, res2.getTotalResults()); -// -// SearchResult res3 = client.ftSearch(index, "@last:Rod"); -// assertEquals(0, res3.getTotalResults()); -// } -// -// @Test -// public void createWithFieldNames() { -// assertOK(client.ftCreate(index, -// FTCreateParams.createParams() -// .addPrefix("student:").addPrefix("pupil:"), -// TextField.of("first").as("given"), -// TextField.of(FieldName.of("last").as("family")))); -// -// client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); -// client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); -// client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); -// client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); -// client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); -// client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); -// client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); -// -// SearchResult noFilters = client.ftSearch(index); -// assertEquals(5, noFilters.getTotalResults()); -// -// SearchResult asFirst = client.ftSearch(index, "@first:Jo*"); -// assertEquals(0, asFirst.getTotalResults()); -// -// SearchResult asGiven = client.ftSearch(index, "@given:Jo*"); -// assertEquals(2, asGiven.getTotalResults()); -// -// SearchResult nonLast = client.ftSearch(index, "@last:Rod"); -// assertEquals(0, nonLast.getTotalResults()); -// -// SearchResult asFamily = client.ftSearch(index, "@family:Rod"); -// assertEquals(1, asFamily.getTotalResults()); -// } -// -// @Test -// public void alterAdd() { -// assertOK(client.ftCreate(index, TextField.of("title"))); -// -// Map fields = new HashMap<>(); -// fields.put("title", "hello world"); -// for (int i = 0; i < 100; i++) { -// addDocument(String.format("doc%d", i), fields); -// } -// SearchResult res = client.ftSearch(index, "hello world"); -// assertEquals(100, res.getTotalResults()); -// -// assertOK(client.ftAlter(index, -// TagField.of("tags"), -// TextField.of("name").weight(0.5))); -// -// for (int i = 0; i < 100; i++) { -// Map fields2 = new HashMap<>(); -// fields2.put("name", "name" + i); -// fields2.put("tags", String.format("tagA,tagB,tag%d", i)); -// addDocument(String.format("doc%d", i), fields2); -// } -// SearchResult res2 = client.ftSearch(index, "@tags:{tagA}"); -// assertEquals(100, res2.getTotalResults()); -// } -// -// @Test -// public void search() { -// assertOK(client.ftCreate(index, FTCreateParams.createParams(), -// TextField.of("title"), TextField.of("body"))); -// -// Map fields = new HashMap<>(); -// fields.put("title", "hello world"); -// fields.put("body", "lorem ipsum"); -// for (int i = 0; i < 100; i++) { -// addDocument(String.format("doc%d", i), fields); -// } -// -// SearchResult res = client.ftSearch(index, "hello world", -// FTSearchParams.searchParams().limit(0, 5).withScores()); -// assertEquals(100, res.getTotalResults()); -// assertEquals(5, res.getDocuments().size()); -// for (Document d : res.getDocuments()) { -// assertTrue(d.getId().startsWith("doc")); -// assertTrue(d.getScore() < 100); -// } -// -// client.del("doc0"); -// -// res = client.ftSearch(index, "hello world"); -// assertEquals(99, res.getTotalResults()); -// -// assertEquals("OK", client.ftDropIndex(index)); -// try { -// client.ftSearch(index, "hello world"); -// fail(); -// } catch (JedisDataException e) { -// } -// } -// -// @Test -// public void numericFilter() { -// assertOK(client.ftCreate(index, TextField.of("title"), NumericField.of("price"))); -// -// Map fields = new HashMap<>(); -// fields.put("title", "hello world"); -// -// for (int i = 0; i < 100; i++) { -// fields.put("price", i); -// addDocument(String.format("doc%d", i), fields); -// } -// -// SearchResult res = client.ftSearch(index, "hello world", -// FTSearchParams.searchParams().filter("price", 0, 49)); -// assertEquals(50, res.getTotalResults()); -// assertEquals(10, res.getDocuments().size()); -// for (Document d : res.getDocuments()) { -// long price = Long.valueOf((String) d.get("price")); -// assertTrue(price >= 0); -// assertTrue(price <= 49); -// } -// -// res = client.ftSearch(index, "hello world", -// FTSearchParams.searchParams().filter("price", 0, true, 49, true)); -// assertEquals(48, res.getTotalResults()); -// assertEquals(10, res.getDocuments().size()); -// for (Document d : res.getDocuments()) { -// long price = Long.valueOf((String) d.get("price")); -// assertTrue(price > 0); -// assertTrue(price < 49); -// } -// res = client.ftSearch(index, "hello world", -// FTSearchParams.searchParams().filter("price", 50, 100)); -// assertEquals(50, res.getTotalResults()); -// assertEquals(10, res.getDocuments().size()); -// for (Document d : res.getDocuments()) { -// long price = Long.valueOf((String) d.get("price")); -// assertTrue(price >= 50); -// assertTrue(price <= 100); -// } -// -// res = client.ftSearch(index, "hello world", -// FTSearchParams.searchParams() -// .filter("price", 20, Double.POSITIVE_INFINITY)); -// assertEquals(80, res.getTotalResults()); -// assertEquals(10, res.getDocuments().size()); -// -// res = client.ftSearch(index, "hello world", -// FTSearchParams.searchParams() -// .filter("price", Double.NEGATIVE_INFINITY, 10)); -// assertEquals(11, res.getTotalResults()); -// assertEquals(10, res.getDocuments().size()); -// -// } -// -// @Test -// public void stopwords() { -// assertOK(client.ftCreate(index, -// FTCreateParams.createParams() -// .stopwords("foo", "bar", "baz"), -// TextField.of("title"))); -// -// Map fields = new HashMap<>(); -// fields.put("title", "hello world foo bar"); -// addDocument("doc1", fields); -// SearchResult res = client.ftSearch(index, "hello world"); -// assertEquals(1, res.getTotalResults()); -// res = client.ftSearch(index, "foo bar"); -// assertEquals(0, res.getTotalResults()); -// } -// -// @Test -// public void noStopwords() { -// assertOK(client.ftCreate(index, -// FTCreateParams.createParams().noStopwords(), -// TextField.of("title"))); -// -// Map fields = new HashMap<>(); -// fields.put("title", "hello world foo bar"); -// fields.put("title", "hello world foo bar to be or not to be"); -// addDocument("doc1", fields); -// -// assertEquals(1, client.ftSearch(index, "hello world").getTotalResults()); -// assertEquals(1, client.ftSearch(index, "foo bar").getTotalResults()); -// assertEquals(1, client.ftSearch(index, "to be or not to be").getTotalResults()); -// } -// -// @Test -// public void geoFilter() { -// assertOK(client.ftCreate(index, TextField.of("title"), GeoField.of("loc"))); -// -// Map fields = new HashMap<>(); -// fields.put("title", "hello world"); -// fields.put("loc", "-0.441,51.458"); -// addDocument("doc1", fields); -// -// fields.put("loc", "-0.1,51.2"); -// addDocument("doc2", fields); -// -// SearchResult res = client.ftSearch(index, "hello world", -// FTSearchParams.searchParams(). -// geoFilter("loc", -0.44, 51.45, 10, GeoUnit.KM)); -// assertEquals(1, res.getTotalResults()); -// -// res = client.ftSearch(index, "hello world", -// FTSearchParams.searchParams(). -// geoFilter("loc", -0.44, 51.45, 100, GeoUnit.KM)); -// assertEquals(2, res.getTotalResults()); -// } -// -// @Test -// public void geoFilterAndGeoCoordinateObject() { -// assertOK(client.ftCreate(index, TextField.of("title"), GeoField.of("loc"))); -// -// Map fields = new HashMap<>(); -// fields.put("title", "hello world"); -// fields.put("loc", new GeoCoordinate(-0.441, 51.458)); -// addDocument("doc1", fields); -// -// fields.put("loc", new GeoCoordinate(-0.1, 51.2)); -// addDocument("doc2", fields); -// -// SearchResult res = client.ftSearch(index, "hello world", -// FTSearchParams.searchParams() -// .geoFilter(new FTSearchParams.GeoFilter("loc", -0.44, 51.45, 10, GeoUnit.KM))); -// assertEquals(1, res.getTotalResults()); -// -// res = client.ftSearch(index, "hello world", -// FTSearchParams.searchParams() -// .geoFilter(new FTSearchParams.GeoFilter("loc", -0.44, 51.45, 100, GeoUnit.KM))); -// assertEquals(2, res.getTotalResults()); -// } -// -// @Test -// public void geoShapeFilterSpherical() throws ParseException { -// final WKTReader reader = new WKTReader(); -// final GeometryFactory factory = new GeometryFactory(); -// -// assertOK(client.ftCreate(index, GeoShapeField.of("geom", GeoShapeField.CoordinateSystem.SPHERICAL))); -// -// // polygon type -// final Polygon small = factory.createPolygon(new Coordinate[]{new Coordinate(34.9001, 29.7001), -// new Coordinate(34.9001, 29.7100), new Coordinate(34.9100, 29.7100), -// new Coordinate(34.9100, 29.7001), new Coordinate(34.9001, 29.7001)}); -// client.hset("small", RediSearchUtil.toStringMap(Collections.singletonMap("geom", small))); -// -// final Polygon large = factory.createPolygon(new Coordinate[]{new Coordinate(34.9001, 29.7001), -// new Coordinate(34.9001, 29.7200), new Coordinate(34.9200, 29.7200), -// new Coordinate(34.9200, 29.7001), new Coordinate(34.9001, 29.7001)}); -// client.hset("large", RediSearchUtil.toStringMap(Collections.singletonMap("geom", large))); -// -// // within condition -// final Polygon within = factory.createPolygon(new Coordinate[]{new Coordinate(34.9000, 29.7000), -// new Coordinate(34.9000, 29.7150), new Coordinate(34.9150, 29.7150), -// new Coordinate(34.9150, 29.7000), new Coordinate(34.9000, 29.7000)}); -// -// SearchResult res = client.ftSearch(index, "@geom:[within $poly]", -// FTSearchParams.searchParams().addParam("poly", within).dialect(3)); -// assertEquals(1, res.getTotalResults()); -// assertEquals(1, res.getDocuments().size()); -// assertEquals(small, reader.read(res.getDocuments().get(0).getString("geom"))); -// -// // contains condition -// final Polygon contains = factory.createPolygon(new Coordinate[]{new Coordinate(34.9002, 29.7002), -// new Coordinate(34.9002, 29.7050), new Coordinate(34.9050, 29.7050), -// new Coordinate(34.9050, 29.7002), new Coordinate(34.9002, 29.7002)}); -// -// res = client.ftSearch(index, "@geom:[contains $poly]", -// FTSearchParams.searchParams().addParam("poly", contains).dialect(3)); -// assertEquals(2, res.getTotalResults()); -// assertEquals(2, res.getDocuments().size()); -// -// // point type -// final Point point = factory.createPoint(new Coordinate(34.9010, 29.7010)); -// client.hset("point", RediSearchUtil.toStringMap(Collections.singletonMap("geom", point))); -// -// res = client.ftSearch(index, "@geom:[within $poly]", -// FTSearchParams.searchParams().addParam("poly", within).dialect(3)); -// assertEquals(2, res.getTotalResults()); -// assertEquals(2, res.getDocuments().size()); +package redis.clients.jedis.modules.search; + +import static org.junit.Assert.*; +import static redis.clients.jedis.util.AssertUtil.assertOK; + +import java.util.*; +import java.util.stream.Collectors; +import org.hamcrest.Matchers; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Point; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKTReader; + +import redis.clients.jedis.GeoCoordinate; +import redis.clients.jedis.RedisProtocol; +import redis.clients.jedis.args.GeoUnit; +import redis.clients.jedis.args.SortingOrder; +import redis.clients.jedis.exceptions.JedisDataException; +import redis.clients.jedis.json.Path; +import redis.clients.jedis.search.*; +import redis.clients.jedis.search.schemafields.*; +import redis.clients.jedis.search.schemafields.VectorField.VectorAlgorithm; +import redis.clients.jedis.modules.RedisModuleCommandsTestBase; + +public class SearchWithParamsTest extends RedisModuleCommandsTestBase { + + private static final String index = "testindex"; + + @BeforeClass + public static void prepare() { + RedisModuleCommandsTestBase.prepare(); + } +// +// @AfterClass +// public static void tearDown() { +//// RedisModuleCommandsTestBase.tearDown(); // } + + private void addDocument(String key, Map map) { + client.hset(key, RediSearchUtil.toStringMap(map)); + } + + private static Map toMap(Object... values) { + Map map = new HashMap<>(); + for (int i = 0; i < values.length; i += 2) { + map.put((String) values[i], values[i + 1]); + } + return map; + } + + private static Map toMap(String... values) { + Map map = new HashMap<>(); + for (int i = 0; i < values.length; i += 2) { + map.put(values[i], values[i + 1]); + } + return map; + } + + @Test + public void create() { + assertOK(client.ftCreate(index, + FTCreateParams.createParams() + .filter("@age>16") + .prefix("student:", "pupil:"), + TextField.of("first"), TextField.of("last"), NumericField.of("age"))); + + client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); + client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); + client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); + client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); + client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); + client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); + client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); + + SearchResult noFilters = client.ftSearch(index); + assertEquals(4, noFilters.getTotalResults()); + + SearchResult res1 = client.ftSearch(index, "@first:Jo*"); + assertEquals(2, res1.getTotalResults()); + + SearchResult res2 = client.ftSearch(index, "@first:Pat"); + assertEquals(1, res2.getTotalResults()); + + SearchResult res3 = client.ftSearch(index, "@last:Rod"); + assertEquals(0, res3.getTotalResults()); + } + + @Test + public void createNoParams() { + assertOK(client.ftCreate(index, + TextField.of("first").weight(1), + TextField.of("last").weight(1), + NumericField.of("age"))); + + addDocument("student:1111", toMap("first", "Joe", "last", "Dod", "age", 18)); + addDocument("student:3333", toMap("first", "El", "last", "Mark", "age", 17)); + addDocument("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", 21)); + addDocument("student:5555", toMap("first", "Joen", "last", "Ko", "age", 20)); + + SearchResult noFilters = client.ftSearch(index); + assertEquals(4, noFilters.getTotalResults()); + + SearchResult res1 = client.ftSearch(index, "@first:Jo*"); + assertEquals(2, res1.getTotalResults()); + + SearchResult res2 = client.ftSearch(index, "@first:Pat"); + assertEquals(1, res2.getTotalResults()); + + SearchResult res3 = client.ftSearch(index, "@last:Rod"); + assertEquals(0, res3.getTotalResults()); + } + + @Test + public void createWithFieldNames() { + assertOK(client.ftCreate(index, + FTCreateParams.createParams() + .addPrefix("student:").addPrefix("pupil:"), + TextField.of("first").as("given"), + TextField.of(FieldName.of("last").as("family")))); + + client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); + client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); + client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); + client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); + client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); + client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); + client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); + + SearchResult noFilters = client.ftSearch(index); + assertEquals(5, noFilters.getTotalResults()); + + SearchResult asFirst = client.ftSearch(index, "@first:Jo*"); + assertEquals(0, asFirst.getTotalResults()); + + SearchResult asGiven = client.ftSearch(index, "@given:Jo*"); + assertEquals(2, asGiven.getTotalResults()); + + SearchResult nonLast = client.ftSearch(index, "@last:Rod"); + assertEquals(0, nonLast.getTotalResults()); + + SearchResult asFamily = client.ftSearch(index, "@family:Rod"); + assertEquals(1, asFamily.getTotalResults()); + } + + @Test + public void alterAdd() { + assertOK(client.ftCreate(index, TextField.of("title"))); + + Map fields = new HashMap<>(); + fields.put("title", "hello world"); + for (int i = 0; i < 100; i++) { + addDocument(String.format("doc%d", i), fields); + } + SearchResult res = client.ftSearch(index, "hello world"); + assertEquals(100, res.getTotalResults()); + + assertOK(client.ftAlter(index, + TagField.of("tags"), + TextField.of("name").weight(0.5))); + + for (int i = 0; i < 100; i++) { + Map fields2 = new HashMap<>(); + fields2.put("name", "name" + i); + fields2.put("tags", String.format("tagA,tagB,tag%d", i)); + addDocument(String.format("doc%d", i), fields2); + } + SearchResult res2 = client.ftSearch(index, "@tags:{tagA}"); + assertEquals(100, res2.getTotalResults()); + } + + @Test + public void search() { + assertOK(client.ftCreate(index, FTCreateParams.createParams(), + TextField.of("title"), TextField.of("body"))); + + Map fields = new HashMap<>(); + fields.put("title", "hello world"); + fields.put("body", "lorem ipsum"); + for (int i = 0; i < 100; i++) { + addDocument(String.format("doc%d", i), fields); + } + + SearchResult res = client.ftSearch(index, "hello world", + FTSearchParams.searchParams().limit(0, 5).withScores()); + assertEquals(100, res.getTotalResults()); + assertEquals(5, res.getDocuments().size()); + for (Document d : res.getDocuments()) { + assertTrue(d.getId().startsWith("doc")); + assertTrue(d.getScore() < 100); + } + + client.del("doc0"); + + res = client.ftSearch(index, "hello world"); + assertEquals(99, res.getTotalResults()); + + assertEquals("OK", client.ftDropIndex(index)); + try { + client.ftSearch(index, "hello world"); + fail(); + } catch (JedisDataException e) { + } + } + + @Test + public void numericFilter() { + assertOK(client.ftCreate(index, TextField.of("title"), NumericField.of("price"))); + + Map fields = new HashMap<>(); + fields.put("title", "hello world"); + + for (int i = 0; i < 100; i++) { + fields.put("price", i); + addDocument(String.format("doc%d", i), fields); + } + + SearchResult res = client.ftSearch(index, "hello world", + FTSearchParams.searchParams().filter("price", 0, 49)); + assertEquals(50, res.getTotalResults()); + assertEquals(10, res.getDocuments().size()); + for (Document d : res.getDocuments()) { + long price = Long.valueOf((String) d.get("price")); + assertTrue(price >= 0); + assertTrue(price <= 49); + } + + res = client.ftSearch(index, "hello world", + FTSearchParams.searchParams().filter("price", 0, true, 49, true)); + assertEquals(48, res.getTotalResults()); + assertEquals(10, res.getDocuments().size()); + for (Document d : res.getDocuments()) { + long price = Long.valueOf((String) d.get("price")); + assertTrue(price > 0); + assertTrue(price < 49); + } + res = client.ftSearch(index, "hello world", + FTSearchParams.searchParams().filter("price", 50, 100)); + assertEquals(50, res.getTotalResults()); + assertEquals(10, res.getDocuments().size()); + for (Document d : res.getDocuments()) { + long price = Long.valueOf((String) d.get("price")); + assertTrue(price >= 50); + assertTrue(price <= 100); + } + + res = client.ftSearch(index, "hello world", + FTSearchParams.searchParams() + .filter("price", 20, Double.POSITIVE_INFINITY)); + assertEquals(80, res.getTotalResults()); + assertEquals(10, res.getDocuments().size()); + + res = client.ftSearch(index, "hello world", + FTSearchParams.searchParams() + .filter("price", Double.NEGATIVE_INFINITY, 10)); + assertEquals(11, res.getTotalResults()); + assertEquals(10, res.getDocuments().size()); + + } + + @Test + public void stopwords() { + assertOK(client.ftCreate(index, + FTCreateParams.createParams() + .stopwords("foo", "bar", "baz"), + TextField.of("title"))); + + Map fields = new HashMap<>(); + fields.put("title", "hello world foo bar"); + addDocument("doc1", fields); + SearchResult res = client.ftSearch(index, "hello world"); + assertEquals(1, res.getTotalResults()); + res = client.ftSearch(index, "foo bar"); + assertEquals(0, res.getTotalResults()); + } + + @Test + public void noStopwords() { + assertOK(client.ftCreate(index, + FTCreateParams.createParams().noStopwords(), + TextField.of("title"))); + + Map fields = new HashMap<>(); + fields.put("title", "hello world foo bar"); + fields.put("title", "hello world foo bar to be or not to be"); + addDocument("doc1", fields); + + assertEquals(1, client.ftSearch(index, "hello world").getTotalResults()); + assertEquals(1, client.ftSearch(index, "foo bar").getTotalResults()); + assertEquals(1, client.ftSearch(index, "to be or not to be").getTotalResults()); + } + + @Test + public void geoFilter() { + assertOK(client.ftCreate(index, TextField.of("title"), GeoField.of("loc"))); + + Map fields = new HashMap<>(); + fields.put("title", "hello world"); + fields.put("loc", "-0.441,51.458"); + addDocument("doc1", fields); + + fields.put("loc", "-0.1,51.2"); + addDocument("doc2", fields); + + SearchResult res = client.ftSearch(index, "hello world", + FTSearchParams.searchParams(). + geoFilter("loc", -0.44, 51.45, 10, GeoUnit.KM)); + assertEquals(1, res.getTotalResults()); + + res = client.ftSearch(index, "hello world", + FTSearchParams.searchParams(). + geoFilter("loc", -0.44, 51.45, 100, GeoUnit.KM)); + assertEquals(2, res.getTotalResults()); + } + + @Test + public void geoFilterAndGeoCoordinateObject() { + assertOK(client.ftCreate(index, TextField.of("title"), GeoField.of("loc"))); + + Map fields = new HashMap<>(); + fields.put("title", "hello world"); + fields.put("loc", new GeoCoordinate(-0.441, 51.458)); + addDocument("doc1", fields); + + fields.put("loc", new GeoCoordinate(-0.1, 51.2)); + addDocument("doc2", fields); + + SearchResult res = client.ftSearch(index, "hello world", + FTSearchParams.searchParams() + .geoFilter(new FTSearchParams.GeoFilter("loc", -0.44, 51.45, 10, GeoUnit.KM))); + assertEquals(1, res.getTotalResults()); + + res = client.ftSearch(index, "hello world", + FTSearchParams.searchParams() + .geoFilter(new FTSearchParams.GeoFilter("loc", -0.44, 51.45, 100, GeoUnit.KM))); + assertEquals(2, res.getTotalResults()); + } + + @Test + public void geoShapeFilterSpherical() throws ParseException { + final WKTReader reader = new WKTReader(); + final GeometryFactory factory = new GeometryFactory(); + + assertOK(client.ftCreate(index, GeoShapeField.of("geom", GeoShapeField.CoordinateSystem.SPHERICAL))); + + // polygon type + final Polygon small = factory.createPolygon(new Coordinate[]{new Coordinate(34.9001, 29.7001), + new Coordinate(34.9001, 29.7100), new Coordinate(34.9100, 29.7100), + new Coordinate(34.9100, 29.7001), new Coordinate(34.9001, 29.7001)}); + client.hset("small", RediSearchUtil.toStringMap(Collections.singletonMap("geom", small))); + + final Polygon large = factory.createPolygon(new Coordinate[]{new Coordinate(34.9001, 29.7001), + new Coordinate(34.9001, 29.7200), new Coordinate(34.9200, 29.7200), + new Coordinate(34.9200, 29.7001), new Coordinate(34.9001, 29.7001)}); + client.hset("large", RediSearchUtil.toStringMap(Collections.singletonMap("geom", large))); + + // within condition + final Polygon within = factory.createPolygon(new Coordinate[]{new Coordinate(34.9000, 29.7000), + new Coordinate(34.9000, 29.7150), new Coordinate(34.9150, 29.7150), + new Coordinate(34.9150, 29.7000), new Coordinate(34.9000, 29.7000)}); + + SearchResult res = client.ftSearch(index, "@geom:[within $poly]", + FTSearchParams.searchParams().addParam("poly", within).dialect(3)); + assertEquals(1, res.getTotalResults()); + assertEquals(1, res.getDocuments().size()); + assertEquals(small, reader.read(res.getDocuments().get(0).getString("geom"))); + + // contains condition + final Polygon contains = factory.createPolygon(new Coordinate[]{new Coordinate(34.9002, 29.7002), + new Coordinate(34.9002, 29.7050), new Coordinate(34.9050, 29.7050), + new Coordinate(34.9050, 29.7002), new Coordinate(34.9002, 29.7002)}); + + res = client.ftSearch(index, "@geom:[contains $poly]", + FTSearchParams.searchParams().addParam("poly", contains).dialect(3)); + assertEquals(2, res.getTotalResults()); + assertEquals(2, res.getDocuments().size()); + + // point type + final Point point = factory.createPoint(new Coordinate(34.9010, 29.7010)); + client.hset("point", RediSearchUtil.toStringMap(Collections.singletonMap("geom", point))); + + res = client.ftSearch(index, "@geom:[within $poly]", + FTSearchParams.searchParams().addParam("poly", within).dialect(3)); + assertEquals(2, res.getTotalResults()); + assertEquals(2, res.getDocuments().size()); + } + + @Test + public void geoShapeFilterFlat() throws ParseException { + final WKTReader reader = new WKTReader(); + final GeometryFactory factory = new GeometryFactory(); + + assertOK(client.ftCreate(index, GeoShapeField.of("geom", GeoShapeField.CoordinateSystem.FLAT))); + + // polygon type + final Polygon small = factory.createPolygon(new Coordinate[]{new Coordinate(1, 1), + new Coordinate(1, 100), new Coordinate(100, 100), new Coordinate(100, 1), new Coordinate(1, 1)}); + client.hset("small", RediSearchUtil.toStringMap(Collections.singletonMap("geom", small))); + + final Polygon large = factory.createPolygon(new Coordinate[]{new Coordinate(1, 1), + new Coordinate(1, 200), new Coordinate(200, 200), new Coordinate(200, 1), new Coordinate(1, 1)}); + client.hset("large", RediSearchUtil.toStringMap(Collections.singletonMap("geom", large))); + + // within condition + final Polygon within = factory.createPolygon(new Coordinate[]{new Coordinate(0, 0), + new Coordinate(0, 150), new Coordinate(150, 150), new Coordinate(150, 0), new Coordinate(0, 0)}); + + SearchResult res = client.ftSearch(index, "@geom:[within $poly]", + FTSearchParams.searchParams().addParam("poly", within).dialect(3)); + assertEquals(1, res.getTotalResults()); + assertEquals(1, res.getDocuments().size()); + assertEquals(small, reader.read(res.getDocuments().get(0).getString("geom"))); + + // contains condition + final Polygon contains = factory.createPolygon(new Coordinate[]{new Coordinate(2, 2), + new Coordinate(2, 50), new Coordinate(50, 50), new Coordinate(50, 2), new Coordinate(2, 2)}); + + res = client.ftSearch(index, "@geom:[contains $poly]", + FTSearchParams.searchParams().addParam("poly", contains).dialect(3)); + assertEquals(2, res.getTotalResults()); + assertEquals(2, res.getDocuments().size()); + + // point type + final Point point = factory.createPoint(new Coordinate(10, 10)); + client.hset("point", RediSearchUtil.toStringMap(Collections.singletonMap("geom", point))); + + res = client.ftSearch(index, "@geom:[within $poly]", + FTSearchParams.searchParams().addParam("poly", within).dialect(3)); + assertEquals(2, res.getTotalResults()); + assertEquals(2, res.getDocuments().size()); + } + + @Test + public void testQueryFlags() { + assertOK(client.ftCreate(index, TextField.of("title"))); + + Map fields = new HashMap<>(); + for (int i = 0; i < 100; i++) { + fields.put("title", i % 2 != 0 ? "hello worlds" : "hello world"); + addDocument(String.format("doc%d", i), fields); + } + + SearchResult res = client.ftSearch(index, "hello", + FTSearchParams.searchParams().withScores()); + assertEquals(100, res.getTotalResults()); + assertEquals(10, res.getDocuments().size()); + + for (Document d : res.getDocuments()) { + assertTrue(d.getId().startsWith("doc")); + assertTrue(((String) d.get("title")).startsWith("hello world")); + } // -// @Test -// public void geoShapeFilterFlat() throws ParseException { -// final WKTReader reader = new WKTReader(); -// final GeometryFactory factory = new GeometryFactory(); -// -// assertOK(client.ftCreate(index, GeoShapeField.of("geom", GeoShapeField.CoordinateSystem.FLAT))); -// -// // polygon type -// final Polygon small = factory.createPolygon(new Coordinate[]{new Coordinate(1, 1), -// new Coordinate(1, 100), new Coordinate(100, 100), new Coordinate(100, 1), new Coordinate(1, 1)}); -// client.hset("small", RediSearchUtil.toStringMap(Collections.singletonMap("geom", small))); -// -// final Polygon large = factory.createPolygon(new Coordinate[]{new Coordinate(1, 1), -// new Coordinate(1, 200), new Coordinate(200, 200), new Coordinate(200, 1), new Coordinate(1, 1)}); -// client.hset("large", RediSearchUtil.toStringMap(Collections.singletonMap("geom", large))); -// -// // within condition -// final Polygon within = factory.createPolygon(new Coordinate[]{new Coordinate(0, 0), -// new Coordinate(0, 150), new Coordinate(150, 150), new Coordinate(150, 0), new Coordinate(0, 0)}); -// -// SearchResult res = client.ftSearch(index, "@geom:[within $poly]", -// FTSearchParams.searchParams().addParam("poly", within).dialect(3)); -// assertEquals(1, res.getTotalResults()); -// assertEquals(1, res.getDocuments().size()); -// assertEquals(small, reader.read(res.getDocuments().get(0).getString("geom"))); -// -// // contains condition -// final Polygon contains = factory.createPolygon(new Coordinate[]{new Coordinate(2, 2), -// new Coordinate(2, 50), new Coordinate(50, 50), new Coordinate(50, 2), new Coordinate(2, 2)}); -// -// res = client.ftSearch(index, "@geom:[contains $poly]", -// FTSearchParams.searchParams().addParam("poly", contains).dialect(3)); -// assertEquals(2, res.getTotalResults()); -// assertEquals(2, res.getDocuments().size()); -// -// // point type -// final Point point = factory.createPoint(new Coordinate(10, 10)); -// client.hset("point", RediSearchUtil.toStringMap(Collections.singletonMap("geom", point))); -// -// res = client.ftSearch(index, "@geom:[within $poly]", -// FTSearchParams.searchParams().addParam("poly", within).dialect(3)); -// assertEquals(2, res.getTotalResults()); -// assertEquals(2, res.getDocuments().size()); -// } -// -// @Test -// public void testQueryFlags() { -// assertOK(client.ftCreate(index, TextField.of("title"))); -// -// Map fields = new HashMap<>(); -// for (int i = 0; i < 100; i++) { -// fields.put("title", i % 2 != 0 ? "hello worlds" : "hello world"); -// addDocument(String.format("doc%d", i), fields); -// } -// -// SearchResult res = client.ftSearch(index, "hello", -// FTSearchParams.searchParams().withScores()); +// res = client.ftSearch(index, "hello", +// FTSearchParams.searchParams().withScores().explainScore()); // assertEquals(100, res.getTotalResults()); // assertEquals(10, res.getDocuments().size()); // @@ -453,884 +463,874 @@ // assertTrue(d.getId().startsWith("doc")); // assertTrue(((String) d.get("title")).startsWith("hello world")); // } -//// -//// res = client.ftSearch(index, "hello", -//// FTSearchParams.searchParams().withScores().explainScore()); -//// assertEquals(100, res.getTotalResults()); -//// assertEquals(10, res.getDocuments().size()); -//// -//// for (Document d : res.getDocuments()) { -//// assertTrue(d.getId().startsWith("doc")); -//// assertTrue(((String) d.get("title")).startsWith("hello world")); -//// } -// -// res = client.ftSearch(index, "hello", -// FTSearchParams.searchParams().noContent()); -// for (Document d : res.getDocuments()) { -// assertTrue(d.getId().startsWith("doc")); -// if (protocol != RedisProtocol.RESP3) { -// assertEquals(1.0, d.getScore(), 0); -// assertNull(d.get("title")); -// } else { -// assertNull(d.getScore()); -// assertThrows(NullPointerException.class, () -> d.get("title")); -// } -// } -// -// // test verbatim vs. stemming -// res = client.ftSearch(index, "hello worlds"); -// assertEquals(100, res.getTotalResults()); -// res = client.ftSearch(index, "hello worlds", FTSearchParams.searchParams().verbatim()); -// assertEquals(50, res.getTotalResults()); -// res = client.ftSearch(index, "hello a world", FTSearchParams.searchParams().verbatim()); -// assertEquals(50, res.getTotalResults()); -// res = client.ftSearch(index, "hello a worlds", FTSearchParams.searchParams().verbatim()); -// assertEquals(50, res.getTotalResults()); -// res = client.ftSearch(index, "hello a world", FTSearchParams.searchParams().verbatim().noStopwords()); -// assertEquals(0, res.getTotalResults()); -// } -// -// @Test -// public void testQueryParams() { -// assertOK(client.ftCreate(index, NumericField.of("numval"))); -// -// client.hset("1", "numval", "1"); -// client.hset("2", "numval", "2"); -// client.hset("3", "numval", "3"); -// -// assertEquals(2, client.ftSearch(index, "@numval:[$min $max]", -// FTSearchParams.searchParams().addParam("min", 1).addParam("max", 2) -// .dialect(2)).getTotalResults()); -// -// Map paramValues = new HashMap<>(); -// paramValues.put("min", 1); -// paramValues.put("max", 2); -// assertEquals(2, client.ftSearch(index, "@numval:[$min $max]", -// FTSearchParams.searchParams().params(paramValues) -// .dialect(2)).getTotalResults()); -// } -// -// @Test -// public void testSortQueryFlags() { -// assertOK(client.ftCreate(index, TextField.of("title").sortable())); -// -// Map fields = new HashMap<>(); -// -// fields.put("title", "b title"); -// addDocument("doc1", fields); -// -// fields.put("title", "a title"); -// addDocument("doc2", fields); -// -// fields.put("title", "c title"); -// addDocument("doc3", fields); -// -// SearchResult res = client.ftSearch(index, "title", -// FTSearchParams.searchParams().sortBy("title", SortingOrder.ASC)); -// -// assertEquals(3, res.getTotalResults()); -// Document doc1 = res.getDocuments().get(0); -// assertEquals("a title", doc1.get("title")); -// -// doc1 = res.getDocuments().get(1); -// assertEquals("b title", doc1.get("title")); -// -// doc1 = res.getDocuments().get(2); -// assertEquals("c title", doc1.get("title")); -// } -// -// @Test -// public void testJsonWithAlias() { -// assertOK(client.ftCreate(index, -// FTCreateParams.createParams() -// .on(IndexDataType.JSON) -// .prefix("king:"), -// TextField.of("$.name").as("name"), -// NumericField.of("$.num").as("num"))); -// -// Map king1 = new HashMap<>(); -// king1.put("name", "henry"); -// king1.put("num", 42); -// client.jsonSet("king:1", Path.ROOT_PATH, king1); -// -// Map king2 = new HashMap<>(); -// king2.put("name", "james"); -// king2.put("num", 3.14); -// client.jsonSet("king:2", Path.ROOT_PATH, king2); -// -// SearchResult res = client.ftSearch(index, "@name:henry"); -// assertEquals(1, res.getTotalResults()); -// assertEquals("king:1", res.getDocuments().get(0).getId()); -// -// res = client.ftSearch(index, "@num:[0 10]"); -// assertEquals(1, res.getTotalResults()); -// assertEquals("king:2", res.getDocuments().get(0).getId()); -// } -// -// @Test -// public void dropIndex() { -// assertOK(client.ftCreate(index, TextField.of("title"))); -// -// Map fields = new HashMap<>(); -// fields.put("title", "hello world"); -// for (int i = 0; i < 100; i++) { -// addDocument(String.format("doc%d", i), fields); -// } -// -// SearchResult res = client.ftSearch(index, "hello world"); -// assertEquals(100, res.getTotalResults()); -// -// assertEquals("OK", client.ftDropIndex(index)); -// -// try { -// client.ftSearch(index, "hello world"); -// fail("Index should not exist."); -// } catch (JedisDataException de) { -// assertTrue(de.getMessage().contains("no such index")); -// } -// assertEquals(100, client.dbSize()); -// } -// -// @Test -// public void dropIndexDD() { -// assertOK(client.ftCreate(index, TextField.of("title"))); -// -// Map fields = new HashMap<>(); -// fields.put("title", "hello world"); -// for (int i = 0; i < 100; i++) { -// addDocument(String.format("doc%d", i), fields); -// } -// -// SearchResult res = client.ftSearch(index, "hello world"); -// assertEquals(100, res.getTotalResults()); -// -// assertEquals("OK", client.ftDropIndexDD(index)); -// -// Set keys = client.keys("*"); -// assertTrue(keys.isEmpty()); -// assertEquals(0, client.dbSize()); -// } -// -// @Test -// public void noStem() { -// assertOK(client.ftCreate(index, new TextField("stemmed").weight(1.0), -// new TextField("notStemmed").weight(1.0).noStem())); -// -// Map doc = new HashMap<>(); -// doc.put("stemmed", "located"); -// doc.put("notStemmed", "located"); -// addDocument("doc", doc); -// -// // Query -// SearchResult res = client.ftSearch(index, "@stemmed:location"); -// assertEquals(1, res.getTotalResults()); -// -// res = client.ftSearch(index, "@notStemmed:location"); -// assertEquals(0, res.getTotalResults()); -// } -// -// @Test -// public void phoneticMatch() { -// assertOK(client.ftCreate(index, new TextField("noPhonetic").weight(1.0), -// new TextField("withPhonetic").weight(1.0).phonetic("dm:en"))); -// -// Map doc = new HashMap<>(); -// doc.put("noPhonetic", "morfix"); -// doc.put("withPhonetic", "morfix"); -// addDocument("doc", doc); -// -// // Query -// SearchResult res = client.ftSearch(index, "@withPhonetic:morphix=>{$phonetic:true}"); -// assertEquals(1, res.getTotalResults()); -// -// try { -// client.ftSearch(index, "@noPhonetic:morphix=>{$phonetic:true}"); -// fail(); -// } catch (JedisDataException e) {/*field does not support phonetics*/ -// } -// -// SearchResult res3 = client.ftSearch(index, "@withPhonetic:morphix=>{$phonetic:false}"); -// assertEquals(0, res3.getTotalResults()); -// } -// -// @Test -// public void info() { -// Collection sc = new ArrayList<>(); -// sc.add(TextField.of("title").weight(5)); -// sc.add(TextField.of("plot").sortable()); -// sc.add(TagField.of("genre").separator(',').sortable()); -// sc.add(NumericField.of("release_year").sortable()); -// sc.add(NumericField.of("rating").sortable()); -// sc.add(NumericField.of("votes").sortable()); -// -// assertOK(client.ftCreate(index, sc)); -// -// Map info = client.ftInfo(index); -// assertEquals(index, info.get("index_name")); -// assertEquals(6, ((List) info.get("attributes")).size()); -// if (protocol != RedisProtocol.RESP3) { -// assertEquals("global_idle", ((List) info.get("cursor_stats")).get(0)); -// assertEquals(0L, ((List) info.get("cursor_stats")).get(1)); -// } else { -// assertEquals(0L, ((Map) info.get("cursor_stats")).get("global_idle")); -// } -// } -// -// @Test -// public void noIndexAndSortBy() { -// assertOK(client.ftCreate(index, TextField.of("f1").sortable().noIndex(), TextField.of("f2"))); -// -// Map mm = new HashMap<>(); -// -// mm.put("f1", "MarkZZ"); -// mm.put("f2", "MarkZZ"); -// addDocument("doc1", mm); -// -// mm.clear(); -// mm.put("f1", "MarkAA"); -// mm.put("f2", "MarkBB"); -// addDocument("doc2", mm); -// -// SearchResult res = client.ftSearch(index, "@f1:Mark*"); -// assertEquals(0, res.getTotalResults()); -// -// res = client.ftSearch(index, "@f2:Mark*"); -// assertEquals(2, res.getTotalResults()); -// -// res = client.ftSearch(index, "@f2:Mark*", -// FTSearchParams.searchParams().sortBy("f1", SortingOrder.DESC)); -// assertEquals(2, res.getTotalResults()); -// -// assertEquals("doc1", res.getDocuments().get(0).getId()); -// -// res = client.ftSearch(index, "@f2:Mark*", -// FTSearchParams.searchParams().sortBy("f1", SortingOrder.ASC)); -// assertEquals("doc2", res.getDocuments().get(0).getId()); -// } -// -// @Test -// public void testHighlightSummarize() { -// assertOK(client.ftCreate(index, TextField.of("text").weight(1))); -// -// Map doc = new HashMap<>(); -// doc.put("text", "Redis is often referred as a data structures server. What this means is that " -// + "Redis provides access to mutable data structures via a set of commands, which are sent " -// + "using a server-client model with TCP sockets and a simple protocol. So different " -// + "processes can query and modify the same data structures in a shared way"); -// // Add a document -// addDocument("foo", doc); -// -// SearchResult res = client.ftSearch(index, "data", FTSearchParams.searchParams().highlight().summarize()); -// assertEquals("is often referred as a data structures server. What this means is that " -// + "Redis provides... What this means is that Redis provides access to mutable data " -// + "structures via a set of commands, which are sent using a... So different processes can " -// + "query and modify the same data structures in a shared... ", -// res.getDocuments().get(0).get("text")); -// -// res = client.ftSearch(index, "data", FTSearchParams.searchParams() -// .highlight(FTSearchParams.highlightParams().tags("", "")) -// .summarize()); -// assertEquals("is often referred as a data structures server. What this means is that " -// + "Redis provides... What this means is that Redis provides access to mutable data " -// + "structures via a set of commands, which are sent using a... So different processes can " -// + "query and modify the same data structures in a shared... ", -// res.getDocuments().get(0).get("text")); -// } -// -// @Test -// public void getTagField() { -// assertOK(client.ftCreate(index, TextField.of("title"), TagField.of("category"))); -// -// Map fields1 = new HashMap<>(); -// fields1.put("title", "hello world"); -// fields1.put("category", "red"); -// addDocument("foo", fields1); -// -// Map fields2 = new HashMap<>(); -// fields2.put("title", "hello world"); -// fields2.put("category", "blue"); -// addDocument("bar", fields2); -// -// Map fields3 = new HashMap<>(); -// fields3.put("title", "hello world"); -// fields3.put("category", "green,yellow"); -// addDocument("baz", fields3); -// -// Map fields4 = new HashMap<>(); -// fields4.put("title", "hello world"); -// fields4.put("category", "orange;purple"); -// addDocument("qux", fields4); -// -// assertEquals(1, client.ftSearch(index, "@category:{red}").getTotalResults()); -// assertEquals(1, client.ftSearch(index, "@category:{blue}").getTotalResults()); -// assertEquals(1, client.ftSearch(index, "hello @category:{red}").getTotalResults()); -// assertEquals(1, client.ftSearch(index, "hello @category:{blue}").getTotalResults()); -// assertEquals(1, client.ftSearch(index, "@category:{yellow}").getTotalResults()); -// assertEquals(0, client.ftSearch(index, "@category:{purple}").getTotalResults()); -// assertEquals(1, client.ftSearch(index, "@category:{orange\\;purple}").getTotalResults()); -// assertEquals(4, client.ftSearch(index, "hello").getTotalResults()); -// -// assertEquals(new HashSet<>(Arrays.asList("red", "blue", "green", "yellow", "orange;purple")), -// client.ftTagVals(index, "category")); -// } -// -// @Test -// public void testGetTagFieldWithNonDefaultSeparator() { -// assertOK(client.ftCreate(index, -// TextField.of("title"), -// TagField.of("category").separator(';'))); -// -// Map fields1 = new HashMap<>(); -// fields1.put("title", "hello world"); -// fields1.put("category", "red"); -// addDocument("foo", fields1); -// -// Map fields2 = new HashMap<>(); -// fields2.put("title", "hello world"); -// fields2.put("category", "blue"); -// addDocument("bar", fields2); -// -// Map fields3 = new HashMap<>(); -// fields3.put("title", "hello world"); -// fields3.put("category", "green;yellow"); -// addDocument("baz", fields3); -// -// Map fields4 = new HashMap<>(); -// fields4.put("title", "hello world"); -// fields4.put("category", "orange,purple"); -// addDocument("qux", fields4); -// -// assertEquals(1, client.ftSearch(index, "@category:{red}").getTotalResults()); -// assertEquals(1, client.ftSearch(index, "@category:{blue}").getTotalResults()); -// assertEquals(1, client.ftSearch(index, "hello @category:{red}").getTotalResults()); -// assertEquals(1, client.ftSearch(index, "hello @category:{blue}").getTotalResults()); -// assertEquals(1, client.ftSearch(index, "hello @category:{yellow}").getTotalResults()); -// assertEquals(0, client.ftSearch(index, "@category:{purple}").getTotalResults()); -// assertEquals(1, client.ftSearch(index, "@category:{orange\\,purple}").getTotalResults()); -// assertEquals(4, client.ftSearch(index, "hello").getTotalResults()); -// -// assertEquals(new HashSet<>(Arrays.asList("red", "blue", "green", "yellow", "orange,purple")), -// client.ftTagVals(index, "category")); -// } -// -// @Test -// public void caseSensitiveTagField() { -// assertOK(client.ftCreate(index, -// TextField.of("title"), -// TagField.of("category").caseSensitive())); -// -// Map fields = new HashMap<>(); -// fields.put("title", "hello world"); -// fields.put("category", "RedX"); -// addDocument("foo", fields); -// -// assertEquals(0, client.ftSearch(index, "@category:{redx}").getTotalResults()); -// assertEquals(0, client.ftSearch(index, "@category:{redX}").getTotalResults()); -// assertEquals(0, client.ftSearch(index, "@category:{Redx}").getTotalResults()); -// assertEquals(1, client.ftSearch(index, "@category:{RedX}").getTotalResults()); -// assertEquals(1, client.ftSearch(index, "hello").getTotalResults()); -// } -// -// @Test -// public void testReturnFields() { -// assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); -// -// Map doc = new HashMap<>(); -// doc.put("field1", "value1"); -// doc.put("field2", "value2"); -// addDocument("doc", doc); -// -// // Query -// SearchResult res = client.ftSearch(index, "*", -// FTSearchParams.searchParams().returnFields("field1")); -// assertEquals(1, res.getTotalResults()); -// Document ret = res.getDocuments().get(0); -// assertEquals("value1", ret.get("field1")); -// assertNull(ret.get("field2")); -// } -// -// @Test -// public void returnWithFieldNames() { -// assertOK(client.ftCreate(index, TextField.of("a"), TextField.of("b"), TextField.of("c"))); -// -// Map map = new HashMap<>(); -// map.put("a", "value1"); -// map.put("b", "value2"); -// map.put("c", "value3"); -// addDocument("doc", map); -// -// // Query -// SearchResult res = client.ftSearch(index, "*", -// FTSearchParams.searchParams().returnFields( -// FieldName.of("a"), FieldName.of("b").as("d"))); -// assertEquals(1, res.getTotalResults()); -// Document doc = res.getDocuments().get(0); -// assertEquals("value1", doc.get("a")); -// assertNull(doc.get("b")); -// assertEquals("value2", doc.get("d")); -// assertNull(doc.get("c")); -// } -// -// @Test -// public void inKeys() { -// assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); -// -// Map doc = new HashMap<>(); -// doc.put("field1", "value"); -// doc.put("field2", "not"); -// // Store it -// addDocument("doc1", doc); -// addDocument("doc2", doc); -// -// // Query -// SearchResult res = client.ftSearch(index, "value", -// FTSearchParams.searchParams().inKeys("doc1")); -// assertEquals(1, res.getTotalResults()); -// assertEquals("doc1", res.getDocuments().get(0).getId()); -// assertEquals("value", res.getDocuments().get(0).get("field1")); -// assertEquals(null, res.getDocuments().get(0).get("value")); -// } -// -// @Test -// public void alias() { -// assertOK(client.ftCreate(index, TextField.of("field1"))); -// -// Map doc = new HashMap<>(); -// doc.put("field1", "value"); -// addDocument("doc1", doc); -// -// assertEquals("OK", client.ftAliasAdd("ALIAS1", index)); -// SearchResult res1 = client.ftSearch("ALIAS1", "*", -// FTSearchParams.searchParams().returnFields("field1")); -// assertEquals(1, res1.getTotalResults()); -// assertEquals("value", res1.getDocuments().get(0).get("field1")); -// -// assertEquals("OK", client.ftAliasUpdate("ALIAS2", index)); -// SearchResult res2 = client.ftSearch("ALIAS2", "*", -// FTSearchParams.searchParams().returnFields("field1")); -// assertEquals(1, res2.getTotalResults()); -// assertEquals("value", res2.getDocuments().get(0).get("field1")); -// -// try { -// client.ftAliasDel("ALIAS3"); -// fail("Should throw JedisDataException"); -// } catch (JedisDataException e) { -// // Alias does not exist -// } -// assertEquals("OK", client.ftAliasDel("ALIAS2")); -// try { -// client.ftAliasDel("ALIAS2"); -// fail("Should throw JedisDataException"); -// } catch (JedisDataException e) { -// // Alias does not exist -// } -// } -// -// @Test -// public void synonym() { -// assertOK(client.ftCreate(index, TextField.of("name").weight(1), TextField.of("addr").weight(1))); -// -// long group1 = 345L; -// long group2 = 789L; -// String group1_str = Long.toString(group1); -// String group2_str = Long.toString(group2); -// assertEquals("OK", client.ftSynUpdate(index, group1_str, "girl", "baby")); -// assertEquals("OK", client.ftSynUpdate(index, group1_str, "child")); -// assertEquals("OK", client.ftSynUpdate(index, group2_str, "child")); -// -// Map> dump = client.ftSynDump(index); -// -// Map> expected = new HashMap<>(); -// expected.put("girl", Arrays.asList(group1_str)); -// expected.put("baby", Arrays.asList(group1_str)); -// expected.put("child", Arrays.asList(group1_str, group2_str)); -// assertEquals(expected, dump); -// } -// -// @Test -// public void slop() { -// assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); -// -// Map doc = new HashMap<>(); -// doc.put("field1", "ok hi jedis"); -// addDocument("doc1", doc); -// -// SearchResult res = client.ftSearch(index, "ok jedis", FTSearchParams.searchParams().slop(0)); -// assertEquals(0, res.getTotalResults()); -// -// res = client.ftSearch(index, "ok jedis", FTSearchParams.searchParams().slop(1)); -// assertEquals(1, res.getTotalResults()); -// assertEquals("doc1", res.getDocuments().get(0).getId()); -// assertEquals("ok hi jedis", res.getDocuments().get(0).get("field1")); -// } -// -// @Test -// public void timeout() { -// assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); -// -// Map map = new HashMap<>(); -// map.put("field1", "value"); -// map.put("field2", "not"); -// client.hset("doc1", map); -// -// SearchResult res = client.ftSearch(index, "value", FTSearchParams.searchParams().timeout(1000)); -// assertEquals(1, res.getTotalResults()); -// assertEquals("doc1", res.getDocuments().get(0).getId()); -// assertEquals("value", res.getDocuments().get(0).get("field1")); -// assertEquals("not", res.getDocuments().get(0).get("field2")); -// } -// -// @Test -// public void inOrder() { -// assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); -// -// Map map = new HashMap<>(); -// map.put("field1", "value"); -// map.put("field2", "not"); -// client.hset("doc2", map); -// client.hset("doc1", map); -// -// SearchResult res = client.ftSearch(index, "value", FTSearchParams.searchParams().inOrder()); -// assertEquals(2, res.getTotalResults()); -// assertEquals("doc2", res.getDocuments().get(0).getId()); -// assertEquals("value", res.getDocuments().get(0).get("field1")); -// assertEquals("not", res.getDocuments().get(0).get("field2")); -// } -// -// @Test -// public void testHNSWVVectorSimilarity() { -// Map attr = new HashMap<>(); -// attr.put("TYPE", "FLOAT32"); -// attr.put("DIM", 2); -// attr.put("DISTANCE_METRIC", "L2"); -// -// assertOK(client.ftCreate(index, VectorField.builder().fieldName("v") -// .algorithm(VectorField.VectorAlgorithm.HNSW).attributes(attr).build())); -// -// client.hset("a", "v", "aaaaaaaa"); -// client.hset("b", "v", "aaaabaaa"); -// client.hset("c", "v", "aaaaabaa"); -// -// FTSearchParams searchParams = FTSearchParams.searchParams() -// .addParam("vec", "aaaaaaaa") -// .sortBy("__v_score", SortingOrder.ASC) -// .returnFields("__v_score") -// .dialect(2); -// Document doc1 = client.ftSearch(index, "*=>[KNN 2 @v $vec]", searchParams).getDocuments().get(0); -// assertEquals("a", doc1.getId()); -// assertEquals("0", doc1.get("__v_score")); -// } -// -// @Test -// public void testFlatVectorSimilarity() { -// assertOK(client.ftCreate(index, -// VectorField.builder().fieldName("v") -// .algorithm(VectorField.VectorAlgorithm.FLAT) -// .addAttribute("TYPE", "FLOAT32") -// .addAttribute("DIM", 2) -// .addAttribute("DISTANCE_METRIC", "L2") -// .build() -// )); -// -// client.hset("a", "v", "aaaaaaaa"); -// client.hset("b", "v", "aaaabaaa"); -// client.hset("c", "v", "aaaaabaa"); -// -// FTSearchParams searchParams = FTSearchParams.searchParams() -// .addParam("vec", "aaaaaaaa") -// .sortBy("__v_score", SortingOrder.ASC) -// .returnFields("__v_score") -// .dialect(2); -// -// Document doc1 = client.ftSearch(index, "*=>[KNN 2 @v $vec]", searchParams).getDocuments().get(0); -// assertEquals("a", doc1.getId()); -// assertEquals("0", doc1.get("__v_score")); -// } -// -// @Test -// public void searchProfile() { -// assertOK(client.ftCreate(index, TextField.of("t1"), TextField.of("t2"))); -// -// Map hash = new HashMap<>(); -// hash.put("t1", "foo"); -// hash.put("t2", "bar"); -// client.hset("doc1", hash); -// -// Map.Entry> reply = client.ftProfileSearch(index, -// FTProfileParams.profileParams(), "foo", FTSearchParams.searchParams()); -// -// SearchResult result = reply.getKey(); -// assertEquals(1, result.getTotalResults()); -// assertEquals(Collections.singletonList("doc1"), result.getDocuments().stream().map(Document::getId).collect(Collectors.toList())); -// -// Map profile = reply.getValue(); -// Map iteratorsProfile; -// if (protocol != RedisProtocol.RESP3) { -// iteratorsProfile = (Map) profile.get("Iterators profile"); -// } else { -// List iteratorsProfileList = (List) profile.get("Iterators profile"); -// assertEquals(1, iteratorsProfileList.size()); -// iteratorsProfile = (Map) iteratorsProfileList.get(0); -// } -// assertEquals("TEXT", iteratorsProfile.get("Type")); -// assertEquals("foo", iteratorsProfile.get("Term")); -// assertEquals(1L, iteratorsProfile.get("Counter")); -// assertEquals(1L, iteratorsProfile.get("Size")); -// assertSame(Double.class, iteratorsProfile.get("Time").getClass()); -// -// assertEquals(Arrays.asList("Index", "Scorer", "Sorter", "Loader"), -// ((List>) profile.get("Result processors profile")).stream() -// .map(map -> map.get("Type")).collect(Collectors.toList())); -// } -// -// @Test -// public void vectorSearchProfile() { -// assertOK(client.ftCreate(index, VectorField.builder().fieldName("v") -// .algorithm(VectorAlgorithm.FLAT).addAttribute("TYPE", "FLOAT32") -// .addAttribute("DIM", 2).addAttribute("DISTANCE_METRIC", "L2").build(), -// TextField.of("t"))); -// -// client.hset("1", toMap("v", "bababaca", "t", "hello")); -// client.hset("2", toMap("v", "babababa", "t", "hello")); -// client.hset("3", toMap("v", "aabbaabb", "t", "hello")); -// client.hset("4", toMap("v", "bbaabbaa", "t", "hello world")); -// client.hset("5", toMap("v", "aaaabbbb", "t", "hello world")); -// -// FTSearchParams searchParams = FTSearchParams.searchParams().addParam("vec", "aaaaaaaa") -// .sortBy("__v_score", SortingOrder.ASC).noContent().dialect(2); -// Map.Entry> reply = client.ftProfileSearch(index, -// FTProfileParams.profileParams(), "*=>[KNN 3 @v $vec]", searchParams); -// assertEquals(3, reply.getKey().getTotalResults()); -// -// assertEquals(Arrays.asList(4, 2, 1).toString(), reply.getKey().getDocuments() -// .stream().map(Document::getId).collect(Collectors.toList()).toString()); -// -// Map profile = reply.getValue(); -// -// if (protocol != RedisProtocol.RESP3) { -// assertEquals("VECTOR", ((Map) profile.get("Iterators profile")).get("Type")); -// } else { -// assertEquals(Arrays.asList("VECTOR"), -// ((List>) profile.get("Iterators profile")).stream() -// .map(map -> map.get("Type")).collect(Collectors.toList())); -// } -// -// List> resultProcessorsProfile -// = (List>) reply.getValue().get("Result processors profile"); -// assertEquals(3, resultProcessorsProfile.size()); -// assertEquals("Index", resultProcessorsProfile.get(0).get("Type")); -// assertEquals("Sorter", resultProcessorsProfile.get(2).get("Type")); -// } -// -// @Test -// public void maxPrefixExpansionSearchProfile() { -// final String configParam = "MAXPREFIXEXPANSIONS"; -// String configValue = (String) client.ftConfigGet(configParam).get(configParam); -// try { -// client.ftConfigSet(configParam, "2"); -// -// assertOK(client.ftCreate(index, TextField.of("t"))); -// client.hset("1", Collections.singletonMap("t", "foo1")); -// client.hset("2", Collections.singletonMap("t", "foo2")); -// client.hset("3", Collections.singletonMap("t", "foo3")); -// -// Map.Entry> reply = client.ftProfileSearch(index, -// FTProfileParams.profileParams(), "foo*", FTSearchParams.searchParams().limit(0, 0)); -// // Warning=Max prefix expansion reached -// if (protocol != RedisProtocol.RESP3) { -// assertEquals("Max prefix expansion reached", -// ((Map) reply.getValue().get("Iterators profile")).get("Warning")); -// } else { -// assertEquals("Max prefix expansion reached", -// ((Map) ((List) reply.getValue().get("Iterators profile")).get(0)).get("Warning")); -// } -// } finally { -// client.ftConfigSet(configParam, configValue); -// } -// } -// -// @Test -// public void noContentSearchProfile() { -// assertOK(client.ftCreate(index, TextField.of("t"))); -// client.hset("1", Collections.singletonMap("t", "foo")); -// client.hset("2", Collections.singletonMap("t", "bar")); -// -// Map.Entry> profile = client.ftProfileSearch(index, -// FTProfileParams.profileParams(), "foo -@t:baz", FTSearchParams.searchParams().noContent()); -// -// Map depth0 = protocol != RedisProtocol.RESP3 -// ? (Map) profile.getValue().get("Iterators profile") -// : ((List>) profile.getValue().get("Iterators profile")).get(0); -// -// assertEquals("INTERSECT", depth0.get("Type")); -// List> depth0_children = (List>) depth0.get("Child iterators"); -// assertEquals("TEXT", depth0_children.get(0).get("Type")); -// Map depth1 = depth0_children.get(1); -// assertEquals("NOT", depth1.get("Type")); -// if (protocol != RedisProtocol.RESP3) { -// List> depth1_children = (List>) depth1.get("Child iterators"); -// assertEquals(1, depth1_children.size()); -// assertEquals("EMPTY", depth1_children.get(0).get("Type")); -// } else { -// assertEquals("EMPTY", ((Map) depth1.get("Child iterator")).get("Type")); -// } -// } -// -// @Test -// public void deepReplySearchProfile() { -// assertOK(client.ftCreate(index, TextField.of("t"))); -// client.hset("1", Collections.singletonMap("t", "hello")); -// client.hset("2", Collections.singletonMap("t", "world")); -// -// Map.Entry> profile -// = client.ftProfileSearch(index, FTProfileParams.profileParams(), -// "hello(hello(hello(hello(hello(hello)))))", FTSearchParams.searchParams().noContent()); -// -// Map depth0 = protocol != RedisProtocol.RESP3 -// ? (Map) profile.getValue().get("Iterators profile") -// : ((List>) profile.getValue().get("Iterators profile")).get(0); -// -// assertEquals("INTERSECT", depth0.get("Type")); -// List> depth0_children = (List>) depth0.get("Child iterators"); -// assertEquals("TEXT", depth0_children.get(0).get("Type")); -// Map depth1 = depth0_children.get(1); -// assertEquals("INTERSECT", depth1.get("Type")); -// List> depth1_children = (List>) depth1.get("Child iterators"); -// assertEquals("TEXT", depth1_children.get(0).get("Type")); -// Map depth2 = depth1_children.get(1); -// assertEquals("INTERSECT", depth2.get("Type")); -// List> depth2_children = (List>) depth2.get("Child iterators"); -// assertEquals("TEXT", depth2_children.get(0).get("Type")); -// Map depth3 = depth2_children.get(1); -// assertEquals("INTERSECT", depth3.get("Type")); -// List> depth3_children = (List>) depth3.get("Child iterators"); -// assertEquals("TEXT", depth3_children.get(0).get("Type")); -// Map depth4 = depth3_children.get(1); -// assertEquals("INTERSECT", depth4.get("Type")); -// List> depth4_children = (List>) depth4.get("Child iterators"); -// assertEquals("TEXT", depth4_children.get(0).get("Type")); -// Map depth5 = depth4_children.get(1); -// assertEquals("TEXT", depth5.get("Type")); -// assertNull(depth5.get("Child iterators")); -// } -// -// @Test -// public void limitedSearchProfile() { -// assertOK(client.ftCreate(index, TextField.of("t"))); -// client.hset("1", Collections.singletonMap("t", "hello")); -// client.hset("2", Collections.singletonMap("t", "hell")); -// client.hset("3", Collections.singletonMap("t", "help")); -// client.hset("4", Collections.singletonMap("t", "helowa")); -// -// Map.Entry> profile = client.ftProfileSearch(index, -// FTProfileParams.profileParams().limited(), "%hell% hel*", FTSearchParams.searchParams().noContent()); -// -// Map depth0 = protocol != RedisProtocol.RESP3 -// ? (Map) profile.getValue().get("Iterators profile") -// : ((List>) profile.getValue().get("Iterators profile")).get(0); -// -// assertEquals("INTERSECT", depth0.get("Type")); -// assertEquals(3L, depth0.get("Counter")); -// -// List> depth0_children = (List>) depth0.get("Child iterators"); -// assertFalse(depth0_children.isEmpty()); -// for (Map depth1 : depth0_children) { -// assertEquals("UNION", depth1.get("Type")); -// assertNotNull(depth1.get("Query type")); -// if (protocol != RedisProtocol.RESP3) { -// List depth1_children = (List) depth1.get("Child iterators"); -// assertEquals(1, depth1_children.size()); -// assertSame(String.class, depth1_children.get(0).getClass()); -// } else { -// assertSame(String.class, depth1.get("Child iterators").getClass()); -// } -// } -// } -// -// @Test -// public void list() { -// assertEquals(Collections.emptySet(), client.ftList()); -// -// final int count = 20; -// Set names = new HashSet<>(); -// for (int i = 0; i < count; i++) { -// final String name = "idx" + i; -// assertOK(client.ftCreate(name, TextField.of("t" + i))); -// names.add(name); -// } -// assertEquals(names, client.ftList()); -// } -// -// @Test -// public void broadcast() { -// String reply = client.ftCreate(index, TextField.of("t")); -// assertOK(reply); -// } -// -// @Test -// public void searchIteration() { -// assertOK(client.ftCreate(index, FTCreateParams.createParams(), -// TextField.of("first"), TextField.of("last"), NumericField.of("age"))); -// -// client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); -// client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); -// client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); -// client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); -// client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); -// client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); -// client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); -// -// FtSearchIteration search = client.ftSearchIteration(3, index, "*", FTSearchParams.searchParams()); -// int total = 0; -// while (!search.isIterationCompleted()) { -// SearchResult result = search.nextBatch(); -// int count = result.getDocuments().size(); -// assertThat(count, Matchers.lessThanOrEqualTo(3)); -// total += count; -// } -// assertEquals(7, total); -// } -// -// @Test -// public void searchIterationCollect() { -// assertOK(client.ftCreate(index, FTCreateParams.createParams(), -// TextField.of("first"), TextField.of("last"), NumericField.of("age"))); -// -// client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); -// client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); -// client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); -// client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); -// client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); -// client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); -// client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); -// -// ArrayList collect = new ArrayList<>(); -// client.ftSearchIteration(3, index, "*", FTSearchParams.searchParams()).collect(collect); -// assertEquals(7, collect.size()); -// assertEquals(Arrays.asList("profesor:5555", "student:1111", "pupil:2222", "student:3333", -// "pupil:4444", "student:5555", "teacher:6666").stream().collect(Collectors.toSet()), -// collect.stream().map(Document::getId).collect(Collectors.toSet())); -// } -// -// @Test -// public void escapeUtil() { -// assertOK(client.ftCreate(index, TextField.of("txt"))); -// -// client.hset("doc1", "txt", RediSearchUtil.escape("hello-world")); -// assertNotEquals("hello-world", client.hget("doc1", "txt")); -// assertEquals("hello-world", RediSearchUtil.unescape(client.hget("doc1", "txt"))); -// -// SearchResult resultNoEscape = client.ftSearch(index, "hello-world"); -// assertEquals(0, resultNoEscape.getTotalResults()); -// -// SearchResult resultEscaped = client.ftSearch(index, RediSearchUtil.escapeQuery("hello-world")); -// assertEquals(1, resultEscaped.getTotalResults()); -// } -// -// @Test -// public void escapeMapUtil() { -// client.hset("doc2", RediSearchUtil.toStringMap(Collections.singletonMap("txt", "hello-world"), true)); -// assertNotEquals("hello-world", client.hget("doc2", "txt")); -// assertEquals("hello-world", RediSearchUtil.unescape(client.hget("doc2", "txt"))); -// } -//} + + res = client.ftSearch(index, "hello", + FTSearchParams.searchParams().noContent()); + for (Document d : res.getDocuments()) { + assertTrue(d.getId().startsWith("doc")); + if (protocol != RedisProtocol.RESP3) { + assertEquals(1.0, d.getScore(), 0); + assertNull(d.get("title")); + } else { + assertNull(d.getScore()); + assertThrows(NullPointerException.class, () -> d.get("title")); + } + } + + // test verbatim vs. stemming + res = client.ftSearch(index, "hello worlds"); + assertEquals(100, res.getTotalResults()); + res = client.ftSearch(index, "hello worlds", FTSearchParams.searchParams().verbatim()); + assertEquals(50, res.getTotalResults()); + res = client.ftSearch(index, "hello a world", FTSearchParams.searchParams().verbatim()); + assertEquals(50, res.getTotalResults()); + res = client.ftSearch(index, "hello a worlds", FTSearchParams.searchParams().verbatim()); + assertEquals(50, res.getTotalResults()); + res = client.ftSearch(index, "hello a world", FTSearchParams.searchParams().verbatim().noStopwords()); + assertEquals(0, res.getTotalResults()); + } + + @Test + public void testQueryParams() { + assertOK(client.ftCreate(index, NumericField.of("numval"))); + + client.hset("1", "numval", "1"); + client.hset("2", "numval", "2"); + client.hset("3", "numval", "3"); + + assertEquals(2, client.ftSearch(index, "@numval:[$min $max]", + FTSearchParams.searchParams().addParam("min", 1).addParam("max", 2) + .dialect(2)).getTotalResults()); + + Map paramValues = new HashMap<>(); + paramValues.put("min", 1); + paramValues.put("max", 2); + assertEquals(2, client.ftSearch(index, "@numval:[$min $max]", + FTSearchParams.searchParams().params(paramValues) + .dialect(2)).getTotalResults()); + } + + @Test + public void testSortQueryFlags() { + assertOK(client.ftCreate(index, TextField.of("title").sortable())); + + Map fields = new HashMap<>(); + + fields.put("title", "b title"); + addDocument("doc1", fields); + + fields.put("title", "a title"); + addDocument("doc2", fields); + + fields.put("title", "c title"); + addDocument("doc3", fields); + + SearchResult res = client.ftSearch(index, "title", + FTSearchParams.searchParams().sortBy("title", SortingOrder.ASC)); + + assertEquals(3, res.getTotalResults()); + Document doc1 = res.getDocuments().get(0); + assertEquals("a title", doc1.get("title")); + + doc1 = res.getDocuments().get(1); + assertEquals("b title", doc1.get("title")); + + doc1 = res.getDocuments().get(2); + assertEquals("c title", doc1.get("title")); + } + + @Test + public void testJsonWithAlias() { + assertOK(client.ftCreate(index, + FTCreateParams.createParams() + .on(IndexDataType.JSON) + .prefix("king:"), + TextField.of("$.name").as("name"), + NumericField.of("$.num").as("num"))); + + Map king1 = new HashMap<>(); + king1.put("name", "henry"); + king1.put("num", 42); + client.jsonSet("king:1", Path.ROOT_PATH, king1); + + Map king2 = new HashMap<>(); + king2.put("name", "james"); + king2.put("num", 3.14); + client.jsonSet("king:2", Path.ROOT_PATH, king2); + + SearchResult res = client.ftSearch(index, "@name:henry"); + assertEquals(1, res.getTotalResults()); + assertEquals("king:1", res.getDocuments().get(0).getId()); + + res = client.ftSearch(index, "@num:[0 10]"); + assertEquals(1, res.getTotalResults()); + assertEquals("king:2", res.getDocuments().get(0).getId()); + } + + @Test + public void dropIndex() { + assertOK(client.ftCreate(index, TextField.of("title"))); + + Map fields = new HashMap<>(); + fields.put("title", "hello world"); + for (int i = 0; i < 100; i++) { + addDocument(String.format("doc%d", i), fields); + } + + SearchResult res = client.ftSearch(index, "hello world"); + assertEquals(100, res.getTotalResults()); + + assertEquals("OK", client.ftDropIndex(index)); + + try { + client.ftSearch(index, "hello world"); + fail("Index should not exist."); + } catch (JedisDataException de) { + assertTrue(de.getMessage().contains("no such index")); + } + assertEquals(100, client.dbSize()); + } + + @Test + public void dropIndexDD() { + assertOK(client.ftCreate(index, TextField.of("title"))); + + Map fields = new HashMap<>(); + fields.put("title", "hello world"); + for (int i = 0; i < 100; i++) { + addDocument(String.format("doc%d", i), fields); + } + + SearchResult res = client.ftSearch(index, "hello world"); + assertEquals(100, res.getTotalResults()); + + assertEquals("OK", client.ftDropIndexDD(index)); + + Set keys = client.keys("*"); + assertTrue(keys.isEmpty()); + assertEquals(0, client.dbSize()); + } + + @Test + public void noStem() { + assertOK(client.ftCreate(index, new TextField("stemmed").weight(1.0), + new TextField("notStemmed").weight(1.0).noStem())); + + Map doc = new HashMap<>(); + doc.put("stemmed", "located"); + doc.put("notStemmed", "located"); + addDocument("doc", doc); + + // Query + SearchResult res = client.ftSearch(index, "@stemmed:location"); + assertEquals(1, res.getTotalResults()); + + res = client.ftSearch(index, "@notStemmed:location"); + assertEquals(0, res.getTotalResults()); + } + + @Test + public void phoneticMatch() { + assertOK(client.ftCreate(index, new TextField("noPhonetic").weight(1.0), + new TextField("withPhonetic").weight(1.0).phonetic("dm:en"))); + + Map doc = new HashMap<>(); + doc.put("noPhonetic", "morfix"); + doc.put("withPhonetic", "morfix"); + addDocument("doc", doc); + + // Query + SearchResult res = client.ftSearch(index, "@withPhonetic:morphix=>{$phonetic:true}"); + assertEquals(1, res.getTotalResults()); + + try { + client.ftSearch(index, "@noPhonetic:morphix=>{$phonetic:true}"); + fail(); + } catch (JedisDataException e) {/*field does not support phonetics*/ + } + + SearchResult res3 = client.ftSearch(index, "@withPhonetic:morphix=>{$phonetic:false}"); + assertEquals(0, res3.getTotalResults()); + } + + @Test + public void info() { + Collection sc = new ArrayList<>(); + sc.add(TextField.of("title").weight(5)); + sc.add(TextField.of("plot").sortable()); + sc.add(TagField.of("genre").separator(',').sortable()); + sc.add(NumericField.of("release_year").sortable()); + sc.add(NumericField.of("rating").sortable()); + sc.add(NumericField.of("votes").sortable()); + + assertOK(client.ftCreate(index, sc)); + + Map info = client.ftInfo(index); + assertEquals(index, info.get("index_name")); + assertEquals(6, ((List) info.get("attributes")).size()); + if (protocol != RedisProtocol.RESP3) { + assertEquals("global_idle", ((List) info.get("cursor_stats")).get(0)); + assertEquals(0L, ((List) info.get("cursor_stats")).get(1)); + } else { + assertEquals(0L, ((Map) info.get("cursor_stats")).get("global_idle")); + } + } + + @Test + public void noIndexAndSortBy() { + assertOK(client.ftCreate(index, TextField.of("f1").sortable().noIndex(), TextField.of("f2"))); + + Map mm = new HashMap<>(); + + mm.put("f1", "MarkZZ"); + mm.put("f2", "MarkZZ"); + addDocument("doc1", mm); + + mm.clear(); + mm.put("f1", "MarkAA"); + mm.put("f2", "MarkBB"); + addDocument("doc2", mm); + + SearchResult res = client.ftSearch(index, "@f1:Mark*"); + assertEquals(0, res.getTotalResults()); + + res = client.ftSearch(index, "@f2:Mark*"); + assertEquals(2, res.getTotalResults()); + + res = client.ftSearch(index, "@f2:Mark*", + FTSearchParams.searchParams().sortBy("f1", SortingOrder.DESC)); + assertEquals(2, res.getTotalResults()); + + assertEquals("doc1", res.getDocuments().get(0).getId()); + + res = client.ftSearch(index, "@f2:Mark*", + FTSearchParams.searchParams().sortBy("f1", SortingOrder.ASC)); + assertEquals("doc2", res.getDocuments().get(0).getId()); + } + + @Test + public void testHighlightSummarize() { + assertOK(client.ftCreate(index, TextField.of("text").weight(1))); + + Map doc = new HashMap<>(); + doc.put("text", "Redis is often referred as a data structures server. What this means is that " + + "Redis provides access to mutable data structures via a set of commands, which are sent " + + "using a server-client model with TCP sockets and a simple protocol. So different " + + "processes can query and modify the same data structures in a shared way"); + // Add a document + addDocument("foo", doc); + + SearchResult res = client.ftSearch(index, "data", FTSearchParams.searchParams().highlight().summarize()); + assertEquals("is often referred as a data structures server. What this means is that " + + "Redis provides... What this means is that Redis provides access to mutable data " + + "structures via a set of commands, which are sent using a... So different processes can " + + "query and modify the same data structures in a shared... ", + res.getDocuments().get(0).get("text")); + + res = client.ftSearch(index, "data", FTSearchParams.searchParams() + .highlight(FTSearchParams.highlightParams().tags("", "")) + .summarize()); + assertEquals("is often referred as a data structures server. What this means is that " + + "Redis provides... What this means is that Redis provides access to mutable data " + + "structures via a set of commands, which are sent using a... So different processes can " + + "query and modify the same data structures in a shared... ", + res.getDocuments().get(0).get("text")); + } + + @Test + public void getTagField() { + assertOK(client.ftCreate(index, TextField.of("title"), TagField.of("category"))); + + Map fields1 = new HashMap<>(); + fields1.put("title", "hello world"); + fields1.put("category", "red"); + addDocument("foo", fields1); + + Map fields2 = new HashMap<>(); + fields2.put("title", "hello world"); + fields2.put("category", "blue"); + addDocument("bar", fields2); + + Map fields3 = new HashMap<>(); + fields3.put("title", "hello world"); + fields3.put("category", "green,yellow"); + addDocument("baz", fields3); + + Map fields4 = new HashMap<>(); + fields4.put("title", "hello world"); + fields4.put("category", "orange;purple"); + addDocument("qux", fields4); + + assertEquals(1, client.ftSearch(index, "@category:{red}").getTotalResults()); + assertEquals(1, client.ftSearch(index, "@category:{blue}").getTotalResults()); + assertEquals(1, client.ftSearch(index, "hello @category:{red}").getTotalResults()); + assertEquals(1, client.ftSearch(index, "hello @category:{blue}").getTotalResults()); + assertEquals(1, client.ftSearch(index, "@category:{yellow}").getTotalResults()); + assertEquals(0, client.ftSearch(index, "@category:{purple}").getTotalResults()); + assertEquals(1, client.ftSearch(index, "@category:{orange\\;purple}").getTotalResults()); + assertEquals(4, client.ftSearch(index, "hello").getTotalResults()); + + assertEquals(new HashSet<>(Arrays.asList("red", "blue", "green", "yellow", "orange;purple")), + client.ftTagVals(index, "category")); + } + + @Test + public void testGetTagFieldWithNonDefaultSeparator() { + assertOK(client.ftCreate(index, + TextField.of("title"), + TagField.of("category").separator(';'))); + + Map fields1 = new HashMap<>(); + fields1.put("title", "hello world"); + fields1.put("category", "red"); + addDocument("foo", fields1); + + Map fields2 = new HashMap<>(); + fields2.put("title", "hello world"); + fields2.put("category", "blue"); + addDocument("bar", fields2); + + Map fields3 = new HashMap<>(); + fields3.put("title", "hello world"); + fields3.put("category", "green;yellow"); + addDocument("baz", fields3); + + Map fields4 = new HashMap<>(); + fields4.put("title", "hello world"); + fields4.put("category", "orange,purple"); + addDocument("qux", fields4); + + assertEquals(1, client.ftSearch(index, "@category:{red}").getTotalResults()); + assertEquals(1, client.ftSearch(index, "@category:{blue}").getTotalResults()); + assertEquals(1, client.ftSearch(index, "hello @category:{red}").getTotalResults()); + assertEquals(1, client.ftSearch(index, "hello @category:{blue}").getTotalResults()); + assertEquals(1, client.ftSearch(index, "hello @category:{yellow}").getTotalResults()); + assertEquals(0, client.ftSearch(index, "@category:{purple}").getTotalResults()); + assertEquals(1, client.ftSearch(index, "@category:{orange\\,purple}").getTotalResults()); + assertEquals(4, client.ftSearch(index, "hello").getTotalResults()); + + assertEquals(new HashSet<>(Arrays.asList("red", "blue", "green", "yellow", "orange,purple")), + client.ftTagVals(index, "category")); + } + + @Test + public void caseSensitiveTagField() { + assertOK(client.ftCreate(index, + TextField.of("title"), + TagField.of("category").caseSensitive())); + + Map fields = new HashMap<>(); + fields.put("title", "hello world"); + fields.put("category", "RedX"); + addDocument("foo", fields); + + assertEquals(0, client.ftSearch(index, "@category:{redx}").getTotalResults()); + assertEquals(0, client.ftSearch(index, "@category:{redX}").getTotalResults()); + assertEquals(0, client.ftSearch(index, "@category:{Redx}").getTotalResults()); + assertEquals(1, client.ftSearch(index, "@category:{RedX}").getTotalResults()); + assertEquals(1, client.ftSearch(index, "hello").getTotalResults()); + } + + @Test + public void testReturnFields() { + assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); + + Map doc = new HashMap<>(); + doc.put("field1", "value1"); + doc.put("field2", "value2"); + addDocument("doc", doc); + + // Query + SearchResult res = client.ftSearch(index, "*", + FTSearchParams.searchParams().returnFields("field1")); + assertEquals(1, res.getTotalResults()); + Document ret = res.getDocuments().get(0); + assertEquals("value1", ret.get("field1")); + assertNull(ret.get("field2")); + } + + @Test + public void returnWithFieldNames() { + assertOK(client.ftCreate(index, TextField.of("a"), TextField.of("b"), TextField.of("c"))); + + Map map = new HashMap<>(); + map.put("a", "value1"); + map.put("b", "value2"); + map.put("c", "value3"); + addDocument("doc", map); + + // Query + SearchResult res = client.ftSearch(index, "*", + FTSearchParams.searchParams().returnFields( + FieldName.of("a"), FieldName.of("b").as("d"))); + assertEquals(1, res.getTotalResults()); + Document doc = res.getDocuments().get(0); + assertEquals("value1", doc.get("a")); + assertNull(doc.get("b")); + assertEquals("value2", doc.get("d")); + assertNull(doc.get("c")); + } + + @Test + public void inKeys() { + assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); + + Map doc = new HashMap<>(); + doc.put("field1", "value"); + doc.put("field2", "not"); + // Store it + addDocument("doc1", doc); + addDocument("doc2", doc); + + // Query + SearchResult res = client.ftSearch(index, "value", + FTSearchParams.searchParams().inKeys("doc1")); + assertEquals(1, res.getTotalResults()); + assertEquals("doc1", res.getDocuments().get(0).getId()); + assertEquals("value", res.getDocuments().get(0).get("field1")); + assertEquals(null, res.getDocuments().get(0).get("value")); + } + + @Test + public void alias() { + assertOK(client.ftCreate(index, TextField.of("field1"))); + + Map doc = new HashMap<>(); + doc.put("field1", "value"); + addDocument("doc1", doc); + + assertEquals("OK", client.ftAliasAdd("ALIAS1", index)); + SearchResult res1 = client.ftSearch("ALIAS1", "*", + FTSearchParams.searchParams().returnFields("field1")); + assertEquals(1, res1.getTotalResults()); + assertEquals("value", res1.getDocuments().get(0).get("field1")); + + assertEquals("OK", client.ftAliasUpdate("ALIAS2", index)); + SearchResult res2 = client.ftSearch("ALIAS2", "*", + FTSearchParams.searchParams().returnFields("field1")); + assertEquals(1, res2.getTotalResults()); + assertEquals("value", res2.getDocuments().get(0).get("field1")); + + try { + client.ftAliasDel("ALIAS3"); + fail("Should throw JedisDataException"); + } catch (JedisDataException e) { + // Alias does not exist + } + assertEquals("OK", client.ftAliasDel("ALIAS2")); + try { + client.ftAliasDel("ALIAS2"); + fail("Should throw JedisDataException"); + } catch (JedisDataException e) { + // Alias does not exist + } + } + + @Test + public void synonym() { + assertOK(client.ftCreate(index, TextField.of("name").weight(1), TextField.of("addr").weight(1))); + + long group1 = 345L; + long group2 = 789L; + String group1_str = Long.toString(group1); + String group2_str = Long.toString(group2); + assertEquals("OK", client.ftSynUpdate(index, group1_str, "girl", "baby")); + assertEquals("OK", client.ftSynUpdate(index, group1_str, "child")); + assertEquals("OK", client.ftSynUpdate(index, group2_str, "child")); + + Map> dump = client.ftSynDump(index); + + Map> expected = new HashMap<>(); + expected.put("girl", Arrays.asList(group1_str)); + expected.put("baby", Arrays.asList(group1_str)); + expected.put("child", Arrays.asList(group1_str, group2_str)); + assertEquals(expected, dump); + } + + @Test + public void slop() { + assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); + + Map doc = new HashMap<>(); + doc.put("field1", "ok hi jedis"); + addDocument("doc1", doc); + + SearchResult res = client.ftSearch(index, "ok jedis", FTSearchParams.searchParams().slop(0)); + assertEquals(0, res.getTotalResults()); + + res = client.ftSearch(index, "ok jedis", FTSearchParams.searchParams().slop(1)); + assertEquals(1, res.getTotalResults()); + assertEquals("doc1", res.getDocuments().get(0).getId()); + assertEquals("ok hi jedis", res.getDocuments().get(0).get("field1")); + } + + @Test + public void timeout() { + assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); + + Map map = new HashMap<>(); + map.put("field1", "value"); + map.put("field2", "not"); + client.hset("doc1", map); + + SearchResult res = client.ftSearch(index, "value", FTSearchParams.searchParams().timeout(1000)); + assertEquals(1, res.getTotalResults()); + assertEquals("doc1", res.getDocuments().get(0).getId()); + assertEquals("value", res.getDocuments().get(0).get("field1")); + assertEquals("not", res.getDocuments().get(0).get("field2")); + } + + @Test + public void inOrder() { + assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); + + Map map = new HashMap<>(); + map.put("field1", "value"); + map.put("field2", "not"); + client.hset("doc2", map); + client.hset("doc1", map); + + SearchResult res = client.ftSearch(index, "value", FTSearchParams.searchParams().inOrder()); + assertEquals(2, res.getTotalResults()); + assertEquals("doc2", res.getDocuments().get(0).getId()); + assertEquals("value", res.getDocuments().get(0).get("field1")); + assertEquals("not", res.getDocuments().get(0).get("field2")); + } + + @Test + public void testHNSWVVectorSimilarity() { + Map attr = new HashMap<>(); + attr.put("TYPE", "FLOAT32"); + attr.put("DIM", 2); + attr.put("DISTANCE_METRIC", "L2"); + + assertOK(client.ftCreate(index, VectorField.builder().fieldName("v") + .algorithm(VectorField.VectorAlgorithm.HNSW).attributes(attr).build())); + + client.hset("a", "v", "aaaaaaaa"); + client.hset("b", "v", "aaaabaaa"); + client.hset("c", "v", "aaaaabaa"); + + FTSearchParams searchParams = FTSearchParams.searchParams() + .addParam("vec", "aaaaaaaa") + .sortBy("__v_score", SortingOrder.ASC) + .returnFields("__v_score") + .dialect(2); + Document doc1 = client.ftSearch(index, "*=>[KNN 2 @v $vec]", searchParams).getDocuments().get(0); + assertEquals("a", doc1.getId()); + assertEquals("0", doc1.get("__v_score")); + } + + @Test + public void testFlatVectorSimilarity() { + assertOK(client.ftCreate(index, + VectorField.builder().fieldName("v") + .algorithm(VectorField.VectorAlgorithm.FLAT) + .addAttribute("TYPE", "FLOAT32") + .addAttribute("DIM", 2) + .addAttribute("DISTANCE_METRIC", "L2") + .build() + )); + + client.hset("a", "v", "aaaaaaaa"); + client.hset("b", "v", "aaaabaaa"); + client.hset("c", "v", "aaaaabaa"); + + FTSearchParams searchParams = FTSearchParams.searchParams() + .addParam("vec", "aaaaaaaa") + .sortBy("__v_score", SortingOrder.ASC) + .returnFields("__v_score") + .dialect(2); + + Document doc1 = client.ftSearch(index, "*=>[KNN 2 @v $vec]", searchParams).getDocuments().get(0); + assertEquals("a", doc1.getId()); + assertEquals("0", doc1.get("__v_score")); + } + + @Test + public void searchProfile() { + assertOK(client.ftCreate(index, TextField.of("t1"), TextField.of("t2"))); + + Map hash = new HashMap<>(); + hash.put("t1", "foo"); + hash.put("t2", "bar"); + client.hset("doc1", hash); + + Map.Entry> reply = client.ftProfileSearch(index, + FTProfileParams.profileParams(), "foo", FTSearchParams.searchParams()); + + SearchResult result = reply.getKey(); + assertEquals(1, result.getTotalResults()); + assertEquals(Collections.singletonList("doc1"), result.getDocuments().stream().map(Document::getId).collect(Collectors.toList())); + + Map profile = reply.getValue(); + Map iteratorsProfile; + if (protocol != RedisProtocol.RESP3) { + iteratorsProfile = (Map) profile.get("Iterators profile"); + } else { + List iteratorsProfileList = (List) profile.get("Iterators profile"); + assertEquals(1, iteratorsProfileList.size()); + iteratorsProfile = (Map) iteratorsProfileList.get(0); + } + assertEquals("TEXT", iteratorsProfile.get("Type")); + assertEquals("foo", iteratorsProfile.get("Term")); + assertEquals(1L, iteratorsProfile.get("Counter")); + assertEquals(1L, iteratorsProfile.get("Size")); + assertSame(Double.class, iteratorsProfile.get("Time").getClass()); + + assertEquals(Arrays.asList("Index", "Scorer", "Sorter", "Loader"), + ((List>) profile.get("Result processors profile")).stream() + .map(map -> map.get("Type")).collect(Collectors.toList())); + } + + @Test + public void vectorSearchProfile() { + assertOK(client.ftCreate(index, VectorField.builder().fieldName("v") + .algorithm(VectorAlgorithm.FLAT).addAttribute("TYPE", "FLOAT32") + .addAttribute("DIM", 2).addAttribute("DISTANCE_METRIC", "L2").build(), + TextField.of("t"))); + + client.hset("1", toMap("v", "bababaca", "t", "hello")); + client.hset("2", toMap("v", "babababa", "t", "hello")); + client.hset("3", toMap("v", "aabbaabb", "t", "hello")); + client.hset("4", toMap("v", "bbaabbaa", "t", "hello world")); + client.hset("5", toMap("v", "aaaabbbb", "t", "hello world")); + + FTSearchParams searchParams = FTSearchParams.searchParams().addParam("vec", "aaaaaaaa") + .sortBy("__v_score", SortingOrder.ASC).noContent().dialect(2); + Map.Entry> reply = client.ftProfileSearch(index, + FTProfileParams.profileParams(), "*=>[KNN 3 @v $vec]", searchParams); + assertEquals(3, reply.getKey().getTotalResults()); + + assertEquals(Arrays.asList(4, 2, 1).toString(), reply.getKey().getDocuments() + .stream().map(Document::getId).collect(Collectors.toList()).toString()); + + Map profile = reply.getValue(); + + if (protocol != RedisProtocol.RESP3) { + assertEquals("VECTOR", ((Map) profile.get("Iterators profile")).get("Type")); + } else { + assertEquals(Arrays.asList("VECTOR"), + ((List>) profile.get("Iterators profile")).stream() + .map(map -> map.get("Type")).collect(Collectors.toList())); + } + + List> resultProcessorsProfile + = (List>) reply.getValue().get("Result processors profile"); + assertEquals(3, resultProcessorsProfile.size()); + assertEquals("Index", resultProcessorsProfile.get(0).get("Type")); + assertEquals("Sorter", resultProcessorsProfile.get(2).get("Type")); + } + + @Test + public void maxPrefixExpansionSearchProfile() { + final String configParam = "MAXPREFIXEXPANSIONS"; + String configValue = (String) client.ftConfigGet(configParam).get(configParam); + try { + client.ftConfigSet(configParam, "2"); + + assertOK(client.ftCreate(index, TextField.of("t"))); + client.hset("1", Collections.singletonMap("t", "foo1")); + client.hset("2", Collections.singletonMap("t", "foo2")); + client.hset("3", Collections.singletonMap("t", "foo3")); + + Map.Entry> reply = client.ftProfileSearch(index, + FTProfileParams.profileParams(), "foo*", FTSearchParams.searchParams().limit(0, 0)); + // Warning=Max prefix expansion reached + if (protocol != RedisProtocol.RESP3) { + assertEquals("Max prefix expansion reached", + ((Map) reply.getValue().get("Iterators profile")).get("Warning")); + } else { + assertEquals("Max prefix expansion reached", + ((Map) ((List) reply.getValue().get("Iterators profile")).get(0)).get("Warning")); + } + } finally { + client.ftConfigSet(configParam, configValue); + } + } + + @Test + public void noContentSearchProfile() { + assertOK(client.ftCreate(index, TextField.of("t"))); + client.hset("1", Collections.singletonMap("t", "foo")); + client.hset("2", Collections.singletonMap("t", "bar")); + + Map.Entry> profile = client.ftProfileSearch(index, + FTProfileParams.profileParams(), "foo -@t:baz", FTSearchParams.searchParams().noContent()); + + Map depth0 = protocol != RedisProtocol.RESP3 + ? (Map) profile.getValue().get("Iterators profile") + : ((List>) profile.getValue().get("Iterators profile")).get(0); + + assertEquals("INTERSECT", depth0.get("Type")); + List> depth0_children = (List>) depth0.get("Child iterators"); + assertEquals("TEXT", depth0_children.get(0).get("Type")); + Map depth1 = depth0_children.get(1); + assertEquals("NOT", depth1.get("Type")); + if (protocol != RedisProtocol.RESP3) { + List> depth1_children = (List>) depth1.get("Child iterators"); + assertEquals(1, depth1_children.size()); + assertEquals("EMPTY", depth1_children.get(0).get("Type")); + } else { + assertEquals("EMPTY", ((Map) depth1.get("Child iterator")).get("Type")); + } + } + + @Test + public void deepReplySearchProfile() { + assertOK(client.ftCreate(index, TextField.of("t"))); + client.hset("1", Collections.singletonMap("t", "hello")); + client.hset("2", Collections.singletonMap("t", "world")); + + Map.Entry> profile + = client.ftProfileSearch(index, FTProfileParams.profileParams(), + "hello(hello(hello(hello(hello(hello)))))", FTSearchParams.searchParams().noContent()); + + Map depth0 = protocol != RedisProtocol.RESP3 + ? (Map) profile.getValue().get("Iterators profile") + : ((List>) profile.getValue().get("Iterators profile")).get(0); + + assertEquals("INTERSECT", depth0.get("Type")); + List> depth0_children = (List>) depth0.get("Child iterators"); + assertEquals("TEXT", depth0_children.get(0).get("Type")); + Map depth1 = depth0_children.get(1); + assertEquals("INTERSECT", depth1.get("Type")); + List> depth1_children = (List>) depth1.get("Child iterators"); + assertEquals("TEXT", depth1_children.get(0).get("Type")); + Map depth2 = depth1_children.get(1); + assertEquals("INTERSECT", depth2.get("Type")); + List> depth2_children = (List>) depth2.get("Child iterators"); + assertEquals("TEXT", depth2_children.get(0).get("Type")); + Map depth3 = depth2_children.get(1); + assertEquals("INTERSECT", depth3.get("Type")); + List> depth3_children = (List>) depth3.get("Child iterators"); + assertEquals("TEXT", depth3_children.get(0).get("Type")); + Map depth4 = depth3_children.get(1); + assertEquals("INTERSECT", depth4.get("Type")); + List> depth4_children = (List>) depth4.get("Child iterators"); + assertEquals("TEXT", depth4_children.get(0).get("Type")); + Map depth5 = depth4_children.get(1); + assertEquals("TEXT", depth5.get("Type")); + assertNull(depth5.get("Child iterators")); + } + + @Test + public void limitedSearchProfile() { + assertOK(client.ftCreate(index, TextField.of("t"))); + client.hset("1", Collections.singletonMap("t", "hello")); + client.hset("2", Collections.singletonMap("t", "hell")); + client.hset("3", Collections.singletonMap("t", "help")); + client.hset("4", Collections.singletonMap("t", "helowa")); + + Map.Entry> profile = client.ftProfileSearch(index, + FTProfileParams.profileParams().limited(), "%hell% hel*", FTSearchParams.searchParams().noContent()); + + Map depth0 = protocol != RedisProtocol.RESP3 + ? (Map) profile.getValue().get("Iterators profile") + : ((List>) profile.getValue().get("Iterators profile")).get(0); + + assertEquals("INTERSECT", depth0.get("Type")); + assertEquals(3L, depth0.get("Counter")); + + List> depth0_children = (List>) depth0.get("Child iterators"); + assertFalse(depth0_children.isEmpty()); + for (Map depth1 : depth0_children) { + assertEquals("UNION", depth1.get("Type")); + assertNotNull(depth1.get("Query type")); + if (protocol != RedisProtocol.RESP3) { + List depth1_children = (List) depth1.get("Child iterators"); + assertEquals(1, depth1_children.size()); + assertSame(String.class, depth1_children.get(0).getClass()); + } else { + assertSame(String.class, depth1.get("Child iterators").getClass()); + } + } + } + + @Test + public void list() { + assertEquals(Collections.emptySet(), client.ftList()); + + final int count = 20; + Set names = new HashSet<>(); + for (int i = 0; i < count; i++) { + final String name = "idx" + i; + assertOK(client.ftCreate(name, TextField.of("t" + i))); + names.add(name); + } + assertEquals(names, client.ftList()); + } + + @Test + public void broadcast() { + String reply = client.ftCreate(index, TextField.of("t")); + assertOK(reply); + } + + @Test + public void searchIteration() { + assertOK(client.ftCreate(index, FTCreateParams.createParams(), + TextField.of("first"), TextField.of("last"), NumericField.of("age"))); + + client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); + client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); + client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); + client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); + client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); + client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); + client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); + + FtSearchIteration search = client.ftSearchIteration(3, index, "*", FTSearchParams.searchParams()); + int total = 0; + while (!search.isIterationCompleted()) { + SearchResult result = search.nextBatch(); + int count = result.getDocuments().size(); + assertThat(count, Matchers.lessThanOrEqualTo(3)); + total += count; + } + assertEquals(7, total); + } + + @Test + public void searchIterationCollect() { + assertOK(client.ftCreate(index, FTCreateParams.createParams(), + TextField.of("first"), TextField.of("last"), NumericField.of("age"))); + + client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); + client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); + client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); + client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); + client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); + client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); + client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); + + ArrayList collect = new ArrayList<>(); + client.ftSearchIteration(3, index, "*", FTSearchParams.searchParams()).collect(collect); + assertEquals(7, collect.size()); + assertEquals(Arrays.asList("profesor:5555", "student:1111", "pupil:2222", "student:3333", + "pupil:4444", "student:5555", "teacher:6666").stream().collect(Collectors.toSet()), + collect.stream().map(Document::getId).collect(Collectors.toSet())); + } + + @Test + public void escapeUtil() { + assertOK(client.ftCreate(index, TextField.of("txt"))); + + client.hset("doc1", "txt", RediSearchUtil.escape("hello-world")); + assertNotEquals("hello-world", client.hget("doc1", "txt")); + assertEquals("hello-world", RediSearchUtil.unescape(client.hget("doc1", "txt"))); + + SearchResult resultNoEscape = client.ftSearch(index, "hello-world"); + assertEquals(0, resultNoEscape.getTotalResults()); + + SearchResult resultEscaped = client.ftSearch(index, RediSearchUtil.escapeQuery("hello-world")); + assertEquals(1, resultEscaped.getTotalResults()); + } + + @Test + public void escapeMapUtil() { + client.hset("doc2", RediSearchUtil.toStringMap(Collections.singletonMap("txt", "hello-world"), true)); + assertNotEquals("hello-world", client.hget("doc2", "txt")); + assertEquals("hello-world", RediSearchUtil.unescape(client.hget("doc2", "txt"))); + } +} From 5e4fd16977b37147bd8b63cba3176ad0ecc234b7 Mon Sep 17 00:00:00 2001 From: c00603587 Date: Fri, 20 Oct 2023 17:17:17 +0800 Subject: [PATCH 15/19] Revert "Support GEOSHAPE field type in RediSearch (#3561)" This reverts commit 5cd40fcd84e2a1479654758b1eb21ac65f8d4f48. # Conflicts: # src/test/java/redis/clients/jedis/examples/GeoShapeFieldsUsageInRediSearch.java Signed-off-by: c00603587 --- .../clients/jedis/examples/GeoShapeFieldsUsageInRediSearch.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/redis/clients/jedis/examples/GeoShapeFieldsUsageInRediSearch.java b/src/test/java/redis/clients/jedis/examples/GeoShapeFieldsUsageInRediSearch.java index e0a820938e..db4db2cb0f 100644 --- a/src/test/java/redis/clients/jedis/examples/GeoShapeFieldsUsageInRediSearch.java +++ b/src/test/java/redis/clients/jedis/examples/GeoShapeFieldsUsageInRediSearch.java @@ -87,7 +87,7 @@ public static void main(String[] args) { FTSearchParams.searchParams() .addParam("poly", within) .dialect(3) // DIALECT '3' is required for this query - ); + ); assertEquals(1, res.getTotalResults()); assertEquals(1, res.getDocuments().size()); From 7e241e803ce3797949d6da905906495e997dd425 Mon Sep 17 00:00:00 2001 From: c00603587 Date: Fri, 20 Oct 2023 10:13:16 +0800 Subject: [PATCH 16/19] test case for testSentinelMasterListener Signed-off-by: c00603587 (cherry picked from commit 23813a22ef2325ad27bb4316f0aa40dad861551e) --- .../clients/jedis/JedisSentinelTest.java | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/test/java/redis/clients/jedis/JedisSentinelTest.java b/src/test/java/redis/clients/jedis/JedisSentinelTest.java index 60ad4cccc4..593c5d34c2 100644 --- a/src/test/java/redis/clients/jedis/JedisSentinelTest.java +++ b/src/test/java/redis/clients/jedis/JedisSentinelTest.java @@ -33,10 +33,10 @@ public class JedisSentinelTest { protected static HostAndPort sentinelForFailover = HostAndPorts.getSentinelServers().get(2); protected static HostAndPort masterForFailover = HostAndPorts.getRedisServers().get(5); - private final Set sentinels = new HashSet<>(); + public final Set sentinels = new HashSet<>(); @Before - public void setup() throws InterruptedException { + public void setUp() throws Exception { sentinels.clear(); sentinels.add(sentinelForFailover.toString()); } @@ -93,14 +93,6 @@ public void sentinelFailover() throws InterruptedException { HostAndPort currentMaster = new HostAndPort(masterHostAndPort.get(0), Integer.parseInt(masterHostAndPort.get(1))); - // get master node from pool connection - SentinelPoolConfig config = new SentinelPoolConfig(); - config.setEnableActiveDetectListener(true); - config.setEnableDefaultSubscribeListener(false); - JedisSentinelPool pool = new JedisSentinelPool(FAILOVER_MASTER_NAME, sentinels, config, 1000, - "foobared", 2); - HostAndPort masterGetFromPoolBefore = pool.getResource().connection.getHostAndPort(); - JedisSentinelTestUtil.waitForNewPromotedMaster(FAILOVER_MASTER_NAME, j, j2); masterHostAndPort = j.sentinelGetMasterAddrByName(FAILOVER_MASTER_NAME); @@ -108,14 +100,38 @@ public void sentinelFailover() throws InterruptedException { Integer.parseInt(masterHostAndPort.get(1))); assertNotEquals(newMaster, currentMaster); + } finally { + j.close(); + j2.close(); + } + } + + @Test + public void testSentinelMasterListener() throws InterruptedException { + Jedis j = new Jedis(sentinelForFailover); + Jedis j2 = new Jedis(sentinelForFailover); + + SentinelPoolConfig config = new SentinelPoolConfig(); + config.setEnableActiveDetectListener(true); + config.setEnableDefaultSubscribeListener(true); + config.setActiveDetectIntervalTimeMillis(5 * 1000); + config.setSubscribeRetryWaitTimeMillis(5 * 1000); + + JedisSentinelPool pool = new JedisSentinelPool(FAILOVER_MASTER_NAME, sentinels, config, 1000, + "foobared", 2); - Thread.sleep(6000); + try { + HostAndPort masterGetFromPoolBefore = pool.getResource().connection.getHostAndPort(); + + JedisSentinelTestUtil.waitForNewPromotedMaster(FAILOVER_MASTER_NAME, j, j2); HostAndPort masterGetFromPoolCurrent = pool.getResource().connection.getHostAndPort(); + assertNotEquals(masterGetFromPoolBefore, masterGetFromPoolCurrent); } finally { j.close(); j2.close(); + pool.destroy(); } } From b9f9aa8a713e1f1919fee29bde288a651d792eec Mon Sep 17 00:00:00 2001 From: c00603587 Date: Wed, 1 Nov 2023 14:34:06 +0800 Subject: [PATCH 17/19] modify the code structure Signed-off-by: c00603587 --- .../clients/jedis/JedisSentinelPool.java | 13 +- .../clients/jedis/SentinelMasterListener.java | 14 -- .../SentineledConnectionProvider.java | 19 ++- .../SentinelActiveDetectListener.java} | 24 ++-- .../sentinel/listenner/SentinelListener.java | 16 +++ .../listenner/SentinelSubscribeListener.java} | 27 ++-- .../clients/jedis/JedisSentinelTest.java | 37 +---- .../clients/jedis/SentinelListenerTest.java | 129 ++++++++++++++++++ 8 files changed, 192 insertions(+), 87 deletions(-) delete mode 100644 src/main/java/redis/clients/jedis/SentinelMasterListener.java rename src/main/java/redis/clients/jedis/{SentinelMasterActiveDetectListener.java => sentinel/listenner/SentinelActiveDetectListener.java} (80%) create mode 100644 src/main/java/redis/clients/jedis/sentinel/listenner/SentinelListener.java rename src/main/java/redis/clients/jedis/{SentinelMasterSubscribeListener.java => sentinel/listenner/SentinelSubscribeListener.java} (86%) create mode 100644 src/test/java/redis/clients/jedis/SentinelListenerTest.java diff --git a/src/main/java/redis/clients/jedis/JedisSentinelPool.java b/src/main/java/redis/clients/jedis/JedisSentinelPool.java index d189237bff..1bf71817c8 100644 --- a/src/main/java/redis/clients/jedis/JedisSentinelPool.java +++ b/src/main/java/redis/clients/jedis/JedisSentinelPool.java @@ -12,6 +12,9 @@ import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; +import redis.clients.jedis.sentinel.listenner.SentinelActiveDetectListener; +import redis.clients.jedis.sentinel.listenner.SentinelListener; +import redis.clients.jedis.sentinel.listenner.SentinelSubscribeListener; import redis.clients.jedis.util.Pool; public class JedisSentinelPool extends Pool { @@ -22,7 +25,7 @@ public class JedisSentinelPool extends Pool { private final JedisClientConfig sentinelClientConfig; - private final Collection masterListeners = new ArrayList<>(); + private final Collection masterListeners = new ArrayList<>(); private volatile HostAndPort currentHostMaster; @@ -213,7 +216,7 @@ private void initMasterListeners(Set sentinels, String masterName, for (HostAndPort sentinel : sentinels) { if (jedisSentinelPoolConfig.isEnableActiveDetectListener()) { masterListeners.add( - new SentinelMasterActiveDetectListener(currentHostMaster, sentinel, sentinelClientConfig, + new SentinelActiveDetectListener(currentHostMaster, sentinel, sentinelClientConfig, masterName, jedisSentinelPoolConfig.getActiveDetectIntervalTimeMillis()) { @Override public void onChange(HostAndPort hostAndPort) { @@ -223,7 +226,7 @@ public void onChange(HostAndPort hostAndPort) { } if (jedisSentinelPoolConfig.isEnableDefaultSubscribeListener()) { - masterListeners.add(new SentinelMasterSubscribeListener(masterName, sentinel, + masterListeners.add(new SentinelSubscribeListener(masterName, sentinel, sentinelClientConfig, jedisSentinelPoolConfig.getSubscribeRetryWaitTimeMillis()) { @Override public void onChange(HostAndPort hostAndPort) { @@ -232,7 +235,7 @@ public void onChange(HostAndPort hostAndPort) { }); } } - masterListeners.forEach(SentinelMasterListener::start); + masterListeners.forEach(SentinelListener::start); } private static Set parseHostAndPorts(Set strings) { @@ -241,7 +244,7 @@ private static Set parseHostAndPorts(Set strings) { @Override public void destroy() { - masterListeners.forEach(SentinelMasterListener::shutdown); + masterListeners.forEach(SentinelListener::shutdown); super.destroy(); } diff --git a/src/main/java/redis/clients/jedis/SentinelMasterListener.java b/src/main/java/redis/clients/jedis/SentinelMasterListener.java deleted file mode 100644 index d69c318c6e..0000000000 --- a/src/main/java/redis/clients/jedis/SentinelMasterListener.java +++ /dev/null @@ -1,14 +0,0 @@ -package redis.clients.jedis; - -/** - * interface for monitor the master failover under sentinel mode We offer two implementation options - * @see SentinelMasterSubscribeListener Passive subscription - * @see SentinelMasterActiveDetectListener Active detection - */ -public interface SentinelMasterListener { - void start(); - - void shutdown(); - - void onChange(HostAndPort hostAndPort); -} \ No newline at end of file diff --git a/src/main/java/redis/clients/jedis/providers/SentineledConnectionProvider.java b/src/main/java/redis/clients/jedis/providers/SentineledConnectionProvider.java index 0825bad56f..0863f392bb 100644 --- a/src/main/java/redis/clients/jedis/providers/SentineledConnectionProvider.java +++ b/src/main/java/redis/clients/jedis/providers/SentineledConnectionProvider.java @@ -4,7 +4,6 @@ import java.util.Collection; import java.util.List; import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.slf4j.Logger; @@ -16,14 +15,12 @@ import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisClientConfig; -import redis.clients.jedis.JedisPubSub; -import redis.clients.jedis.SentinelMasterActiveDetectListener; -import redis.clients.jedis.SentinelMasterListener; -import redis.clients.jedis.SentinelMasterSubscribeListener; import redis.clients.jedis.SentinelPoolConfig; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; -import redis.clients.jedis.util.IOUtils; +import redis.clients.jedis.sentinel.listenner.SentinelActiveDetectListener; +import redis.clients.jedis.sentinel.listenner.SentinelListener; +import redis.clients.jedis.sentinel.listenner.SentinelSubscribeListener; public class SentineledConnectionProvider implements ConnectionProvider { @@ -41,7 +38,7 @@ public class SentineledConnectionProvider implements ConnectionProvider { private final GenericObjectPoolConfig masterPoolConfig; - private final Collection sentinelListeners = new ArrayList<>(); + private final Collection sentinelListeners = new ArrayList<>(); private final JedisClientConfig sentinelClientConfig; @@ -95,7 +92,7 @@ private void initMasterListeners(Set sentinels, String masterName, for (HostAndPort sentinel : sentinels) { if (jedisSentinelPoolConfig.isEnableActiveDetectListener()) { sentinelListeners.add( - new SentinelMasterActiveDetectListener(currentMaster, sentinel, sentinelClientConfig, + new SentinelActiveDetectListener(currentMaster, sentinel, sentinelClientConfig, masterName, jedisSentinelPoolConfig.getActiveDetectIntervalTimeMillis()) { @Override public void onChange(HostAndPort hostAndPort) { @@ -105,7 +102,7 @@ public void onChange(HostAndPort hostAndPort) { } if (jedisSentinelPoolConfig.isEnableDefaultSubscribeListener()) { - sentinelListeners.add(new SentinelMasterSubscribeListener(masterName, sentinel, + sentinelListeners.add(new SentinelSubscribeListener(masterName, sentinel, sentinelClientConfig, jedisSentinelPoolConfig.getSubscribeRetryWaitTimeMillis()) { @Override public void onChange(HostAndPort hostAndPort) { @@ -115,7 +112,7 @@ public void onChange(HostAndPort hostAndPort) { } } - sentinelListeners.forEach(SentinelMasterListener::start); + sentinelListeners.forEach(SentinelListener::start); } @Override @@ -130,7 +127,7 @@ public Connection getConnection(CommandArguments args) { @Override public void close() { - sentinelListeners.forEach(SentinelMasterListener::shutdown); + sentinelListeners.forEach(SentinelListener::shutdown); pool.close(); } diff --git a/src/main/java/redis/clients/jedis/SentinelMasterActiveDetectListener.java b/src/main/java/redis/clients/jedis/sentinel/listenner/SentinelActiveDetectListener.java similarity index 80% rename from src/main/java/redis/clients/jedis/SentinelMasterActiveDetectListener.java rename to src/main/java/redis/clients/jedis/sentinel/listenner/SentinelActiveDetectListener.java index bfe50ff44f..103585a00a 100644 --- a/src/main/java/redis/clients/jedis/SentinelMasterActiveDetectListener.java +++ b/src/main/java/redis/clients/jedis/sentinel/listenner/SentinelActiveDetectListener.java @@ -1,21 +1,25 @@ -package redis.clients.jedis; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +package redis.clients.jedis.sentinel.listenner; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisClientConfig; + /** * active detect master node .in case of the subscribe message lost - * @see SentinelMasterSubscribeListener subscribe failover message from "+switch-master" channel + * @see SentinelSubscribeListener subscribe failover message from "+switch-master" channel */ -public abstract class SentinelMasterActiveDetectListener extends Thread - implements SentinelMasterListener { +public abstract class SentinelActiveDetectListener extends Thread + implements SentinelListener { private static final Logger LOG = LoggerFactory - .getLogger(SentinelMasterActiveDetectListener.class); + .getLogger(SentinelActiveDetectListener.class); private List currentHostMaster; private HostAndPort sentinel; @@ -26,9 +30,9 @@ public abstract class SentinelMasterActiveDetectListener extends Thread private AtomicBoolean running = new AtomicBoolean(false); private volatile Jedis j; - public SentinelMasterActiveDetectListener(HostAndPort currentHostMaster, HostAndPort sentinel, + public SentinelActiveDetectListener(HostAndPort currentHostMaster, HostAndPort sentinel, JedisClientConfig jedisClientConfig, String masterName, long activeDetectIntervalTimeMillis) { - super(String.format("SentinelMasterActiveDetectListener-%s-[%s:%d]", masterName, + super(String.format("SentinelActiveDetectListener-%s-[%s:%d]", masterName, sentinel.getHost(), sentinel.getPort())); this.currentHostMaster = Arrays.asList(currentHostMaster.getHost(), String.valueOf(currentHostMaster.getPort())); diff --git a/src/main/java/redis/clients/jedis/sentinel/listenner/SentinelListener.java b/src/main/java/redis/clients/jedis/sentinel/listenner/SentinelListener.java new file mode 100644 index 0000000000..c40c0b5577 --- /dev/null +++ b/src/main/java/redis/clients/jedis/sentinel/listenner/SentinelListener.java @@ -0,0 +1,16 @@ +package redis.clients.jedis.sentinel.listenner; + +import redis.clients.jedis.HostAndPort; + +/** + * interface for monitor the master failover under sentinel mode We offer two implementation options + * @see SentinelSubscribeListener Passive subscription + * @see SentinelActiveDetectListener Active detection + */ +public interface SentinelListener { + void start(); + + void shutdown(); + + void onChange(HostAndPort hostAndPort); +} \ No newline at end of file diff --git a/src/main/java/redis/clients/jedis/SentinelMasterSubscribeListener.java b/src/main/java/redis/clients/jedis/sentinel/listenner/SentinelSubscribeListener.java similarity index 86% rename from src/main/java/redis/clients/jedis/SentinelMasterSubscribeListener.java rename to src/main/java/redis/clients/jedis/sentinel/listenner/SentinelSubscribeListener.java index dd2d32fbfe..1621a3816e 100644 --- a/src/main/java/redis/clients/jedis/SentinelMasterSubscribeListener.java +++ b/src/main/java/redis/clients/jedis/sentinel/listenner/SentinelSubscribeListener.java @@ -1,22 +1,27 @@ -package redis.clients.jedis; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import redis.clients.jedis.exceptions.JedisException; +package redis.clients.jedis.sentinel.listenner; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisClientConfig; +import redis.clients.jedis.JedisPubSub; +import redis.clients.jedis.exceptions.JedisException; + /** * subscribe failover message from "+switch-master" channel , the default listener mode use this - * @see SentinelMasterActiveDetectListener active detect master node .in case of the subscribe + * @see SentinelActiveDetectListener active detect master node .in case of the subscribe * message lost */ -public abstract class SentinelMasterSubscribeListener extends Thread - implements SentinelMasterListener { +public abstract class SentinelSubscribeListener extends Thread + implements SentinelListener { - private static final Logger LOG = LoggerFactory.getLogger(SentinelMasterSubscribeListener.class); + private static final Logger LOG = LoggerFactory.getLogger(SentinelSubscribeListener.class); private String masterName; private HostAndPort sentinel; @@ -25,9 +30,9 @@ public abstract class SentinelMasterSubscribeListener extends Thread private volatile Jedis j; private AtomicBoolean running = new AtomicBoolean(false); - public SentinelMasterSubscribeListener(String masterName, HostAndPort sentinel, + public SentinelSubscribeListener(String masterName, HostAndPort sentinel, JedisClientConfig sentinelClientConfig, long subscribeRetryWaitTimeMillis) { - super(String.format("SentinelMaterSubscribeListener-%s-[%s:%d]", masterName, sentinel.getHost(), + super(String.format("SentinelSubscribeListener-%s-[%s:%d]", masterName, sentinel.getHost(), sentinel.getPort())); this.masterName = masterName; this.sentinel = sentinel; diff --git a/src/test/java/redis/clients/jedis/JedisSentinelTest.java b/src/test/java/redis/clients/jedis/JedisSentinelTest.java index 593c5d34c2..6fc01eb2f8 100644 --- a/src/test/java/redis/clients/jedis/JedisSentinelTest.java +++ b/src/test/java/redis/clients/jedis/JedisSentinelTest.java @@ -6,10 +6,8 @@ import static org.junit.Assert.fail; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import org.junit.After; import org.junit.Before; @@ -33,12 +31,8 @@ public class JedisSentinelTest { protected static HostAndPort sentinelForFailover = HostAndPorts.getSentinelServers().get(2); protected static HostAndPort masterForFailover = HostAndPorts.getRedisServers().get(5); - public final Set sentinels = new HashSet<>(); - @Before - public void setUp() throws Exception { - sentinels.clear(); - sentinels.add(sentinelForFailover.toString()); + public void setup() throws InterruptedException { } @After @@ -106,35 +100,6 @@ public void sentinelFailover() throws InterruptedException { } } - @Test - public void testSentinelMasterListener() throws InterruptedException { - Jedis j = new Jedis(sentinelForFailover); - Jedis j2 = new Jedis(sentinelForFailover); - - SentinelPoolConfig config = new SentinelPoolConfig(); - config.setEnableActiveDetectListener(true); - config.setEnableDefaultSubscribeListener(true); - config.setActiveDetectIntervalTimeMillis(5 * 1000); - config.setSubscribeRetryWaitTimeMillis(5 * 1000); - - JedisSentinelPool pool = new JedisSentinelPool(FAILOVER_MASTER_NAME, sentinels, config, 1000, - "foobared", 2); - - try { - HostAndPort masterGetFromPoolBefore = pool.getResource().connection.getHostAndPort(); - - JedisSentinelTestUtil.waitForNewPromotedMaster(FAILOVER_MASTER_NAME, j, j2); - - HostAndPort masterGetFromPoolCurrent = pool.getResource().connection.getHostAndPort(); - - assertNotEquals(masterGetFromPoolBefore, masterGetFromPoolCurrent); - } finally { - j.close(); - j2.close(); - pool.destroy(); - } - } - @Test public void sentinelMonitor() { Jedis j = new Jedis(sentinel); diff --git a/src/test/java/redis/clients/jedis/SentinelListenerTest.java b/src/test/java/redis/clients/jedis/SentinelListenerTest.java new file mode 100644 index 0000000000..ba7d6bbf15 --- /dev/null +++ b/src/test/java/redis/clients/jedis/SentinelListenerTest.java @@ -0,0 +1,129 @@ +package redis.clients.jedis; + +import org.junit.Before; +import org.junit.Test; +import java.util.HashSet; +import java.util.Set; + +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; + +public class SentinelListenerTest { + + private static final String MASTER_NAME = "mymasterfailover"; + + public static final HostAndPort sentinel1 = HostAndPorts.getSentinelServers().get(2); + + public final Set sentinels = new HashSet<>(); + + public final Set hostAndPortsSentinels = new HashSet<>(); + + @Before + public void setUp() throws Exception { + sentinels.clear(); + hostAndPortsSentinels.clear(); + + sentinels.add(sentinel1.toString()); + + hostAndPortsSentinels.add(sentinel1); + } + + @Test + public void testSentinelSubscribeListener() { + // case 1: default : subscribe on ,active off + SentinelPoolConfig config = new SentinelPoolConfig(); + + JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 1000, "foobared", + 2); + HostAndPort hostPort1 = pool.getResource().connection.getHostAndPort(); + + Jedis sentinel = new Jedis(sentinel1); + sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); + + HostAndPort hostPort2 = null; + for (int i = 0; i < 24; i++) { // timeout time 24*5000 + hostPort2 = pool.getResource().connection.getHostAndPort(); + + if (!hostPort2.equals(hostPort1)) { + break; + } + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + pool.destroy(); + assertNotNull(hostPort2); + assertNotEquals(hostPort1, hostPort2); + } + + @Test + public void testSentinelActiveDetectListener() { + // case 2: subscribe off ,active on + SentinelPoolConfig config = new SentinelPoolConfig(); + config.setEnableActiveDetectListener(true); + config.setEnableDefaultSubscribeListener(false); + + JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 1000, "foobared", + 2); + HostAndPort hostPort1 = pool.getResource().connection.getHostAndPort(); + + Jedis sentinel = new Jedis(sentinel1); + sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); + + HostAndPort hostPort2 = null; + for (int i = 0; i < 24; i++) { // timeout time 24*5000 + hostPort2 = pool.getResource().connection.getHostAndPort(); + + if (!hostPort2.equals(hostPort1)) { + break; + } + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + pool.destroy(); + assertNotNull(hostPort2); + assertNotEquals(hostPort1, hostPort2); + } + + @Test + public void testALLSentinelListener() { + // case 2: subscribe on ,active on + SentinelPoolConfig config = new SentinelPoolConfig(); + config.setEnableActiveDetectListener(true); + config.setEnableDefaultSubscribeListener(true); + config.setActiveDetectIntervalTimeMillis(5 * 1000); + config.setSubscribeRetryWaitTimeMillis(5 * 1000); + + JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 1000, "foobared", + 2); + HostAndPort hostPort1 = pool.getResource().connection.getHostAndPort(); + + Jedis sentinel = new Jedis(sentinel1); + sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); + + HostAndPort hostPort2 = null; + for (int i = 0; i < 24; i++) { // timeout time 24*5000 + hostPort2 = pool.getResource().connection.getHostAndPort(); + + if (!hostPort2.equals(hostPort1)) { + break; + } + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + pool.destroy(); + assertNotNull(hostPort2); + assertNotEquals(hostPort1, hostPort2); + } +} From d7e9dd2afcf36de51265ede85ab2b21f8ef8669d Mon Sep 17 00:00:00 2001 From: c00603587 Date: Wed, 1 Nov 2023 15:08:35 +0800 Subject: [PATCH 18/19] modify comment Signed-off-by: c00603587 --- .../sentinel/listenner/SentinelListener.java | 4 +- .../clients/jedis/SentinelListenerTest.java | 66 +------------------ 2 files changed, 3 insertions(+), 67 deletions(-) diff --git a/src/main/java/redis/clients/jedis/sentinel/listenner/SentinelListener.java b/src/main/java/redis/clients/jedis/sentinel/listenner/SentinelListener.java index c40c0b5577..6400e32f40 100644 --- a/src/main/java/redis/clients/jedis/sentinel/listenner/SentinelListener.java +++ b/src/main/java/redis/clients/jedis/sentinel/listenner/SentinelListener.java @@ -4,8 +4,8 @@ /** * interface for monitor the master failover under sentinel mode We offer two implementation options - * @see SentinelSubscribeListener Passive subscription - * @see SentinelActiveDetectListener Active detection + * @see SentinelSubscribeListener subscribe failover message from "+switch-master" channel + * @see SentinelActiveDetectListener active detect master node .in case of the subscribe message lost */ public interface SentinelListener { void start(); diff --git a/src/test/java/redis/clients/jedis/SentinelListenerTest.java b/src/test/java/redis/clients/jedis/SentinelListenerTest.java index ba7d6bbf15..a9b5d244d8 100644 --- a/src/test/java/redis/clients/jedis/SentinelListenerTest.java +++ b/src/test/java/redis/clients/jedis/SentinelListenerTest.java @@ -29,71 +29,7 @@ public void setUp() throws Exception { } @Test - public void testSentinelSubscribeListener() { - // case 1: default : subscribe on ,active off - SentinelPoolConfig config = new SentinelPoolConfig(); - - JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 1000, "foobared", - 2); - HostAndPort hostPort1 = pool.getResource().connection.getHostAndPort(); - - Jedis sentinel = new Jedis(sentinel1); - sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); - - HostAndPort hostPort2 = null; - for (int i = 0; i < 24; i++) { // timeout time 24*5000 - hostPort2 = pool.getResource().connection.getHostAndPort(); - - if (!hostPort2.equals(hostPort1)) { - break; - } - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - pool.destroy(); - assertNotNull(hostPort2); - assertNotEquals(hostPort1, hostPort2); - } - - @Test - public void testSentinelActiveDetectListener() { - // case 2: subscribe off ,active on - SentinelPoolConfig config = new SentinelPoolConfig(); - config.setEnableActiveDetectListener(true); - config.setEnableDefaultSubscribeListener(false); - - JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 1000, "foobared", - 2); - HostAndPort hostPort1 = pool.getResource().connection.getHostAndPort(); - - Jedis sentinel = new Jedis(sentinel1); - sentinel.sendCommand(Protocol.Command.SENTINEL, "failover", MASTER_NAME); - - HostAndPort hostPort2 = null; - for (int i = 0; i < 24; i++) { // timeout time 24*5000 - hostPort2 = pool.getResource().connection.getHostAndPort(); - - if (!hostPort2.equals(hostPort1)) { - break; - } - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - pool.destroy(); - assertNotNull(hostPort2); - assertNotEquals(hostPort1, hostPort2); - } - - @Test - public void testALLSentinelListener() { + public void testSentinelListener() { // case 2: subscribe on ,active on SentinelPoolConfig config = new SentinelPoolConfig(); config.setEnableActiveDetectListener(true); From 465e5bae7733ffc1f0fc31cb1f51776dbf6f4e19 Mon Sep 17 00:00:00 2001 From: c00603587 Date: Wed, 1 Nov 2023 15:42:22 +0800 Subject: [PATCH 19/19] move to new package Signed-off-by: c00603587 --- src/main/java/redis/clients/jedis/JedisSentinelPool.java | 6 +++--- .../jedis/providers/SentineledConnectionProvider.java | 6 +++--- .../SentinelActiveDetectListener.java | 2 +- .../sentinel/{listenner => listener}/SentinelListener.java | 2 +- .../{listenner => listener}/SentinelSubscribeListener.java | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) rename src/main/java/redis/clients/jedis/sentinel/{listenner => listener}/SentinelActiveDetectListener.java (98%) rename src/main/java/redis/clients/jedis/sentinel/{listenner => listener}/SentinelListener.java (90%) rename src/main/java/redis/clients/jedis/sentinel/{listenner => listener}/SentinelSubscribeListener.java (98%) diff --git a/src/main/java/redis/clients/jedis/JedisSentinelPool.java b/src/main/java/redis/clients/jedis/JedisSentinelPool.java index 1bf71817c8..f20ed02d27 100644 --- a/src/main/java/redis/clients/jedis/JedisSentinelPool.java +++ b/src/main/java/redis/clients/jedis/JedisSentinelPool.java @@ -12,9 +12,9 @@ import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; -import redis.clients.jedis.sentinel.listenner.SentinelActiveDetectListener; -import redis.clients.jedis.sentinel.listenner.SentinelListener; -import redis.clients.jedis.sentinel.listenner.SentinelSubscribeListener; +import redis.clients.jedis.sentinel.listener.SentinelActiveDetectListener; +import redis.clients.jedis.sentinel.listener.SentinelListener; +import redis.clients.jedis.sentinel.listener.SentinelSubscribeListener; import redis.clients.jedis.util.Pool; public class JedisSentinelPool extends Pool { diff --git a/src/main/java/redis/clients/jedis/providers/SentineledConnectionProvider.java b/src/main/java/redis/clients/jedis/providers/SentineledConnectionProvider.java index 0863f392bb..5f38ea81ca 100644 --- a/src/main/java/redis/clients/jedis/providers/SentineledConnectionProvider.java +++ b/src/main/java/redis/clients/jedis/providers/SentineledConnectionProvider.java @@ -18,9 +18,9 @@ import redis.clients.jedis.SentinelPoolConfig; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; -import redis.clients.jedis.sentinel.listenner.SentinelActiveDetectListener; -import redis.clients.jedis.sentinel.listenner.SentinelListener; -import redis.clients.jedis.sentinel.listenner.SentinelSubscribeListener; +import redis.clients.jedis.sentinel.listener.SentinelActiveDetectListener; +import redis.clients.jedis.sentinel.listener.SentinelListener; +import redis.clients.jedis.sentinel.listener.SentinelSubscribeListener; public class SentineledConnectionProvider implements ConnectionProvider { diff --git a/src/main/java/redis/clients/jedis/sentinel/listenner/SentinelActiveDetectListener.java b/src/main/java/redis/clients/jedis/sentinel/listener/SentinelActiveDetectListener.java similarity index 98% rename from src/main/java/redis/clients/jedis/sentinel/listenner/SentinelActiveDetectListener.java rename to src/main/java/redis/clients/jedis/sentinel/listener/SentinelActiveDetectListener.java index 103585a00a..fa208f80bc 100644 --- a/src/main/java/redis/clients/jedis/sentinel/listenner/SentinelActiveDetectListener.java +++ b/src/main/java/redis/clients/jedis/sentinel/listener/SentinelActiveDetectListener.java @@ -1,4 +1,4 @@ -package redis.clients.jedis.sentinel.listenner; +package redis.clients.jedis.sentinel.listener; import java.util.Arrays; import java.util.List; diff --git a/src/main/java/redis/clients/jedis/sentinel/listenner/SentinelListener.java b/src/main/java/redis/clients/jedis/sentinel/listener/SentinelListener.java similarity index 90% rename from src/main/java/redis/clients/jedis/sentinel/listenner/SentinelListener.java rename to src/main/java/redis/clients/jedis/sentinel/listener/SentinelListener.java index 6400e32f40..e890eee4ae 100644 --- a/src/main/java/redis/clients/jedis/sentinel/listenner/SentinelListener.java +++ b/src/main/java/redis/clients/jedis/sentinel/listener/SentinelListener.java @@ -1,4 +1,4 @@ -package redis.clients.jedis.sentinel.listenner; +package redis.clients.jedis.sentinel.listener; import redis.clients.jedis.HostAndPort; diff --git a/src/main/java/redis/clients/jedis/sentinel/listenner/SentinelSubscribeListener.java b/src/main/java/redis/clients/jedis/sentinel/listener/SentinelSubscribeListener.java similarity index 98% rename from src/main/java/redis/clients/jedis/sentinel/listenner/SentinelSubscribeListener.java rename to src/main/java/redis/clients/jedis/sentinel/listener/SentinelSubscribeListener.java index 1621a3816e..a4dfc687ed 100644 --- a/src/main/java/redis/clients/jedis/sentinel/listenner/SentinelSubscribeListener.java +++ b/src/main/java/redis/clients/jedis/sentinel/listener/SentinelSubscribeListener.java @@ -1,4 +1,4 @@ -package redis.clients.jedis.sentinel.listenner; +package redis.clients.jedis.sentinel.listener; import java.util.Arrays; import java.util.List;