From 690b8d175d8b07fe502608af4081809e63933a45 Mon Sep 17 00:00:00 2001 From: shacharPash Date: Tue, 3 Oct 2023 17:39:46 +0300 Subject: [PATCH 01/15] Initial support for OSS Cluster API (#170) * delete mock * coverage * fix test * indent * change to var - check * cluster test * add opthin to connect cluster with dotnet test * use key in topk tests * get env vars inside RedisFixture * skip if redis * add skip where needed * Execute broadcast * delete cluster tests * RedisFixture fix * add to contributing * run cluster on CI * wip * fix / * -d * delete restore * return restore * add -RC3 * add RC3 to docker-compose * try define both .net 6 and 7 * Skip if cluster where needed * add names * skip configOnTimeout if cluster * try to fix win tests * tests names +fix win version * fix versions * versions * win verer * wording * change to OSSCluster * isOSSCluster * update skip reason to OSS cluster * general dispose * general dispose for the rest of the disposes --------- Co-authored-by: Chayim --- .github/cluster.env | 2 + .github/docker-compose.yml | 30 +++++ .github/dockers/Dockerfile.cluster | 7 ++ .github/dockers/cluster.redis.conf | 8 ++ .github/dockers/create_cluster.sh | 47 ++++++++ .github/standalone.env | 1 + .github/workflows/reusable.yml | 72 ++++++++++++ .../NRedisStack.Tests/SkipIfRedisAttribute.cs | 107 ++++++++++++++++++ 8 files changed, 274 insertions(+) create mode 100644 .github/cluster.env create mode 100644 .github/docker-compose.yml create mode 100644 .github/dockers/Dockerfile.cluster create mode 100644 .github/dockers/cluster.redis.conf create mode 100644 .github/dockers/create_cluster.sh create mode 100644 .github/standalone.env create mode 100644 .github/workflows/reusable.yml create mode 100644 tests/NRedisStack.Tests/SkipIfRedisAttribute.cs diff --git a/.github/cluster.env b/.github/cluster.env new file mode 100644 index 00000000..0d2a2906 --- /dev/null +++ b/.github/cluster.env @@ -0,0 +1,2 @@ +REDIS_CLUSTER=127.0.0.1:16379 +NUM_REDIS_CLUSTER_NODES=6 \ No newline at end of file diff --git a/.github/docker-compose.yml b/.github/docker-compose.yml new file mode 100644 index 00000000..d56217a5 --- /dev/null +++ b/.github/docker-compose.yml @@ -0,0 +1,30 @@ +--- +version: "3.8" +services: + + redis-stack-7.2.0-RC3: + image: redis/redis-stack-server:7.2.0-RC3 + ports: ["6379:6379"] + + redis-stack-6.2.6: + image: redis/redis-stack-server:6.2.6-v9 + ports: ["6379:6379"] + + redis-stack-edge: + image: redis/redis-stack-server:edge + ports: ["6379:6379"] + + redis-stack-cluster: + container_name: redis-cluster + build: + context: . + dockerfile: dockers/Dockerfile.cluster + ports: + - 16379:16379 + - 16380:16380 + - 16381:16381 + - 16382:16382 + - 16383:16383 + - 16384:16384 + volumes: + - "./dockers/cluster.redis.conf:/redis.conf:ro" \ No newline at end of file diff --git a/.github/dockers/Dockerfile.cluster b/.github/dockers/Dockerfile.cluster new file mode 100644 index 00000000..3a0d7341 --- /dev/null +++ b/.github/dockers/Dockerfile.cluster @@ -0,0 +1,7 @@ +FROM redis/redis-stack-server:edge as rss + +COPY dockers/create_cluster.sh /create_cluster.sh +RUN ls -R /opt/redis-stack +RUN chmod a+x /create_cluster.sh + +ENTRYPOINT [ "/create_cluster.sh"] diff --git a/.github/dockers/cluster.redis.conf b/.github/dockers/cluster.redis.conf new file mode 100644 index 00000000..d4de46fb --- /dev/null +++ b/.github/dockers/cluster.redis.conf @@ -0,0 +1,8 @@ +protected-mode no +enable-debug-command yes +loadmodule /opt/redis-stack/lib/redisearch.so +loadmodule /opt/redis-stack/lib/redisgraph.so +loadmodule /opt/redis-stack/lib/redistimeseries.so +loadmodule /opt/redis-stack/lib/rejson.so +loadmodule /opt/redis-stack/lib/redisbloom.so +loadmodule /opt/redis-stack/lib/redisgears.so v8-plugin-path /opt/redis-stack/lib/libredisgears_v8_plugin.so diff --git a/.github/dockers/create_cluster.sh b/.github/dockers/create_cluster.sh new file mode 100644 index 00000000..da9a0cb6 --- /dev/null +++ b/.github/dockers/create_cluster.sh @@ -0,0 +1,47 @@ +#! /bin/bash + +mkdir -p /nodes +touch /nodes/nodemap +if [ -z ${START_PORT} ]; then + START_PORT=16379 +fi +if [ -z ${END_PORT} ]; then + END_PORT=16384 +fi +if [ ! -z "$3" ]; then + START_PORT=$2 + START_PORT=$3 +fi +echo "STARTING: ${START_PORT}" +echo "ENDING: ${END_PORT}" + +for PORT in `seq ${START_PORT} ${END_PORT}`; do + mkdir -p /nodes/$PORT + if [[ -e /redis.conf ]]; then + cp /redis.conf /nodes/$PORT/redis.conf + else + touch /nodes/$PORT/redis.conf + fi + cat << EOF >> /nodes/$PORT/redis.conf +port ${PORT} +cluster-enabled yes +daemonize yes +logfile /redis.log +dir /nodes/$PORT +EOF + + set -x + /opt/redis-stack/bin/redis-server /nodes/$PORT/redis.conf + sleep 1 + if [ $? -ne 0 ]; then + echo "Redis failed to start, exiting." + continue + fi + echo 127.0.0.1:$PORT >> /nodes/nodemap +done +if [ -z "${REDIS_PASSWORD}" ]; then + echo yes | /opt/redis-stack/bin/redis-cli --cluster create `seq -f 127.0.0.1:%g ${START_PORT} ${END_PORT}` --cluster-replicas 1 +else + echo yes | opt/redis-stack/bin/redis-cli -a ${REDIS_PASSWORD} --cluster create `seq -f 127.0.0.1:%g ${START_PORT} ${END_PORT}` --cluster-replicas 1 +fi +tail -f /redis.log diff --git a/.github/standalone.env b/.github/standalone.env new file mode 100644 index 00000000..e0cfd098 --- /dev/null +++ b/.github/standalone.env @@ -0,0 +1 @@ +REDIS=localhost:6379 \ No newline at end of file diff --git a/.github/workflows/reusable.yml b/.github/workflows/reusable.yml new file mode 100644 index 00000000..0d1a63ac --- /dev/null +++ b/.github/workflows/reusable.yml @@ -0,0 +1,72 @@ +name: Build and Test +on: + workflow_call: + inputs: + + redis_stack_type: + required: true + type: string + + dotnet_version: + required: true + type: string + + clr_version: + required: true + type: string + + dotenv_file: + required: true + type: string +jobs: + + build_and_test: + name: Test + runs-on: ubuntu-latest + + env: + USER_NAME: ${{ secrets.USER_NAME }} + PASSWORD: ${{ secrets.PASSWORD }} + ENDPOINT: ${{ secrets.ENDPOINT }} + steps: + + - uses: actions/checkout@v3 + + - name: .NET Core 6 + uses: actions/setup-dotnet@v2 + with: + dotnet-version: '6.0.x' + + - name: .NET Core 7 + uses: actions/setup-dotnet@v2 + with: + dotnet-version: '7.0.x' + + - name: run redis-stack-server docker + working-directory: .github + run: docker-compose up -d redis-stack-${{inputs.redis_stack_type}} + + - name: set variables in dotenv + uses: c-py/action-dotenv-to-setenv@v2 + with: + env-file: ${{inputs.dotenv_file}} + + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore /p:ContinuousIntegrationBuild=true + - name: Test + run: | + echo "${{secrets.REDIS_CA_PEM}}" > tests/NRedisStack.Tests/bin/Debug/net6.0/redis_ca.pem + echo "${{secrets.REDIS_USER_CRT}}" > tests/NRedisStack.Tests/bin/Debug/net6.0/redis_user.crt + echo "${{secrets.REDIS_USER_PRIVATE_KEY}}" > tests/NRedisStack.Tests/bin/Debug/net6.0/redis_user_private.key + ls -R + dotnet test -f ${{inputs.clr_version}} --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover + - name: Codecov + uses: codecov/codecov-action@v3 + with: + token: ${{secrets.CODECOV_TOKEN}} + verbose: true + - name: Build + run: dotnet pack -c Release + diff --git a/tests/NRedisStack.Tests/SkipIfRedisAttribute.cs b/tests/NRedisStack.Tests/SkipIfRedisAttribute.cs new file mode 100644 index 00000000..2b7e0741 --- /dev/null +++ b/tests/NRedisStack.Tests/SkipIfRedisAttribute.cs @@ -0,0 +1,107 @@ +using Xunit; + +namespace NRedisStack.Tests; +public enum Comparison +{ + LessThan, + GreaterThanOrEqual, +} + +public enum Is +{ + Standalone, + OSSCluster +} + +[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +public class SkipIfRedisAttribute : FactAttribute +{ + private readonly string _targetVersion; + private readonly Comparison _comparison; + private readonly Is? _is = null; + private readonly string DefaultRedisConnectionString = Environment.GetEnvironmentVariable("REDIS") ?? "localhost:6379"; + + public SkipIfRedisAttribute( + Is _is, + Comparison comparison = Comparison.LessThan, + string targetVersion = "0.0.0") + { + this._is = _is; + _comparison = comparison; + _targetVersion = targetVersion; + } + + public SkipIfRedisAttribute(string targetVersion) // defaults to LessThan + { + _comparison = Comparison.LessThan; + _targetVersion = targetVersion; + } + + public SkipIfRedisAttribute(Comparison comparison, string targetVersion) + { + _comparison = comparison; + _targetVersion = targetVersion; + } + + public override string? Skip + { + get + { + string skipReason = ""; + bool skipped = false; + using (RedisFixture redisFixture = new RedisFixture()) + { + + // Cluster check + if (_is != null) + { + switch (_is) + { + case Is.OSSCluster: + if (redisFixture.isOSSCluster) + { + skipReason = skipReason + " Redis server is OSS cluster."; + skipped = true; + } + break; + + case Is.Standalone: + if (!redisFixture.isOSSCluster) + { + skipReason = skipReason + " Redis server is not OSS cluster."; + skipped = true; + } + break; + } + } + // Version check (if Is.Standalone/Is.Cluster is set then ) + + var serverVersion = redisFixture.Redis.GetServer(redisFixture.Redis.GetEndPoints()[0]).Version; + var targetVersion = new Version(_targetVersion); + int comparisonResult = serverVersion.CompareTo(targetVersion); + + switch (_comparison) + { + case Comparison.LessThan: + if (comparisonResult < 0) + { + skipReason = skipReason + $" Redis server version ({serverVersion}) is less than {_targetVersion}."; + skipped = true; + } + break; + case Comparison.GreaterThanOrEqual: + if (comparisonResult >= 0) + { + skipReason = skipReason + $" Redis server version ({serverVersion}) is greater than or equal to {_targetVersion}."; + skipped = true; + } + break; + } + } + + if (skipped) + return "Test skipped, because:" + skipReason; + return null; + } + } +} \ No newline at end of file From 387f461d5568ffe3eb1bb9fd4590a7444b70fd34 Mon Sep 17 00:00:00 2001 From: shacharPash Date: Tue, 3 Oct 2023 17:44:13 +0300 Subject: [PATCH 02/15] add GeoShapeField --- src/NRedisStack/Search/Schema.cs | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/NRedisStack/Search/Schema.cs b/src/NRedisStack/Search/Schema.cs index 442b2a72..62394143 100644 --- a/src/NRedisStack/Search/Schema.cs +++ b/src/NRedisStack/Search/Schema.cs @@ -13,6 +13,7 @@ public enum FieldType { Text, Geo, + GeoShape, Numeric, Tag, Vector @@ -38,6 +39,7 @@ internal void AddSchemaArgs(List args) { FieldType.Text => "TEXT", FieldType.Geo => "GEO", + FieldType.GeoShape => "GEOSHAPE", FieldType.Numeric => "NUMERIC", FieldType.Tag => "TAG", FieldType.Vector => "VECTOR", @@ -178,6 +180,37 @@ internal override void AddFieldTypeArgs(List args) } + public class GeoShapeField : Field + { + public enum CoordinateSystem + { + /// + /// For cartesian (X,Y). + /// + FLAT, + + /// + /// For geographic (lon, lat). + /// + SPHERICAL + } + private CoordinateSystem system { get; } + + internal GeoShapeField(FieldName name, CoordinateSystem system) + : base(name, FieldType.GeoShape) + { + this.system = system; + } + + internal GeoShapeField(string name, CoordinateSystem system) + : this(FieldName.Of(name), system) { } + + internal override void AddFieldTypeArgs(List args) + { + args.Add(system.ToString()); + } + } + public class NumericField : Field { public bool Sortable { get; } From 02f573579b1122096d399fea37dafc414457229c Mon Sep 17 00:00:00 2001 From: shacharPash <93581407+shacharPash@users.noreply.github.com> Date: Tue, 3 Oct 2023 17:47:16 +0300 Subject: [PATCH 03/15] Update SkipIfRedisAttribute.cs --- tests/NRedisStack.Tests/SkipIfRedisAttribute.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/NRedisStack.Tests/SkipIfRedisAttribute.cs b/tests/NRedisStack.Tests/SkipIfRedisAttribute.cs index 277f4ad1..75ba2045 100644 --- a/tests/NRedisStack.Tests/SkipIfRedisAttribute.cs +++ b/tests/NRedisStack.Tests/SkipIfRedisAttribute.cs @@ -75,7 +75,6 @@ public override string? Skip break; } } - // Version check (if Is.Standalone/Is.OSSCluster is set then ) var serverVersion = redisFixture.Redis.GetServer(redisFixture.Redis.GetEndPoints()[0]).Version; @@ -106,4 +105,4 @@ public override string? Skip return null; } } -} \ No newline at end of file +} From e7bc7307977f315db40937862b1e5d2f740881ce Mon Sep 17 00:00:00 2001 From: shacharPash Date: Tue, 3 Oct 2023 17:48:27 +0300 Subject: [PATCH 04/15] delete line --- tests/NRedisStack.Tests/SkipIfRedisAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/NRedisStack.Tests/SkipIfRedisAttribute.cs b/tests/NRedisStack.Tests/SkipIfRedisAttribute.cs index 75ba2045..856c9bca 100644 --- a/tests/NRedisStack.Tests/SkipIfRedisAttribute.cs +++ b/tests/NRedisStack.Tests/SkipIfRedisAttribute.cs @@ -105,4 +105,4 @@ public override string? Skip return null; } } -} +} \ No newline at end of file From 1fbc1b5de797763fec9a471c480b873febdc10a2 Mon Sep 17 00:00:00 2001 From: shacharPash Date: Tue, 3 Oct 2023 18:18:08 +0300 Subject: [PATCH 05/15] AddGeoShapeField --- src/NRedisStack/Search/Schema.cs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/NRedisStack/Search/Schema.cs b/src/NRedisStack/Search/Schema.cs index 62394143..8aaabd08 100644 --- a/src/NRedisStack/Search/Schema.cs +++ b/src/NRedisStack/Search/Schema.cs @@ -1,4 +1,5 @@ using NRedisStack.Search.Literals; +using static NRedisStack.Search.Schema.GeoShapeField; using static NRedisStack.Search.Schema.VectorField; namespace NRedisStack.Search @@ -321,6 +322,30 @@ public Schema AddTextField(FieldName name, double weight = 1.0, bool sortable = return this; } + /// + /// Add a GeoShape field to the schema. + /// + /// The field's name. + /// The coordinate system to use. + /// The object. + public Schema AddGeoShapeField(string name, CoordinateSystem system) + { + Fields.Add(new GeoShapeField(name, system)); + return this; + } + + /// + /// Add a GeoShape field to the schema. + /// + /// The field's name. + /// The coordinate system to use. + /// The object. + public Schema AddGeoShapeField(FieldName name, CoordinateSystem system) + { + Fields.Add(new GeoShapeField(name, system)); + return this; + } + /// /// Add a Geo field to the schema. /// From 222c2f2d356315b371e99ef73baa91bf8498432c Mon Sep 17 00:00:00 2001 From: shacharPash Date: Wed, 4 Oct 2023 17:20:33 +0300 Subject: [PATCH 06/15] add test --- .../NRedisStack.Tests.csproj | 3 +- tests/NRedisStack.Tests/Search/SearchTests.cs | 71 ++++++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/tests/NRedisStack.Tests/NRedisStack.Tests.csproj b/tests/NRedisStack.Tests/NRedisStack.Tests.csproj index aa4d38d6..60324ee5 100644 --- a/tests/NRedisStack.Tests/NRedisStack.Tests.csproj +++ b/tests/NRedisStack.Tests/NRedisStack.Tests.csproj @@ -21,11 +21,12 @@ + - + diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index 74703d1d..a7042c70 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -6,6 +6,9 @@ using NRedisStack.Search.Aggregation; using NRedisStack.Search.Literals.Enums; using System.Runtime.InteropServices; +using NetTopologySuite.IO; +using NetTopologySuite.Geometries; + namespace NRedisStack.Tests.Search; @@ -2757,4 +2760,70 @@ public void Issue175() Assert.True(ft.Create("myIndex", ftParams, schema)); } -} \ No newline at end of file + + [Fact] + public void GeoShapeFilterSpherical() + { + IDatabase db = redisFixture.Redis.GetDatabase(); + db.Execute("FLUSHALL"); + var ft = db.FT(); + + WKTReader reader = new WKTReader(); + GeometryFactory factory = new GeometryFactory(); + + Assert.True(ft.Create(index, new Schema().AddGeoShapeField("geom", GeoShapeField.CoordinateSystem.SPHERICAL))); + + // Create polygons + Polygon small = factory.CreatePolygon(new Coordinate[] { + new Coordinate(34.9001, 29.7001), + new Coordinate(34.9001, 29.7100), + new Coordinate(34.9100, 29.7100), + new Coordinate(34.9100, 29.7001), + new Coordinate(34.9001, 29.7001) + }); + db.HashSet("small", "geom", small.ToString()); + + Polygon large = factory.CreatePolygon(new Coordinate[] { + new Coordinate(34.9001, 29.7001), + new Coordinate(34.9001, 29.7200), + new Coordinate(34.9200, 29.7200), + new Coordinate(34.9200, 29.7001), + new Coordinate(34.9001, 29.7001) + }); + db.HashSet("large", "geom", large.ToString()); + + Polygon within = factory.CreatePolygon(new Coordinate[] { + new Coordinate(34.9000, 29.7000), + new Coordinate(34.9000, 29.7150), + new Coordinate(34.9150, 29.7150), + new Coordinate(34.9150, 29.7000), + new Coordinate(34.9000, 29.7000) + }); + + var res = ft.Search(index, new Query($"@geom:[within $poly]").AddParam("poly", within).Dialect(3)); + Assert.Equal(1, res.TotalResults); + Assert.Single(res.Documents); + Assert.Equal(small, reader.Read(res.Documents[0]["geom"].ToString())); + + Polygon contains = factory.CreatePolygon(new Coordinate[] { + new Coordinate(34.9002, 29.7002), + new Coordinate(34.9002, 29.7050), + new Coordinate(34.9050, 29.7050), + new Coordinate(34.9050, 29.7002), + new Coordinate(34.9002, 29.7002) + }); + + res = ft.Search(index, new Query($"@geom:[contains $poly]").AddParam("poly", contains).Dialect(3)); + Assert.Equal(2, res.TotalResults); + Assert.Equal(2, res.Documents.Count); + + // Create a point + Point point = factory.CreatePoint(new Coordinate(34.9010, 29.7010)); + db.HashSet("point", "geom", point.ToString()); + + res = ft.Search(index, new Query($"@geom:[within $poly]").AddParam("poly", point).Dialect(3)); + Assert.Equal(2, res.TotalResults); + Assert.Equal(2, res.Documents.Count); + + } +} From 779d85cd55571042b50649a030c2dc3ddf4bff3c Mon Sep 17 00:00:00 2001 From: shacharPash Date: Wed, 4 Oct 2023 17:45:30 +0300 Subject: [PATCH 07/15] upgrade SE.Redis --- src/NRedisStack/NRedisStack.csproj | 2 +- tests/Doc/Doc.csproj | 2 +- tests/NRedisStack.Tests/NRedisStack.Tests.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/NRedisStack/NRedisStack.csproj b/src/NRedisStack/NRedisStack.csproj index 9b698a4c..04011cad 100644 --- a/src/NRedisStack/NRedisStack.csproj +++ b/src/NRedisStack/NRedisStack.csproj @@ -16,7 +16,7 @@ - + diff --git a/tests/Doc/Doc.csproj b/tests/Doc/Doc.csproj index 0958f541..c8fef667 100644 --- a/tests/Doc/Doc.csproj +++ b/tests/Doc/Doc.csproj @@ -16,7 +16,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/tests/NRedisStack.Tests/NRedisStack.Tests.csproj b/tests/NRedisStack.Tests/NRedisStack.Tests.csproj index 60324ee5..e66bf0a1 100644 --- a/tests/NRedisStack.Tests/NRedisStack.Tests.csproj +++ b/tests/NRedisStack.Tests/NRedisStack.Tests.csproj @@ -23,7 +23,7 @@ - + From e721dd9198511d05a78eb239ca236fc6124d7d85 Mon Sep 17 00:00:00 2001 From: shacharPash Date: Sun, 15 Oct 2023 15:16:36 +0300 Subject: [PATCH 08/15] fix test --- src/NRedisStack/NRedisStack.csproj | 1 + src/NRedisStack/Search/Query.cs | 1 + tests/NRedisStack.Tests/Search/SearchTests.cs | 7 +++---- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/NRedisStack/NRedisStack.csproj b/src/NRedisStack/NRedisStack.csproj index 04011cad..11b92832 100644 --- a/src/NRedisStack/NRedisStack.csproj +++ b/src/NRedisStack/NRedisStack.csproj @@ -15,6 +15,7 @@ + diff --git a/src/NRedisStack/Search/Query.cs b/src/NRedisStack/Search/Query.cs index baa41598..3f77d67e 100644 --- a/src/NRedisStack/Search/Query.cs +++ b/src/NRedisStack/Search/Query.cs @@ -617,6 +617,7 @@ public Query SetSortBy(string field, bool? ascending = null) /// Parameters can be referenced in the query string by a $ , followed by the parameter name, /// e.g., $user , and each such reference in the search query to a parameter name is substituted /// by the corresponding parameter value. + /// Note: when calling this function with an externally supplied parameter, value should be a string. /// /// /// can be String, long or float diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index a7042c70..e7b17c80 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -2800,7 +2800,7 @@ public void GeoShapeFilterSpherical() new Coordinate(34.9000, 29.7000) }); - var res = ft.Search(index, new Query($"@geom:[within $poly]").AddParam("poly", within).Dialect(3)); + var res = ft.Search(index, new Query($"@geom:[within $poly]").AddParam("poly", within.ToString()).Dialect(3)); Assert.Equal(1, res.TotalResults); Assert.Single(res.Documents); Assert.Equal(small, reader.Read(res.Documents[0]["geom"].ToString())); @@ -2813,7 +2813,7 @@ public void GeoShapeFilterSpherical() new Coordinate(34.9002, 29.7002) }); - res = ft.Search(index, new Query($"@geom:[contains $poly]").AddParam("poly", contains).Dialect(3)); + res = ft.Search(index, new Query($"@geom:[contains $poly]").AddParam("poly", contains.ToString()).Dialect(3)); Assert.Equal(2, res.TotalResults); Assert.Equal(2, res.Documents.Count); @@ -2821,9 +2821,8 @@ public void GeoShapeFilterSpherical() Point point = factory.CreatePoint(new Coordinate(34.9010, 29.7010)); db.HashSet("point", "geom", point.ToString()); - res = ft.Search(index, new Query($"@geom:[within $poly]").AddParam("poly", point).Dialect(3)); + res = ft.Search(index, new Query($"@geom:[within $poly]").AddParam("poly", within.ToString()).Dialect(3)); Assert.Equal(2, res.TotalResults); Assert.Equal(2, res.Documents.Count); - } } From 244889bebd98a0d3769f08a62154fa4e8f3a5df3 Mon Sep 17 00:00:00 2001 From: shacharPash Date: Sun, 15 Oct 2023 15:22:36 +0300 Subject: [PATCH 09/15] add async test --- tests/NRedisStack.Tests/Search/SearchTests.cs | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index e7b17c80..3b5626e3 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -2825,4 +2825,69 @@ public void GeoShapeFilterSpherical() Assert.Equal(2, res.TotalResults); Assert.Equal(2, res.Documents.Count); } + + [Fact] + public async Task GeoShapeFilterSphericalAsync() + { + IDatabase db = redisFixture.Redis.GetDatabase(); + db.Execute("FLUSHALL"); + var ft = db.FT(); + + WKTReader reader = new WKTReader(); + GeometryFactory factory = new GeometryFactory(); + + Assert.True(await ft.CreateAsync(index, new Schema().AddGeoShapeField("geom", GeoShapeField.CoordinateSystem.SPHERICAL))); + + // Create polygons + Polygon small = factory.CreatePolygon(new Coordinate[] { + new Coordinate(34.9001, 29.7001), + new Coordinate(34.9001, 29.7100), + new Coordinate(34.9100, 29.7100), + new Coordinate(34.9100, 29.7001), + new Coordinate(34.9001, 29.7001) + }); + db.HashSet("small", "geom", small.ToString()); + + Polygon large = factory.CreatePolygon(new Coordinate[] { + new Coordinate(34.9001, 29.7001), + new Coordinate(34.9001, 29.7200), + new Coordinate(34.9200, 29.7200), + new Coordinate(34.9200, 29.7001), + new Coordinate(34.9001, 29.7001) + }); + db.HashSet("large", "geom", large.ToString()); + + Polygon within = factory.CreatePolygon(new Coordinate[] { + new Coordinate(34.9000, 29.7000), + new Coordinate(34.9000, 29.7150), + new Coordinate(34.9150, 29.7150), + new Coordinate(34.9150, 29.7000), + new Coordinate(34.9000, 29.7000) + }); + + var res = await ft.SearchAsync(index, new Query($"@geom:[within $poly]").AddParam("poly", within.ToString()).Dialect(3)); + Assert.Equal(1, res.TotalResults); + Assert.Single(res.Documents); + Assert.Equal(small, reader.Read(res.Documents[0]["geom"].ToString())); + + Polygon contains = factory.CreatePolygon(new Coordinate[] { + new Coordinate(34.9002, 29.7002), + new Coordinate(34.9002, 29.7050), + new Coordinate(34.9050, 29.7050), + new Coordinate(34.9050, 29.7002), + new Coordinate(34.9002, 29.7002) + }); + + res = await ft.SearchAsync(index, new Query($"@geom:[contains $poly]").AddParam("poly", contains.ToString()).Dialect(3)); + Assert.Equal(2, res.TotalResults); + Assert.Equal(2, res.Documents.Count); + + // Create a point + Point point = factory.CreatePoint(new Coordinate(34.9010, 29.7010)); + db.HashSet("point", "geom", point.ToString()); + + res = await ft.SearchAsync(index, new Query($"@geom:[within $poly]").AddParam("poly", within.ToString()).Dialect(3)); + Assert.Equal(2, res.TotalResults); + Assert.Equal(2, res.Documents.Count); + } } From da05e24244573f9dda6b09ca5b6ac7abb9cb0e60 Mon Sep 17 00:00:00 2001 From: shacharPash Date: Sun, 15 Oct 2023 17:02:18 +0300 Subject: [PATCH 10/15] add tests + Skips --- tests/NRedisStack.Tests/Search/SearchTests.cs | 96 ++++++++++++++++++- 1 file changed, 94 insertions(+), 2 deletions(-) diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index 3b5626e3..a6d8e2a7 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -2761,7 +2761,7 @@ public void Issue175() Assert.True(ft.Create("myIndex", ftParams, schema)); } - [Fact] + [SkipIfRedis(Comparison.LessThan, "7.1.242")] public void GeoShapeFilterSpherical() { IDatabase db = redisFixture.Redis.GetDatabase(); @@ -2826,7 +2826,7 @@ public void GeoShapeFilterSpherical() Assert.Equal(2, res.Documents.Count); } - [Fact] + [SkipIfRedis(Comparison.LessThan, "7.1.242")] public async Task GeoShapeFilterSphericalAsync() { IDatabase db = redisFixture.Redis.GetDatabase(); @@ -2890,4 +2890,96 @@ public async Task GeoShapeFilterSphericalAsync() Assert.Equal(2, res.TotalResults); Assert.Equal(2, res.Documents.Count); } + + [SkipIfRedis(Comparison.LessThan, "7.1.242")] + public void GeoShapeFilterFlat() + { + IDatabase db = redisFixture.Redis.GetDatabase(); + db.Execute("FLUSHALL"); + var ft = db.FT(); + WKTReader reader = new WKTReader(); + GeometryFactory factory = new GeometryFactory(); + + Assert.True(ft.Create(index, new Schema().AddGeoShapeField("geom", GeoShapeField.CoordinateSystem.FLAT))); + + // polygon type + Polygon small = factory.CreatePolygon(new Coordinate[]{new Coordinate(1, 1), + new Coordinate(1, 100), new Coordinate(100, 100), new Coordinate(100, 1), new Coordinate(1, 1)}); + db.HashSet("small", "geom", small.ToString()); + + Polygon large = factory.CreatePolygon(new Coordinate[]{new Coordinate(1, 1), + new Coordinate(1, 200), new Coordinate(200, 200), new Coordinate(200, 1), new Coordinate(1, 1)}); + db.HashSet("large", "geom", large.ToString()); + + // within condition + Polygon within = factory.CreatePolygon(new Coordinate[]{new Coordinate(0, 0), + new Coordinate(0, 150), new Coordinate(150, 150), new Coordinate(150, 0), new Coordinate(0, 0)}); + + SearchResult res = ft.Search(index, new Query("@geom:[within $poly]").AddParam("poly", within.ToString()).Dialect(3)); + Assert.Equal(1, res.TotalResults); + Assert.Single(res.Documents); + Assert.Equal(small, reader.Read(res.Documents[0]["geom"].ToString())); + + // contains condition + Polygon contains = factory.CreatePolygon(new Coordinate[]{new Coordinate(2, 2), + new Coordinate(2, 50), new Coordinate(50, 50), new Coordinate(50, 2), new Coordinate(2, 2)}); + + res = ft.Search(index, new Query("@geom:[contains $poly]").AddParam("poly", contains.ToString()).Dialect(3)); + Assert.Equal(2, res.TotalResults); + Assert.Equal(2, res.Documents.Count); + + // point type + Point point = factory.CreatePoint(new Coordinate(10, 10)); + db.HashSet("point", "geom", point.ToString()); + + res = ft.Search(index, new Query("@geom:[within $poly]").AddParam("poly", within.ToString()).Dialect(3)); + Assert.Equal(2, res.TotalResults); + Assert.Equal(2, res.Documents.Count); + } + + [SkipIfRedis(Comparison.LessThan, "7.1.242")] + public async Task GeoShapeFilterFlatAsync() + { + IDatabase db = redisFixture.Redis.GetDatabase(); + db.Execute("FLUSHALL"); + var ft = db.FT(); + WKTReader reader = new WKTReader(); + GeometryFactory factory = new GeometryFactory(); + + Assert.True(await ft.CreateAsync(index, new Schema().AddGeoShapeField("geom", GeoShapeField.CoordinateSystem.FLAT))); + + // polygon type + Polygon small = factory.CreatePolygon(new Coordinate[]{new Coordinate(1, 1), + new Coordinate(1, 100), new Coordinate(100, 100), new Coordinate(100, 1), new Coordinate(1, 1)}); + db.HashSet("small", "geom", small.ToString()); + + Polygon large = factory.CreatePolygon(new Coordinate[]{new Coordinate(1, 1), + new Coordinate(1, 200), new Coordinate(200, 200), new Coordinate(200, 1), new Coordinate(1, 1)}); + db.HashSet("large", "geom", large.ToString()); + + // within condition + Polygon within = factory.CreatePolygon(new Coordinate[]{new Coordinate(0, 0), + new Coordinate(0, 150), new Coordinate(150, 150), new Coordinate(150, 0), new Coordinate(0, 0)}); + + SearchResult res = await ft.SearchAsync(index, new Query("@geom:[within $poly]").AddParam("poly", within.ToString()).Dialect(3)); + Assert.Equal(1, res.TotalResults); + Assert.Single(res.Documents); + Assert.Equal(small, reader.Read(res.Documents[0]["geom"].ToString())); + + // contains condition + Polygon contains = factory.CreatePolygon(new Coordinate[]{new Coordinate(2, 2), + new Coordinate(2, 50), new Coordinate(50, 50), new Coordinate(50, 2), new Coordinate(2, 2)}); + + res = await ft.SearchAsync(index, new Query("@geom:[contains $poly]").AddParam("poly", contains.ToString()).Dialect(3)); + Assert.Equal(2, res.TotalResults); + Assert.Equal(2, res.Documents.Count); + + // point type + Point point = factory.CreatePoint(new Coordinate(10, 10)); + db.HashSet("point", "geom", point.ToString()); + + res = await ft.SearchAsync(index, new Query("@geom:[within $poly]").AddParam("poly", within.ToString()).Dialect(3)); + Assert.Equal(2, res.TotalResults); + Assert.Equal(2, res.Documents.Count); + } } From dcdafd9f4f4aec4a10eb585291be66e5c965dd33 Mon Sep 17 00:00:00 2001 From: shacharPash Date: Sun, 15 Oct 2023 17:18:01 +0300 Subject: [PATCH 11/15] skip id less than 7.2.1 --- tests/NRedisStack.Tests/Search/SearchTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index a6d8e2a7..3bb27ff4 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -2761,7 +2761,7 @@ public void Issue175() Assert.True(ft.Create("myIndex", ftParams, schema)); } - [SkipIfRedis(Comparison.LessThan, "7.1.242")] + [SkipIfRedis(Comparison.LessThan, "7.2.1")] public void GeoShapeFilterSpherical() { IDatabase db = redisFixture.Redis.GetDatabase(); @@ -2826,7 +2826,7 @@ public void GeoShapeFilterSpherical() Assert.Equal(2, res.Documents.Count); } - [SkipIfRedis(Comparison.LessThan, "7.1.242")] + [SkipIfRedis(Comparison.LessThan, "7.2.1")] public async Task GeoShapeFilterSphericalAsync() { IDatabase db = redisFixture.Redis.GetDatabase(); @@ -2891,7 +2891,7 @@ public async Task GeoShapeFilterSphericalAsync() Assert.Equal(2, res.Documents.Count); } - [SkipIfRedis(Comparison.LessThan, "7.1.242")] + [SkipIfRedis(Comparison.LessThan, "7.2.1")] public void GeoShapeFilterFlat() { IDatabase db = redisFixture.Redis.GetDatabase(); @@ -2937,7 +2937,7 @@ public void GeoShapeFilterFlat() Assert.Equal(2, res.Documents.Count); } - [SkipIfRedis(Comparison.LessThan, "7.1.242")] + [SkipIfRedis(Comparison.LessThan, "7.2.1")] public async Task GeoShapeFilterFlatAsync() { IDatabase db = redisFixture.Redis.GetDatabase(); From 432ee111d45160061f079e003037a3c185eb242c Mon Sep 17 00:00:00 2001 From: shacharPash Date: Sun, 15 Oct 2023 17:27:26 +0300 Subject: [PATCH 12/15] skip if cluster --- tests/NRedisStack.Tests/Search/SearchTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index 3bb27ff4..685fc0a2 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -2761,7 +2761,7 @@ public void Issue175() Assert.True(ft.Create("myIndex", ftParams, schema)); } - [SkipIfRedis(Comparison.LessThan, "7.2.1")] + [SkipIfRedis(Is.OSSCluster, Comparison.LessThan, "7.2.1")] public void GeoShapeFilterSpherical() { IDatabase db = redisFixture.Redis.GetDatabase(); @@ -2826,7 +2826,7 @@ public void GeoShapeFilterSpherical() Assert.Equal(2, res.Documents.Count); } - [SkipIfRedis(Comparison.LessThan, "7.2.1")] + [SkipIfRedis(Is.OSSCluster, Comparison.LessThan, "7.2.1")] public async Task GeoShapeFilterSphericalAsync() { IDatabase db = redisFixture.Redis.GetDatabase(); @@ -2891,7 +2891,7 @@ public async Task GeoShapeFilterSphericalAsync() Assert.Equal(2, res.Documents.Count); } - [SkipIfRedis(Comparison.LessThan, "7.2.1")] + [SkipIfRedis(Is.OSSCluster, Comparison.LessThan, "7.2.1")] public void GeoShapeFilterFlat() { IDatabase db = redisFixture.Redis.GetDatabase(); @@ -2937,7 +2937,7 @@ public void GeoShapeFilterFlat() Assert.Equal(2, res.Documents.Count); } - [SkipIfRedis(Comparison.LessThan, "7.2.1")] + [SkipIfRedis(Is.OSSCluster, Comparison.LessThan, "7.2.1")] public async Task GeoShapeFilterFlatAsync() { IDatabase db = redisFixture.Redis.GetDatabase(); From 174ca4dfcde43c45fd0728473f89c77bc5d4b5d2 Mon Sep 17 00:00:00 2001 From: shacharPash Date: Mon, 16 Oct 2023 16:55:01 +0300 Subject: [PATCH 13/15] add example --- Examples/GeoShape.md | 123 ++++++++++++++++++ .../Examples/ExampleTests.cs | 2 + 2 files changed, 125 insertions(+) create mode 100644 Examples/GeoShape.md diff --git a/Examples/GeoShape.md b/Examples/GeoShape.md new file mode 100644 index 00000000..25247fe3 --- /dev/null +++ b/Examples/GeoShape.md @@ -0,0 +1,123 @@ +# GeoShape Fields Usage In RediSearch + +As of RediSearch 2.8.4, advanced GEO querying with GEOSHAPE fields is supported. + +Any object/library producing a +[well-known text (WKT)](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) in `string` format can be used. + +In this example, we'll demonstrate how to use GeoShape fields in RediSearch with [NetTopologySuite](https://github.com/NetTopologySuite/NetTopologySuite) library. + +## Example + +### Modules Needed + +```c# +using StackExchange.Redis; +using NRedisStack.RedisStackCommands; +using NRedisStack.Search; +using NetTopologySuite.Geometries; +using NetTopologySuite.IO; +``` + +### Setup + +```csharp +// Connect to the Redis server: +var redis = ConnectionMultiplexer.Connect("localhost"); +var db = redis.GetDatabase(); +// Get a reference to the database and for search commands: +var ft = db.FT(); + +// Create WTKReader and GeometryFactory objects: +WKTReader reader = new WKTReader(); +GeometryFactory factory = new GeometryFactory(); + +``` + +### Create the index + +```csharp +ft.Create(index, new Schema().AddGeoShapeField("geom", GeoShapeField.CoordinateSystem.FLAT)); +``` + +### Prepare the data + +```csharp +Polygon small = factory.CreatePolygon(new Coordinate[]{new Coordinate(1, 1), +new Coordinate(1, 100), new Coordinate(100, 100), new Coordinate(100, 1), new Coordinate(1, 1)}); +db.HashSet("small", "geom", small.ToString()); + +Polygon large = factory.CreatePolygon(new Coordinate[]{new Coordinate(1, 1), +new Coordinate(1, 200), new Coordinate(200, 200), new Coordinate(200, 1), new Coordinate(1, 1)}); +db.HashSet("large", "geom", large.ToString()); +``` + +## Polygon type + +### Querying within condition + +```csharp +Polygon within = factory.CreatePolygon(new Coordinate[]{new Coordinate(0, 0), +new Coordinate(0, 150), new Coordinate(150, 150), new Coordinate(150, 0), new Coordinate(0, 0)}); + +SearchResult res = ft.Search(index, new Query("@geom:[within $poly]").AddParam("poly", within.ToString()).Dialect(3)); +``` + +The search result from redis is: + +```bash +1) (integer) 1 +2) "small" +3) 1) "geom" + 2) "POLYGON ((1 1, 1 100, 100 100, 100 1, 1 1))" +``` + +we can use the reader to get the polygon object: + +```csharp +reader.Read(res.Documents[0]["geom"].ToString()); +``` + +### Querying contains condition + +```csharp +Polygon contains = factory.CreatePolygon(new Coordinate[]{new Coordinate(2, 2), +new Coordinate(2, 50), new Coordinate(50, 50), new Coordinate(50, 2), new Coordinate(2, 2)}); + +res = ft.Search(index, new Query("@geom:[contains $poly]").AddParam("poly", contains.ToString()).Dialect(3)); // DIALECT 3 is required for this query + +``` + +The search result from redis is: + +```bash +1) (integer) 2 +2) "small" +3) 1) "geom" + 2) "POLYGON ((1 1, 1 100, 100 100, 100 1, 1 1))" +4) "large" +5) 1) "geom" + 2) "POLYGON ((1 1, 1 200, 200 200, 200 1, 1 1))" +``` + +### Point type + +```csharp +Point point = factory.CreatePoint(new Coordinate(10, 10)); +db.HashSet("point", "geom", point.ToString()); + +res = ft.Search(index, new Query("@geom:[within $poly]").AddParam("poly", within.ToString()).Dialect(3)); + +``` + +The search result from redis is: + +```bash +1) (integer) 2 +2) "small" +3) 1) "geom" + 2) "POLYGON ((1 1, 1 100, 100 100, 100 1, 1 1))" +4) "point" +5) 1) "geom" + 2) "POINT (10 10)" +``` diff --git a/tests/NRedisStack.Tests/Examples/ExampleTests.cs b/tests/NRedisStack.Tests/Examples/ExampleTests.cs index be3f2ca7..adc5636f 100644 --- a/tests/NRedisStack.Tests/Examples/ExampleTests.cs +++ b/tests/NRedisStack.Tests/Examples/ExampleTests.cs @@ -1373,6 +1373,8 @@ public void AdvancedQueryOperationsTest() Assert.Equal(expectedResSet, resSet); } + // GeoShape Example Test is in SearchTests.cs, The test name is: GeoShapeFilterFlat. + private static void SortAndCompare(List expectedList, List res) { res.Sort(); From bf52646b3a6443f3b9bdeb06fb0e74253a7c4c47 Mon Sep 17 00:00:00 2001 From: shacharPash Date: Mon, 16 Oct 2023 17:26:39 +0300 Subject: [PATCH 14/15] chayim review --- Examples/GeoShape.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Examples/GeoShape.md b/Examples/GeoShape.md index 25247fe3..2ad4d7c1 100644 --- a/Examples/GeoShape.md +++ b/Examples/GeoShape.md @@ -1,11 +1,11 @@ # GeoShape Fields Usage In RediSearch -As of RediSearch 2.8.4, advanced GEO querying with GEOSHAPE fields is supported. +NRedisStack now supports GEOSHAPE field querying. -Any object/library producing a -[well-known text (WKT)](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) in `string` format can be used. -In this example, we'll demonstrate how to use GeoShape fields in RediSearch with [NetTopologySuite](https://github.com/NetTopologySuite/NetTopologySuite) library. +Any object that serializes the [well-known text (WKT)](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) as a `string` can be used with NRedisStack. + +Using GeoShape fields in searches with the [NetTopologySuite](https://github.com/NetTopologySuite/NetTopologySuite) library. ## Example From c22527e300ca54510618649d0cef4453da04e5e1 Mon Sep 17 00:00:00 2001 From: shacharPash Date: Tue, 17 Oct 2023 11:10:05 +0300 Subject: [PATCH 15/15] chayims review 2 --- .../{GeoShape.md => GeoShapeQueryExample.md} | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) rename Examples/{GeoShape.md => GeoShapeQueryExample.md} (80%) diff --git a/Examples/GeoShape.md b/Examples/GeoShapeQueryExample.md similarity index 80% rename from Examples/GeoShape.md rename to Examples/GeoShapeQueryExample.md index 2ad4d7c1..5854d9f7 100644 --- a/Examples/GeoShape.md +++ b/Examples/GeoShapeQueryExample.md @@ -2,7 +2,6 @@ NRedisStack now supports GEOSHAPE field querying. - Any object that serializes the [well-known text (WKT)](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) as a `string` can be used with NRedisStack. Using GeoShape fields in searches with the [NetTopologySuite](https://github.com/NetTopologySuite/NetTopologySuite) library. @@ -60,7 +59,9 @@ db.HashSet("large", "geom", large.ToString()); Polygon within = factory.CreatePolygon(new Coordinate[]{new Coordinate(0, 0), new Coordinate(0, 150), new Coordinate(150, 150), new Coordinate(150, 0), new Coordinate(0, 0)}); -SearchResult res = ft.Search(index, new Query("@geom:[within $poly]").AddParam("poly", within.ToString()).Dialect(3)); +SearchResult res = ft.Search(index, new Query("@geom:[within $poly]") + .AddParam("poly", within.ToString()) // Note serializing the argument to string + .Dialect(3)); // DIALECT 3 is required for this query ``` The search result from redis is: @@ -72,7 +73,7 @@ The search result from redis is: 2) "POLYGON ((1 1, 1 100, 100 100, 100 1, 1 1))" ``` -we can use the reader to get the polygon object: +Use the reader to get the polygon: ```csharp reader.Read(res.Documents[0]["geom"].ToString()); @@ -84,11 +85,13 @@ reader.Read(res.Documents[0]["geom"].ToString()); Polygon contains = factory.CreatePolygon(new Coordinate[]{new Coordinate(2, 2), new Coordinate(2, 50), new Coordinate(50, 50), new Coordinate(50, 2), new Coordinate(2, 2)}); -res = ft.Search(index, new Query("@geom:[contains $poly]").AddParam("poly", contains.ToString()).Dialect(3)); // DIALECT 3 is required for this query +res = ft.Search(index, new Query("@geom:[contains $poly]") + .AddParam("poly", contains.ToString()) // Note serializing the argument to string + .Dialect(3)); // DIALECT 3 is required for this query ``` -The search result from redis is: +Our search result: ```bash 1) (integer) 2 @@ -100,17 +103,19 @@ The search result from redis is: 2) "POLYGON ((1 1, 1 200, 200 200, 200 1, 1 1))" ``` -### Point type +### Searching with Coordinates ```csharp Point point = factory.CreatePoint(new Coordinate(10, 10)); db.HashSet("point", "geom", point.ToString()); -res = ft.Search(index, new Query("@geom:[within $poly]").AddParam("poly", within.ToString()).Dialect(3)); +res = ft.Search(index, new Query("@geom:[within $poly]") + .AddParam("poly", within.ToString()) // Note serializing the argument to string + .Dialect(3)); // DIALECT 3 is required for this query ``` -The search result from redis is: +Our search result: ```bash 1) (integer) 2