You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by go...@apache.org on 2018/11/09 01:20:48 UTC
[geode] branch develop updated: [GEODE-5998] Add geospatial
commands to Redis adapter (#2802)
This is an automated email from the ASF dual-hosted git repository.
gosullivan pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git
The following commit(s) were added to refs/heads/develop by this push:
new 7bf0225 [GEODE-5998] Add geospatial commands to Redis adapter (#2802)
7bf0225 is described below
commit 7bf02251fd047cb1cf575c01b80a9807108618da
Author: Aditya Anchuri <aa...@pivotal.io>
AuthorDate: Thu Nov 8 17:20:39 2018 -0800
[GEODE-5998] Add geospatial commands to Redis adapter (#2802)
This PR adds the following geospatial commands to the Redis adapter:
* GEOADD
* GEOPOS
* GEODIST
* GEOHASH
* GEORADIUS
* GEORADIUSBYMEMBER
This leverages a lot of the same logic as the SortedSetExecutor used for ZRANGE commands. The geocoding logic uses the geo library, which implements the same public domain algorithm that Redis uses.
---
.../integrationTest/resources/assembly_content.txt | 3 +
.../integrationTest/resources/expected_jars.txt | 3 +
geode-core/build.gradle | 1 +
.../java/org/apache/geode/redis/GeoCoderTest.java} | 35 +-
.../java/org/apache/geode/redis/GeoJUnitTest.java | 421 +++++++++++++++++++++
.../org/apache/geode/redis/internal/Coder.java | 163 ++++----
.../EchoExecutor.java => CoderException.java} | 30 +-
.../org/apache/geode/redis/internal/GeoCoder.java | 213 +++++++++++
.../{executor/EchoExecutor.java => GeoCoord.java} | 30 +-
.../redis/internal/GeoRadiusResponseElement.java | 68 ++++
.../org/apache/geode/redis/internal/HashArea.java | 30 ++
.../EchoExecutor.java => HashNeighbors.java} | 39 +-
...oExecutor.java => MemberNotFoundException.java} | 31 +-
.../java/org/apache/geode/redis/internal/Pair.java | 30 ++
.../geode/redis/internal/RedisCommandType.java | 125 ++++++
.../geode/redis/internal/RedisConstants.java | 26 +-
.../redis/internal/executor/AbstractExecutor.java | 27 ++
.../redis/internal/executor/EchoExecutor.java | 2 +-
.../redis/internal/executor/KeysExecutor.java | 5 +-
.../redis/internal/executor/SortedSetQuery.java | 6 +
.../redis/internal/executor/TypeExecutor.java | 6 +-
.../redis/internal/executor/hash/HGetExecutor.java | 10 +-
.../executor/hash/HIncrByFloatExecutor.java | 4 +-
.../internal/executor/hash/HKeysExecutor.java | 4 +-
.../internal/executor/hash/HMGetExecutor.java | 3 +-
.../internal/executor/hash/HValsExecutor.java | 3 +-
.../internal/executor/list/LIndexExecutor.java | 3 +-
.../redis/internal/executor/list/PopExecutor.java | 6 +-
.../internal/executor/set/SMembersExecutor.java | 3 +-
.../redis/internal/executor/set/SPopExecutor.java | 3 +-
.../internal/executor/set/SRandMemberExecutor.java | 12 +-
.../redis/internal/executor/set/SetOpExecutor.java | 9 +-
.../executor/sortedset/GeoAddExecutor.java | 76 ++++
.../GeoDistExecutor.java} | 39 +-
.../GeoHashExecutor.java} | 42 +-
.../GeoPosExecutor.java} | 45 ++-
.../sortedset/GeoRadiusByMemberExecutor.java | 124 ++++++
.../executor/sortedset/GeoRadiusExecutor.java | 120 ++++++
.../executor/sortedset/GeoRadiusParameters.java | 132 +++++++
.../executor/sortedset/GeoSortedSetExecutor.java | 94 +++++
.../executor/sortedset/ZIncrByExecutor.java | 4 +-
.../executor/sortedset/ZScoreExecutor.java | 3 +-
.../internal/executor/string/GetExecutor.java | 9 +-
.../internal/executor/string/GetRangeExecutor.java | 3 +-
.../internal/executor/string/GetSetExecutor.java | 8 +-
.../executor/string/IncrByFloatExecutor.java | 4 +-
.../internal/executor/string/MGetExecutor.java | 3 +-
.../internal/executor/string/MSetExecutor.java | 1 -
.../sanctioned-geode-core-serializables.txt | 11 +
geode-core/src/test/resources/expected-pom.xml | 6 +
gradle/dependency-versions.properties | 1 +
51 files changed, 1760 insertions(+), 319 deletions(-)
diff --git a/geode-assembly/src/integrationTest/resources/assembly_content.txt b/geode-assembly/src/integrationTest/resources/assembly_content.txt
index 13c7f40..ad28303 100644
--- a/geode-assembly/src/integrationTest/resources/assembly_content.txt
+++ b/geode-assembly/src/integrationTest/resources/assembly_content.txt
@@ -1192,10 +1192,12 @@ lib/commons-digester-2.1.jar
lib/commons-io-2.6.jar
lib/commons-lang-2.6.jar
lib/commons-logging-1.2.jar
+lib/commons-math3-3.2.jar
lib/commons-modeler-2.0.1.jar
lib/commons-validator-1.6.jar
lib/fastutil-8.2.1.jar
lib/findbugs-annotations-1.3.9-1.jar
+lib/geo-0.7.1.jar
lib/geode-common-1.8.0-SNAPSHOT.jar
lib/geode-connectors-1.8.0-SNAPSHOT.jar
lib/geode-core-1.8.0-SNAPSHOT.jar
@@ -1211,6 +1213,7 @@ lib/geode-rebalancer-1.8.0-SNAPSHOT.jar
lib/geode-wan-1.8.0-SNAPSHOT.jar
lib/geode-web-1.8.0-SNAPSHOT.jar
lib/gfsh-dependencies.jar
+lib/grumpy-core-0.2.2.jar
lib/jackson-annotations-2.9.6.jar
lib/jackson-core-2.9.6.jar
lib/jackson-databind-2.9.6.jar
diff --git a/geode-assembly/src/integrationTest/resources/expected_jars.txt b/geode-assembly/src/integrationTest/resources/expected_jars.txt
index d1e6d29..4697b40 100644
--- a/geode-assembly/src/integrationTest/resources/expected_jars.txt
+++ b/geode-assembly/src/integrationTest/resources/expected_jars.txt
@@ -104,3 +104,6 @@ springfox-swagger-common
springfox-swagger-ui
swagger-annotations
swagger-models
+commons-math
+geo
+grumpy-core
diff --git a/geode-core/build.gradle b/geode-core/build.gradle
index a2c7f00..67920ed 100755
--- a/geode-core/build.gradle
+++ b/geode-core/build.gradle
@@ -198,6 +198,7 @@ dependencies {
compile('org.apache.logging.log4j:log4j-api:' + project.'log4j.version')
compile('org.apache.logging.log4j:log4j-core:' + project.'log4j.version')
+ compile('com.github.davidmoten:geo:' + project.'geo.version')
runtimeOnly('org.fusesource.jansi:jansi:' + project.'jansi.version') {
ext.optional = true
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/EchoExecutor.java b/geode-core/src/integrationTest/java/org/apache/geode/redis/GeoCoderTest.java
similarity index 50%
copy from geode-core/src/main/java/org/apache/geode/redis/internal/executor/EchoExecutor.java
copy to geode-core/src/integrationTest/java/org/apache/geode/redis/GeoCoderTest.java
index 407e653..19db1d3 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/EchoExecutor.java
+++ b/geode-core/src/integrationTest/java/org/apache/geode/redis/GeoCoderTest.java
@@ -12,27 +12,28 @@
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
-package org.apache.geode.redis.internal.executor;
+package org.apache.geode.redis;
-import java.util.List;
+import static org.junit.Assert.assertEquals;
-import org.apache.geode.redis.internal.Coder;
-import org.apache.geode.redis.internal.Command;
-import org.apache.geode.redis.internal.ExecutionHandlerContext;
-import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+import com.github.davidmoten.geo.LatLong;
+import org.junit.Test;
-public class EchoExecutor extends AbstractExecutor {
+import org.apache.geode.redis.internal.CoderException;
+import org.apache.geode.redis.internal.GeoCoder;
- @Override
- public void executeCommand(Command command, ExecutionHandlerContext context) {
- List<byte[]> commandElems = command.getProcessedCommand();
- if (commandElems.size() < 2) {
- command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.ECHO));
- return;
- }
-
- byte[] echoMessage = commandElems.get(1);
- command.setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), echoMessage));
+public class GeoCoderTest {
+ @Test
+ public void testGeoHash() throws CoderException {
+ String hash = GeoCoder.geohash(Double.toString(13.361389).getBytes(),
+ Double.toString(38.115556).getBytes());
+ assertEquals("sqc8b49rnyte", hash);
}
+ @Test
+ public void testGeoPos() throws CoderException {
+ LatLong pos = GeoCoder.geoPos("sqc8b49rnyte");
+ assertEquals(13.361389, pos.getLon(), 0.000001);
+ assertEquals(38.115556, pos.getLat(), 0.000001);
+ }
}
diff --git a/geode-core/src/integrationTest/java/org/apache/geode/redis/GeoJUnitTest.java b/geode-core/src/integrationTest/java/org/apache/geode/redis/GeoJUnitTest.java
new file mode 100755
index 0000000..d8d29fd
--- /dev/null
+++ b/geode-core/src/integrationTest/java/org/apache/geode/redis/GeoJUnitTest.java
@@ -0,0 +1,421 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.redis;
+
+import static org.apache.geode.distributed.ConfigurationProperties.LOCATORS;
+import static org.apache.geode.distributed.ConfigurationProperties.LOG_LEVEL;
+import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT;
+import static org.apache.geode.internal.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import redis.clients.jedis.GeoCoordinate;
+import redis.clients.jedis.GeoRadiusResponse;
+import redis.clients.jedis.GeoUnit;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.params.geo.GeoRadiusParam;
+
+import org.apache.geode.cache.CacheFactory;
+import org.apache.geode.cache.GemFireCache;
+import org.apache.geode.cache.Region;
+import org.apache.geode.internal.AvailablePortHelper;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.test.junit.categories.RedisTest;
+
+@Category({RedisTest.class})
+public class GeoJUnitTest {
+ private static Jedis jedis;
+ private static GeodeRedisServer server;
+ private static GemFireCache cache;
+ private static Random rand;
+ private static int port = 6379;
+
+ @BeforeClass
+ public static void setUp() throws IOException {
+ rand = new Random();
+ CacheFactory cf = new CacheFactory();
+ // cf.set("log-file", "redis.log");
+ cf.set(LOG_LEVEL, "error");
+ cf.set(MCAST_PORT, "0");
+ cf.set(LOCATORS, "");
+ cache = cf.create();
+ port = AvailablePortHelper.getRandomAvailableTCPPort();
+ server = new GeodeRedisServer("localhost", port);
+
+ server.start();
+ jedis = new Jedis("localhost", port, 10000000);
+ }
+
+ @Test
+ public void testGeoAdd() {
+ Map<String, GeoCoordinate> memberCoordinateMap = new HashMap<>();
+ memberCoordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556));
+ memberCoordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669));
+ Long l = jedis.geoadd("Sicily", memberCoordinateMap);
+ assertTrue(l == 2L);
+
+ Region<ByteArrayWrapper, ByteArrayWrapper> sicilyRegion = cache.getRegion("Sicily");
+ assertNotNull("Expected region to be not NULL", sicilyRegion);
+
+ // Check GeoHash
+ String hash =
+ sicilyRegion.get(new ByteArrayWrapper(new String("Palermo").getBytes())).toString();
+ assertEquals("sqc8b49rnyte", hash);
+
+ hash = sicilyRegion.get(new ByteArrayWrapper(new String("Catania").getBytes())).toString();
+ assertEquals("sqdtr74hyu5n", hash);
+ }
+
+ @Test
+ public void testGeoHash() {
+ Map<String, GeoCoordinate> memberCoordinateMap = new HashMap<>();
+ memberCoordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556));
+ memberCoordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669));
+ Long l = jedis.geoadd("Sicily", memberCoordinateMap);
+ assertTrue(l == 2L);
+
+ List<String> hashes = jedis.geohash("Sicily", "Palermo", "Catania", "Rome");
+
+ assertEquals("sqc8b49rnyte", hashes.get(0));
+ assertEquals("sqdtr74hyu5n", hashes.get(1));
+ assertEquals(null, hashes.get(2));
+ }
+
+ @Test
+ public void testGeoPos() {
+ Map<String, GeoCoordinate> memberCoordinateMap = new HashMap<>();
+ memberCoordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556));
+ memberCoordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669));
+ Long l = jedis.geoadd("Sicily", memberCoordinateMap);
+ assertTrue(l == 2L);
+
+ List<GeoCoordinate> positions = jedis.geopos("Sicily", "Palermo", "Catania", "Rome");
+
+ assertEquals(13.361389, positions.get(0).getLongitude(), 0.000001);
+ assertEquals(38.115556, positions.get(0).getLatitude(), 0.000001);
+ assertEquals(15.087269, positions.get(1).getLongitude(), 0.000001);
+ assertEquals(37.502669, positions.get(1).getLatitude(), 0.000001);
+ assertEquals(null, positions.get(2));
+ }
+
+ @Test
+ public void testGeoDist() {
+ Map<String, GeoCoordinate> memberCoordinateMap = new HashMap<>();
+ memberCoordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556));
+ memberCoordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669));
+ Long l = jedis.geoadd("Sicily", memberCoordinateMap);
+ assertTrue(l == 2L);
+
+ Double dist = jedis.geodist("Sicily", "Palermo", "Catania");
+ assertEquals(166274.1516, dist, 5.0);
+
+ dist = jedis.geodist("Sicily", "Palermo", "Catania", GeoUnit.KM);
+ assertEquals(166.2742, dist, 0.005);
+
+ dist = jedis.geodist("Sicily", "Palermo", "Catania", GeoUnit.M);
+ assertEquals(166274.1516, dist, 5.0);
+
+ dist = jedis.geodist("Sicily", "Palermo", "Catania", GeoUnit.MI);
+ assertEquals(103.3182, dist, 0.003);
+
+ dist = jedis.geodist("Sicily", "Palermo", "Catania", GeoUnit.FT);
+ assertEquals(545520.0960, dist, 15.0);
+
+ dist = jedis.geodist("Sicily", "Palermo", "Foo");
+ assertNull(dist);
+ }
+
+ @Test
+ public void testGeoRadiusBasic() {
+ Map<String, GeoCoordinate> memberCoordinateMap = new HashMap<>();
+ memberCoordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556));
+ memberCoordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669));
+ Long l = jedis.geoadd("Sicily", memberCoordinateMap);
+ assertTrue(l == 2L);
+
+ List<GeoRadiusResponse> gr = jedis.georadius("Sicily", 15.0, 37.0, 100000, GeoUnit.M);
+ assertEquals(1, gr.size());
+ assertEquals("Catania", gr.get(0).getMemberByString());
+
+ gr = jedis.georadius("Sicily", 15.0, 37.0, 200, GeoUnit.KM);
+ assertEquals(2, gr.size());
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("Catania")));
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("Palermo")));
+ }
+
+ @Test
+ public void testGeoRadiusWithDist() {
+ Map<String, GeoCoordinate> memberCoordinateMap = new HashMap<>();
+ memberCoordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556));
+ memberCoordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669));
+ Long l = jedis.geoadd("Sicily", memberCoordinateMap);
+ assertTrue(l == 2L);
+
+ List<GeoRadiusResponse> gr = jedis.georadius("Sicily", 15.0, 37.0, 100, GeoUnit.KM,
+ GeoRadiusParam.geoRadiusParam().withDist());
+ assertEquals(1, gr.size());
+ assertEquals("Catania", gr.get(0).getMemberByString());
+ assertEquals(56.4413, gr.get(0).getDistance(), 0.0001);
+ }
+
+ @Test
+ public void testGeoRadiusWithCoord() {
+ Map<String, GeoCoordinate> memberCoordinateMap = new HashMap<>();
+ memberCoordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556));
+ memberCoordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669));
+ Long l = jedis.geoadd("Sicily", memberCoordinateMap);
+ assertTrue(l == 2L);
+
+ List<GeoRadiusResponse> gr = jedis.georadius("Sicily", 15.0, 37.0, 100, GeoUnit.KM,
+ GeoRadiusParam.geoRadiusParam().withCoord());
+ assertEquals(1, gr.size());
+ assertEquals("Catania", gr.get(0).getMemberByString());
+ assertEquals(15.087269, gr.get(0).getCoordinate().getLongitude(), 0.0001);
+ assertEquals(37.502669, gr.get(0).getCoordinate().getLatitude(), 0.0001);
+ }
+
+ @Test
+ public void testGeoRadiusCount() {
+ Map<String, GeoCoordinate> memberCoordinateMap = new HashMap<>();
+ memberCoordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556));
+ memberCoordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669));
+ Long l = jedis.geoadd("Sicily", memberCoordinateMap);
+ assertTrue(l == 2L);
+
+ List<GeoRadiusResponse> gr = jedis.georadius("Sicily", 15.0, 37.0, 200,
+ GeoUnit.KM, GeoRadiusParam.geoRadiusParam().count(1));
+ assertEquals(1, gr.size());
+ }
+
+ @Test
+ public void testGeoRadiusOrdered() {
+ Map<String, GeoCoordinate> memberCoordinateMap = new HashMap<>();
+ memberCoordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556));
+ memberCoordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669));
+ Long l = jedis.geoadd("Sicily", memberCoordinateMap);
+ assertTrue(l == 2L);
+
+ List<GeoRadiusResponse> gr = jedis.georadius("Sicily", 15.0, 37.0, 200,
+ GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending());
+ assertEquals(2, gr.size());
+ assertEquals("Catania", gr.get(0).getMemberByString());
+ assertEquals("Palermo", gr.get(1).getMemberByString());
+
+ gr = jedis.georadius("Sicily", 15.0, 37.0, 200,
+ GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortDescending());
+ assertEquals(2, gr.size());
+ assertEquals("Palermo", gr.get(0).getMemberByString());
+ assertEquals("Catania", gr.get(1).getMemberByString());
+ }
+
+ @Test
+ public void testGeoRadiusWithCoordDistCountAsc() {
+ Map<String, GeoCoordinate> memberCoordinateMap = new HashMap<>();
+ memberCoordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556));
+ memberCoordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669));
+ Long l = jedis.geoadd("Sicily", memberCoordinateMap);
+ assertTrue(l == 2L);
+
+ List<GeoRadiusResponse> gr = jedis.georadius("Sicily", 15.0, 37.0, 200, GeoUnit.KM,
+ GeoRadiusParam.geoRadiusParam().withCoord().withDist().count(1).sortAscending());
+ assertEquals(1, gr.size());
+ assertEquals("Catania", gr.get(0).getMemberByString());
+ assertEquals(15.087269, gr.get(0).getCoordinate().getLongitude(), 0.0001);
+ assertEquals(37.502669, gr.get(0).getCoordinate().getLatitude(), 0.0001);
+ assertEquals(56.4413, gr.get(0).getDistance(), 0.0001);
+ }
+
+ @Test
+ public void testGeoRadiusByMemberBasic() {
+ Map<String, GeoCoordinate> memberCoordinateMap = new HashMap<>();
+ memberCoordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556));
+ memberCoordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669));
+ Long l = jedis.geoadd("Sicily", memberCoordinateMap);
+ assertTrue(l == 2L);
+
+ List<GeoRadiusResponse> gr = jedis.georadiusByMember("Sicily", "Catania", 250, GeoUnit.KM);
+ assertEquals(1, gr.size());
+ assertEquals("Palermo", gr.get(0).getMemberByString());
+ }
+
+ @Test
+ public void testGeoRadiusByMemberWithDist() {
+ Map<String, GeoCoordinate> memberCoordinateMap = new HashMap<>();
+ memberCoordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556));
+ memberCoordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669));
+ Long l = jedis.geoadd("Sicily", memberCoordinateMap);
+ assertTrue(l == 2L);
+
+ List<GeoRadiusResponse> gr = jedis.georadiusByMember("Sicily", "Catania", 250,
+ GeoUnit.KM, GeoRadiusParam.geoRadiusParam().withDist());
+ assertEquals(1, gr.size());
+ assertEquals("Palermo", gr.get(0).getMemberByString());
+ assertEquals(166.2742, gr.get(0).getDistance(), 0.0001);
+ }
+
+ @Test
+ public void testGeoRadiusByMemberWithCoord() {
+ Map<String, GeoCoordinate> memberCoordinateMap = new HashMap<>();
+ memberCoordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556));
+ memberCoordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669));
+ Long l = jedis.geoadd("Sicily", memberCoordinateMap);
+ assertTrue(l == 2L);
+
+ List<GeoRadiusResponse> gr = jedis.georadiusByMember("Sicily", "Catania", 250,
+ GeoUnit.KM, GeoRadiusParam.geoRadiusParam().withCoord());
+ assertEquals(1, gr.size());
+ assertEquals("Palermo", gr.get(0).getMemberByString());
+ assertEquals(13.361389, gr.get(0).getCoordinate().getLongitude(), 0.0001);
+ assertEquals(38.115556, gr.get(0).getCoordinate().getLatitude(), 0.0001);
+ }
+
+
+ @Test
+ public void testGeoRadiusByMemberFull() {
+ Map<String, GeoCoordinate> memberCoordinateMap = new HashMap<>();
+ // 47.599246, -122.333826
+ memberCoordinateMap.put("Galvanize", new GeoCoordinate(-122.333826, 47.599246));
+ // 47.600249, -122.331166
+ memberCoordinateMap.put("Flatstick", new GeoCoordinate(-122.331166, 47.600249));
+ // 47.601120, -122.332063
+ memberCoordinateMap.put("Fuel Sports", new GeoCoordinate(-122.332063, 47.601120));
+ // 47.600657, -122.334362
+ memberCoordinateMap.put("Central Saloon", new GeoCoordinate(-122.334362, 47.600657));
+ // 47.598519, -122.334405
+ memberCoordinateMap.put("Cowgirls", new GeoCoordinate(-122.334405, 47.598519));
+
+ // 47.608336, -122.340746
+ memberCoordinateMap.put("Jarrbar", new GeoCoordinate(-122.340746, 47.608336));
+ // 47.612499, -122.336871
+ memberCoordinateMap.put("Oliver's lounge", new GeoCoordinate(-122.336871, 47.612499));
+ // 47.612622, -122.320288
+ memberCoordinateMap.put("Garage", new GeoCoordinate(-122.320288, 47.612622));
+ // 47.607362, -122.316517
+ memberCoordinateMap.put("Ba Bar", new GeoCoordinate(-122.316517, 47.607362));
+
+ // 47.615146, -122.322355
+ memberCoordinateMap.put("Bill's", new GeoCoordinate(-122.322355, 47.615146));
+ // 47.621821, -122.336747
+ memberCoordinateMap.put("Brave Horse Tavern", new GeoCoordinate(-122.336747, 47.621821));
+
+ // 47.616580, -122.200777
+ memberCoordinateMap.put("Earl's", new GeoCoordinate(-122.200777, 47.616580));
+ // 47.615165, -122.201317
+ memberCoordinateMap.put("Wild Ginger", new GeoCoordinate(-122.201317, 47.615165));
+
+ Long l = jedis.geoadd("Seattle", memberCoordinateMap);
+ assertTrue(l == 13L);
+
+ List<GeoRadiusResponse> gr = jedis.georadiusByMember("Seattle", "Galvanize", 0.2, GeoUnit.MI);
+ assertEquals(4, gr.size());
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("Flatstick")));
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("Fuel Sports")));
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("Central Saloon")));
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("Cowgirls")));
+
+ gr = jedis.georadiusByMember("Seattle", "Galvanize", 1.2, GeoUnit.MI);
+ assertEquals(8, gr.size());
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("Jarrbar")));
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("Oliver's lounge")));
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("Garage")));
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("Ba Bar")));
+
+ gr = jedis.georadiusByMember("Seattle", "Galvanize", 2.0, GeoUnit.MI);
+ assertEquals(10, gr.size());
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("Bill's")));
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("Brave Horse Tavern")));
+
+ gr = jedis.georadiusByMember("Seattle", "Galvanize", 10.0, GeoUnit.MI);
+ assertEquals(12, gr.size());
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("Earl's")));
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("Wild Ginger")));
+ }
+
+ @Test
+ public void testGeoRadiusByMemberNorth() {
+ Map<String, GeoCoordinate> memberCoordinateMap = new HashMap<>();
+ // 66.084124, -18.592468
+ memberCoordinateMap.put("Northern Iceland", new GeoCoordinate(-18.592468, 66.084124));
+ // 64.203491, -51.726331
+ memberCoordinateMap.put("Nuuk", new GeoCoordinate(-51.726331, 64.203491));
+ // 55.861822, -4.237179
+ memberCoordinateMap.put("Glasgow", new GeoCoordinate(-4.237179, 55.861822));
+ // 51.585005, -0.159837
+ memberCoordinateMap.put("London", new GeoCoordinate(-0.159837, 51.585005));
+ // 59.933071, 10.769027
+ memberCoordinateMap.put("Oslo", new GeoCoordinate(10.769027, 59.933071));
+
+ Long l = jedis.geoadd("North", memberCoordinateMap);
+ assertTrue(l == 5L);
+
+ List<GeoRadiusResponse> gr = jedis.georadiusByMember("North", "Northern Iceland", 1590.0,
+ GeoUnit.KM, GeoRadiusParam.geoRadiusParam().withDist());
+
+ assertEquals(2, gr.size());
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("Nuuk")));
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("Glasgow")));
+
+ gr = jedis.georadiusByMember("North", "Northern Iceland", 3000.0,
+ GeoUnit.KM, GeoRadiusParam.geoRadiusParam().withDist());
+ assertEquals(4, gr.size());
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("Nuuk")));
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("Glasgow")));
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("Oslo")));
+ assertTrue(gr.stream().anyMatch(r -> r.getMemberByString().equals("London")));
+ }
+
+ @Test
+ public void testGeoRadiusByMemberInvalid() {
+ Map<String, GeoCoordinate> memberCoordinateMap = new HashMap<>();
+ memberCoordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556));
+ memberCoordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669));
+ Long l = jedis.geoadd("Sicily", memberCoordinateMap);
+ assertTrue(l == 2L);
+
+ Exception ex = null;
+ try {
+ jedis.georadiusByMember("Sicily", "Roma", 91.0, GeoUnit.KM);
+ } catch (Exception e) {
+ ex = e;
+ }
+
+ assertNotNull(ex);
+ assertTrue(ex.getMessage().contains("could not decode requested zset member"));
+ }
+
+ @After
+ public void flushAll() {
+ jedis.flushAll();
+ }
+
+ @AfterClass
+ public static void tearDown() {
+ jedis.close();
+ cache.close();
+ server.shutdown();
+ }
+}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/Coder.java b/geode-core/src/main/java/org/apache/geode/redis/internal/Coder.java
index 44e8447..00d5619 100644
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/Coder.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/Coder.java
@@ -17,7 +17,6 @@ package org.apache.geode.redis.internal;
import java.io.UnsupportedEncodingException;
import java.text.DecimalFormat;
import java.util.Collection;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -93,6 +92,7 @@ public class Coder {
public static final String CHARSET = "UTF-8";
protected static final DecimalFormat decimalFormatter = new DecimalFormat("#");
+
static {
decimalFormatter.setMaximumFractionDigits(10);
}
@@ -107,73 +107,63 @@ public class Coder {
*/
public static final String N_INF = "-inf";
- public static ByteBuf getBulkStringResponse(ByteBufAllocator alloc, byte[] value) {
- ByteBuf response = alloc.buffer(value.length + 20);
- response.writeByte(BULK_STRING_ID);
- response.writeBytes(intToBytes(value.length));
- response.writeBytes(CRLFar);
- response.writeBytes(value);
- response.writeBytes(CRLFar);
- return response;
- }
+ public static ByteBuf getBulkStringResponse(ByteBufAllocator alloc, Object v)
+ throws CoderException {
+ ByteBuf response;
+ byte[] toWrite;
- public static ByteBuf getBulkStringResponse(ByteBufAllocator alloc, double value) {
- ByteBuf response = alloc.buffer();
- byte[] doub = doubleToBytes(value);
- response.writeByte(BULK_STRING_ID);
- response.writeBytes(intToBytes(doub.length));
- response.writeBytes(CRLFar);
- response.writeBytes(doub);
- response.writeBytes(CRLFar);
- return response;
- }
+ if (v == null) {
+ response = alloc.buffer();
+ response.writeBytes(bNIL);
+ return response;
+ } else if (v instanceof byte[]) {
+ byte[] value = (byte[]) v;
+ response = alloc.buffer(value.length + 20);
+ toWrite = value;
+ } else if (v instanceof ByteArrayWrapper) {
+ byte[] value = ((ByteArrayWrapper) v).toBytes();
+ response = alloc.buffer(value.length + 20);
+ toWrite = value;
+ } else if (v instanceof Double) {
+ response = alloc.buffer();
+ toWrite = doubleToBytes(((Double) v).doubleValue());
+ } else if (v instanceof String) {
+ String value = (String) v;
+ response = alloc.buffer(value.length() + 20);
+ toWrite = stringToBytes(value);
+ } else {
+ throw new CoderException();
+ }
- public static ByteBuf getBulkStringResponse(ByteBufAllocator alloc, String value) {
- byte[] valueAr = stringToBytes(value);
- int length = valueAr == null ? 0 : valueAr.length;
- ByteBuf response = alloc.buffer(length + 20);
response.writeByte(BULK_STRING_ID);
- response.writeBytes(intToBytes(length));
+ response.writeBytes(intToBytes(toWrite.length));
response.writeBytes(CRLFar);
- response.writeBytes(valueAr);
+ response.writeBytes(toWrite);
response.writeBytes(CRLFar);
- return response;
- }
- public static ByteBuf getBulkStringArrayResponse(ByteBufAllocator alloc, List<String> items) {
- Iterator<String> it = items.iterator();
- ByteBuf response = alloc.buffer();
- response.writeByte(ARRAY_ID);
- response.writeBytes(intToBytes(items.size()));
- response.writeBytes(CRLFar);
- while (it.hasNext()) {
- String next = it.next();
- response.writeByte(BULK_STRING_ID);
- response.writeBytes(intToBytes(next.length()));
- response.writeBytes(CRLFar);
- response.writeBytes(stringToBytes(next));
- response.writeBytes(CRLFar);
- }
return response;
}
- public static ByteBuf getBulkStringArrayResponse(ByteBufAllocator alloc,
- Collection<ByteArrayWrapper> items) {
- Iterator<ByteArrayWrapper> it = items.iterator();
+ public static ByteBuf getBulkStringArrayResponse(ByteBufAllocator alloc, Collection<?> items)
+ throws CoderException {
ByteBuf response = alloc.buffer();
response.writeByte(ARRAY_ID);
response.writeBytes(intToBytes(items.size()));
response.writeBytes(CRLFar);
- while (it.hasNext()) {
- ByteArrayWrapper nextWrapper = it.next();
- if (nextWrapper != null) {
- response.writeByte(BULK_STRING_ID);
- response.writeBytes(intToBytes(nextWrapper.length()));
- response.writeBytes(CRLFar);
- response.writeBytes(nextWrapper.toBytes());
- response.writeBytes(CRLFar);
- } else
- response.writeBytes(getNilResponse(alloc));
+ for (Object next : items) {
+ ByteBuf tmp = null;
+ try {
+ if (next instanceof Collection) {
+ Collection<?> nextItems = (Collection<?>) next;
+ tmp = getBulkStringArrayResponse(alloc, nextItems);
+ response.writeBytes(tmp);
+ } else {
+ tmp = getBulkStringResponse(alloc, next);
+ response.writeBytes(tmp);
+ }
+ } finally {
+ tmp.release();
+ }
}
return response;
@@ -181,14 +171,12 @@ public class Coder {
public static ByteBuf getKeyValArrayResponse(ByteBufAllocator alloc,
Collection<Entry<ByteArrayWrapper, ByteArrayWrapper>> items) {
- Iterator<Map.Entry<ByteArrayWrapper, ByteArrayWrapper>> it = items.iterator();
ByteBuf response = alloc.buffer();
response.writeByte(ARRAY_ID);
int size = 0;
ByteBuf tmp = alloc.buffer();
- while (it.hasNext()) {
- Map.Entry<ByteArrayWrapper, ByteArrayWrapper> next = it.next();
+ for (Map.Entry<ByteArrayWrapper, ByteArrayWrapper> next : items) {
byte[] key;
byte[] nextByteArray;
try {
@@ -221,6 +209,7 @@ public class Coder {
public static ByteBuf getScanResponse(ByteBufAllocator alloc, List<?> items) {
ByteBuf response = alloc.buffer();
+
response.writeByte(ARRAY_ID);
response.writeBytes(intToBytes(2));
response.writeBytes(CRLFar);
@@ -231,13 +220,11 @@ public class Coder {
response.writeBytes(cursor);
response.writeBytes(CRLFar);
items = items.subList(1, items.size());
- Iterator<?> it = items.iterator();
response.writeByte(ARRAY_ID);
response.writeBytes(intToBytes(items.size()));
response.writeBytes(CRLFar);
- while (it.hasNext()) {
- Object nextObject = it.next();
+ for (Object nextObject : items) {
if (nextObject instanceof String) {
String next = (String) nextObject;
response.writeByte(BULK_STRING_ID);
@@ -325,40 +312,40 @@ public class Coder {
public static ByteBuf getBulkStringArrayResponseOfValues(ByteBufAllocator alloc,
Collection<?> items) {
- Iterator<?> it = items.iterator();
ByteBuf response = alloc.buffer();
response.writeByte(Coder.ARRAY_ID);
ByteBuf tmp = alloc.buffer();
int size = 0;
- while (it.hasNext()) {
- Object next = it.next();
- ByteArrayWrapper nextWrapper = null;
- if (next instanceof Entry) {
- try {
- nextWrapper = (ByteArrayWrapper) ((Entry<?, ?>) next).getValue();
- } catch (EntryDestroyedException e) {
- continue;
+ try {
+ for (Object next : items) {
+ ByteArrayWrapper nextWrapper = null;
+ if (next instanceof Entry) {
+ try {
+ nextWrapper = (ByteArrayWrapper) ((Entry<?, ?>) next).getValue();
+ } catch (EntryDestroyedException e) {
+ continue;
+ }
+ } else if (next instanceof Struct) {
+ nextWrapper = (ByteArrayWrapper) ((Struct) next).getFieldValues()[1];
}
- } else if (next instanceof Struct) {
- nextWrapper = (ByteArrayWrapper) ((Struct) next).getFieldValues()[1];
- }
- if (nextWrapper != null) {
- tmp.writeByte(Coder.BULK_STRING_ID);
- tmp.writeBytes(intToBytes(nextWrapper.length()));
- tmp.writeBytes(Coder.CRLFar);
- tmp.writeBytes(nextWrapper.toBytes());
- tmp.writeBytes(Coder.CRLFar);
- } else {
- tmp.writeBytes(Coder.bNIL);
+ if (nextWrapper != null) {
+ tmp.writeByte(Coder.BULK_STRING_ID);
+ tmp.writeBytes(intToBytes(nextWrapper.length()));
+ tmp.writeBytes(Coder.CRLFar);
+ tmp.writeBytes(nextWrapper.toBytes());
+ tmp.writeBytes(Coder.CRLFar);
+ } else {
+ tmp.writeBytes(Coder.bNIL);
+ }
+ size++;
}
- size++;
- }
- response.writeBytes(intToBytes(size));
- response.writeBytes(Coder.CRLFar);
- response.writeBytes(tmp);
-
- tmp.release();
+ response.writeBytes(intToBytes(size));
+ response.writeBytes(Coder.CRLFar);
+ response.writeBytes(tmp);
+ } finally {
+ tmp.release();
+ }
return response;
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/EchoExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/CoderException.java
old mode 100755
new mode 100644
similarity index 50%
copy from geode-core/src/main/java/org/apache/geode/redis/internal/executor/EchoExecutor.java
copy to geode-core/src/main/java/org/apache/geode/redis/internal/CoderException.java
index 407e653..7de1116
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/EchoExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/CoderException.java
@@ -12,27 +12,25 @@
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
-package org.apache.geode.redis.internal.executor;
-import java.util.List;
+package org.apache.geode.redis.internal;
-import org.apache.geode.redis.internal.Coder;
-import org.apache.geode.redis.internal.Command;
-import org.apache.geode.redis.internal.ExecutionHandlerContext;
-import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+public class CoderException extends Exception {
+ private static final long serialVersionUID = 4707944288714910949L;
-public class EchoExecutor extends AbstractExecutor {
+ public CoderException() {
+ super();
+ }
- @Override
- public void executeCommand(Command command, ExecutionHandlerContext context) {
- List<byte[]> commandElems = command.getProcessedCommand();
- if (commandElems.size() < 2) {
- command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.ECHO));
- return;
- }
+ public CoderException(String message) {
+ super(message);
+ }
- byte[] echoMessage = commandElems.get(1);
- command.setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), echoMessage));
+ public CoderException(Throwable cause) {
+ super(cause);
}
+ public CoderException(String message, Throwable cause) {
+ super(message, cause);
+ }
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/GeoCoder.java b/geode-core/src/main/java/org/apache/geode/redis/internal/GeoCoder.java
new file mode 100644
index 0000000..f71dc2e
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/GeoCoder.java
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.redis.internal;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import com.github.davidmoten.geo.GeoHash;
+import com.github.davidmoten.geo.LatLong;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+
+public class GeoCoder {
+
+ /**
+ * Earth radius for distance calculations.
+ */
+ private static final double EARTH_RADIUS_IN_METERS = 6372797.560856;
+
+ public static ByteBuf getBulkStringGeoCoordinateArrayResponse(ByteBufAllocator alloc,
+ Collection<LatLong> items)
+ throws CoderException {
+ ByteBuf response = alloc.buffer();
+ response.writeByte(Coder.ARRAY_ID);
+ ByteBuf tmp = alloc.buffer();
+ int size = 0;
+ try {
+ for (LatLong next : items) {
+ if (next == null) {
+ tmp.writeBytes(Coder.bNIL);
+ } else {
+ tmp.writeBytes(Coder.getBulkStringArrayResponse(alloc,
+ Arrays.asList(
+ Double.toString(next.getLon()),
+ Double.toString(next.getLat()))));
+ }
+ size++;
+ }
+
+ response.writeBytes(Coder.intToBytes(size));
+ response.writeBytes(Coder.CRLFar);
+ response.writeBytes(tmp);
+ } finally {
+ tmp.release();
+ }
+
+ return response;
+ }
+
+ public static ByteBuf geoRadiusResponse(ByteBufAllocator alloc,
+ Collection<GeoRadiusResponseElement> list)
+ throws CoderException {
+ if (list.isEmpty())
+ return Coder.getEmptyArrayResponse(alloc);
+
+ List<Object> responseElements = new ArrayList<>();
+ for (GeoRadiusResponseElement element : list) {
+ String name = element.getName();
+
+ String distStr = "";
+ if (element.isShowDist()) {
+ distStr = element.getDistFromCenter().toString();
+ }
+
+ List<String> coord = new ArrayList<>();
+ if (element.getCoord().isPresent()) {
+ coord.add(Double.toString(element.getCoord().get().getLon()));
+ coord.add(Double.toString(element.getCoord().get().getLat()));
+ }
+
+ String hash = "";
+ if (element.getHash().isPresent()) {
+ hash = element.getHash().get();
+ }
+
+ if (distStr != "" || !coord.isEmpty() || hash != "") {
+ List<Object> elementData = new ArrayList<>();
+ elementData.add(name);
+ if (distStr != "")
+ elementData.add(distStr);
+ if (!coord.isEmpty())
+ elementData.add(coord);
+ if (hash != "")
+ elementData.add(hash);
+
+ responseElements.add(elementData);
+ } else {
+ responseElements.add(name);
+ }
+ }
+
+ return Coder.getBulkStringArrayResponse(alloc, responseElements);
+ }
+
+ /**
+ * Converts geohash to lat/long.
+ *
+ * @param hash geohash as base32
+ * @return a LatLong object containing the coordinates
+ */
+ public static LatLong geoPos(String hash) {
+ return GeoHash.decodeHash(hash);
+ }
+
+ /**
+ * Calculates distance between two points.
+ *
+ * @param hash1 geohash of first point
+ * @param hash2 geohash of second point
+ * @return distance in meters
+ */
+ public static Double geoDist(String hash1, String hash2) {
+ LatLong coord1 = geoPos(hash1);
+ LatLong coord2 = geoPos(hash2);
+
+ Double lat1 = Math.toRadians(coord1.getLat());
+ Double long1 = Math.toRadians(coord1.getLon());
+ Double lat2 = Math.toRadians(coord2.getLat());
+ Double long2 = Math.toRadians(coord2.getLon());
+
+ return dist(long1, lat1, long2, lat2);
+ }
+
+ /**
+ * Calculates geohash given latitude and longitude as byte arrays encoding decimals.
+ *
+ * @param lon byte array encoding longitude as decimal
+ * @param lat byte array encoding latitude as decimal
+ * @return geohash as base32
+ */
+ public static String geohash(byte[] lon, byte[] lat) throws IllegalArgumentException {
+ Double longitude = Coder.bytesToDouble(lon);
+ Double latitude = Coder.bytesToDouble(lat);
+ return GeoHash.encodeHash(latitude, longitude);
+ }
+
+ public static Set<String> geohashSearchAreas(double longitude, double latitude,
+ double radiusMeters) {
+ HashArea boundingBox = boundingBox(longitude, latitude, radiusMeters);
+ int steps =
+ Math.max(1, GeoHash.hashLengthToCoverBoundingBox(boundingBox.maxlat, boundingBox.maxlon,
+ boundingBox.minlat, boundingBox.minlon));
+
+ List<String> extra = new ArrayList<>();
+ // Large distance boundary condition
+ if (steps == 1) {
+ extra.addAll(GeoHash.neighbours(GeoHash.encodeHash(latitude, longitude, steps)));
+ }
+
+ Set<String> areas = GeoHash.coverBoundingBox(boundingBox.maxlat, boundingBox.maxlon,
+ boundingBox.minlat, boundingBox.minlon, steps).getHashes();
+ if (!extra.isEmpty()) {
+ extra.forEach(ex -> areas.add(ex));
+ }
+
+ return areas;
+ }
+
+ public static HashArea boundingBox(double longitude, double latitude,
+ double radiusMeters) {
+ double minlon = longitude - Math
+ .toDegrees((radiusMeters / EARTH_RADIUS_IN_METERS) * Math.cos(Math.toRadians(latitude)));
+ double maxlon = longitude + Math
+ .toDegrees((radiusMeters / EARTH_RADIUS_IN_METERS) * Math.cos(Math.toRadians(latitude)));
+ double minlat = latitude - Math.toDegrees(radiusMeters / EARTH_RADIUS_IN_METERS);
+ double maxlat = latitude + Math.toDegrees(radiusMeters / EARTH_RADIUS_IN_METERS);
+
+ return new HashArea(minlon, maxlon, minlat, maxlat);
+ }
+
+ public static Double dist(Double long1, Double lat1, Double long2, Double lat2) {
+ Double hav =
+ haversine(lat2 - lat1) + (Math.cos(lat1) * Math.cos(lat2) * haversine(long2 - long1));
+ Double distAngle = Math.acos(1 - (2 * hav));
+
+ return EARTH_RADIUS_IN_METERS * distAngle;
+ }
+
+ public static double haversine(Double rad) {
+ return 0.5 * (1 - Math.cos(rad));
+ }
+
+ public static double parseUnitScale(String unit) throws IllegalArgumentException {
+ switch (unit) {
+ case "km":
+ return 0.001;
+ case "m":
+ return 1.0;
+ case "ft":
+ return 3.28084;
+ case "mi":
+ return 0.000621371;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/EchoExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/GeoCoord.java
old mode 100755
new mode 100644
similarity index 50%
copy from geode-core/src/main/java/org/apache/geode/redis/internal/executor/EchoExecutor.java
copy to geode-core/src/main/java/org/apache/geode/redis/internal/GeoCoord.java
index 407e653..c3e31a4
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/EchoExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/GeoCoord.java
@@ -12,27 +12,23 @@
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
-package org.apache.geode.redis.internal.executor;
-import java.util.List;
+package org.apache.geode.redis.internal;
-import org.apache.geode.redis.internal.Coder;
-import org.apache.geode.redis.internal.Command;
-import org.apache.geode.redis.internal.ExecutionHandlerContext;
-import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+public class GeoCoord {
+ double longitude;
+ double latitude;
-public class EchoExecutor extends AbstractExecutor {
-
- @Override
- public void executeCommand(Command command, ExecutionHandlerContext context) {
- List<byte[]> commandElems = command.getProcessedCommand();
- if (commandElems.size() < 2) {
- command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.ECHO));
- return;
- }
+ public GeoCoord(double lon, double lat) {
+ this.longitude = lon;
+ this.latitude = lat;
+ }
- byte[] echoMessage = commandElems.get(1);
- command.setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), echoMessage));
+ public double getLongitude() {
+ return longitude;
}
+ public double getLatitude() {
+ return latitude;
+ }
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/GeoRadiusResponseElement.java b/geode-core/src/main/java/org/apache/geode/redis/internal/GeoRadiusResponseElement.java
new file mode 100644
index 0000000..4e0dd81
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/GeoRadiusResponseElement.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.redis.internal;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Optional;
+
+import com.github.davidmoten.geo.LatLong;
+
+public class GeoRadiusResponseElement {
+ private String name;
+ private Optional<LatLong> coord;
+ private Double distFromCenter;
+ private boolean showDist;
+ private Optional<String> hash;
+
+ public String getName() {
+ return name;
+ }
+
+ public Optional<LatLong> getCoord() {
+ return coord;
+ }
+
+ public Double getDistFromCenter() {
+ return distFromCenter;
+ }
+
+ public boolean isShowDist() {
+ return showDist;
+ }
+
+ public Optional<String> getHash() {
+ return hash;
+ }
+
+ public GeoRadiusResponseElement(String n, Optional<LatLong> c, Double d, boolean sh,
+ Optional<String> h) {
+ this.name = n;
+ this.coord = c;
+ this.distFromCenter = d;
+ this.showDist = sh;
+ this.hash = h;
+ }
+
+ public static void sortByDistanceAscending(List<GeoRadiusResponseElement> elements) {
+ Collections.sort(elements, Comparator.comparing(GeoRadiusResponseElement::getDistFromCenter));
+ }
+
+ public static void sortByDistanceDescending(List<GeoRadiusResponseElement> elements) {
+ Collections.sort(elements,
+ Comparator.comparing((GeoRadiusResponseElement x) -> -1.0 * x.getDistFromCenter()));
+ }
+}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/HashArea.java b/geode-core/src/main/java/org/apache/geode/redis/internal/HashArea.java
new file mode 100644
index 0000000..4a1c7cb
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/HashArea.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.redis.internal;
+
+public class HashArea {
+ public Double minlon;
+ public Double maxlon;
+ public Double minlat;
+ public Double maxlat;
+
+ public HashArea(double minlon, double maxlon, double minlat, double maxlat) {
+ this.minlon = minlon;
+ this.maxlon = maxlon;
+ this.minlat = minlat;
+ this.maxlat = maxlat;
+ }
+}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/EchoExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/HashNeighbors.java
old mode 100755
new mode 100644
similarity index 51%
copy from geode-core/src/main/java/org/apache/geode/redis/internal/executor/EchoExecutor.java
copy to geode-core/src/main/java/org/apache/geode/redis/internal/HashNeighbors.java
index 407e653..be39f40
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/EchoExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/HashNeighbors.java
@@ -12,27 +12,30 @@
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
-package org.apache.geode.redis.internal.executor;
-import java.util.List;
-
-import org.apache.geode.redis.internal.Coder;
-import org.apache.geode.redis.internal.Command;
-import org.apache.geode.redis.internal.ExecutionHandlerContext;
-import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+package org.apache.geode.redis.internal;
-public class EchoExecutor extends AbstractExecutor {
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
- @Override
- public void executeCommand(Command command, ExecutionHandlerContext context) {
- List<byte[]> commandElems = command.getProcessedCommand();
- if (commandElems.size() < 2) {
- command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.ECHO));
- return;
- }
+public class HashNeighbors {
+ public String center;
+ public String west;
+ public String east;
+ public String north;
+ public String south;
+ public String northwest;
+ public String northeast;
+ public String southwest;
+ public String southeast;
- byte[] echoMessage = commandElems.get(1);
- command.setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), echoMessage));
+ public List<String> get() {
+ return Arrays
+ .asList(center, west, east, north, south, northwest, northeast, southwest, southeast)
+ .stream()
+ .filter(n -> n != null)
+ .distinct()
+ .collect(Collectors.toList());
}
-
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/EchoExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/MemberNotFoundException.java
old mode 100755
new mode 100644
similarity index 50%
copy from geode-core/src/main/java/org/apache/geode/redis/internal/executor/EchoExecutor.java
copy to geode-core/src/main/java/org/apache/geode/redis/internal/MemberNotFoundException.java
index 407e653..0492098
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/EchoExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/MemberNotFoundException.java
@@ -12,27 +12,26 @@
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
-package org.apache.geode.redis.internal.executor;
-import java.util.List;
-import org.apache.geode.redis.internal.Coder;
-import org.apache.geode.redis.internal.Command;
-import org.apache.geode.redis.internal.ExecutionHandlerContext;
-import org.apache.geode.redis.internal.RedisConstants.ArityDef;
+package org.apache.geode.redis.internal;
-public class EchoExecutor extends AbstractExecutor {
+public class MemberNotFoundException extends Exception {
+ private static final long serialVersionUID = 4707944288714910949L;
- @Override
- public void executeCommand(Command command, ExecutionHandlerContext context) {
- List<byte[]> commandElems = command.getProcessedCommand();
- if (commandElems.size() < 2) {
- command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.ECHO));
- return;
- }
+ public MemberNotFoundException() {
+ super();
+ }
+
+ public MemberNotFoundException(String message) {
+ super(message);
+ }
- byte[] echoMessage = commandElems.get(1);
- command.setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), echoMessage));
+ public MemberNotFoundException(Throwable cause) {
+ super(cause);
}
+ public MemberNotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/Pair.java b/geode-core/src/main/java/org/apache/geode/redis/internal/Pair.java
new file mode 100644
index 0000000..aaff10e
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/Pair.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.redis.internal;
+
+public class Pair<T, U> {
+ T fst;
+ U snd;
+
+ Pair(Object fst, Object snd) {
+ this.fst = (T) fst;
+ this.snd = (U) snd;
+ }
+
+ public static Pair of(Object fst, Object snd) {
+ return new Pair(fst, snd);
+ }
+}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/RedisCommandType.java b/geode-core/src/main/java/org/apache/geode/redis/internal/RedisCommandType.java
index a4a42ad..c2625eb 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/RedisCommandType.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/RedisCommandType.java
@@ -81,6 +81,12 @@ import org.apache.geode.redis.internal.executor.set.SRemExecutor;
import org.apache.geode.redis.internal.executor.set.SScanExecutor;
import org.apache.geode.redis.internal.executor.set.SUnionExecutor;
import org.apache.geode.redis.internal.executor.set.SUnionStoreExecutor;
+import org.apache.geode.redis.internal.executor.sortedset.GeoAddExecutor;
+import org.apache.geode.redis.internal.executor.sortedset.GeoDistExecutor;
+import org.apache.geode.redis.internal.executor.sortedset.GeoHashExecutor;
+import org.apache.geode.redis.internal.executor.sortedset.GeoPosExecutor;
+import org.apache.geode.redis.internal.executor.sortedset.GeoRadiusByMemberExecutor;
+import org.apache.geode.redis.internal.executor.sortedset.GeoRadiusExecutor;
import org.apache.geode.redis.internal.executor.sortedset.ZAddExecutor;
import org.apache.geode.redis.internal.executor.sortedset.ZCardExecutor;
import org.apache.geode.redis.internal.executor.sortedset.ZCountExecutor;
@@ -2589,6 +2595,125 @@ public enum RedisCommandType {
}
},
+
+ /**************************************
+ * Geospatial commands ****************
+ **************************************/
+
+ GEOADD {
+ private Executor executor;
+
+ @Override
+ public Executor getExecutor() {
+ if (executor == null) {
+ executor = new GeoAddExecutor();
+ }
+ return executor;
+ }
+
+ private final RedisDataType dataType = RedisDataType.REDIS_SORTEDSET;
+
+ @Override
+ public RedisDataType getDataType() {
+ return this.dataType;
+ }
+ },
+
+ GEOHASH {
+ private Executor executor;
+
+ @Override
+ public Executor getExecutor() {
+ if (executor == null) {
+ executor = new GeoHashExecutor();
+ }
+ return executor;
+ }
+
+ private final RedisDataType dataType = RedisDataType.REDIS_SORTEDSET;
+
+ @Override
+ public RedisDataType getDataType() {
+ return this.dataType;
+ }
+ },
+
+ GEOPOS {
+ private Executor executor;
+
+ @Override
+ public Executor getExecutor() {
+ if (executor == null) {
+ executor = new GeoPosExecutor();
+ }
+ return executor;
+ }
+
+ private final RedisDataType dataType = RedisDataType.REDIS_SORTEDSET;
+
+ @Override
+ public RedisDataType getDataType() {
+ return this.dataType;
+ }
+ },
+
+ GEODIST {
+ private Executor executor;
+
+ @Override
+ public Executor getExecutor() {
+ if (executor == null) {
+ executor = new GeoDistExecutor();
+ }
+ return executor;
+ }
+
+ private final RedisDataType dataType = RedisDataType.REDIS_SORTEDSET;
+
+ @Override
+ public RedisDataType getDataType() {
+ return this.dataType;
+ }
+ },
+
+ GEORADIUS {
+ private Executor executor;
+
+ @Override
+ public Executor getExecutor() {
+ if (executor == null) {
+ executor = new GeoRadiusExecutor();
+ }
+ return executor;
+ }
+
+ private final RedisDataType dataType = RedisDataType.REDIS_SORTEDSET;
+
+ @Override
+ public RedisDataType getDataType() {
+ return this.dataType;
+ }
+ },
+
+ GEORADIUSBYMEMBER {
+ private Executor executor;
+
+ @Override
+ public Executor getExecutor() {
+ if (executor == null) {
+ executor = new GeoRadiusByMemberExecutor();
+ }
+ return executor;
+ }
+
+ private final RedisDataType dataType = RedisDataType.REDIS_SORTEDSET;
+
+ @Override
+ public RedisDataType getDataType() {
+ return this.dataType;
+ }
+ },
+
/***************************************
************ Transactions *************
***************************************/
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/RedisConstants.java b/geode-core/src/main/java/org/apache/geode/redis/internal/RedisConstants.java
index ad42dbb..ddfa639 100644
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/RedisConstants.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/RedisConstants.java
@@ -31,7 +31,8 @@ public class RedisConstants {
*/
static final String PARSING_EXCEPTION_MESSAGE =
"The command received by GeodeRedisServer was improperly formatted";
- static final String SERVER_ERROR_MESSAGE = "The server had an internal error please try again";
+ public static final String SERVER_ERROR_MESSAGE =
+ "The server had an internal error please try again";
static final String SERVER_ERROR_UNKNOWN_RESPONSE = "Unkown response";
static final String SERVER_ERROR_SHUTDOWN = "The server is shutting down";
static final String ERROR_UNSUPPORTED_OPERATION_IN_TRANSACTION =
@@ -39,7 +40,10 @@ public class RedisConstants {
static final String ERROR_TRANSACTION_EXCEPTION =
"This transcation cannot be initiated, make sure the command is executed against a replicate region or your data is collocated. If you are using persistent regions, make sure transactions are enabled";
public static final String ERROR_NOT_NUMERIC = "Illegal non numeric argument";
- public static final String ERROR_UNKOWN_COMMAND = "Unable to process uknown command";
+ public static final String ERROR_INVALID_ARGUMENT_UNIT_NUM =
+ "Either illegal non numeric argument or invalid unit" +
+ "(please use either km/m/ft/mi)";
+ public static final String ERROR_UNKOWN_COMMAND = "Unable to process unknown command";
public static final String ERROR_COMMIT_CONFLICT =
"There has been a conflict with another transaction";
public static final String ERROR_REGION_CREATION =
@@ -50,6 +54,7 @@ public class RedisConstants {
"Keys cannot be watched or unwatched because GemFire watches all keys by default for transactions";
public static final String ERROR_ILLEGAL_GLOB = "Incorrect syntax for given glob regex";
public static final String ERROR_OUT_OF_RANGE = "The number provided is out of range";
+ public static final String ERROR_INVALID_LATLONG = "Invalid longitude-latitude pair";
public static final String ERROR_NESTED_MULTI = "The MULTI command cannot be nested";
public static final String ERROR_NAN_INF_INCR = "increment would produce NaN or Infinity";
public static final String ERROR_NO_PASS =
@@ -57,6 +62,7 @@ public class RedisConstants {
public static final String ERROR_INVALID_PWD =
"Attemping to authenticate with an invalid password";
public static final String ERROR_NOT_AUTH = "Must authenticate before sending any requests";
+ public static final String ERROR_ZSET_MEMBER_NOT_FOUND = "could not decode requested zset member";
public static class ArityDef {
@@ -246,6 +252,22 @@ public class RedisConstants {
"The wrong number of arguments or syntax was provided, the format for the ZSCORE command is \"ZSCORE key member\"";
/*
+ * Geospatial
+ */
+ public static final String GEOADD =
+ "The wrong number of arguments or syntax was provided, the format for the GEOADD command is \"GEOADD key longitude latitude member [longitude latitude member ...]\", or not every latitude/longitude pair matches to a member";
+ public static final String GEOHASH =
+ "The wrong number of arguments or syntax was provided, the format for the GEOHASH command is \"GEOHASH key member [member...]\"";
+ public static final String GEOPOS =
+ "The wrong number of arguments or syntax was provided, the format for the GEOPOS command is \"GEOPOS key member [member...]\"";
+ public static final String GEODIST =
+ "The wrong number of arguments or syntax was provided, the format for the GEODIST command is \"GEODIST key member member [unit]\"";
+ public static final String GEORADIUS =
+ "The wrong number of arguments or syntax was provided, the format for the GEORADIUS command is \"GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC]\"";
+ public static final String GEORADIUSBYMEMBER =
+ "The wrong number of arguments or syntax was provided, the format for the GEORADIUSBYMEMBER command is \"GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC]\"";
+
+ /*
* String
*/
public static final String APPEND =
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/AbstractExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/AbstractExecutor.java
index f95e1a4..dfe0668 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/AbstractExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/AbstractExecutor.java
@@ -14,12 +14,20 @@
*/
package org.apache.geode.redis.internal.executor;
+import java.util.Collection;
+
+import io.netty.buffer.ByteBuf;
+
import org.apache.geode.cache.Region;
import org.apache.geode.cache.query.Query;
import org.apache.geode.redis.GeodeRedisServer;
import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.CoderException;
+import org.apache.geode.redis.internal.Command;
import org.apache.geode.redis.internal.ExecutionHandlerContext;
import org.apache.geode.redis.internal.Executor;
+import org.apache.geode.redis.internal.RedisConstants;
import org.apache.geode.redis.internal.RedisDataType;
import org.apache.geode.redis.internal.RedisDataTypeMismatchException;
import org.apache.geode.redis.internal.RegionProvider;
@@ -135,4 +143,23 @@ public abstract class AbstractExecutor implements Executor {
else
return Math.max(index + size, -1);
}
+
+ protected void respondBulkStrings(Command command, ExecutionHandlerContext context,
+ Object message) {
+ ByteBuf rsp;
+ try {
+ if (message instanceof Collection) {
+ rsp = Coder.getBulkStringArrayResponse(context.getByteBufAllocator(),
+ (Collection<?>) message);
+ } else {
+ rsp = Coder.getBulkStringResponse(context.getByteBufAllocator(), message);
+ }
+ } catch (CoderException e) {
+ command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(),
+ RedisConstants.SERVER_ERROR_MESSAGE));
+ return;
+ }
+
+ command.setResponse(rsp);
+ }
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/EchoExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/EchoExecutor.java
index 407e653..3ad9885 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/EchoExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/EchoExecutor.java
@@ -32,7 +32,7 @@ public class EchoExecutor extends AbstractExecutor {
}
byte[] echoMessage = commandElems.get(1);
- command.setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), echoMessage));
+ respondBulkStrings(command, context, echoMessage);
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/KeysExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/KeysExecutor.java
index cb11762..c4466fc 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/KeysExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/KeysExecutor.java
@@ -61,9 +61,6 @@ public class KeysExecutor extends AbstractExecutor {
if (matchingKeys.isEmpty())
command.setResponse(Coder.getEmptyArrayResponse(context.getByteBufAllocator()));
else
- command.setResponse(
- Coder.getBulkStringArrayResponse(context.getByteBufAllocator(), matchingKeys));
-
-
+ respondBulkStrings(command, context, matchingKeys);
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/SortedSetQuery.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/SortedSetQuery.java
index a0a8f63..b71a37f 100644
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/SortedSetQuery.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/SortedSetQuery.java
@@ -266,6 +266,12 @@ public enum SortedSetQuery {
+ ".entrySet entry ORDER BY entry.value desc, entry.key desc LIMIT $1";
}
},
+ GEORADIUS {
+ public String getQueryString(String fullpath) {
+ return "SELECT DISTINCT entry.key, entry.value FROM " + fullpath
+ + ".entries entry WHERE entry.value.toString LIKE $1 ORDER BY entry.value asc";
+ }
+ },
ZRANK {
public String getQueryString(String fullpath) {
return "SELECT COUNT(*) FROM " + fullpath
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/TypeExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/TypeExecutor.java
index d97bc05..c42fd62 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/TypeExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/TypeExecutor.java
@@ -36,12 +36,10 @@ public class TypeExecutor extends AbstractExecutor {
ByteArrayWrapper key = command.getKey();
RedisDataType type = context.getRegionProvider().getRedisDataType(key);
-
if (type == null)
- command.setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), "none"));
+ respondBulkStrings(command, context, "none");
else
- command
- .setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), type.toString()));
+ respondBulkStrings(command, context, type.toString());
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HGetExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HGetExecutor.java
index 8574263..b3a9aa2 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HGetExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HGetExecutor.java
@@ -47,15 +47,7 @@ public class HGetExecutor extends HashExecutor {
byte[] byteField = commandElems.get(FIELD_INDEX);
ByteArrayWrapper field = new ByteArrayWrapper(byteField);
-
- ByteArrayWrapper valueWrapper = keyRegion.get(field);
-
- if (valueWrapper != null) {
- command.setResponse(
- Coder.getBulkStringResponse(context.getByteBufAllocator(), valueWrapper.toBytes()));
- } else
- command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
-
+ respondBulkStrings(command, context, keyRegion.get(field));
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HIncrByFloatExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HIncrByFloatExecutor.java
index 8b4af48..c837686 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HIncrByFloatExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HIncrByFloatExecutor.java
@@ -73,7 +73,7 @@ public class HIncrByFloatExecutor extends HashExecutor {
if (oldValue == null) {
keyRegion.put(field, new ByteArrayWrapper(incrArray));
- command.setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), increment));
+ respondBulkStrings(command, context, increment);
return;
}
@@ -98,7 +98,7 @@ public class HIncrByFloatExecutor extends HashExecutor {
value += increment;
keyRegion.put(field, new ByteArrayWrapper(Coder.doubleToBytes(value)));
- command.setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), value));
+ respondBulkStrings(command, context, value);
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HKeysExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HKeysExecutor.java
index 8d0a6b7..808668b 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HKeysExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HKeysExecutor.java
@@ -54,8 +54,6 @@ public class HKeysExecutor extends HashExecutor {
return;
}
- // String response = getBulkStringArrayResponse(keys);
-
- command.setResponse(Coder.getBulkStringArrayResponse(context.getByteBufAllocator(), keys));
+ respondBulkStrings(command, context, keys);
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HMGetExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HMGetExecutor.java
index 495e86b..696edad 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HMGetExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HMGetExecutor.java
@@ -65,7 +65,6 @@ public class HMGetExecutor extends HashExecutor {
for (ByteArrayWrapper field : fields)
values.add(results.get(field));
- command.setResponse(Coder.getBulkStringArrayResponse(context.getByteBufAllocator(), values));
-
+ respondBulkStrings(command, context, values);
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HValsExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HValsExecutor.java
index 1ca8071..c9fb533 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HValsExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HValsExecutor.java
@@ -48,13 +48,12 @@ public class HValsExecutor extends HashExecutor {
}
Collection<ByteArrayWrapper> vals = new ArrayList(keyRegion.values());
-
if (vals.isEmpty()) {
command.setResponse(Coder.getEmptyArrayResponse(context.getByteBufAllocator()));
return;
}
- command.setResponse(Coder.getBulkStringArrayResponse(context.getByteBufAllocator(), vals));
+ respondBulkStrings(command, context, vals);
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/list/LIndexExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/list/LIndexExecutor.java
index c80a505..dd10e29 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/list/LIndexExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/list/LIndexExecutor.java
@@ -97,8 +97,7 @@ public class LIndexExecutor extends ListExecutor {
Object[] entryArray = entry.getFieldValues();
ByteArrayWrapper valueWrapper = (ByteArrayWrapper) entryArray[1];
- command.setResponse(
- Coder.getBulkStringResponse(context.getByteBufAllocator(), valueWrapper.toBytes()));
+ respondBulkStrings(command, context, valueWrapper);
}
private Struct getEntryAtIndex(ExecutionHandlerContext context, ByteArrayWrapper key, int index)
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/list/PopExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/list/PopExecutor.java
index 3ca7224..05f8e28 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/list/PopExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/list/PopExecutor.java
@@ -121,11 +121,7 @@ public abstract class PopExecutor extends ListExecutor implements Extendable {
index = metaIndex;
i++;
} while (!removed && keyRegion.size() != LIST_EMPTY_SIZE);
- if (valueWrapper != null)
- command.setResponse(
- Coder.getBulkStringResponse(context.getByteBufAllocator(), valueWrapper.toBytes()));
- else
- command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
+ respondBulkStrings(command, context, valueWrapper);
}
protected abstract ListDirection popType();
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/set/SMembersExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/set/SMembersExecutor.java
index dc94897..cc23e0b 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/set/SMembersExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/set/SMembersExecutor.java
@@ -49,7 +49,6 @@ public class SMembersExecutor extends SetExecutor {
}
Set<ByteArrayWrapper> members = new HashSet(keyRegion.keySet()); // Emulate copy on read
-
- command.setResponse(Coder.getBulkStringArrayResponse(context.getByteBufAllocator(), members));
+ respondBulkStrings(command, context, members);
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/set/SPopExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/set/SPopExecutor.java
index 0d2c2a9..5bd0acb 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/set/SPopExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/set/SPopExecutor.java
@@ -54,7 +54,8 @@ public class SPopExecutor extends SetExecutor {
if (keyRegion.isEmpty()) {
context.getRegionProvider().removeKey(key);
}
- command.setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), pop.toBytes()));
+
+ respondBulkStrings(command, context, pop);
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/set/SRandMemberExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/set/SRandMemberExecutor.java
index fe45b61..16bbaf6 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/set/SRandMemberExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/set/SRandMemberExecutor.java
@@ -66,8 +66,7 @@ public class SRandMemberExecutor extends SetExecutor {
int members = keyRegion.size();
if (members <= count && count != 1) {
- command.setResponse(Coder.getBulkStringArrayResponse(context.getByteBufAllocator(),
- new HashSet<ByteArrayWrapper>(keyRegion.keySet())));
+ respondBulkStrings(command, context, new HashSet<ByteArrayWrapper>(keyRegion.keySet()));
return;
}
@@ -77,16 +76,14 @@ public class SRandMemberExecutor extends SetExecutor {
if (count == 1) {
ByteArrayWrapper randEntry = entries[rand.nextInt(entries.length)];
- command.setResponse(
- Coder.getBulkStringResponse(context.getByteBufAllocator(), randEntry.toBytes()));
+ respondBulkStrings(command, context, randEntry);
} else if (count > 0) {
Set<ByteArrayWrapper> randEntries = new HashSet<ByteArrayWrapper>();
do {
ByteArrayWrapper s = entries[rand.nextInt(entries.length)];
randEntries.add(s);
} while (randEntries.size() < count);
- command.setResponse(
- Coder.getBulkStringArrayResponse(context.getByteBufAllocator(), randEntries));
+ respondBulkStrings(command, context, randEntries);
} else {
count = -count;
List<ByteArrayWrapper> randEntries = new ArrayList<ByteArrayWrapper>();
@@ -94,8 +91,7 @@ public class SRandMemberExecutor extends SetExecutor {
ByteArrayWrapper s = entries[rand.nextInt(entries.length)];
randEntries.add(s);
}
- command.setResponse(
- Coder.getBulkStringArrayResponse(context.getByteBufAllocator(), randEntries));
+ respondBulkStrings(command, context, randEntries);
}
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/set/SetOpExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/set/SetOpExecutor.java
index 7ca72a5..fe0e7e8 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/set/SetOpExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/set/SetOpExecutor.java
@@ -70,11 +70,7 @@ public abstract class SetOpExecutor extends SetExecutor implements Extendable {
command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), 0));
context.getRegionProvider().removeKey(destination);
} else {
- if (firstSet == null)
- command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
- else
- command.setResponse(
- Coder.getBulkStringArrayResponse(context.getByteBufAllocator(), firstSet));
+ respondBulkStrings(command, context, firstSet);
}
return;
}
@@ -102,8 +98,7 @@ public abstract class SetOpExecutor extends SetExecutor implements Extendable {
if (resultSet == null || resultSet.isEmpty())
command.setResponse(Coder.getEmptyArrayResponse(context.getByteBufAllocator()));
else
- command.setResponse(
- Coder.getBulkStringArrayResponse(context.getByteBufAllocator(), resultSet));
+ respondBulkStrings(command, context, resultSet);
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoAddExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoAddExecutor.java
new file mode 100644
index 0000000..67f4920
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoAddExecutor.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.redis.internal.executor.sortedset;
+
+import static org.apache.geode.redis.internal.RedisConstants.ERROR_INVALID_LATLONG;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.GeoCoder;
+import org.apache.geode.redis.internal.RedisConstants;
+import org.apache.geode.redis.internal.RedisDataType;
+
+public class GeoAddExecutor extends GeoSortedSetExecutor {
+
+ @Override
+ public void executeCommand(Command command, ExecutionHandlerContext context) {
+ int numberOfAdds = 0;
+ List<byte[]> commandElems = command.getProcessedCommand();
+ ByteArrayWrapper key = command.getKey();
+
+ if (commandElems.size() < 5 || ((commandElems.size() - 2) % 3) != 0) {
+ command.setResponse(
+ Coder.getErrorResponse(context.getByteBufAllocator(), RedisConstants.ArityDef.GEOADD));
+ return;
+ }
+
+ Region<ByteArrayWrapper, ByteArrayWrapper> keyRegion =
+ getOrCreateRegion(context, key, RedisDataType.REDIS_SORTEDSET);
+
+ Map<ByteArrayWrapper, ByteArrayWrapper> tempMap = new HashMap<>();
+ for (int i = 2; i < commandElems.size(); i += 3) {
+ byte[] longitude = commandElems.get(i);
+ byte[] latitude = commandElems.get(i + 1);
+ byte[] member = commandElems.get(i + 2);
+
+ String score;
+ try {
+ score = GeoCoder.geohash(longitude, latitude);
+ } catch (IllegalArgumentException e) {
+ command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(),
+ "ERR " + ERROR_INVALID_LATLONG +
+ " " + longitude.toString() + " " + latitude.toString()));
+ return;
+ }
+
+ tempMap.put(new ByteArrayWrapper(member), new ByteArrayWrapper(score.getBytes()));
+ }
+
+ for (ByteArrayWrapper m : tempMap.keySet()) {
+ Object oldVal = keyRegion.put(m, tempMap.get(m));
+ if (oldVal == null)
+ numberOfAdds++;
+ }
+
+ command.setResponse(Coder.getIntegerResponse(context.getByteBufAllocator(), numberOfAdds));
+ }
+}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HGetExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoDistExecutor.java
old mode 100755
new mode 100644
similarity index 63%
copy from geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HGetExecutor.java
copy to geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoDistExecutor.java
index 8574263..4879e9d
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HGetExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoDistExecutor.java
@@ -12,7 +12,8 @@
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
-package org.apache.geode.redis.internal.executor.hash;
+
+package org.apache.geode.redis.internal.executor.sortedset;
import java.util.List;
@@ -21,41 +22,37 @@ import org.apache.geode.redis.internal.ByteArrayWrapper;
import org.apache.geode.redis.internal.Coder;
import org.apache.geode.redis.internal.Command;
import org.apache.geode.redis.internal.ExecutionHandlerContext;
-import org.apache.geode.redis.internal.RedisConstants.ArityDef;
-import org.apache.geode.redis.internal.RedisDataType;
+import org.apache.geode.redis.internal.GeoCoder;
+import org.apache.geode.redis.internal.RedisConstants;
-public class HGetExecutor extends HashExecutor {
+public class GeoDistExecutor extends GeoSortedSetExecutor {
@Override
public void executeCommand(Command command, ExecutionHandlerContext context) {
List<byte[]> commandElems = command.getProcessedCommand();
+ ByteArrayWrapper key = command.getKey();
- if (commandElems.size() < 3) {
- command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.HGET));
+ if (commandElems.size() < 4 || commandElems.size() > 5) {
+ command.setResponse(
+ Coder.getErrorResponse(context.getByteBufAllocator(), RedisConstants.ArityDef.GEODIST));
return;
}
- ByteArrayWrapper key = command.getKey();
-
- checkDataType(key, RedisDataType.REDIS_HASH, context);
Region<ByteArrayWrapper, ByteArrayWrapper> keyRegion = getRegion(context, key);
-
- if (keyRegion == null) {
+ ByteArrayWrapper hw1 = keyRegion.get(new ByteArrayWrapper(commandElems.get(2)));
+ ByteArrayWrapper hw2 = keyRegion.get(new ByteArrayWrapper(commandElems.get(3)));
+ if (hw1 == null || hw2 == null) {
command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
return;
}
- byte[] byteField = commandElems.get(FIELD_INDEX);
- ByteArrayWrapper field = new ByteArrayWrapper(byteField);
+ Double dist = GeoCoder.geoDist(hw1.toString(), hw2.toString());
- ByteArrayWrapper valueWrapper = keyRegion.get(field);
-
- if (valueWrapper != null) {
- command.setResponse(
- Coder.getBulkStringResponse(context.getByteBufAllocator(), valueWrapper.toBytes()));
- } else
- command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
+ if (commandElems.size() == 5) {
+ String unit = new String(commandElems.get(4));
+ dist = dist * GeoCoder.parseUnitScale(unit);
+ }
+ respondBulkStrings(command, context, Double.toString(dist));
}
-
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HGetExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoHashExecutor.java
old mode 100755
new mode 100644
similarity index 61%
copy from geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HGetExecutor.java
copy to geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoHashExecutor.java
index 8574263..98a9eef
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HGetExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoHashExecutor.java
@@ -12,8 +12,10 @@
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
-package org.apache.geode.redis.internal.executor.hash;
+package org.apache.geode.redis.internal.executor.sortedset;
+
+import java.util.ArrayList;
import java.util.List;
import org.apache.geode.cache.Region;
@@ -21,41 +23,35 @@ import org.apache.geode.redis.internal.ByteArrayWrapper;
import org.apache.geode.redis.internal.Coder;
import org.apache.geode.redis.internal.Command;
import org.apache.geode.redis.internal.ExecutionHandlerContext;
-import org.apache.geode.redis.internal.RedisConstants.ArityDef;
-import org.apache.geode.redis.internal.RedisDataType;
+import org.apache.geode.redis.internal.RedisConstants;
-public class HGetExecutor extends HashExecutor {
+public class GeoHashExecutor extends GeoSortedSetExecutor {
@Override
public void executeCommand(Command command, ExecutionHandlerContext context) {
List<byte[]> commandElems = command.getProcessedCommand();
+ ByteArrayWrapper key = command.getKey();
if (commandElems.size() < 3) {
- command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.HGET));
+ command.setResponse(
+ Coder.getErrorResponse(context.getByteBufAllocator(), RedisConstants.ArityDef.GEOHASH));
return;
}
- ByteArrayWrapper key = command.getKey();
-
- checkDataType(key, RedisDataType.REDIS_HASH, context);
+ List<String> hashes = new ArrayList<>();
Region<ByteArrayWrapper, ByteArrayWrapper> keyRegion = getRegion(context, key);
- if (keyRegion == null) {
- command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
- return;
- }
-
- byte[] byteField = commandElems.get(FIELD_INDEX);
- ByteArrayWrapper field = new ByteArrayWrapper(byteField);
-
- ByteArrayWrapper valueWrapper = keyRegion.get(field);
+ for (int i = 2; i < commandElems.size(); i++) {
+ byte[] member = commandElems.get(i);
- if (valueWrapper != null) {
- command.setResponse(
- Coder.getBulkStringResponse(context.getByteBufAllocator(), valueWrapper.toBytes()));
- } else
- command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
+ ByteArrayWrapper hashWrapper = keyRegion.get(new ByteArrayWrapper(member));
+ if (hashWrapper != null) {
+ hashes.add(hashWrapper.toString());
+ } else {
+ hashes.add(null);
+ }
+ }
+ respondBulkStrings(command, context, hashes);
}
-
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HGetExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoPosExecutor.java
old mode 100755
new mode 100644
similarity index 61%
copy from geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HGetExecutor.java
copy to geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoPosExecutor.java
index 8574263..20b17eb
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/hash/HGetExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoPosExecutor.java
@@ -12,50 +12,49 @@
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
-package org.apache.geode.redis.internal.executor.hash;
+package org.apache.geode.redis.internal.executor.sortedset;
+
+import java.util.ArrayList;
import java.util.List;
+import com.github.davidmoten.geo.LatLong;
+
import org.apache.geode.cache.Region;
import org.apache.geode.redis.internal.ByteArrayWrapper;
import org.apache.geode.redis.internal.Coder;
import org.apache.geode.redis.internal.Command;
import org.apache.geode.redis.internal.ExecutionHandlerContext;
-import org.apache.geode.redis.internal.RedisConstants.ArityDef;
-import org.apache.geode.redis.internal.RedisDataType;
+import org.apache.geode.redis.internal.GeoCoder;
+import org.apache.geode.redis.internal.RedisConstants;
-public class HGetExecutor extends HashExecutor {
+public class GeoPosExecutor extends GeoSortedSetExecutor {
@Override
public void executeCommand(Command command, ExecutionHandlerContext context) {
List<byte[]> commandElems = command.getProcessedCommand();
+ ByteArrayWrapper key = command.getKey();
if (commandElems.size() < 3) {
- command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), ArityDef.HGET));
+ command.setResponse(
+ Coder.getErrorResponse(context.getByteBufAllocator(), RedisConstants.ArityDef.GEOPOS));
return;
}
- ByteArrayWrapper key = command.getKey();
-
- checkDataType(key, RedisDataType.REDIS_HASH, context);
+ List<LatLong> positions = new ArrayList<>();
Region<ByteArrayWrapper, ByteArrayWrapper> keyRegion = getRegion(context, key);
- if (keyRegion == null) {
- command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
- return;
- }
-
- byte[] byteField = commandElems.get(FIELD_INDEX);
- ByteArrayWrapper field = new ByteArrayWrapper(byteField);
-
- ByteArrayWrapper valueWrapper = keyRegion.get(field);
+ for (int i = 2; i < commandElems.size(); i++) {
+ byte[] member = commandElems.get(i);
- if (valueWrapper != null) {
- command.setResponse(
- Coder.getBulkStringResponse(context.getByteBufAllocator(), valueWrapper.toBytes()));
- } else
- command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
+ ByteArrayWrapper hashWrapper = keyRegion.get(new ByteArrayWrapper(member));
+ if (hashWrapper != null) {
+ positions.add(GeoCoder.geoPos(hashWrapper.toString()));
+ } else {
+ positions.add(null);
+ }
+ }
+ respondGeoCoordinates(command, context, positions);
}
-
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoRadiusByMemberExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoRadiusByMemberExecutor.java
new file mode 100644
index 0000000..6579236
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoRadiusByMemberExecutor.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.redis.internal.executor.sortedset;
+
+import static org.apache.geode.redis.internal.RedisConstants.ERROR_INVALID_ARGUMENT_UNIT_NUM;
+import static org.apache.geode.redis.internal.RedisConstants.ERROR_ZSET_MEMBER_NOT_FOUND;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+import com.github.davidmoten.geo.LatLong;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.query.internal.StructImpl;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.GeoCoder;
+import org.apache.geode.redis.internal.GeoRadiusResponseElement;
+import org.apache.geode.redis.internal.MemberNotFoundException;
+import org.apache.geode.redis.internal.RedisCommandParserException;
+import org.apache.geode.redis.internal.RedisConstants;
+import org.apache.geode.redis.internal.RedisDataType;
+
+public class GeoRadiusByMemberExecutor extends GeoSortedSetExecutor {
+
+ @Override
+ public void executeCommand(Command command, ExecutionHandlerContext context) {
+ List<byte[]> commandElems = command.getProcessedCommand();
+
+ if (commandElems.size() < 5) {
+ command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(),
+ RedisConstants.ArityDef.GEORADIUSBYMEMBER));
+ return;
+ }
+
+ ByteArrayWrapper key = command.getKey();
+ checkDataType(key, RedisDataType.REDIS_SORTEDSET, context);
+ Region<ByteArrayWrapper, ByteArrayWrapper> keyRegion = getRegion(context, key);
+
+ if (keyRegion == null) {
+ command.setResponse(Coder.getEmptyArrayResponse(context.getByteBufAllocator()));
+ return;
+ }
+
+ GeoRadiusParameters params;
+ try {
+ params = new GeoRadiusParameters(keyRegion, commandElems,
+ GeoRadiusParameters.CommandType.GEORADIUSBYMEMBER);
+ } catch (IllegalArgumentException e) {
+ command.setResponse(
+ Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_INVALID_ARGUMENT_UNIT_NUM));
+ return;
+ } catch (RedisCommandParserException e) {
+ command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(),
+ RedisConstants.ArityDef.GEORADIUSBYMEMBER));
+ return;
+ } catch (MemberNotFoundException e) {
+ command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), "ERR "
+ + ERROR_ZSET_MEMBER_NOT_FOUND));
+ return;
+ }
+
+ Set<String> hn = GeoCoder.geohashSearchAreas(params.lon, params.lat, params.radius);
+
+ List<GeoRadiusResponseElement> results = new ArrayList<>();
+ for (String neighbor : hn) {
+ try {
+ List<StructImpl> range = getGeoRadiusRange(context, key, neighbor);
+ for (StructImpl point : range) {
+ String name = point.get("key").toString();
+ String hash = point.get("value").toString();
+
+ Double dist = GeoCoder.geoDist(params.centerHashPrecise, hash) * params.distScale;
+
+ // Post-filter for accuracy
+ if (dist > (params.radius * params.distScale))
+ continue;
+
+ Optional<LatLong> coord =
+ params.withCoord ? Optional.of(GeoCoder.geoPos(hash)) : Optional.empty();
+ Optional<String> hashOpt =
+ params.withHash ? Optional.of(hash) : Optional.empty();
+
+ // Because of the way hashing works, sometimes you can get the same requested member back
+ // in the results
+ if (!name.equals(params.member))
+ results.add(new GeoRadiusResponseElement(name, coord, dist, params.withDist, hashOpt));
+ }
+ } catch (Exception e) {
+ command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), e.getMessage()));
+ return;
+ }
+ }
+
+ if (params.order == GeoRadiusParameters.SortOrder.ASC) {
+ GeoRadiusResponseElement.sortByDistanceAscending(results);
+ } else if (params.order == GeoRadiusParameters.SortOrder.DESC) {
+ GeoRadiusResponseElement.sortByDistanceDescending(results);
+ }
+
+ if (params.count != null && params.count < results.size()) {
+ results = results.subList(0, params.count);
+ }
+
+ respondGeoRadius(command, context, results);
+ }
+}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoRadiusExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoRadiusExecutor.java
new file mode 100644
index 0000000..6b160b6
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoRadiusExecutor.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.redis.internal.executor.sortedset;
+
+import static org.apache.geode.redis.internal.RedisConstants.ERROR_INVALID_ARGUMENT_UNIT_NUM;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+import com.github.davidmoten.geo.LatLong;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.query.internal.StructImpl;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.GeoCoder;
+import org.apache.geode.redis.internal.GeoRadiusResponseElement;
+import org.apache.geode.redis.internal.MemberNotFoundException;
+import org.apache.geode.redis.internal.RedisCommandParserException;
+import org.apache.geode.redis.internal.RedisConstants;
+import org.apache.geode.redis.internal.RedisDataType;
+
+public class GeoRadiusExecutor extends GeoSortedSetExecutor {
+
+ @Override
+ public void executeCommand(Command command, ExecutionHandlerContext context) {
+ List<byte[]> commandElems = command.getProcessedCommand();
+
+ if (commandElems.size() < 6) {
+ command.setResponse(
+ Coder.getErrorResponse(context.getByteBufAllocator(), RedisConstants.ArityDef.GEORADIUS));
+ return;
+ }
+
+ ByteArrayWrapper key = command.getKey();
+ checkDataType(key, RedisDataType.REDIS_SORTEDSET, context);
+ Region<ByteArrayWrapper, ByteArrayWrapper> keyRegion = getRegion(context, key);
+
+ if (keyRegion == null) {
+ command.setResponse(Coder.getEmptyArrayResponse(context.getByteBufAllocator()));
+ return;
+ }
+
+ GeoRadiusParameters params;
+ try {
+ params = new GeoRadiusParameters(keyRegion, commandElems,
+ GeoRadiusParameters.CommandType.GEORADIUS);
+ } catch (IllegalArgumentException e) {
+ command.setResponse(
+ Coder.getErrorResponse(context.getByteBufAllocator(), ERROR_INVALID_ARGUMENT_UNIT_NUM));
+ return;
+ } catch (RedisCommandParserException e) {
+ command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(),
+ RedisConstants.ArityDef.GEORADIUS));
+ return;
+ } catch (MemberNotFoundException e) {
+ /* Not possible for GEORADIUS */
+ return;
+ }
+
+ Set<String> hn = GeoCoder.geohashSearchAreas(params.lon, params.lat, params.radius);
+
+ List<GeoRadiusResponseElement> results = new ArrayList<>();
+ for (String neighbor : hn) {
+ try {
+ List<StructImpl> range = getGeoRadiusRange(context, key, neighbor);
+ for (StructImpl point : range) {
+ String name = point.get("key").toString();
+ String hash = point.get("value").toString();
+
+ Double dist = GeoCoder.geoDist(params.centerHashPrecise, hash) * params.distScale;
+
+ // Post-filter for accuracy
+ if (dist > (params.radius * params.distScale))
+ continue;
+
+ Optional<LatLong> coord =
+ params.withCoord ? Optional.of(GeoCoder.geoPos(hash)) : Optional.empty();
+ Optional<String> hashOpt =
+ params.withHash ? Optional.of(hash) : Optional.empty();
+
+ results.add(new GeoRadiusResponseElement(name, coord, dist, params.withDist, hashOpt));
+ }
+ } catch (Exception e) {
+ command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(), e.getMessage()));
+ return;
+ }
+ }
+
+ if (params.order == GeoRadiusParameters.SortOrder.ASC) {
+ GeoRadiusResponseElement.sortByDistanceAscending(results);
+ } else if (params.order == GeoRadiusParameters.SortOrder.DESC) {
+ GeoRadiusResponseElement.sortByDistanceDescending(results);
+ }
+
+ if (params.count != null && params.count < results.size()) {
+ results = results.subList(0, params.count);
+ }
+
+ respondGeoRadius(command, context, results);
+ }
+
+}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoRadiusParameters.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoRadiusParameters.java
new file mode 100644
index 0000000..6097b40
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoRadiusParameters.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.redis.internal.executor.sortedset;
+
+import java.util.List;
+
+import com.github.davidmoten.geo.LatLong;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.GeoCoder;
+import org.apache.geode.redis.internal.MemberNotFoundException;
+import org.apache.geode.redis.internal.RedisCommandParserException;
+
+public class GeoRadiusParameters {
+ final double lon;
+ final double lat;
+ final double radius;
+ final String unit;
+ final String member;
+
+ final boolean withDist;
+ final boolean withCoord;
+ final boolean withHash;
+ final Integer count;
+ final SortOrder order;
+
+ final Double distScale;
+ final String centerHashPrecise;
+
+ public enum CommandType {
+ GEORADIUS, GEORADIUSBYMEMBER
+ }
+
+ public enum SortOrder {
+ ASC, DESC, UNSORTED
+ }
+
+ public GeoRadiusParameters(Region<ByteArrayWrapper, ByteArrayWrapper> keyRegion,
+ List<byte[]> commandElems, CommandType cmdType) throws IllegalArgumentException,
+ RedisCommandParserException, MemberNotFoundException {
+ byte[] radArray;
+
+ switch (cmdType) {
+ case GEORADIUS:
+ byte[] lonArray = commandElems.get(2);
+ byte[] latArray = commandElems.get(3);
+ radArray = commandElems.get(4);
+ unit = new String(commandElems.get(5));
+ centerHashPrecise = GeoCoder.geohash(lonArray, latArray);
+ lon = Coder.bytesToDouble(lonArray);
+ lat = Coder.bytesToDouble(latArray);
+ member = null;
+ break;
+ default:
+ byte[] memberArray = commandElems.get(2);
+ radArray = commandElems.get(3);
+ unit = new String(commandElems.get(4));
+ member = new String(memberArray);
+ ByteArrayWrapper hashWrapper = keyRegion.get(new ByteArrayWrapper(memberArray));
+ if (hashWrapper == null) {
+ throw new MemberNotFoundException();
+ }
+
+ centerHashPrecise = hashWrapper.toString();
+ LatLong pos = GeoCoder.geoPos(centerHashPrecise);
+ lon = pos.getLon();
+ lat = pos.getLat();
+ break;
+ }
+
+ distScale = GeoCoder.parseUnitScale(unit);
+ radius = Coder.bytesToDouble(radArray) / distScale;
+
+ int i = (cmdType == CommandType.GEORADIUS) ? 6 : 5;
+
+ boolean showDist = false;
+ boolean showCoord = false;
+ boolean showHash = false;
+ for (; i < commandElems.size() && (new String(commandElems.get(i))).contains("with"); i++) {
+ String elem = new String(commandElems.get(i));
+
+ if (elem.equals("withdist"))
+ showDist = true;
+ if (elem.equals("withcoord"))
+ showCoord = true;
+ if (elem.equals("withhash"))
+ showHash = true;
+ }
+ withDist = showDist;
+ withCoord = showCoord;
+ withHash = showHash;
+
+ if (i < commandElems.size() && (new String(commandElems.get(i))).equals("count")) {
+ count = Coder.bytesToInt(commandElems.get(++i));
+ i++;
+ } else {
+ count = null;
+ }
+
+ if (i < commandElems.size() && (new String(commandElems.get(i))).contains("sc")) {
+ String elem = new String(commandElems.get(i++));
+
+ if (elem.equals("asc"))
+ order = SortOrder.ASC;
+ else if (elem.equals("desc"))
+ order = SortOrder.DESC;
+ else
+ order = SortOrder.UNSORTED;
+ } else {
+ order = SortOrder.UNSORTED;
+ }
+
+ if (i < commandElems.size()) {
+ throw new RedisCommandParserException();
+ }
+ }
+}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoSortedSetExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoSortedSetExecutor.java
new file mode 100755
index 0000000..70f0f40
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/GeoSortedSetExecutor.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.redis.internal.executor.sortedset;
+
+import java.util.List;
+
+import com.github.davidmoten.geo.LatLong;
+import io.netty.buffer.ByteBuf;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.query.Query;
+import org.apache.geode.cache.query.SelectResults;
+import org.apache.geode.cache.query.internal.StructImpl;
+import org.apache.geode.redis.internal.ByteArrayWrapper;
+import org.apache.geode.redis.internal.Coder;
+import org.apache.geode.redis.internal.CoderException;
+import org.apache.geode.redis.internal.Command;
+import org.apache.geode.redis.internal.ExecutionHandlerContext;
+import org.apache.geode.redis.internal.GeoCoder;
+import org.apache.geode.redis.internal.GeoRadiusResponseElement;
+import org.apache.geode.redis.internal.RedisConstants;
+import org.apache.geode.redis.internal.RedisDataType;
+import org.apache.geode.redis.internal.executor.AbstractExecutor;
+import org.apache.geode.redis.internal.executor.SortedSetQuery;
+
+public abstract class GeoSortedSetExecutor extends AbstractExecutor {
+
+ @Override
+ protected Region<ByteArrayWrapper, ByteArrayWrapper> getOrCreateRegion(
+ ExecutionHandlerContext context, ByteArrayWrapper key, RedisDataType type) {
+ @SuppressWarnings("unchecked")
+ Region<ByteArrayWrapper, ByteArrayWrapper> r =
+ (Region<ByteArrayWrapper, ByteArrayWrapper>) context
+ .getRegionProvider().getOrCreateRegion(key, type, context);
+ return r;
+ }
+
+ protected Region<ByteArrayWrapper, ByteArrayWrapper> getRegion(ExecutionHandlerContext context,
+ ByteArrayWrapper key) {
+ @SuppressWarnings("unchecked")
+ Region<ByteArrayWrapper, ByteArrayWrapper> r =
+ (Region<ByteArrayWrapper, ByteArrayWrapper>) context.getRegionProvider().getRegion(key);
+ return r;
+ }
+
+ protected List<StructImpl> getGeoRadiusRange(ExecutionHandlerContext context,
+ ByteArrayWrapper key, String hash) throws Exception {
+ Query query = getQuery(key, SortedSetQuery.GEORADIUS, context);
+ Object[] params = {hash + "%"};
+ SelectResults<StructImpl> results = (SelectResults<StructImpl>) query.execute(params);
+ return results.asList();
+ }
+
+ protected void respondGeoRadius(Command command, ExecutionHandlerContext context,
+ List<GeoRadiusResponseElement> results) {
+ ByteBuf rsp;
+ try {
+ rsp = GeoCoder.geoRadiusResponse(context.getByteBufAllocator(), results);
+ } catch (CoderException e) {
+ command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(),
+ RedisConstants.SERVER_ERROR_MESSAGE));
+ return;
+ }
+
+ command.setResponse(rsp);
+ }
+
+ protected void respondGeoCoordinates(Command command, ExecutionHandlerContext context,
+ List<LatLong> positions) {
+ ByteBuf rsp;
+ try {
+ rsp = GeoCoder.getBulkStringGeoCoordinateArrayResponse(context.getByteBufAllocator(),
+ positions);
+ } catch (CoderException e) {
+ command.setResponse(Coder.getErrorResponse(context.getByteBufAllocator(),
+ RedisConstants.SERVER_ERROR_MESSAGE));
+ return;
+ }
+
+ command.setResponse(rsp);
+ }
+}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZIncrByExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZIncrByExecutor.java
index aa95525..e63c5cb 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZIncrByExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZIncrByExecutor.java
@@ -60,7 +60,7 @@ public class ZIncrByExecutor extends SortedSetExecutor {
if (score == null) {
keyRegion.put(member, new DoubleWrapper(incr));
- command.setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), incr));
+ respondBulkStrings(command, context, incr);
return;
}
double result = score.score + incr;
@@ -70,7 +70,7 @@ public class ZIncrByExecutor extends SortedSetExecutor {
}
score.score = result;
keyRegion.put(member, score);
- command.setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), score.score));
+ respondBulkStrings(command, context, score.score);
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZScoreExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZScoreExecutor.java
index 158adb3..00db72c 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZScoreExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/sortedset/ZScoreExecutor.java
@@ -51,8 +51,7 @@ public class ZScoreExecutor extends SortedSetExecutor {
command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
return;
}
- command
- .setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), score.toString()));
+ respondBulkStrings(command, context, score.toString());
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/GetExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/GetExecutor.java
index 31aac75..6a4e1e5 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/GetExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/GetExecutor.java
@@ -38,14 +38,7 @@ public class GetExecutor extends StringExecutor {
checkDataType(key, RedisDataType.REDIS_STRING, context);
ByteArrayWrapper wrapper = r.get(key);
- if (wrapper == null) {
- command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
- return;
- } else {
- command.setResponse(
- Coder.getBulkStringResponse(context.getByteBufAllocator(), wrapper.toBytes()));
- }
-
+ respondBulkStrings(command, context, wrapper);
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/GetRangeExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/GetRangeExecutor.java
index 56c246f..804488d 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/GetRangeExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/GetRangeExecutor.java
@@ -90,7 +90,6 @@ public class GetRangeExecutor extends StringExecutor {
return;
}
- command.setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), returnRange));
-
+ respondBulkStrings(command, context, returnRange);
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/GetSetExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/GetSetExecutor.java
index adfcbea..9d93b3e 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/GetSetExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/GetSetExecutor.java
@@ -47,12 +47,6 @@ public class GetSetExecutor extends StringExecutor {
ByteArrayWrapper oldValueWrapper = r.get(key);
r.put(key, newValueWrapper);
- if (oldValueWrapper == null) {
- command.setResponse(Coder.getNilResponse(context.getByteBufAllocator()));
- } else {
- command.setResponse(
- Coder.getBulkStringResponse(context.getByteBufAllocator(), oldValueWrapper.toBytes()));
- }
-
+ respondBulkStrings(command, context, oldValueWrapper);
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/IncrByFloatExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/IncrByFloatExecutor.java
index 0c20f66..221a080 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/IncrByFloatExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/IncrByFloatExecutor.java
@@ -84,7 +84,7 @@ public class IncrByFloatExecutor extends StringExecutor {
if (valueWrapper == null) {
r.put(key, new ByteArrayWrapper(incrArray));
- command.setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), increment));
+ respondBulkStrings(command, context, increment);
return;
}
@@ -122,7 +122,7 @@ public class IncrByFloatExecutor extends StringExecutor {
stringValue = "" + value;
r.put(key, new ByteArrayWrapper(Coder.stringToBytes(stringValue)));
- command.setResponse(Coder.getBulkStringResponse(context.getByteBufAllocator(), value));
+ respondBulkStrings(command, context, value);
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/MGetExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/MGetExecutor.java
index 7c9e808..5d65f6c 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/MGetExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/MGetExecutor.java
@@ -60,8 +60,7 @@ public class MGetExecutor extends StringExecutor {
for (ByteArrayWrapper key : keys)
values.add(results.get(key));
- command.setResponse(Coder.getBulkStringArrayResponse(context.getByteBufAllocator(), values));
-
+ respondBulkStrings(command, context, values);
}
}
diff --git a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/MSetExecutor.java b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/MSetExecutor.java
index 5952625..3827247 100755
--- a/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/MSetExecutor.java
+++ b/geode-core/src/main/java/org/apache/geode/redis/internal/executor/string/MSetExecutor.java
@@ -56,7 +56,6 @@ public class MSetExecutor extends StringExecutor {
r.putAll(map);
command.setResponse(Coder.getSimpleStringResponse(context.getByteBufAllocator(), SUCCESS));
-
}
}
diff --git a/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt b/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt
index 6987220..ebf4602 100644
--- a/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt
+++ b/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt
@@ -632,6 +632,8 @@ org/apache/geode/pdx/internal/EnumInfo$PdxInstanceEnumInfo,true,7907582104525106
org/apache/geode/pdx/internal/FieldNotFoundInPdxVersion,true,1292033563588485577
org/apache/geode/pdx/internal/PdxInputStream,false
org/apache/geode/pdx/internal/PdxReaderImpl,true,-6094553093860427759,blobType:org/apache/geode/pdx/internal/PdxType,dis:org/apache/geode/pdx/internal/PdxInputStream
+org/apache/geode/redis/internal/CoderException,true,4707944288714910949
+org/apache/geode/redis/internal/MemberNotFoundException,true,4707944288714910949
org/apache/geode/redis/internal/RedisCommandParserException,true,4707944288714910949
org/apache/geode/redis/internal/RedisCommandType,false
org/apache/geode/redis/internal/RedisCommandType$1,false,dataType:org/apache/geode/redis/internal/RedisDataType,executor:org/apache/geode/redis/internal/Executor
@@ -651,6 +653,12 @@ org/apache/geode/redis/internal/RedisCommandType$110,false,dataType:org/apache/g
org/apache/geode/redis/internal/RedisCommandType$111,false,dataType:org/apache/geode/redis/internal/RedisDataType,executor:org/apache/geode/redis/internal/Executor
org/apache/geode/redis/internal/RedisCommandType$112,false,dataType:org/apache/geode/redis/internal/RedisDataType,executor:org/apache/geode/redis/internal/Executor
org/apache/geode/redis/internal/RedisCommandType$113,false,dataType:org/apache/geode/redis/internal/RedisDataType,executor:org/apache/geode/redis/internal/Executor
+org/apache/geode/redis/internal/RedisCommandType$114,false,dataType:org/apache/geode/redis/internal/RedisDataType,executor:org/apache/geode/redis/internal/Executor
+org/apache/geode/redis/internal/RedisCommandType$115,false,dataType:org/apache/geode/redis/internal/RedisDataType,executor:org/apache/geode/redis/internal/Executor
+org/apache/geode/redis/internal/RedisCommandType$116,false,dataType:org/apache/geode/redis/internal/RedisDataType,executor:org/apache/geode/redis/internal/Executor
+org/apache/geode/redis/internal/RedisCommandType$117,false,dataType:org/apache/geode/redis/internal/RedisDataType,executor:org/apache/geode/redis/internal/Executor
+org/apache/geode/redis/internal/RedisCommandType$118,false,dataType:org/apache/geode/redis/internal/RedisDataType,executor:org/apache/geode/redis/internal/Executor
+org/apache/geode/redis/internal/RedisCommandType$119,false,dataType:org/apache/geode/redis/internal/RedisDataType,executor:org/apache/geode/redis/internal/Executor
org/apache/geode/redis/internal/RedisCommandType$12,false,dataType:org/apache/geode/redis/internal/RedisDataType,executor:org/apache/geode/redis/internal/Executor
org/apache/geode/redis/internal/RedisCommandType$13,false,dataType:org/apache/geode/redis/internal/RedisDataType,executor:org/apache/geode/redis/internal/Executor
org/apache/geode/redis/internal/RedisCommandType$14,false,dataType:org/apache/geode/redis/internal/RedisDataType,executor:org/apache/geode/redis/internal/Executor
@@ -807,12 +815,15 @@ org/apache/geode/redis/internal/executor/SortedSetQuery$42,false
org/apache/geode/redis/internal/executor/SortedSetQuery$43,false
org/apache/geode/redis/internal/executor/SortedSetQuery$44,false
org/apache/geode/redis/internal/executor/SortedSetQuery$45,false
+org/apache/geode/redis/internal/executor/SortedSetQuery$46,false
org/apache/geode/redis/internal/executor/SortedSetQuery$5,false
org/apache/geode/redis/internal/executor/SortedSetQuery$6,false
org/apache/geode/redis/internal/executor/SortedSetQuery$7,false
org/apache/geode/redis/internal/executor/SortedSetQuery$8,false
org/apache/geode/redis/internal/executor/SortedSetQuery$9,false
org/apache/geode/redis/internal/executor/list/ListExecutor$ListDirection,false
+org/apache/geode/redis/internal/executor/sortedset/GeoRadiusParameters$CommandType,false
+org/apache/geode/redis/internal/executor/sortedset/GeoRadiusParameters$SortOrder,false
org/apache/geode/security/AuthenticationFailedException,true,-8202866472279088879
org/apache/geode/security/AuthenticationRequiredException,true,4675976651103154919
org/apache/geode/security/GemFireSecurityException,true,3814254578203076926,cause:java/lang/Throwable
diff --git a/geode-core/src/test/resources/expected-pom.xml b/geode-core/src/test/resources/expected-pom.xml
index 0ff3525..418271e 100644
--- a/geode-core/src/test/resources/expected-pom.xml
+++ b/geode-core/src/test/resources/expected-pom.xml
@@ -210,6 +210,12 @@
<scope>compile</scope>
</dependency>
<dependency>
+ <groupId>com.github.davidmoten</groupId>
+ <artifactId>geo</artifactId>
+ <version>0.7.1</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>9.4.12.v20180830</version>
diff --git a/gradle/dependency-versions.properties b/gradle/dependency-versions.properties
index a1203dc..4c6dab3 100644
--- a/gradle/dependency-versions.properties
+++ b/gradle/dependency-versions.properties
@@ -39,6 +39,7 @@ dom4j.version = 1.6.1
fastutil.version = 8.2.1
google-gson.version=2.8.5
guava.version = 25.1-jre
+geo.version = 0.7.1
hamcrest-all.version = 1.3
httpclient.version = 4.5.6
httpcore.version = 4.4.10