使用 NHibernate 和 SQL Server Geography 进行高级距离搜索

发布于 2024-08-12 18:08:33 字数 1824 浏览 9 评论 0原文

我在存储库中有一个现有的高级搜索方法,它检查 FormCollection 是否存在搜索条件,如果存在,则向搜索添加一个条件,例如

public IList<Residence> GetForAdvancedSearch(FormCollection collection)
{
  var criteria = Session.CreateCriteria(typeof(Residence))
    .SetResultTransformer(new DistinctRootEntityResultTransformer());

  if (collection["MinBedrooms"] != null)
  {
    criteria
      .Add(Restrictions.Ge("Bedrooms", int.Parse(collection["MinBedrooms"])));
  }

  // ... many criteria omitted for brevity

  return criteria.List<Residence>();
}

我还有一个基本的距离搜索找出每个住宅距搜索条件有多远。查询的 HBM 是

<sql-query name="Residence.Nearest">
  <return alias="residence" class="Residences.Domain.Residence, Residences"/>
  <return-scalar column="Distance" type="float"/>
  SELECT R.*, dbo.GetDistance(:point, R.Coordinate) AS Distance
  FROM Residence R
  WHERE Distance < 10
  ORDER BY Distance
</sql-query>

我必须定义一个函数来计算距离,因为没有办法让 NHibernate 转义地理函数中的冒号:

 CREATE FUNCTION dbo.GetDistance
 (
  @firstPoint nvarchar(100), 
  @secondPoint GEOMETRY
 )
 RETURNS float
 AS
 BEGIN
  RETURN GEOGRAPHY::STGeomFromText(
  @firstPoint, 4326).STDistance(@secondPoint.STAsText()) / 1609.344
 END

并且存储库因此调用命名查询:

return Session
   .GetNamedQuery("Residence.Nearest")
   .SetString("point", String.Format("POINT({0} {1})", latitude, longitude))
   .List();

所以我的问题是;如何将两者结合起来(或从头开始),以便我可以过滤高级搜索结果以仅包含搜索位置 10 英里以内的住宅?

更新 我尝试使用 NHibernate.Spatial 和以下代码:

criteria.Add(SpatialExpression.IsWithinDistance(
    "Coordinate", new Coordinate(latitude, longitude), 10));

SpatialExpression.IsWithinDistance 返回了 System.NotImplementedException

I've got an existing advanced search method in a repository that checks a FormCollection for the existence of search criteria, and if present, adds a criterion to the search e.g.

public IList<Residence> GetForAdvancedSearch(FormCollection collection)
{
  var criteria = Session.CreateCriteria(typeof(Residence))
    .SetResultTransformer(new DistinctRootEntityResultTransformer());

  if (collection["MinBedrooms"] != null)
  {
    criteria
      .Add(Restrictions.Ge("Bedrooms", int.Parse(collection["MinBedrooms"])));
  }

  // ... many criteria omitted for brevity

  return criteria.List<Residence>();
}

I've also got a basic distance search to find how far each residence is from the search criteria. The HBM for the query is

<sql-query name="Residence.Nearest">
  <return alias="residence" class="Residences.Domain.Residence, Residences"/>
  <return-scalar column="Distance" type="float"/>
  SELECT R.*, dbo.GetDistance(:point, R.Coordinate) AS Distance
  FROM Residence R
  WHERE Distance < 10
  ORDER BY Distance
</sql-query>

I had to define a function to calculate the distance, as there was no way to get NHibernate to escape the colons in the geography function:

 CREATE FUNCTION dbo.GetDistance
 (
  @firstPoint nvarchar(100), 
  @secondPoint GEOMETRY
 )
 RETURNS float
 AS
 BEGIN
  RETURN GEOGRAPHY::STGeomFromText(
  @firstPoint, 4326).STDistance(@secondPoint.STAsText()) / 1609.344
 END

And the repository calls the named query thus:

return Session
   .GetNamedQuery("Residence.Nearest")
   .SetString("point", String.Format("POINT({0} {1})", latitude, longitude))
   .List();

So my question is; how do I combine the two (or start from scratch), so I can filter the advanced search results to include only residences within 10 miles of the search location?

UPDATE I have tried using NHibernate.Spatial with the following code:

criteria.Add(SpatialExpression.IsWithinDistance(
    "Coordinate", new Coordinate(latitude, longitude), 10));

but SpatialExpression.IsWithinDistance returned a System.NotImplementedException.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一世旳自豪 2024-08-19 18:08:33

您看过NHibernate.Spatial 项目吗?这可能会为您的问题提供一个简单的解决方案。

另一种方法是创建您自己的 ICriterion 实现 - 如果您从 AbstractCriterion 派生并且面向特定的数据库平台,那么这并不太棘手。这将允许您将距离函数与其他标准结合起来。

Have you seen the NHibernate.Spatial project? This may provide an easy solution to your problem.

The alternative is to create your own implementation of ICriterion - this is not too tricky if you derive from AbstractCriterion and you target your particular database platform. This would then allow you to combine your distance function with other criteria.

行雁书 2024-08-19 18:08:33

创建一个投影,实际上将一个新的距离列添加到结果中,该距离列是通过称为 UDF 计算的,然后向其添加限制:

var query = String.Format(
    "dbo.GetDistance('POINT({0} {1}', Coordinate) AS Distance", 
    latitude, longitude);
criteria
    .Add(Restrictions.Le(Projections.SqlProjection(
        query, 
        new [] {"Distance"}, 
        new [] {NHibernateUtil.Double}), 10));

更新

nb 虽然这在我发布时必须有效它,它不再起作用了。 NHibernate 不喜欢“.”在 dbo 之后,并说

“无法解析属性:Residences.Domain.Residence 的 dbo”。

如果我删除“dbo”。我明白了

“‘GetDistance’不是可识别的内置函数名称。”

Create a projection that, in effect, adds a new distance column to the results, which is calculated by called the UDF, and then add a restriction to it:

var query = String.Format(
    "dbo.GetDistance('POINT({0} {1}', Coordinate) AS Distance", 
    latitude, longitude);
criteria
    .Add(Restrictions.Le(Projections.SqlProjection(
        query, 
        new [] {"Distance"}, 
        new [] {NHibernateUtil.Double}), 10));

UPDATE

n.b. Although this must have worked when I posted it, it doesn't work any more. NHibernate doesn't like the '.' after dbo, and says

"could not resolve property: dbo of: Residences.Domain.Residence".

If I remove the 'dbo.' I get

"'GetDistance' is not a recognized built-in function name."

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文