Skip to content

Commit

Permalink
NCBC-47: Add support for spatial view queries
Browse files Browse the repository at this point in the history
Change-Id: I339bc3ea0c4a1ff4bf7c782db729a2009a5ca61f
Reviewed-on: http://review.couchbase.org/22420
Reviewed-by: Matt Ingenthron <matt@couchbase.com>
Reviewed-by: Volker Mische <volker.mische@gmail.com>
Tested-by: John C. Zablocki <john@couchbase.com>
  • Loading branch information
jzablocki authored and John C. Zablocki committed Nov 21, 2012
1 parent ffaba6e commit 48cc56f
Show file tree
Hide file tree
Showing 22 changed files with 970 additions and 198 deletions.
2 changes: 2 additions & 0 deletions src/Couchbase.Tests/Couchbase.Tests.csproj
Expand Up @@ -51,6 +51,8 @@
</ItemGroup>
<ItemGroup>
<Compile Include="ConfigHelperTests.cs" />
<Compile Include="CouchbaseClientSpatialViewTests.cs" />
<Compile Include="HelperTests\DocHelperTests.cs" />
<Compile Include="HttpClientConfigTests.cs" />
<Compile Include="CouchbaseAuthenticatedViewTests.cs" />
<Compile Include="CouchbaseClientGenericViewTests.cs" />
Expand Down
99 changes: 99 additions & 0 deletions src/Couchbase.Tests/CouchbaseClientSpatialViewTests.cs
@@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Couchbase.Extensions;

namespace Couchbase.Tests
{
[TestFixture]
public class CouchbaseClientSpatialViewTests : CouchbaseClientViewTestsBase
{
[Test]
public void When_Querying_Spatial_View_Results_Are_Returned()
{
var view = _Client.GetSpatialView("cities", "by_location");
foreach (var item in view)
{
Assert.That(item.Id, Is.Not.Null, "Item Id was null");
Assert.That(item.BoundingBox, Is.Not.Null, "Bounding box was null");
Assert.That(item.BoundingBox.Length, Is.GreaterThan(0), "Bounding length box was empty");
Assert.That(item.Geometry.Type, Is.StringMatching("Point"), "Type was not place");
Assert.That(item.Geometry.Coordinates, Is.InstanceOf<float[]>(), "Coordinates were missing");
}

Assert.That(view.Count(), Is.GreaterThan(0), "View count was 0");
}

[Test]
public void When_Querying_Spatial_View_With_Generics_And_Should_Lookup_Doc_By_Id_Is_True_Results_Are_Returned()
{
var view = _Client.GetSpatialView<City>("cities", "by_location", true);
foreach (var item in view)
{
Assert.That(item.Id, Is.Not.Null, "Id was null");
Assert.That(item.Name, Is.Not.Null, "Name was null");
Assert.That(item.State, Is.Not.Null, "State was null");
Assert.That(item.Type, Is.StringMatching("city"), "Type was not city");
}

Assert.That(view.Count(), Is.GreaterThan(0), "View count was 0");
}

[Test]
public void When_Querying_Spatial_View_With_Generics_And_Should_Lookup_Doc_By_Id_Is_False_Results_Are_Returned()
{
var view = _Client.GetSpatialView<CityProjection>("cities", "by_location_with_city_name", false);
foreach (var item in view)
{
Assert.That(item.CityState, Is.Not.Null);
}

Assert.That(view.Count(), Is.GreaterThan(0), "View count was 0");
}

[Test]
public void When_Querying_Spatial_View_With_Limit_Rows_Are_Limited()
{
var view = _Client.GetSpatialView("cities", "by_location").Limit(2);
Assert.That(view.Count(), Is.EqualTo(2), "View count was not 2");
}

[Test]
public void When_Querying_Spatial_View_With_Bounding_Box_Rows_Are_Limited()
{
var hasAtLeastOneRecord = false;
var view = _Client.GetSpatialView("cities", "by_location").BoundingBox(-73.789673f, 41.093704f, -71.592407f, 42.079742f); //bbox around Connecticut
foreach (var item in view)
{
hasAtLeastOneRecord = true;
var doc = _Client.GetJson<City>(item.Id);
Assert.That(doc.State, Is.StringMatching("CT"), "State was " + doc.State + " not CT");
}

Assert.That(hasAtLeastOneRecord, Is.True, "No records found");
}
}
}

