module Sunspot module DSL class RestrictionWithNear < Restriction def initialize(field, scope, query, negated) super(field, scope, negated) @query = query end # # Perform a Geohash-based location restriction for the given `location` # field. Though this uses the same API as other attribute-field # restrictions, there are several differences between this and other # scoping methods: # # * It can only be called from the top-level query; it cannot be nested # in a `dynamic`, `any_of`, or `all_of` block. This is because geohash # queries are not sent to Solr as filter queries like other scopes, but # rather are part of the fulltext query sent to Solr. # * Because it is included with the fulltext query (if any), location # restrictions can be given boost. By default, an "exact" # (maximum-precision) match will give the result a boost of 1.0; each # lower level of precision gives a boost of 1/2 the next highest # precision. See below for options to modify this behavior. # # ==== What is a Geohash? # # Geohash is a clever algorithm that creates a decodable digest of a # geographical point. It does this by dividing the globe into # quadrants, encoding the quadrant in which the point sits in the hash, # dividing the quadrant into smaller quadrants, and repeating an arbitrary # number of times (the "precision"). Because of the way Geohash are # built, the shared Geohash prefix length of two locations will # <em>usually</em> increase as the distance between the points decreases. # Put another way, the geohashes of two nearby points will # <em>usually</em> have a longer shared prefix than two points which are # distant from one another. # # Read more about Geohashes on # {Wikipedia}[http://en.wikipedia.org/wiki/Geohash] or play around with # generating your own at {geohash.org}[http://geohash.org/]. # # In Sunspot, GeoHashes can have a precision between 3 and 12; this is the # number of characters in the hash. The precisions have the following # maximum bounding box sizes, in miles: # # <dt>3</dt> # <dd>389.07812</dd> # <dt>4</dt> # <dd>97.26953</dd> # <dt>5</dt> # <dd>24.31738</dd> # <dt>6</dt> # <dd>6.07935</dd> # <dt>7</dt> # <dd>1.51984 # <dt>8</dt> # <dd>0.37996</dd> # <dt>9</dt> # <dd>0.09499</dd> # <dt>10</dt> # <dd>0.02375</dd> # <dt>11</dt> # <dd>0.00594</dd> # <dt>12</dt> # <dd>0.00148</dd> # # ==== Score, boost, and sorting with location search # # The concept of relevance scoring is a familiar one from fulltext search; # Solr (or Lucene, actually) gives each result document a score based on # how relevant the document's text is to the search phrase. Sunspot's # location search also uses scoring to determine geographical relevance; # using boosts, longer prefix matches (which are, in general, # geographically closer to the search origin) are assigned higher # relevance. This means that the results of a pure location search are # <em>roughly</em> in order of geographical distance, as long as no other # sort is specified explicitly. # # This geographical relevance plays on the same field as fulltext scoring; # if you use both fulltext and geographical components in a single search, # both types of relevance will be taken into account when scoring the # matches. Thus, a very close fulltext match that's further away from the # geographical origin will be scored similarly to a less precise fulltext # match that is very close to the geographical origin. That's likely to be # consistent with the way most users would expect a fulltext geographical # search to work. # # ==== Options # # <dt><code>:precision</code></dt> # <dd>The minimum precision at which locations should match. See the table # of precisions and bounding-box sizes above; the proximity value will # ensure that all matching documents share a bounding box of the # corresponding maximum size with your origin point. The default value # is 7, meaning all results will share a bounding box with edges of # about one and a half miles with the origin.</dd> # <dt><code>:boost</code></dt> # <dd>The boost to apply to maximum-precision matches. Default is 1.0. You # can use this option to adjust the weight given to geographic # proximity versus fulltext matching, if you are doing both in a # search.</dd> # <dt><code>:precision_factor</code></dt> # <dd>This option determines how much boost is applied to matches at lower # precisions. The default value, 16.0, means that a match at precision # N is 1/16 as relevant as a match at precision N+1 (this is consistent # with the fact that each precision's bounding box is about sixteen # times the size of the next highest precision.)</dd> # # ==== Example # # Sunspot.search(Post) do # fulltext('pizza') # with(:location).near(-40.0, -70.0, :boost => 2, :precision => 6) # end # def near(lat, lng, options = {}) @query.fulltext.add_location(@field, lat, lng, options) end # # Performs a query that is filtered by a radius around a given # latitude and longitude. # # ==== Parameters # # :lat<Numeric>:: # Latitude (in degrees) # :lon<Numeric>:: # Longitude (in degrees) # :radius<Numeric>:: # Radius (in kilometers) # def in_radius(lat, lon, radius) @query.add_geo(Sunspot::Query::Geofilt.new(@field, lat, lon, radius)) end end end end