Skip to content
Browse files

Use solr-spatial-light instead of LocalSolr

After some incidental changes to the Solr schema during testing,
discovered that LocalSolr searches weren't actually using the dismax
query parser, even when requested. So, wrote a lightweight replacement
called solr-spatial-light that exposes distance filtering and sorting
using lucene-spatial without getting in the way of Solr's other
operations. Since that seems to be working fine, integrating it into
Sunspot.
  • Loading branch information...
1 parent 5a5b104 commit 2a92f1d43566bb92b02d4568eab1a546ceb4bc56 Mat Brown committed Dec 31, 2009
View
14 lib/sunspot/dsl/query.rb
@@ -152,10 +152,18 @@ def adjust_solr_params( &block )
# Scope the search by geographical distance from a given point.
# +coordinates+ should either respond to #first and #last (e.g. a
# two-element array), or to #lat and one of #lng, #lon, or #long.
- # +miles+ is the radius around the point for which to return documents.
+ # +options+ should be one or both of the following:
#
- def near(coordinates, miles)
- @query.add_location_restriction(coordinates, miles)
+ # :distance:: The maximum distance in miles from which results can come
+ # :sort::
+ # Whether to sort by distance from these coordinates. If other sorts are
+ # specified, they take precedence over distance sort.
+ #
+ def near(coordinates, options)
+ if options.respond_to?(:to_f)
+ options = { :distance => options }
+ end
+ @query.add_location_restriction(coordinates, options)
end
end
end
View
2 lib/sunspot/field_factory.rb
@@ -135,7 +135,7 @@ def populate_document(document, model)
if coordinates = @data_extractor.value_for(model)
coordinates = Util::Coordinates.new(coordinates)
document << Solr::Field.new(:lat => coordinates.lat)
- document << Solr::Field.new(:long => coordinates.lng)
+ document << Solr::Field.new(:lng => coordinates.lng)
end
end
end
View
21 lib/sunspot/query/local.rb
@@ -7,20 +7,19 @@ module Query
# generates the appropriate parameters.
#
class Local #:nodoc:
- def initialize(coordinates, radius)
- if radius < 1
- raise ArgumentError, "LocalSolr does not seem to support a radius of less than 1 mile."
- end
- @coordinates, @radius = Util::Coordinates.new(coordinates), radius
+ def initialize(coordinates, options)
+ @coordinates, @options = Util::Coordinates.new(coordinates), options
end
def to_params
- {
- :qt => 'geo',
- :lat => @coordinates.lat,
- :long => @coordinates.lng,
- :radius => @radius
- }
+ local_params = [
+ [:radius, @options[:distance]],
+ [:sort, @options[:sort]]
+ ].map do |key,value|
+ "#{key}=#{value}" if value
+ end.compact.join(" ") #TODO Centralized local param builder
+ query = "{!#{local_params}}#{@coordinates.lat},#{@coordinates.lng}"
+ { :spatial => query }
end
end
end
View
491 solr/solr/conf/schema.xml
@@ -1,517 +1,66 @@
<?xml version='1.0' encoding='UTF-8'?>
-<!--
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<!--
- This is the Solr schema file. This file should be named "schema.xml" and
- should be in the conf directory under the solr home
- (i.e. ./solr/conf/schema.xml by default)
- or located where the classloader for the Solr webapp can find it.
-
- This example schema is the recommended starting point for users.
- It should be kept correct and concise, usable out-of-the-box.
-
- For more information, on how to customize this file, please see
- http://wiki.apache.org/solr/SchemaXml
-
- PERFORMANCE NOTE: this schema includes many optional features and should not
- be used for benchmarking. To improve performance one could
- - set stored="false" for all fields possible (esp large fields) when you
- only need to search on the field but don't need to return the original
- value.
- - set indexed="false" if you don't need to search on the field, but only
- return the field as a result of searching on other indexed fields.
- - remove all unneeded copyField statements
- - for best index size and searching performance, set "index" to false
- for all general text fields, use copyField to copy them to the
- catchall "text" field, and use that for searching.
- - For maximum indexing performance, use the StreamingUpdateSolrServer
- java client.
- - Remember to run the JVM in server mode, and use a higher logging level
- that avoids logging every request
--->
<schema name='example' version='1.2'>
- <!-- attribute "name" is the name of this schema and is only used for display purposes.
- Applications should change this to reflect the nature of the search collection.
- version="1.2" is Solr's version number for the schema syntax and semantics. It should
- not normally be changed by applications.
- 1.0: multiValued attribute did not exist, all fields are multiValued by nature
- 1.1: multiValued attribute introduced, false by default
- 1.2: omitTermFreqAndPositions attribute introduced, true by default except for text fields.
- -->
<types>
- <!-- field type definitions. The "name" attribute is
- just a label to be used by field definitions. The "class"
- attribute and any other attributes determine the real
- behavior of the fieldType.
- Class names starting with "solr" refer to java classes in the
- org.apache.solr.analysis package.
- -->
- <!-- The StrField type is not analyzed, but indexed/stored verbatim.
- - StrField and TextField support an optional compressThreshold which
- limits compression (if enabled in the derived fields) to values which
- exceed a certain size (in characters).
- -->
- <!-- boolean type: "true" or "false" -->
- <!--Binary data type. The data should be sent/retrieved in as Base64 encoded Strings -->
- <fieldtype name='binary' class='solr.BinaryField'/>
- <!-- The optional sortMissingLast and sortMissingFirst attributes are
- currently supported on types that are sorted internally as strings.
- This includes "string","boolean","sint","slong","sfloat","sdouble","pdate"
- - If sortMissingLast="true", then a sort on this field will cause documents
- without the field to come after documents with the field,
- regardless of the requested sort order (asc or desc).
- - If sortMissingFirst="true", then a sort on this field will cause documents
- without the field to come before documents with the field,
- regardless of the requested sort order.
- - If sortMissingLast="false" and sortMissingFirst="false" (the default),
- then default lucene sorting will be used which places docs without the
- field first in an ascending sort and last in a descending sort.
- -->
- <!--
- Default numeric field types. For faster range queries, consider the tint/tfloat/tlong/tdouble types.
- -->
- <fieldType name='int' class='solr.TrieIntField' omitNorms='true' positionIncrementGap='0' precisionStep='0'/>
- <fieldType name='float' class='solr.TrieFloatField' omitNorms='true' positionIncrementGap='0' precisionStep='0'/>
- <fieldType name='long' class='solr.TrieLongField' omitNorms='true' positionIncrementGap='0' precisionStep='0'/>
- <fieldType name='double' class='solr.TrieDoubleField' omitNorms='true' positionIncrementGap='0' precisionStep='0'/>
- <!--
- Numeric field types that index each value at various levels of precision
- to accelerate range queries when the number of values between the range
- endpoints is large. See the javadoc for NumericRangeQuery for internal
- implementation details.
-
- Smaller precisionStep values (specified in bits) will lead to more tokens
- indexed per value, slightly larger index size, and faster range queries.
- A precisionStep of 0 disables indexing at different precision levels.
- -->
- <fieldType name='tlong' class='solr.TrieLongField' omitNorms='true' positionIncrementGap='0' precisionStep='8'/>
- <!-- The format for this date field is of the form 1995-12-31T23:59:59Z, and
- is a more restricted form of the canonical representation of dateTime
- http://www.w3.org/TR/xmlschema-2/#dateTime
- The trailing "Z" designates UTC time and is mandatory.
- Optional fractional seconds are allowed: 1995-12-31T23:59:59.999Z
- All other components are mandatory.
-
- Expressions can also be used to denote calculations that should be
- performed relative to "NOW" to determine the value, ie...
-
- NOW/HOUR
- ... Round to the start of the current hour
- NOW-1DAY
- ... Exactly 1 day prior to now
- NOW/DAY+6MONTHS+3DAYS
- ... 6 months and 3 days in the future from the start of
- the current day
-
- Consult the DateField javadocs for more information.
-
- Note: For faster range queries, consider the tdate type
- -->
- <!-- A Trie based date field for faster date range queries and date faceting. -->
- <!--
- Note:
- These should only be used for compatibility with existing indexes (created with older Solr versions)
- or if "sortMissingFirst" or "sortMissingLast" functionality is needed. Use Trie based fields instead.
-
- Plain numeric field types that store and index the text
- value verbatim (and hence don't support range queries, since the
- lexicographic ordering isn't equal to the numeric ordering)
- -->
- <fieldType name='pint' class='solr.IntField' omitNorms='true'/>
- <fieldType name='plong' class='solr.LongField' omitNorms='true'/>
- <fieldType name='pfloat' class='solr.FloatField' omitNorms='true'/>
- <fieldType name='pdouble' class='solr.DoubleField' omitNorms='true'/>
- <fieldType name='pdate' class='solr.DateField' omitNorms='true' sortMissingLast='true'/>
- <!--
- Note:
- These should only be used for compatibility with existing indexes (created with older Solr versions)
- or if "sortMissingFirst" or "sortMissingLast" functionality is needed. Use Trie based fields instead.
-
- Numeric field types that manipulate the value into
- a string value that isn't human-readable in its internal form,
- but with a lexicographic ordering the same as the numeric ordering,
- so that range queries work correctly.
- -->
- <fieldType name='slong' class='solr.SortableLongField' omitNorms='true' sortMissingLast='true'/>
- <fieldType name='sdouble' class='solr.SortableDoubleField' omitNorms='true' sortMissingLast='true'/>
- <!-- The "RandomSortField" is not used to store or search any
- data. You can declare fields of this type it in your schema
- to generate pseudo-random orderings of your docs for sorting
- purposes. The ordering is generated based on the field name
- and the version of the index, As long as the index version
- remains unchanged, and the same field name is reused,
- the ordering of the docs will be consistent.
- If you want different psuedo-random orderings of documents,
- for the same version of the index, use a dynamicField and
- change the name
- -->
- <fieldType name='random' class='solr.RandomSortField' indexed='true'/>
- <!-- solr.TextField allows the specification of custom text analyzers
- specified as a tokenizer and a list of token filters. Different
- analyzers may be specified for indexing and querying.
-
- The optional positionIncrementGap puts space between multiple fields of
- this type on the same document, with the purpose of preventing false phrase
- matching across fields.
-
- For more info on customizing your analyzer chain, please see
- http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters
- -->
- <!-- One can also specify an existing Analyzer class that has a
- default constructor via the class attribute on the analyzer element
- <fieldType name="text_greek" class="solr.TextField">
- <analyzer class="org.apache.lucene.analysis.el.GreekAnalyzer"/>
- </fieldType>
- -->
- <!-- A text field that only splits on whitespace for exact matching of words -->
- <fieldType name='text_ws' class='solr.TextField' positionIncrementGap='100'>
- <analyzer>
- <tokenizer class='solr.WhitespaceTokenizerFactory'/>
- </analyzer>
- </fieldType>
- <!-- A text field that uses WordDelimiterFilter to enable splitting and matching of
- words on case-change, alpha numeric boundaries, and non-alphanumeric chars,
- so that a query of "wifi" or "wi fi" could match a document containing "Wi-Fi".
- Synonyms and stopwords are customized by external files, and stemming is enabled.
- -->
- <!-- Less flexible matching, but less false matches. Probably not ideal for product names,
- but may be good for SKUs. Can insert dashes in the wrong place and still match. -->
- <fieldType name='textTight' class='solr.TextField' positionIncrementGap='100'>
- <analyzer>
- <tokenizer class='solr.WhitespaceTokenizerFactory'/>
- <filter class='solr.SynonymFilterFactory' expand='false' synonyms='synonyms.txt' ignoreCase='true'/>
- <filter class='solr.StopFilterFactory' words='stopwords.txt' ignoreCase='true'/>
- <filter catenateWords='1' catenateNumbers='1' class='solr.WordDelimiterFilterFactory' generateWordParts='0' generateNumberParts='0' catenateAll='0'/>
- <filter class='solr.LowerCaseFilterFactory'/>
- <filter class='solr.SnowballPorterFilterFactory' language='English' protected='protwords.txt'/>
- <!-- this filter can remove any duplicate tokens that appear at the same position - sometimes
- possible with WordDelimiterFilter in conjuncton with stemming. -->
- <filter class='solr.RemoveDuplicatesTokenFilterFactory'/>
- </analyzer>
- </fieldType>
- <!-- A general unstemmed text field - good if one does not know the language of the field -->
- <fieldType name='textgen' class='solr.TextField' positionIncrementGap='100'>
- <analyzer type='index'>
- <tokenizer class='solr.WhitespaceTokenizerFactory'/>
- <filter class='solr.StopFilterFactory' words='stopwords.txt' enablePositionIncrements='true' ignoreCase='true'/>
- <filter catenateWords='1' catenateNumbers='1' class='solr.WordDelimiterFilterFactory' generateWordParts='1' generateNumberParts='1' catenateAll='0' splitOnCaseChange='0'/>
- <filter class='solr.LowerCaseFilterFactory'/>
- </analyzer>
- <analyzer type='query'>
- <tokenizer class='solr.WhitespaceTokenizerFactory'/>
- <filter class='solr.SynonymFilterFactory' expand='true' synonyms='synonyms.txt' ignoreCase='true'/>
- <filter class='solr.StopFilterFactory' words='stopwords.txt' enablePositionIncrements='true' ignoreCase='true'/>
- <filter catenateWords='0' catenateNumbers='0' class='solr.WordDelimiterFilterFactory' generateWordParts='1' generateNumberParts='1' catenateAll='0' splitOnCaseChange='0'/>
- <filter class='solr.LowerCaseFilterFactory'/>
- </analyzer>
- </fieldType>
- <!-- A general unstemmed text field that indexes tokens normally and also
- reversed (via ReversedWildcardFilterFactory), to enable more efficient
- leading wildcard queries. -->
- <fieldType name='text_rev' class='solr.TextField' positionIncrementGap='100'>
- <analyzer type='index'>
- <tokenizer class='solr.WhitespaceTokenizerFactory'/>
- <filter class='solr.StopFilterFactory' words='stopwords.txt' enablePositionIncrements='true' ignoreCase='true'/>
- <filter catenateWords='1' catenateNumbers='1' class='solr.WordDelimiterFilterFactory' generateWordParts='1' generateNumberParts='1' catenateAll='0' splitOnCaseChange='0'/>
- <filter class='solr.LowerCaseFilterFactory'/>
- <filter maxPosQuestion='2' class='solr.ReversedWildcardFilterFactory' maxFractionAsterisk='0.33' withOriginal='true' maxPosAsterisk='3'/>
- </analyzer>
- <analyzer type='query'>
- <tokenizer class='solr.WhitespaceTokenizerFactory'/>
- <filter class='solr.SynonymFilterFactory' expand='true' synonyms='synonyms.txt' ignoreCase='true'/>
- <filter class='solr.StopFilterFactory' words='stopwords.txt' enablePositionIncrements='true' ignoreCase='true'/>
- <filter catenateWords='0' catenateNumbers='0' class='solr.WordDelimiterFilterFactory' generateWordParts='1' generateNumberParts='1' catenateAll='0' splitOnCaseChange='0'/>
- <filter class='solr.LowerCaseFilterFactory'/>
- </analyzer>
- </fieldType>
- <!-- charFilter + WhitespaceTokenizer -->
- <!--
- <fieldType name="textCharNorm" class="solr.TextField" positionIncrementGap="100" >
- <analyzer>
- <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ISOLatin1Accent.txt"/>
- <tokenizer class="solr.WhitespaceTokenizerFactory"/>
- </analyzer>
- </fieldType>
- -->
- <!-- This is an example of using the KeywordTokenizer along
- With various TokenFilterFactories to produce a sortable field
- that does not include some properties of the source text
- -->
- <fieldType name='alphaOnlySort' class='solr.TextField' omitNorms='true' sortMissingLast='true'>
- <analyzer>
- <!-- KeywordTokenizer does no actual tokenizing, so the entire
- input string is preserved as a single token
- -->
- <tokenizer class='solr.KeywordTokenizerFactory'/>
- <!-- The LowerCase TokenFilter does what you expect, which can be
- when you want your sorting to be case insensitive
- -->
- <filter class='solr.LowerCaseFilterFactory'/>
- <!-- The TrimFilter removes any leading or trailing whitespace -->
- <filter class='solr.TrimFilterFactory'/>
- <!-- The PatternReplaceFilter gives you the flexibility to use
- Java Regular expression to replace any sequence of characters
- matching a pattern with an arbitrary replacement string,
- which may include back references to portions of the original
- string matched by the pattern.
-
- See the Java Regular Expression documentation for more
- information on pattern and replacement string syntax.
-
- http://java.sun.com/j2se/1.5.0/docs/api/java/util/regex/package-summary.html
- -->
- <filter class='solr.PatternReplaceFilterFactory' replace='all' pattern='([^a-z])' replacement=''/>
- </analyzer>
- </fieldType>
- <fieldtype name='phonetic' class='solr.TextField' stored='false' indexed='true'>
+ <fieldType name='text' class='solr.TextField' omitNorms='false'>
<analyzer>
<tokenizer class='solr.StandardTokenizerFactory'/>
- <filter class='solr.DoubleMetaphoneFilterFactory' inject='false'/>
- </analyzer>
- </fieldtype>
- <fieldtype name='payloads' class='solr.TextField' stored='false' indexed='true'>
- <analyzer>
- <tokenizer class='solr.WhitespaceTokenizerFactory'/>
- <!--
- The DelimitedPayloadTokenFilter can put payloads on tokens... for example,
- a token of "foo|1.4" would be indexed as "foo" with a payload of 1.4f
- Attributes of the DelimitedPayloadTokenFilterFactory :
- "delimiter" - a one character delimiter. Default is | (pipe)
- "encoder" - how to encode the following value into a playload
- float -> org.apache.lucene.analysis.payloads.FloatEncoder,
- integer -> o.a.l.a.p.IntegerEncoder
- identity -> o.a.l.a.p.IdentityEncoder
- Fully Qualified class name implementing PayloadEncoder, Encoder must have a no arg constructor.
- -->
- <filter class='solr.DelimitedPayloadTokenFilterFactory' encoder='float'/>
- </analyzer>
- </fieldtype>
- <!-- lowercases the entire field value, keeping it as a single token. -->
- <fieldType name='lowercase' class='solr.TextField' positionIncrementGap='100'>
- <analyzer>
- <tokenizer class='solr.KeywordTokenizerFactory'/>
+ <filter class='solr.StandardFilterFactory'/>
<filter class='solr.LowerCaseFilterFactory'/>
</analyzer>
</fieldType>
- <!-- since fields of this type are by default not stored or indexed,
- any data added to them will be ignored outright. -->
- <fieldtype name='ignored' class='solr.StrField' stored='false' multiValued='true' indexed='false'/>
- <!-- *** This fieldType is used by Sunspot! *** -->
<fieldType name='string' class='solr.StrField' omitNorms='true'/>
- <!-- *** This fieldType is used by Sunspot! *** -->
<fieldType name='tdouble' class='solr.TrieDoubleField' omitNorms='true'/>
- <!-- *** This fieldType is used by Sunspot! *** -->
<fieldType name='rand' class='solr.RandomSortField' omitNorms='true'/>
- <!-- *** This fieldType is used by Sunspot! *** -->
- <fieldType name='text' class='solr.TextField' omitNorms='false'>
- <analyzer>
- <tokenizer class='solr.StandardTokenizerFactory'/>
- <filter class='solr.StandardFilterFactory'/>
- <filter class='solr.LowerCaseFilterFactory'/>
- </analyzer>
- </fieldType>
- <!-- *** This fieldType is used by Sunspot! *** -->
<fieldType name='boolean' class='solr.BoolField' omitNorms='true'/>
- <!-- *** This fieldType is used by Sunspot! *** -->
- <fieldType name='date' class='solr.DateField' omitNorms='true'/>
- <!-- *** This fieldType is used by Sunspot! *** -->
<fieldType name='sfloat' class='solr.SortableFloatField' omitNorms='true'/>
- <!-- *** This fieldType is used by Sunspot! *** -->
+ <fieldType name='date' class='solr.DateField' omitNorms='true'/>
<fieldType name='sint' class='solr.SortableIntField' omitNorms='true'/>
- <!-- *** This fieldType is used by Sunspot! *** -->
<fieldType name='tint' class='solr.TrieIntField' omitNorms='true'/>
- <!-- *** This fieldType is used by Sunspot! *** -->
<fieldType name='tfloat' class='solr.TrieFloatField' omitNorms='true'/>
- <!-- *** This fieldType is used by Sunspot! *** -->
<fieldType name='tdate' class='solr.TrieDateField' omitNorms='true'/>
</types>
<fields>
- <!-- Valid attributes for fields:
- name: mandatory - the name for the field
- type: mandatory - the name of a previously defined type from the
- <types> section
- indexed: true if this field should be indexed (searchable or sortable)
- stored: true if this field should be retrievable
- compressed: [false] if this field should be stored using gzip compression
- (this will only apply if the field type is compressable; among
- the standard field types, only TextField and StrField are)
- multiValued: true if this field may contain multiple values per document
- omitNorms: (expert) set to true to omit the norms associated with
- this field (this disables length normalization and index-time
- boosting for the field, and saves some memory). Only full-text
- fields or fields that need an index-time boost need norms.
- termVectors: [false] set to true to store the term vector for a
- given field.
- When using MoreLikeThis, fields used for similarity should be
- stored for best performance.
- termPositions: Store position information with the term vector.
- This will increase storage costs.
- termOffsets: Store offset information with the term vector. This
- will increase storage costs.
- default: a value that should be used if no value is specified
- when adding a document.
- -->
- <field name='sku' stored='true' type='textTight' indexed='true' omitNorms='true'/>
- <field name='name' stored='true' type='textgen' indexed='true'/>
- <field name='alphaNameSort' stored='false' type='alphaOnlySort' indexed='true'/>
- <field name='manu' stored='true' type='textgen' indexed='true' omitNorms='true'/>
- <field name='cat' stored='true' type='text_ws' multiValued='true' indexed='true' omitNorms='true'/>
- <field name='features' stored='true' type='text' multiValued='true' indexed='true'/>
- <field name='includes' termOffsets='true' stored='true' type='text' termVectors='true' indexed='true' termPositions='true'/>
- <field name='weight' stored='true' type='float' indexed='true'/>
- <field name='price' stored='true' type='float' indexed='true'/>
- <field name='popularity' stored='true' type='int' indexed='true'/>
- <field name='inStock' stored='true' type='boolean' indexed='true'/>
- <!-- Common metadata fields, named specifically to match up with
- SolrCell metadata when parsing rich documents such as Word, PDF.
- Some fields are multiValued only because Tika currently may return
- multiple values for them.
- -->
- <field name='title' stored='true' type='text' multiValued='true' indexed='true'/>
- <field name='subject' stored='true' type='text' indexed='true'/>
- <field name='description' stored='true' type='text' indexed='true'/>
- <field name='comments' stored='true' type='text' indexed='true'/>
- <field name='author' stored='true' type='textgen' indexed='true'/>
- <field name='keywords' stored='true' type='textgen' indexed='true'/>
- <field name='category' stored='true' type='textgen' indexed='true'/>
- <field name='content_type' stored='true' type='string' multiValued='true' indexed='true'/>
- <field name='last_modified' stored='true' type='date' indexed='true'/>
- <field name='links' stored='true' type='string' multiValued='true' indexed='true'/>
- <!-- catchall field, containing all other searchable text fields (implemented
- via copyField further on in this schema -->
- <!-- catchall text field that indexes tokens both normally and in reverse for efficient
- leading wildcard queries. -->
- <field name='text_rev' stored='false' type='text_rev' multiValued='true' indexed='true'/>
- <!-- non-tokenized version of manufacturer to make it easier to sort or group
- results by manufacturer. copied from "manu" via copyField -->
- <field name='manu_exact' stored='false' type='string' indexed='true'/>
- <field name='payloads' stored='true' type='payloads' indexed='true'/>
- <!-- Uncommenting the following will create a "timestamp" field using
- a default value of "NOW" to indicate when each document was indexed.
- -->
- <!--
- <field name="timestamp" type="date" indexed="true" stored="true" default="NOW" multiValued="false"/>
- -->
- <!-- Dynamic field definitions. If a field name is not found, dynamicFields
- will be used if the name matches any of the patterns.
- RESTRICTION: the glob-like pattern in the name attribute must have
- a "*" only at the start or the end.
- EXAMPLE: name="*_i" will match any field ending in _i (like myid_i, z_i)
- Longer patterns will be matched first. if equal size patterns
- both match, the first appearing in the schema will be used. -->
- <dynamicField name='*_l' stored='true' type='long' indexed='true'/>
- <dynamicField name='*_t' stored='true' type='text' indexed='true'/>
- <!-- some trie-coded dynamic fields for faster range queries -->
- <dynamicField name='*_ti' stored='true' type='tint' indexed='true'/>
- <dynamicField name='*_tl' stored='true' type='tlong' indexed='true'/>
- <dynamicField name='*_tf' stored='true' type='tfloat' indexed='true'/>
- <dynamicField name='*_td' stored='true' type='tdouble' indexed='true'/>
- <dynamicField name='*_tdt' stored='true' type='tdate' indexed='true'/>
- <dynamicField name='*_pi' stored='true' type='pint' indexed='true'/>
- <dynamicField name='ignored_*' type='ignored' multiValued='true'/>
- <dynamicField name='attr_*' stored='true' type='textgen' multiValued='true' indexed='true'/>
- <!-- uncomment the following to ignore any fields that don't already match an existing
- field name or dynamic field, rather than reporting them as an error.
- alternately, change the type="ignored" to some other type e.g. "text" if you want
- unknown fields indexed and/or stored by default -->
- <!--dynamicField name="*" type="ignored" multiValued="true" /-->
- <!-- *** This field is used by Sunspot! *** -->
<field name='id' stored='true' type='string' multiValued='false' indexed='true'/>
- <!-- *** This field is used by Sunspot! *** -->
<field name='type' stored='false' type='string' multiValued='true' indexed='true'/>
- <!-- *** This field is used by Sunspot! *** -->
<field name='class_name' stored='false' type='string' multiValued='false' indexed='true'/>
- <!-- *** This field is used by Sunspot! *** -->
<field name='text' stored='false' type='string' multiValued='true' indexed='true'/>
- <!-- *** This field is used by Sunspot! *** -->
<field name='lat' stored='true' type='tdouble' multiValued='false' indexed='true'/>
- <!-- *** This field is used by Sunspot! *** -->
- <field name='long' stored='true' type='tdouble' multiValued='false' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
+ <field name='lng' stored='true' type='tdouble' multiValued='false' indexed='true'/>
<dynamicField name='random_*' stored='false' type='rand' multiValued='false' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='_local*' stored='false' type='tdouble' multiValued='false' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_text' stored='false' type='text' multiValued='true' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_texts' stored='true' type='text' multiValued='true' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_b' stored='false' type='boolean' multiValued='false' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_bm' stored='false' type='boolean' multiValued='true' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_bs' stored='true' type='boolean' multiValued='false' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_bms' stored='true' type='boolean' multiValued='true' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_d' stored='false' type='date' multiValued='false' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_dm' stored='false' type='date' multiValued='true' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_ds' stored='true' type='date' multiValued='false' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_dms' stored='true' type='date' multiValued='true' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_f' stored='false' type='sfloat' multiValued='false' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_fm' stored='false' type='sfloat' multiValued='true' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_fs' stored='true' type='sfloat' multiValued='false' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_fms' stored='true' type='sfloat' multiValued='true' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_i' stored='false' type='sint' multiValued='false' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_im' stored='false' type='sint' multiValued='true' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_is' stored='true' type='sint' multiValued='false' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_ims' stored='true' type='sint' multiValued='true' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_s' stored='false' type='string' multiValued='false' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_sm' stored='false' type='string' multiValued='true' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_ss' stored='true' type='string' multiValued='false' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_sms' stored='true' type='string' multiValued='true' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_it' stored='false' type='tint' multiValued='false' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_itm' stored='false' type='tint' multiValued='true' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_its' stored='true' type='tint' multiValued='false' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_itms' stored='true' type='tint' multiValued='true' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_ft' stored='false' type='tfloat' multiValued='false' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_ftm' stored='false' type='tfloat' multiValued='true' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_fts' stored='true' type='tfloat' multiValued='false' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_ftms' stored='true' type='tfloat' multiValued='true' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_dt' stored='false' type='tdate' multiValued='false' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_dtm' stored='false' type='tdate' multiValued='true' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_dts' stored='true' type='tdate' multiValued='false' indexed='true'/>
- <!-- *** This dynamicField is used by Sunspot! *** -->
<dynamicField name='*_dtms' stored='true' type='tdate' multiValued='true' indexed='true'/>
</fields>
<!-- Field to use to determine and enforce document uniqueness.
@@ -526,33 +75,5 @@
</defaultSearchField>
<!-- SolrQueryParser configuration: defaultOperator="AND|OR" -->
<solrQueryParser defaultOperator='AND'/>
- <!-- copyField commands copy one field to another at the time a document
- is added to the index. It's used either to index the same field differently,
- or to add multiple fields to the same field for easier/faster searching. -->
- <copyField dest='text' source='cat'/>
- <copyField dest='text' source='name'/>
- <copyField dest='text' source='manu'/>
- <copyField dest='text' source='features'/>
- <copyField dest='text' source='includes'/>
- <copyField dest='manu_exact' source='manu'/>
- <!-- Above, multiple source fields are copied to the [text] field.
- Another way to map multiple source fields to the same
- destination field is to use the dynamic field syntax.
- copyField also supports a maxChars to copy setting. -->
- <!-- <copyField source="*_t" dest="text" maxChars="3000"/> -->
- <!-- copy name to alphaNameSort, a field designed for sorting by name -->
- <!-- <copyField source="name" dest="alphaNameSort"/> -->
- <!-- Similarity is the scoring routine for each document vs. a query.
- A custom similarity may be specified here, but the default is fine
- for most applications. -->
- <!-- <similarity class="org.apache.lucene.search.DefaultSimilarity"/> -->
- <!-- ... OR ...
- Specify a SimilarityFactory class name implementation
- allowing parameters to be used.
- -->
- <!--
- <similarity class="com.example.solr.CustomSimilarityFactory">
- <str name="paramkey">param value</str>
- </similarity>
- -->
-</schema>
+ <copyField dest="text" source="*_text"/>
+</schema>
View
34 solr/solr/conf/solrconfig.xml
@@ -488,6 +488,9 @@
will be used.
-->
<requestHandler name="standard" class="solr.SearchHandler" default="true">
+ <arr name="last-components">
+ <str>spatial</str>
+ </arr>
<!-- default values for query parameters -->
<lst name="defaults">
<str name="echoParams">explicit</str>
@@ -643,6 +646,10 @@
</arr>
-->
+ <!-- solr-spatial-light search component -->
+
+ <searchComponent name="spatial" class="me.outofti.solrspatiallight.SpatialQueryComponent" />
+
<!-- The spell check component can return a list of alternative spelling
suggestions. -->
<searchComponent name="spellcheck" class="solr.SpellCheckComponent">
@@ -1029,31 +1036,4 @@
<healthcheck type="file">server-enabled</healthcheck>
-->
</admin>
-
- <!-- configuration for LocalSolr -->
- <updateRequestProcessorChain>
- <processor class='com.pjaol.search.solr.update.LocalUpdateProcessorFactory'>
- <str name='latField'>lat</str>
- <str name='lngField'>long</str>
- <int name='startTier'>9</int>
- <int name='endTier'>16</int>
- </processor>
- <processor class='solr.RunUpdateProcessorFactory'></processor>
- <processor class='solr.LogUpdateProcessorFactory'></processor>
- </updateRequestProcessorChain>
- <searchComponent class='com.pjaol.search.solr.component.LocalSolrQueryComponent' name='localsolr'>
- <str name='latField'>lat</str>
- <str name='lngField'>long</str>
- </searchComponent>
- <requestHandler class='org.apache.solr.handler.component.SearchHandler' name='geo'>
- <str name="defType">lucene</str>
- <arr name='components'>
- <str>localsolr</str>
- <str>facet</str>
- <str>mlt</str>
- <str>highlight</str>
- <str>debug</str>
- </arr>
- </requestHandler>
-
</config>
View
BIN solr/solr/lib/geoapi-nogenerics-2.1-M2.jar
Binary file not shown.
View
BIN solr/solr/lib/gt2-referencing-2.3.1.jar
Binary file not shown.
View
BIN solr/solr/lib/jsr108-0.01.jar
Binary file not shown.
View
BIN solr/solr/lib/localsolr.jar
Binary file not shown.
View
BIN solr/solr/lib/solr-spatial-light-0.0.1.jar
Binary file not shown.
View
6 spec/api/indexer/attributes_spec.rb
@@ -70,7 +70,7 @@
it 'should index latitude and longitude as a pair' do
session.index(post(:coordinates => [40.7, -73.5]))
- connection.should have_add_with(:lat => 40.7, :long => -73.5)
+ connection.should have_add_with(:lat => 40.7, :lng => -73.5)
end
[
@@ -83,13 +83,13 @@
session.index(post(
:coordinates => OpenStruct.new(lat_attr => 40.7, lng_attr => -73.5)
))
- connection.should have_add_with(:lat => 40.7, :long => -73.5)
+ connection.should have_add_with(:lat => 40.7, :lng => -73.5)
end
end
it 'should index latitude and longitude from a block' do
session.index(Photo.new(:lat => 30, :lng => -60))
- connection.should have_add_with(:lat => 30.0, :long => -60.0)
+ connection.should have_add_with(:lat => 30.0, :lng => -60.0)
end
it 'should correctly index an attribute field with block access' do
View
30 spec/api/query/local_spec.rb
@@ -1,25 +1,25 @@
require File.join(File.dirname(__FILE__), 'spec_helper')
describe 'local query' do
- it 'sets query type to geo when geo search performed' do
+ it 'sends lat and lng, and distance when geo search is performed' do
session.search Post do
- near [40.7, -73.5], 5
+ near [40.7, -73.5], :distance => 5
end
- connection.should have_last_search_with(:qt => 'geo')
+ connection.should have_last_search_with(:spatial => "{!radius=5}40.7,-73.5")
end
- it 'sets lat and lng when geo search is performed' do
+ it 'sets lat, lng, and sort flag when sorted geo search is performed' do
session.search Post do
- near [40.7, -73.5], 5
+ near [40.7, -73.5], :sort => true
end
- connection.should have_last_search_with(:lat => 40.7, :long => -73.5)
+ connection.should have_last_search_with(:spatial => "{!sort=true}40.7,-73.5")
end
- it 'sets radius when geo search is performed' do
+ it 'sets radius and sort when both are specified' do
session.search Post do
- near [40.7, -73.5], 5
+ near [40.7, -73.5], :distance => 5, :sort => true
end
- connection.should have_last_search_with(:radius => 5)
+ connection.should have_last_search_with(:spatial => "{!radius=5 sort=true}40.7,-73.5")
end
[
@@ -30,17 +30,9 @@
].each do |lat_attr, lng_attr|
it "sets coordinates using #{lat_attr.inspect}, #{lng_attr.inspect}" do
session.search Post do
- near OpenStruct.new(lat_attr => 40.7, lng_attr => -73.5), 5
+ near OpenStruct.new(lat_attr => 40.7, lng_attr => -73.5), :distance => 5
end
- connection.should have_last_search_with(:lat => 40.7, :long => -73.5)
+ connection.should have_last_search_with(:spatial => "{!radius=5}40.7,-73.5")
end
end
-
- it 'raises ArgumentError if radius is less than 1' do
- lambda do
- session.search Post do
- near [40, -70], 0.5
- end
- end.should raise_error(ArgumentError)
- end
end
View
6 spec/api/query/ordering_pagination_spec.rb
@@ -63,12 +63,6 @@
connection.should have_last_search_with(:sort => 'score desc')
end
- it 'orders by geo distance' do
- session.search Post do
- order_by :distance, :asc
- end
- end
-
it 'throws an ArgumentError if a bogus order direction is given' do
lambda do
session.search Post do
View
22 spec/integration/local_search_spec.rb
@@ -17,38 +17,48 @@
end
it 'should find all the posts within a given radius' do
- search = Sunspot.search(Post) { |query| query.near(ORIGIN, 20) }
+ search = Sunspot.search(Post) { |query| query.near(ORIGIN, :distance => 20) }
search.results.to_set.should == @posts[0..2].to_set
end
it 'should perform a radial search with fulltext matching' do
search = Sunspot.search(Post) do |query|
query.keywords 'teacup'
- query.near(ORIGIN, 20)
+ query.near(ORIGIN, :distance => 20)
end
search.results.should == [@posts[1]]
end
+ it 'should use dismax for fulltext matching in local search' do
+ lambda do
+ search = Sunspot.new_search(Post)
+ search.build do |query|
+ query.keywords 'teacup['
+ query.near(ORIGIN, :distance => 20)
+ end
+ search.execute
+ end.should_not raise_error
+ end
+
it 'should perform a radial search with attribute scoping' do
search = Sunspot.search(Post) do |query|
- query.near(ORIGIN,20)
+ query.near(ORIGIN, :distance => 20)
query.with(:title, 'teacup')
end
search.results.should == [@posts[1]]
end
it 'should order by arbitrary field' do
search = Sunspot.search(Post) do |query|
- query.near(ORIGIN, 20)
+ query.near(ORIGIN, :distance => 20)
query.order_by(:blog_id)
end
search.results.should == @posts[0..2].reverse
end
it 'should order by geo distance' do
search = Sunspot.search(Post) do |query|
- query.near(ORIGIN, 20)
- query.order_by(:distance)
+ query.near(ORIGIN, :distance => 20, :sort => true)
end
search.results.should == @posts[0..2]
end

0 comments on commit 2a92f1d

Please sign in to comment.
Something went wrong with that request. Please try again.