#region [ License information ]
/* ************************************************************
*
* @author Couchbase <info@couchbase.com>
* @copyright 2012 Couchbase, Inc.
*
* Licensed 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.
*
* ************************************************************/
#endregion
44 changes: 22 additions & 22 deletions src/Couchbase.Tests/Data/CityDocs.json
@@ -1,22 +1,22 @@
{ "name" : "Hartford", "state" : "CT", "type" : "city" }
{ "name" : "Bridgeport", "state" : "CT", "type" : "city" }
{ "name" : "Stamford", "state" : "CT", "type" : "city" }
{ "name" : "New Haven", "state" : "CT", "type" : "city" }
{ "name" : "New London", "state" : "CT", "type" : "city" }
{ "name" : "Norwalk", "state" : "CT", "type" : "city" }
{ "name" : "Boston", "state" : "MA", "type" : "city" }
{ "name" : "Cambridge", "state" : "MA", "type" : "city" }
{ "name" : "Somerville", "state" : "MA", "type" : "city" }
{ "name" : "Springfield", "state" : "MA", "type" : "city" }
{ "name" : "Worcester", "state" : "MA", "type" : "city" }
{ "name" : "Portsmouth", "state" : "NH", "type" : "city" }
{ "name" : "Concord", "state" : "NH", "type" : "city" }
{ "name" : "Manchester", "state" : "NH", "type" : "city" }
{ "name" : "Burlington", "state" : "VT", "type" : "city" }
{ "name" : "Montpelier", "state" : "VT", "type" : "city" }
{ "name" : "Newport", "state" : "VT", "type" : "city" }
{ "name" : "Providence", "state" : "RI", "type" : "city" }
{ "name" : "Warwick", "state" : "RI", "type" : "city" }
{ "name" : "Newport", "state" : "RI", "type" : "city" }
{ "name" : "Bangor", "state" : "ME", "type" : "city" }
{ "name" : "Portland", "state" : "ME", "type" : "city" }
{ "name" : "Hartford", "state" : "CT", "type" : "city", "loc" : [-72.67408, 41.763309] }
{ "name" : "Bridgeport", "state" : "CT", "type" : "city", "loc" : [-73.191269, 41.181881] }
{ "name" : "Stamford", "state" : "CT", "type" : "city", "loc" : [-73.542229, 41.051819] }
{ "name" : "New Haven", "state" : "CT", "type" : "city", "loc" : [-72.92498, 41.307129] }
{ "name" : "New London", "state" : "CT", "type" : "city", "loc" : [-72.096474, 41.356441] }
{ "name" : "Norwalk", "state" : "CT", "type" : "city", "loc" : [-73.407654, 41.113659] }
{ "name" : "Boston", "state" : "MA", "type" : "city", "loc" : [-71.056702, 42.358631] }
{ "name" : "Cambridge", "state" : "MA", "type" : "city", "loc" : [-71.10601, 42.366791] }
{ "name" : "Somerville", "state" : "MA", "type" : "city", "loc" : [-71.098259, 42.386681] }
{ "name" : "Springfield", "state" : "MA", "type" : "city", "loc" : [-72.589287, 42.10125] }
{ "name" : "Worcester", "state" : "MA", "type" : "city", "loc" : [-71.802193, 42.263409] }
{ "name" : "Portsmouth", "state" : "NH", "type" : "city", "loc" : [-70.772118, 43.070621] }
{ "name" : "Concord", "state" : "NH", "type" : "city", "loc" : [-71.536598, 43.207249] }
{ "name" : "Manchester", "state" : "NH", "type" : "city", "loc" : [-71.463089, 42.991169] }
{ "name" : "Burlington", "state" : "VT", "type" : "city", "loc" : [-73.213226, 44.475922] }
{ "name" : "Montpelier", "state" : "VT", "type" : "city", "loc" : [-72.576263, 44.260288] }
{ "name" : "Newport", "state" : "VT", "type" : "city", "loc" : [-72.210617, 44.935341] }
{ "name" : "Providence", "state" : "RI", "type" : "city", "loc" : [-71.411987, 41.823872] }
{ "name" : "Warwick", "state" : "RI", "type" : "city", "loc" : [-71.461678, 41.698589] }
{ "name" : "Newport", "state" : "RI", "type" : "city", "loc" : [-71.311333, 41.492161] }
{ "name" : "Bangor", "state" : "ME", "type" : "city", "loc" : [-68.770767, 44.801708] }
{ "name" : "Portland", "state" : "ME", "type" : "city", "loc" : [-70.256653, 43.659142] }
6 changes: 5 additions & 1 deletion src/Couchbase.Tests/Data/CityViews.json
Expand Up @@ -13,5 +13,9 @@
"map": "function (doc) { if (doc.type == \"city\") { emit(doc.state, doc.name); } }",
"reduce": "_count"
}
}
},
"spatial" : {
"by_location" : "function (doc, meta) { if (doc.type == \"city\") { emit({ \"type\": \"Point\", \"coordinates\": doc.loc}, null); } }",
"by_location_with_city_name" : "function (doc, meta) { if (doc.type == \"city\") { emit({ \"type\": \"Point\", \"coordinates\": doc.loc}, { \"cityState\" : doc.name + \",\" + doc.state }); } }"
}
}
45 changes: 45 additions & 0 deletions src/Couchbase.Tests/HelperTests/DocHelperTests.cs
@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Couchbase.Helpers;
using Newtonsoft.Json;

