RavenDb 空间查询
RavenDb Spatial Query
我们正在从 MongoDb 转移到 RavenDb,运行 进入一个问题。
我们有一个简单的模型 (C# DotNet)
using System.Globalization;
public class Location
{
#region Properties
public double Latitude { get; set; }
public double Longitude { get; set; }
#endregion Properties
#region Methods
public override string ToString()
{
var numberFormatInfo = new NumberFormatInfo { NumberDecimalSeparator = "." };
return $"{Longitude.ToString(numberFormatInfo)} {Latitude.ToString(numberFormatInfo)}";
}
#endregion Methods
}
public class Place
{
public Location[] Area {get;set;} // Please note the array
}
客户可以 select 一个地方作为一对,或者他们可以在交互式地图上绘制一个多边形来表示这个地方,该多边形将存储在上面的区域 属性 中.
在 MongoDb 中,这再简单不过了
var locationQuery = new FilterDefinitionBuilder<Place>()
.GeoWithinBox(
field: x => x.Area,
lowerLeftX: query.Criteria.LongitudeBottomLeft,
lowerLeftY: query.Criteria.LatitudeBottomLeft,
upperRightX: query.Criteria.LongitudeTopRight,
upperRightY: query.Criteria.LatitudeTopRight);
上面的查询class,表示客户正在查看的地图上当前可见的区域。
尽管我尽了最大努力,但我无法破译有关如何实现相同目标的文档。
我希望有人能够阐明这个问题,以及如何使用
RavenDb Dotnet 客户端 (RavenDB.Client)
谢谢
更新和示例!
在下面 Ayende Rahien 的精彩回答之后,我想我会在这里添加一些细节,以简化其他登陆这里的旅程。
这是索引的样子。
using System.Linq;
using Raven.Client.Documents.Indexes;
public class PlaceAreaIndex : AbstractIndexCreationTask<Place>
{
public PlaceAreaIndex()
{
Map = places => from place in places
select new
{
Area = place.Area.Select(location => CreateSpatialField(location.Latitude, location.Longitude))
};
}
}
在使用查询之前,您需要在服务器上注册您的索引。
new PlaceAreaIndex().Execute(store);
我添加了一些帮助程序 classes 来帮助我对此进行测试,我将它们添加到此处以便示例让您尽可能接近 运行 代码
public class Box
{
#region Constructor
public Box(Location topRight, Location bottomLeft)
{
TopRight = topRight;
BottomLeft = bottomLeft;
TopLeft = new Location { Latitude = topRight.Latitude, Longitude = bottomLeft.Longitude };
BottomRight = new Location { Latitude = bottomLeft.Latitude, Longitude = topRight.Longitude };
}
#endregion Constructor
#region Properties
public Location TopRight { get;}
public Location TopLeft { get; }
public Location BottomLeft { get; }
public Location BottomRight { get; }
#endregion Properties
#region Methods
public override string ToString()
{
return $"POLYGON (({TopRight}, {TopLeft}, {BottomLeft}, {BottomRight}, {TopRight}))";
}
#endregion Methods
}
然后使用助手
var topRight = Location.New(latitude: -22.674847351188916, longitude: 31.25061035156253);
var bottomLeft = Location.New(latitude: -28.9600886880069, longitude: 25.1422119140623);
var box = new Box(topRight: topRight, bottomLeft: bottomLeft);
之后可以这样查询
var places = await session
.Query<Place, PlaceAreaIndex>()
.Spatial(
factory => factory.Area,
criteria => criteria.RelatesToShape(
shapeWkt: box.ToString(),
relation: SpatialRelation.Within))
.ToArrayAsync();
希望对你有帮助。
IIUC,问题是您可能有特定位置或多边形,对吗?
在 RavenDB 中表示多边形时,您需要为此使用 WKT。
例如,这是这样的:
POLYGON((14.316406249999982 37.541615344157876,32.68554687499998 37.541615344157876,32.68554687499998 25.439901234431595,14.316406249999982 25.439901234431595,14.316406249999982 37.541615344157876))
这可以被 RavenDB 内部的空间引擎使用,并允许您使用 spatial.Contains
.
进行查询
但是,这可能不是您想要的,具体取决于使用情况。
如果您想要的只是找到所有 Places
在矩形内具有 a 位置的所有 Places
,则将其威胁为一个数组会更容易位置。
另一方面,如果这真的是一个多边形,那就不同了。
可能更容易展示一些东西。 RavenDB中的示例数据有空间数据,像这样:
$wkt = "POLYGON((-1.0800308814195025 51.942980464942416,0.5789046654554975 51.942980464942416,0.5789046654554975 51.01935531918276,-1.0800308814195025 51.01935531918276,-1.0800308814195025 51.942980464942416))"
from Employees
where spatial.within( spatial.point(Address.Location.Latitude , Address .Location.Longitude),
spatial.wkt($wkt))
然后您可以看到新的空间选项卡,它将向您显示结果:
这显示了矩形 (wkt) 和一个点。
不过,如果你有一个数组,你也可以使用:
"Locations": [
{
"Latitude": 51.52384070000001,
"Longitude": -0.0944233
},
{
"Latitude": 51.48145355123356,
"Longitude": -0.17475717569860105
}
],
但是,需要一个明确的索引来处理,你可以这样写:
from e in docs.Employees
select new
{
Locations = e.Locations.Select(l => CreateSpatialField(l.Latitude, l.Longitude))
}
此索引员工的所有个人位置。
然后您可以使用以下方式查询它:
$wkt = "POLYGON((-1.0800308814195025 51.942980464942416,0.5789046654554975 51.942980464942416,0.5789046654554975 51.01935531918276,-1.0800308814195025 51.01935531918276,-1.0800308814195025 51.942980464942416))"
from index 'Employees/ByLocations'
where spatial.within( Locations, spatial.wkt($wkt))
然而,另一种选择是当您需要将位置列表转换为 ploygon 时。
例如,考虑这个 WKT:
POLYGON((-0.09345874990495329 51.7543103056377,-0.6823256691840518 51.56348306507395,-0.2252946874049533 51.30046869394702,0.09990102742639895 51.42120466591834,-0.09345874990495329 51.7543103056377))
这代表这个多边形:
然后您可以使用与以前相同的查询样式,但请注意:
- 您的
contains
查询现在需要包含 整个 多边形。您也可以使用 intersect
,但是使用单点选项可能会更好。
- 您需要了解 WKT 中积分的顺序。这很重要。你需要逆时针。
我们正在从 MongoDb 转移到 RavenDb,运行 进入一个问题。
我们有一个简单的模型 (C# DotNet)
using System.Globalization;
public class Location
{
#region Properties
public double Latitude { get; set; }
public double Longitude { get; set; }
#endregion Properties
#region Methods
public override string ToString()
{
var numberFormatInfo = new NumberFormatInfo { NumberDecimalSeparator = "." };
return $"{Longitude.ToString(numberFormatInfo)} {Latitude.ToString(numberFormatInfo)}";
}
#endregion Methods
}
public class Place
{
public Location[] Area {get;set;} // Please note the array
}
客户可以 select 一个地方作为一对,或者他们可以在交互式地图上绘制一个多边形来表示这个地方,该多边形将存储在上面的区域 属性 中.
在 MongoDb 中,这再简单不过了
var locationQuery = new FilterDefinitionBuilder<Place>()
.GeoWithinBox(
field: x => x.Area,
lowerLeftX: query.Criteria.LongitudeBottomLeft,
lowerLeftY: query.Criteria.LatitudeBottomLeft,
upperRightX: query.Criteria.LongitudeTopRight,
upperRightY: query.Criteria.LatitudeTopRight);
上面的查询class,表示客户正在查看的地图上当前可见的区域。
尽管我尽了最大努力,但我无法破译有关如何实现相同目标的文档。 我希望有人能够阐明这个问题,以及如何使用 RavenDb Dotnet 客户端 (RavenDB.Client)
谢谢
更新和示例!
在下面 Ayende Rahien 的精彩回答之后,我想我会在这里添加一些细节,以简化其他登陆这里的旅程。
这是索引的样子。
using System.Linq;
using Raven.Client.Documents.Indexes;
public class PlaceAreaIndex : AbstractIndexCreationTask<Place>
{
public PlaceAreaIndex()
{
Map = places => from place in places
select new
{
Area = place.Area.Select(location => CreateSpatialField(location.Latitude, location.Longitude))
};
}
}
在使用查询之前,您需要在服务器上注册您的索引。
new PlaceAreaIndex().Execute(store);
我添加了一些帮助程序 classes 来帮助我对此进行测试,我将它们添加到此处以便示例让您尽可能接近 运行 代码
public class Box
{
#region Constructor
public Box(Location topRight, Location bottomLeft)
{
TopRight = topRight;
BottomLeft = bottomLeft;
TopLeft = new Location { Latitude = topRight.Latitude, Longitude = bottomLeft.Longitude };
BottomRight = new Location { Latitude = bottomLeft.Latitude, Longitude = topRight.Longitude };
}
#endregion Constructor
#region Properties
public Location TopRight { get;}
public Location TopLeft { get; }
public Location BottomLeft { get; }
public Location BottomRight { get; }
#endregion Properties
#region Methods
public override string ToString()
{
return $"POLYGON (({TopRight}, {TopLeft}, {BottomLeft}, {BottomRight}, {TopRight}))";
}
#endregion Methods
}
然后使用助手
var topRight = Location.New(latitude: -22.674847351188916, longitude: 31.25061035156253);
var bottomLeft = Location.New(latitude: -28.9600886880069, longitude: 25.1422119140623);
var box = new Box(topRight: topRight, bottomLeft: bottomLeft);
之后可以这样查询
var places = await session
.Query<Place, PlaceAreaIndex>()
.Spatial(
factory => factory.Area,
criteria => criteria.RelatesToShape(
shapeWkt: box.ToString(),
relation: SpatialRelation.Within))
.ToArrayAsync();
希望对你有帮助。
IIUC,问题是您可能有特定位置或多边形,对吗? 在 RavenDB 中表示多边形时,您需要为此使用 WKT。
例如,这是这样的:
POLYGON((14.316406249999982 37.541615344157876,32.68554687499998 37.541615344157876,32.68554687499998 25.439901234431595,14.316406249999982 25.439901234431595,14.316406249999982 37.541615344157876))
这可以被 RavenDB 内部的空间引擎使用,并允许您使用 spatial.Contains
.
但是,这可能不是您想要的,具体取决于使用情况。
如果您想要的只是找到所有 Places
在矩形内具有 a 位置的所有 Places
,则将其威胁为一个数组会更容易位置。
另一方面,如果这真的是一个多边形,那就不同了。
可能更容易展示一些东西。 RavenDB中的示例数据有空间数据,像这样:
$wkt = "POLYGON((-1.0800308814195025 51.942980464942416,0.5789046654554975 51.942980464942416,0.5789046654554975 51.01935531918276,-1.0800308814195025 51.01935531918276,-1.0800308814195025 51.942980464942416))"
from Employees
where spatial.within( spatial.point(Address.Location.Latitude , Address .Location.Longitude),
spatial.wkt($wkt))
然后您可以看到新的空间选项卡,它将向您显示结果:
这显示了矩形 (wkt) 和一个点。
不过,如果你有一个数组,你也可以使用:
"Locations": [
{
"Latitude": 51.52384070000001,
"Longitude": -0.0944233
},
{
"Latitude": 51.48145355123356,
"Longitude": -0.17475717569860105
}
],
但是,需要一个明确的索引来处理,你可以这样写:
from e in docs.Employees
select new
{
Locations = e.Locations.Select(l => CreateSpatialField(l.Latitude, l.Longitude))
}
此索引员工的所有个人位置。
然后您可以使用以下方式查询它:
$wkt = "POLYGON((-1.0800308814195025 51.942980464942416,0.5789046654554975 51.942980464942416,0.5789046654554975 51.01935531918276,-1.0800308814195025 51.01935531918276,-1.0800308814195025 51.942980464942416))"
from index 'Employees/ByLocations'
where spatial.within( Locations, spatial.wkt($wkt))
然而,另一种选择是当您需要将位置列表转换为 ploygon 时。
例如,考虑这个 WKT:
POLYGON((-0.09345874990495329 51.7543103056377,-0.6823256691840518 51.56348306507395,-0.2252946874049533 51.30046869394702,0.09990102742639895 51.42120466591834,-0.09345874990495329 51.7543103056377))
这代表这个多边形:
然后您可以使用与以前相同的查询样式,但请注意:
- 您的
contains
查询现在需要包含 整个 多边形。您也可以使用intersect
,但是使用单点选项可能会更好。 - 您需要了解 WKT 中积分的顺序。这很重要。你需要逆时针。