Расширенный поиск по расстояниям с использованием NHibernate и SQL Server Geography - PullRequest
1 голос
/ 02 декабря 2009

У меня есть существующий метод расширенного поиска в хранилище, который проверяет 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.

Ответы [ 2 ]

0 голосов
/ 03 декабря 2009

Создайте проекцию, которая, по сути, добавляет к результатам новый столбец расстояния, который рассчитывается как 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));

UPDATE

n.b. Хотя, должно быть, это сработало, когда я его опубликовал, это больше не работает. NHibernate не любит "." после дбо и говорит

"не удалось разрешить свойство: dbo of: Residences.Domain.Residence".

Если я уберу 'dbo.' Я получаю

"GetDistance" не является распознанным именем встроенной функции. "

0 голосов
/ 03 декабря 2009

Вы видели NHibernate.Spatial проект ? Это может обеспечить простое решение вашей проблемы.

Альтернатива состоит в том, чтобы создать собственную реализацию ICriterion - это не так уж сложно, если вы унаследованы от AbstractCriterion и нацелены на конкретную платформу базы данных. Это позволит вам затем объединить вашу функцию расстояния с другими критериями.

...