namespace Couchbase.Tests.HelperTests
{
[TestFixture]
public class DocHelperTests
{
[Test]
public void When_Inserting_Id_Into_Doc_Json_String_Is_Valid_And_Contains_Id()
{
var json = "{ \"message\" : \"Test\" }";
var jsonWithId = DocHelper.InsertId(json, "8675309");

Assert.That(jsonWithId, Is.StringContaining("\"_id\":\"8675309\""));
Assert.That(JsonConvert.DeserializeObject(jsonWithId), Is.Not.Null);
}
}
}

#region [ License information ]
/* ************************************************************
*
* @author Couchbase <info@couchbase.com>
* @copyright 2012 Couchbase, Inc.
*
* Licensed 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.
*
* ************************************************************/
#endregion
14 changes: 14 additions & 0 deletions src/Couchbase/Couchbase.csproj
Expand Up @@ -51,6 +51,7 @@
<HintPath>..\packages\Newtonsoft.Json.4.5.1\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.configuration" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
Expand All @@ -71,8 +72,15 @@
<ItemGroup>
<Compile Include="Configuration\HttpClientElement.cs" />
<Compile Include="Configuration\IHttpClientConfiguration.cs" />
<Compile Include="CouchbaseSpatialView`1.cs" />
<Compile Include="CouchbaseSpatialView.cs" />
<Compile Include="CouchbaseSpatialViewBase.cs" />
<Compile Include="CouchbaseViewHandler.cs" />
<Compile Include="Extensions\CouchbaseClientExtensions.cs" />
<Compile Include="Helpers\DocHelper.cs" />
<Compile Include="Helpers\JsonHelper.cs" />
<Compile Include="ISpatialView`1.cs" />
<Compile Include="ISpatialViewRow.cs" />
<Compile Include="Management\Bucket.cs" />
<Compile Include="BucketConfigListener.cs" />
<Compile Include="BucketConfigSettings.cs" />
Expand Down Expand Up @@ -150,7 +158,10 @@
<Compile Include="Results\IObserveOperationResult.cs" />
<Compile Include="Results\ObserveOperationResult.cs" />
<Compile Include="Settings\ObserveSettings.cs" />
<Compile Include="SpatialViewGeometry.cs" />
<Compile Include="SpatialViewRow.cs" />
<Compile Include="VBucketAwareOperationFactory.cs" />
<Compile Include="ViewParamsBuilder.cs" />
<Compile Include="WebClientWithTimeout.cs">
<SubType>Component</SubType>
</Compile>
Expand All @@ -168,6 +179,9 @@
<Name>Enyim.Caching</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Geo\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<GitTagMatch>mb*</GitTagMatch>
Expand Down
12 changes: 12 additions & 0 deletions src/Couchbase/CouchbaseClient.cs
Expand Up @@ -715,6 +715,18 @@ public IView<T> GetView<T>(string designName, string viewName, bool shouldLookup
return new CouchbaseView<T>(this, this, designName, viewName, shouldLookupDocById);
}

public ISpatialView<ISpatialViewRow> GetSpatialView(string designName, string viewName)
{
getViewSetup(ref designName, ref viewName);
return new CouchbaseSpatialView(this, this, designName, viewName);
}

public ISpatialView<T> GetSpatialView<T>(string designName, string viewName, bool shouldLookupDocById = false)
{
getViewSetup(ref designName, ref viewName);
return new CouchbaseSpatialView<T>(this, this, designName, viewName, shouldLookupDocById);
}

public IDictionary<string, object> Get(IView view)
{
var keys = view.Select(row => row.ItemId);
Expand Down
41 changes: 41 additions & 0 deletions src/Couchbase/CouchbaseSpatialView.cs
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;

namespace Couchbase
{
internal class CouchbaseSpatialView : CouchbaseSpatialViewBase<ISpatialViewRow>
{
internal CouchbaseSpatialView(ICouchbaseClient client, IHttpClientLocator clientLocator, string designDocument, string indexName)
:base(client, clientLocator, designDocument, indexName) {}

public override IEnumerator<ISpatialViewRow> GetEnumerator()
{
return ViewHandler.TransformResults<ISpatialViewRow>(
jr => JsonConvert.DeserializeObject<SpatialViewRow>(Json.ParseRaw(jr)), BuildParams());
}
}
}

#region [ License information ]
/* ************************************************************
*
* @author Couchbase <info@couchbase.com>
* @copyright 2012 Couchbase, Inc.
*
* Licensed 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.
*
* ************************************************************/
#endregion

0 comments on commit 48cc56f

Please sign in to comment.