多点空间搜索

时间:2014-12-04 01:35:25

标签: django django-haystack geodjango

编辑:添加了更好的说明

我正在使用django-haystack 2.3.1与Elasticsearch后端,Django 1.7和Python 3.4

在我的应用程序中,我有一个Vendor模型和一个Location模型。地点包含完整的美国邮政地址,纬度/经度和GEOS点对象。供应商拥有带位置的ManyToManyField,维护多个供应商可在一个位置使用,一个位置可为多个供应商提供服务。当用户搜索供应商时,我需要过滤搜索结果,以便仅显示在指定邮政编码5英里范围内的一个或多个位置可用的供应商(通过Nominatim进行地理编码为纬度和经度)。

如果供应商只有一个关联的位置,那么使用django-haystack会很容易。我可以做我的索引

class VendorIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.CharField(document=True, use_template=True)
    location = indexes.LocationField(model_attr='location__point')
    # Other fields

并将以下行添加到我的搜索表单的search()方法中:

# Check to see if a start_date was chosen.
    if self.cleaned_data['zip_code']:
        location = self.geolocator.geocode(str(self.cleaned_data['zip_code']), timeout=10)
        point = Point(location.longitude, location.latitude)
        sqs = sqs.dwithin(
            'location', point, D(mi=settings.SEARCH_MILE_RADIUS),
        )

问题是搜索可能提供供应商的所有位置。在Geodjango查询中,我可以将Location表示为MultiPoint ...但haystack仅支持单点对象。我不需要找到5英里范围内供应商位置的数量,我只需要过滤掉所有不包含至少一个位于所需距离内的位置的供应商。输入位置。

必须有某种方法可以实现这一点,对吗?

elasticsearch空间查询文档包含以下注释:

  

每个文档的多个位置

     

geo_distance过滤器可以使用多个位置点   文献。一旦单个位置点与过滤器匹配,即可   文件将包含在过滤器中。

很明显,elasticsearch可以支持我想做的事情。现在我需要知道干草堆是否也可以支持这个,或者我是否需要在干草堆周围进行一些黑客攻击。

2 个答案:

答案 0 :(得分:1)

所以我通过创建自定义字段类型来处理多个位置来解决这个问题。

如果其他人需要使用django-haystack的单个模型实例的多个位置,就像我一样,您可以从github下载this gist以使用MultiLocationField。它与elasticsearch后端一起工作。我对其他后端并不积极。

半相关:

另请注意,django-haystack 中存在错误,其弹性搜索涉及计算距离。当您使用D(mi = 10)指定距离时(显然您可以选择单位和距离,mi和10只是占位符),然后django-haystack将其转换为km并将其作为距离参数发送到elasticsearch而不指定单位。由于elasticsearch的默认距离单位是米,因此elasticsearch将此km距离解释为米,因此它将关闭1000倍。在我意识到这是一个错误之前,我浪费了很长时间试图弄清楚我的代码出了什么问题。当我真的想要5公里的距离时,我做了D(km = 5000)这样的事情。不知道为什么django-haystack不只是快速修改这个bug。

答案 1 :(得分:0)

你想要 GeoDjango ! haystack用于全文搜索

https://docs.djangoproject.com/en/dev/ref/contrib/gis/

http://postgis.net/

然后,您可以使用类似Django QuerySet的语法来表达地理空间查询,就像文档中的“查找5英里范围内的所有Zipcodes”一样:

 Zipcode.objects.filter(poly__distance_lt=(some_point, Distance(m=5)))

 # ^ from https://docs.djangoproject.com/en/dev/ref/contrib/gis/geoquerysets/#distance-lookups