You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by mi...@apache.org on 2015/09/02 15:06:22 UTC
svn commit: r1700800 [16/24] - in /lucene/dev/branches/lucene6699: ./
dev-tools/ dev-tools/eclipse/ dev-tools/idea/.idea/ dev-tools/scripts/
lucene/ lucene/analysis/ lucene/analysis/common/
lucene/analysis/common/src/java/org/apache/lucene/analysis/ar/...
Modified: lucene/dev/branches/lucene6699/solr/server/solr/configsets/basic_configs/conf/schema.xml
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6699/solr/server/solr/configsets/basic_configs/conf/schema.xml?rev=1700800&r1=1700799&r2=1700800&view=diff
==============================================================================
--- lucene/dev/branches/lucene6699/solr/server/solr/configsets/basic_configs/conf/schema.xml (original)
+++ lucene/dev/branches/lucene6699/solr/server/solr/configsets/basic_configs/conf/schema.xml Wed Sep 2 13:06:13 2015
@@ -194,8 +194,8 @@
<!-- sortMissingLast and sortMissingFirst attributes are optional attributes are
currently supported on types that are sorted internally as strings
and on numeric types.
- This includes "string","boolean", and, as of 3.5 (and 4.x),
- int, float, long, date, double, including the "Trie" variants.
+ This includes "string","boolean", and, as of 3.5 (and 4.x),
+ int, float, long, date, double, including the "Trie" variants.
- 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).
@@ -306,9 +306,9 @@
<!-- A general text field that has reasonable, generic
cross-language defaults: it tokenizes with StandardTokenizer,
- removes stop words from case-insensitive "stopwords.txt"
- (empty by default), and down cases. At query time only, it
- also applies synonyms. -->
+ removes stop words from case-insensitive "stopwords.txt"
+ (empty by default), and down cases. At query time only, it
+ also applies synonyms. -->
<fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="solr.StandardTokenizerFactory"/>
@@ -344,11 +344,11 @@
words="lang/stopwords_en.txt"
/>
<filter class="solr.LowerCaseFilterFactory"/>
- <filter class="solr.EnglishPossessiveFilterFactory"/>
+ <filter class="solr.EnglishPossessiveFilterFactory"/>
<filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
- <!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
+ <!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
<filter class="solr.EnglishMinimalStemFilterFactory"/>
- -->
+ -->
<filter class="solr.PorterStemFilterFactory"/>
</analyzer>
<analyzer type="query">
@@ -359,23 +359,23 @@
words="lang/stopwords_en.txt"
/>
<filter class="solr.LowerCaseFilterFactory"/>
- <filter class="solr.EnglishPossessiveFilterFactory"/>
+ <filter class="solr.EnglishPossessiveFilterFactory"/>
<filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
- <!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
+ <!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
<filter class="solr.EnglishMinimalStemFilterFactory"/>
- -->
+ -->
<filter class="solr.PorterStemFilterFactory"/>
</analyzer>
</fieldType>
<!-- A text field with defaults appropriate for English, plus
- aggressive word-splitting and autophrase features enabled.
- This field is just like text_en, except it adds
- WordDelimiterFilter to enable splitting and matching of
- words on case-change, alpha numeric boundaries, and
- non-alphanumeric chars. This means certain compound word
- cases will work, for example query "wi fi" will match
- document "WiFi" or "wi-fi".
+ aggressive word-splitting and autophrase features enabled.
+ This field is just like text_en, except it adds
+ WordDelimiterFilter to enable splitting and matching of
+ words on case-change, alpha numeric boundaries, and
+ non-alphanumeric chars. This means certain compound word
+ cases will work, for example query "wi fi" will match
+ document "WiFi" or "wi-fi".
-->
<fieldType name="text_en_splitting" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="true">
<analyzer type="index">
@@ -426,7 +426,7 @@
</fieldType>
<!-- Just like text_general except it reverses the characters of
- each token, to enable more efficient leading wildcard queries. -->
+ each token, to enable more efficient leading wildcard queries. -->
<fieldType name="text_general_rev" class="solr.TextField" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="solr.StandardTokenizerFactory"/>
Modified: lucene/dev/branches/lucene6699/solr/server/solr/configsets/data_driven_schema_configs/conf/solrconfig.xml
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6699/solr/server/solr/configsets/data_driven_schema_configs/conf/solrconfig.xml?rev=1700800&r1=1700799&r2=1700800&view=diff
==============================================================================
--- lucene/dev/branches/lucene6699/solr/server/solr/configsets/data_driven_schema_configs/conf/solrconfig.xml (original)
+++ lucene/dev/branches/lucene6699/solr/server/solr/configsets/data_driven_schema_configs/conf/solrconfig.xml Wed Sep 2 13:06:13 2015
@@ -1094,7 +1094,7 @@
<!-- maximum threshold of documents a query term can appear to be considered for correction -->
<float name="maxQueryFrequency">0.01</float>
<!-- uncomment this to require suggestions to occur in 1% of the documents
- <float name="thresholdTokenFrequency">.01</float>
+ <float name="thresholdTokenFrequency">.01</float>
-->
</lst>
@@ -1542,6 +1542,8 @@
-->
<queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy">
<str name="template.base.dir">${velocity.template.base.dir:}</str>
+ <str name="solr.resource.loader.enabled">${velocity.solr.resource.loader.enabled:true}</str>
+ <str name="params.resource.loader.enabled">${velocity.params.resource.loader.enabled:false}</str>
</queryResponseWriter>
<!-- XSLT response writer transforms the XML output by any xslt file found
Modified: lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/schema.xml
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/schema.xml?rev=1700800&r1=1700799&r2=1700800&view=diff
==============================================================================
--- lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/schema.xml (original)
+++ lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/schema.xml Wed Sep 2 13:06:13 2015
@@ -298,12 +298,12 @@
<!-- Create a string version of author for faceting -->
<copyField source="author" dest="author_s"/>
-
+
<!-- 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. -->
-
+ 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 -->
@@ -330,8 +330,8 @@
<!-- sortMissingLast and sortMissingFirst attributes are optional attributes are
currently supported on types that are sorted internally as strings
and on numeric types.
- This includes "string","boolean", and, as of 3.5 (and 4.x),
- int, float, long, date, double, including the "Trie" variants.
+ This includes "string","boolean", and, as of 3.5 (and 4.x),
+ int, float, long, date, double, including the "Trie" variants.
- 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).
@@ -451,9 +451,9 @@
<!-- A general text field that has reasonable, generic
cross-language defaults: it tokenizes with StandardTokenizer,
- removes stop words from case-insensitive "stopwords.txt"
- (empty by default), and down cases. At query time only, it
- also applies synonyms. -->
+ removes stop words from case-insensitive "stopwords.txt"
+ (empty by default), and down cases. At query time only, it
+ also applies synonyms. -->
<fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="solr.StandardTokenizerFactory"/>
@@ -489,11 +489,11 @@
words="lang/stopwords_en.txt"
/>
<filter class="solr.LowerCaseFilterFactory"/>
- <filter class="solr.EnglishPossessiveFilterFactory"/>
+ <filter class="solr.EnglishPossessiveFilterFactory"/>
<filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
- <!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
+ <!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
<filter class="solr.EnglishMinimalStemFilterFactory"/>
- -->
+ -->
<filter class="solr.PorterStemFilterFactory"/>
</analyzer>
<analyzer type="query">
@@ -504,23 +504,23 @@
words="lang/stopwords_en.txt"
/>
<filter class="solr.LowerCaseFilterFactory"/>
- <filter class="solr.EnglishPossessiveFilterFactory"/>
+ <filter class="solr.EnglishPossessiveFilterFactory"/>
<filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
- <!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
+ <!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
<filter class="solr.EnglishMinimalStemFilterFactory"/>
- -->
+ -->
<filter class="solr.PorterStemFilterFactory"/>
</analyzer>
</fieldType>
<!-- A text field with defaults appropriate for English, plus
- aggressive word-splitting and autophrase features enabled.
- This field is just like text_en, except it adds
- WordDelimiterFilter to enable splitting and matching of
- words on case-change, alpha numeric boundaries, and
- non-alphanumeric chars. This means certain compound word
- cases will work, for example query "wi fi" will match
- document "WiFi" or "wi-fi".
+ aggressive word-splitting and autophrase features enabled.
+ This field is just like text_en, except it adds
+ WordDelimiterFilter to enable splitting and matching of
+ words on case-change, alpha numeric boundaries, and
+ non-alphanumeric chars. This means certain compound word
+ cases will work, for example query "wi fi" will match
+ document "WiFi" or "wi-fi".
-->
<fieldType name="text_en_splitting" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="true">
<analyzer type="index">
@@ -571,7 +571,7 @@
</fieldType>
<!-- Just like text_general except it reverses the characters of
- each token, to enable more efficient leading wildcard queries. -->
+ each token, to enable more efficient leading wildcard queries. -->
<fieldType name="text_general_rev" class="solr.TextField" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="solr.StandardTokenizerFactory"/>
@@ -646,10 +646,10 @@
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
+ "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"/>
@@ -670,10 +670,10 @@
-->
<fieldType name="descendent_path" class="solr.TextField">
<analyzer type="index">
- <tokenizer class="solr.PathHierarchyTokenizerFactory" delimiter="/" />
+ <tokenizer class="solr.PathHierarchyTokenizerFactory" delimiter="/" />
</analyzer>
<analyzer type="query">
- <tokenizer class="solr.KeywordTokenizerFactory" />
+ <tokenizer class="solr.KeywordTokenizerFactory" />
</analyzer>
</fieldType>
<!--
@@ -682,10 +682,10 @@
-->
<fieldType name="ancestor_path" class="solr.TextField">
<analyzer type="index">
- <tokenizer class="solr.KeywordTokenizerFactory" />
+ <tokenizer class="solr.KeywordTokenizerFactory" />
</analyzer>
<analyzer type="query">
- <tokenizer class="solr.PathHierarchyTokenizerFactory" delimiter="/" />
+ <tokenizer class="solr.PathHierarchyTokenizerFactory" delimiter="/" />
</analyzer>
</fieldType>
Modified: lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml?rev=1700800&r1=1700799&r2=1700800&view=diff
==============================================================================
--- lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml (original)
+++ lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml Wed Sep 2 13:06:13 2015
@@ -1212,7 +1212,7 @@
<!-- maximum threshold of documents a query term can appear to be considered for correction -->
<float name="maxQueryFrequency">0.01</float>
<!-- uncomment this to require suggestions to occur in 1% of the documents
- <float name="thresholdTokenFrequency">.01</float>
+ <float name="thresholdTokenFrequency">.01</float>
-->
</lst>
@@ -1360,64 +1360,54 @@
You'll need to set the solr.clustering.enabled system property
when running solr to run with clustering enabled:
+ -Dsolr.clustering.enabled=true
- java -Dsolr.clustering.enabled=true -jar start.jar
-
- http://wiki.apache.org/solr/ClusteringComponent
- http://carrot2.github.io/solr-integration-strategies/
+ https://cwiki.apache.org/confluence/display/solr/Result+Clustering
-->
<searchComponent name="clustering"
enable="${solr.clustering.enabled:false}"
class="solr.clustering.ClusteringComponent" >
- <lst name="engine">
- <str name="name">lingo</str>
-
- <!-- Class name of a clustering algorithm compatible with the Carrot2 framework.
-
- Currently available open source algorithms are:
- * org.carrot2.clustering.lingo.LingoClusteringAlgorithm
- * org.carrot2.clustering.stc.STCClusteringAlgorithm
- * org.carrot2.clustering.kmeans.BisectingKMeansClusteringAlgorithm
-
- See http://project.carrot2.org/algorithms.html for more information.
-
- A commercial algorithm Lingo3G (needs to be installed separately) is defined as:
- * com.carrotsearch.lingo3g.Lingo3GClusteringAlgorithm
- -->
- <str name="carrot.algorithm">org.carrot2.clustering.lingo.LingoClusteringAlgorithm</str>
+ <!--
+ Declaration of "engines" (clustering algorithms).
- <!-- Override location of the clustering algorithm's resources
- (attribute definitions and lexical resources).
+ The open source algorithms from Carrot2.org project:
+ * org.carrot2.clustering.lingo.LingoClusteringAlgorithm
+ * org.carrot2.clustering.stc.STCClusteringAlgorithm
+ * org.carrot2.clustering.kmeans.BisectingKMeansClusteringAlgorithm
+ See http://project.carrot2.org/algorithms.html for more information.
- A directory from which to load algorithm-specific stop words,
- stop labels and attribute definition XMLs.
+ Commercial algorithm Lingo3G (needs to be installed separately):
+ * com.carrotsearch.lingo3g.Lingo3GClusteringAlgorithm
+ -->
- For an overview of Carrot2 lexical resources, see:
- http://download.carrot2.org/head/manual/#chapter.lexical-resources
+ <lst name="engine">
+ <str name="name">lingo3g</str>
+ <bool name="optional">true</bool>
+ <str name="carrot.algorithm">com.carrotsearch.lingo3g.Lingo3GClusteringAlgorithm</str>
+ <str name="carrot.resourcesDir">clustering/carrot2</str>
+ </lst>
- For an overview of Lingo3G lexical resources, see:
- http://download.carrotsearch.com/lingo3g/manual/#chapter.lexical-resources
- -->
+ <lst name="engine">
+ <str name="name">lingo</str>
+ <str name="carrot.algorithm">org.carrot2.clustering.lingo.LingoClusteringAlgorithm</str>
<str name="carrot.resourcesDir">clustering/carrot2</str>
</lst>
- <!-- An example definition for the STC clustering algorithm. -->
<lst name="engine">
<str name="name">stc</str>
<str name="carrot.algorithm">org.carrot2.clustering.stc.STCClusteringAlgorithm</str>
+ <str name="carrot.resourcesDir">clustering/carrot2</str>
</lst>
- <!-- An example definition for the bisecting kmeans clustering algorithm. -->
<lst name="engine">
<str name="name">kmeans</str>
<str name="carrot.algorithm">org.carrot2.clustering.kmeans.BisectingKMeansClusteringAlgorithm</str>
+ <str name="carrot.resourcesDir">clustering/carrot2</str>
</lst>
</searchComponent>
- <!-- A request handler for demonstrating the clustering component
-
- This is purely as an example.
-
+ <!-- A request handler for demonstrating the clustering component.
+ This is meant as an example.
In reality you will likely want to add the component to your
already specified request handlers.
-->
@@ -1447,14 +1437,14 @@
text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 manu^1.1 cat^1.4
</str>
<str name="q.alt">*:*</str>
- <str name="rows">10</str>
+ <str name="rows">100</str>
<str name="fl">*,score</str>
</lst>
<arr name="last-components">
<str>clustering</str>
</arr>
</requestHandler>
-
+
<!-- Terms Component
http://wiki.apache.org/solr/TermsComponent
Modified: lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/velocity/facet_ranges.vm
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/velocity/facet_ranges.vm?rev=1700800&r1=1700799&r2=1700800&view=diff
==============================================================================
--- lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/velocity/facet_ranges.vm (original)
+++ lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/velocity/facet_ranges.vm Wed Sep 2 13:06:13 2015
@@ -10,14 +10,14 @@
#foreach ($field in $response.response.facet_counts.facet_ranges)
## Hide facets without value
#if($field.value.counts.size() > 0)
- #set($name = $field.key)
- #set($display = $name)
- #set($f = $field.value.counts)
- #set($start = $field.value.start)
- #set($end = $field.value.end)
- #set($gap = $field.value.gap)
- #set($before = $field.value.before)
- #set($after = $field.value.after)
- #display_facet_range($f, $display, $name, $start, $end, $gap, $before, $after)
+ #set($name = $field.key)
+ #set($display = $name)
+ #set($f = $field.value.counts)
+ #set($start = $field.value.start)
+ #set($end = $field.value.end)
+ #set($gap = $field.value.gap)
+ #set($before = $field.value.before)
+ #set($after = $field.value.after)
+ #display_facet_range($f, $display, $name, $start, $end, $gap, $before, $after)
#end ## end if has any values
#end ## end for each facet range
Modified: lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/velocity/jquery.autocomplete.css
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/velocity/jquery.autocomplete.css?rev=1700800&r1=1700799&r2=1700800&view=diff
==============================================================================
--- lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/velocity/jquery.autocomplete.css (original)
+++ lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/velocity/jquery.autocomplete.css Wed Sep 2 13:06:13 2015
@@ -1,48 +1,48 @@
.ac_results {
- padding: 0px;
- border: 1px solid black;
- background-color: white;
- overflow: hidden;
- z-index: 99999;
+ padding: 0px;
+ border: 1px solid black;
+ background-color: white;
+ overflow: hidden;
+ z-index: 99999;
}
.ac_results ul {
- width: 100%;
- list-style-position: outside;
- list-style: none;
- padding: 0;
- margin: 0;
+ width: 100%;
+ list-style-position: outside;
+ list-style: none;
+ padding: 0;
+ margin: 0;
}
.ac_results li {
- margin: 0px;
- padding: 2px 5px;
- cursor: default;
- display: block;
- /*
- if width will be 100% horizontal scrollbar will apear
- when scroll mode will be used
- */
- /*width: 100%;*/
- font: menu;
- font-size: 12px;
- /*
- it is very important, if line-height not setted or setted
- in relative units scroll will be broken in firefox
- */
- line-height: 16px;
- overflow: hidden;
+ margin: 0px;
+ padding: 2px 5px;
+ cursor: default;
+ display: block;
+ /*
+ if width will be 100% horizontal scrollbar will apear
+ when scroll mode will be used
+ */
+ /*width: 100%;*/
+ font: menu;
+ font-size: 12px;
+ /*
+ it is very important, if line-height not setted or setted
+ in relative units scroll will be broken in firefox
+ */
+ line-height: 16px;
+ overflow: hidden;
}
.ac_loading {
- background: white url('indicator.gif') right center no-repeat;
+ background: white url('indicator.gif') right center no-repeat;
}
.ac_odd {
- background-color: #eee;
+ background-color: #eee;
}
.ac_over {
- background-color: #0A246A;
- color: white;
+ background-color: #0A246A;
+ color: white;
}
Modified: lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/velocity/jquery.autocomplete.js
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/velocity/jquery.autocomplete.js?rev=1700800&r1=1700799&r2=1700800&view=diff
==============================================================================
--- lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/velocity/jquery.autocomplete.js (original)
+++ lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/velocity/jquery.autocomplete.js Wed Sep 2 13:06:13 2015
@@ -12,752 +12,752 @@
*/
;(function($) {
-
+
$.fn.extend({
- autocomplete: function(urlOrData, options) {
- var isUrl = typeof urlOrData == "string";
- options = $.extend({}, $.Autocompleter.defaults, {
- url: isUrl ? urlOrData : null,
- data: isUrl ? null : urlOrData,
- delay: isUrl ? $.Autocompleter.defaults.delay : 10,
- max: options && !options.scroll ? 10 : 150
- }, options);
-
- // if highlight is set to false, replace it with a do-nothing function
- options.highlight = options.highlight || function(value) { return value; };
-
- // if the formatMatch option is not specified, then use formatItem for backwards compatibility
- options.formatMatch = options.formatMatch || options.formatItem;
-
- return this.each(function() {
- new $.Autocompleter(this, options);
- });
- },
- result: function(handler) {
- return this.bind("result", handler);
- },
- search: function(handler) {
- return this.trigger("search", [handler]);
- },
- flushCache: function() {
- return this.trigger("flushCache");
- },
- setOptions: function(options){
- return this.trigger("setOptions", [options]);
- },
- unautocomplete: function() {
- return this.trigger("unautocomplete");
- }
+ autocomplete: function(urlOrData, options) {
+ var isUrl = typeof urlOrData == "string";
+ options = $.extend({}, $.Autocompleter.defaults, {
+ url: isUrl ? urlOrData : null,
+ data: isUrl ? null : urlOrData,
+ delay: isUrl ? $.Autocompleter.defaults.delay : 10,
+ max: options && !options.scroll ? 10 : 150
+ }, options);
+
+ // if highlight is set to false, replace it with a do-nothing function
+ options.highlight = options.highlight || function(value) { return value; };
+
+ // if the formatMatch option is not specified, then use formatItem for backwards compatibility
+ options.formatMatch = options.formatMatch || options.formatItem;
+
+ return this.each(function() {
+ new $.Autocompleter(this, options);
+ });
+ },
+ result: function(handler) {
+ return this.bind("result", handler);
+ },
+ search: function(handler) {
+ return this.trigger("search", [handler]);
+ },
+ flushCache: function() {
+ return this.trigger("flushCache");
+ },
+ setOptions: function(options){
+ return this.trigger("setOptions", [options]);
+ },
+ unautocomplete: function() {
+ return this.trigger("unautocomplete");
+ }
});
$.Autocompleter = function(input, options) {
- var KEY = {
- UP: 38,
- DOWN: 40,
- DEL: 46,
- TAB: 9,
- RETURN: 13,
- ESC: 27,
- COMMA: 188,
- PAGEUP: 33,
- PAGEDOWN: 34,
- BACKSPACE: 8
- };
-
- // Create $ object for input element
- var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
-
- var timeout;
- var previousValue = "";
- var cache = $.Autocompleter.Cache(options);
- var hasFocus = 0;
- var lastKeyPressCode;
- var config = {
- mouseDownOnSelect: false
- };
- var select = $.Autocompleter.Select(options, input, selectCurrent, config);
-
- var blockSubmit;
-
- // prevent form submit in opera when selecting with return key
- $.browser.opera && $(input.form).bind("submit.autocomplete", function() {
- if (blockSubmit) {
- blockSubmit = false;
- return false;
- }
- });
-
- // only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all
- $input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) {
- // track last key pressed
- lastKeyPressCode = event.keyCode;
- switch(event.keyCode) {
-
- case KEY.UP:
- event.preventDefault();
- if ( select.visible() ) {
- select.prev();
- } else {
- onChange(0, true);
- }
- break;
-
- case KEY.DOWN:
- event.preventDefault();
- if ( select.visible() ) {
- select.next();
- } else {
- onChange(0, true);
- }
- break;
-
- case KEY.PAGEUP:
- event.preventDefault();
- if ( select.visible() ) {
- select.pageUp();
- } else {
- onChange(0, true);
- }
- break;
-
- case KEY.PAGEDOWN:
- event.preventDefault();
- if ( select.visible() ) {
- select.pageDown();
- } else {
- onChange(0, true);
- }
- break;
-
- // matches also semicolon
- case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
- case KEY.TAB:
- case KEY.RETURN:
- if( selectCurrent() ) {
- // stop default to prevent a form submit, Opera needs special handling
- event.preventDefault();
- blockSubmit = true;
- return false;
- }
- break;
-
- case KEY.ESC:
- select.hide();
- break;
-
- default:
- clearTimeout(timeout);
- timeout = setTimeout(onChange, options.delay);
- break;
- }
- }).focus(function(){
- // track whether the field has focus, we shouldn't process any
- // results if the field no longer has focus
- hasFocus++;
- }).blur(function() {
- hasFocus = 0;
- if (!config.mouseDownOnSelect) {
- hideResults();
- }
- }).click(function() {
- // show select when clicking in a focused field
- if ( hasFocus++ > 1 && !select.visible() ) {
- onChange(0, true);
- }
- }).bind("search", function() {
- // TODO why not just specifying both arguments?
- var fn = (arguments.length > 1) ? arguments[1] : null;
- function findValueCallback(q, data) {
- var result;
- if( data && data.length ) {
- for (var i=0; i < data.length; i++) {
- if( data[i].result.toLowerCase() == q.toLowerCase() ) {
- result = data[i];
- break;
- }
- }
- }
- if( typeof fn == "function" ) fn(result);
- else $input.trigger("result", result && [result.data, result.value]);
- }
- $.each(trimWords($input.val()), function(i, value) {
- request(value, findValueCallback, findValueCallback);
- });
- }).bind("flushCache", function() {
- cache.flush();
- }).bind("setOptions", function() {
- $.extend(options, arguments[1]);
- // if we've updated the data, repopulate
- if ( "data" in arguments[1] )
- cache.populate();
- }).bind("unautocomplete", function() {
- select.unbind();
- $input.unbind();
- $(input.form).unbind(".autocomplete");
- });
-
-
- function selectCurrent() {
- var selected = select.selected();
- if( !selected )
- return false;
-
- var v = selected.result;
- previousValue = v;
-
- if ( options.multiple ) {
- var words = trimWords($input.val());
- if ( words.length > 1 ) {
- v = words.slice(0, words.length - 1).join( options.multipleSeparator ) + options.multipleSeparator + v;
- }
- v += options.multipleSeparator;
- }
-
- $input.val(v);
- hideResultsNow();
- $input.trigger("result", [selected.data, selected.value]);
- return true;
- }
-
- function onChange(crap, skipPrevCheck) {
- if( lastKeyPressCode == KEY.DEL ) {
- select.hide();
- return;
- }
-
- var currentValue = $input.val();
-
- if ( !skipPrevCheck && currentValue == previousValue )
- return;
-
- previousValue = currentValue;
-
- currentValue = lastWord(currentValue);
- if ( currentValue.length >= options.minChars) {
- $input.addClass(options.loadingClass);
- if (!options.matchCase)
- currentValue = currentValue.toLowerCase();
- request(currentValue, receiveData, hideResultsNow);
- } else {
- stopLoading();
- select.hide();
- }
- };
-
- function trimWords(value) {
- if ( !value ) {
- return [""];
- }
- var words = value.split( options.multipleSeparator );
- var result = [];
- $.each(words, function(i, value) {
- if ( $.trim(value) )
- result[i] = $.trim(value);
- });
- return result;
- }
-
- function lastWord(value) {
- if ( !options.multiple )
- return value;
- var words = trimWords(value);
- return words[words.length - 1];
- }
-
- // fills in the input box w/the first match (assumed to be the best match)
- // q: the term entered
- // sValue: the first matching result
- function autoFill(q, sValue){
- // autofill in the complete box w/the first match as long as the user hasn't entered in more data
- // if the last user key pressed was backspace, don't autofill
- if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
- // fill in the value (keep the case the user has typed)
- $input.val($input.val() + sValue.substring(lastWord(previousValue).length));
- // select the portion of the value not typed by the user (so the next character will erase)
- $.Autocompleter.Selection(input, previousValue.length, previousValue.length + sValue.length);
- }
- };
-
- function hideResults() {
- clearTimeout(timeout);
- timeout = setTimeout(hideResultsNow, 200);
- };
-
- function hideResultsNow() {
- var wasVisible = select.visible();
- select.hide();
- clearTimeout(timeout);
- stopLoading();
- if (options.mustMatch) {
- // call search and run callback
- $input.search(
- function (result){
- // if no value found, clear the input box
- if( !result ) {
- if (options.multiple) {
- var words = trimWords($input.val()).slice(0, -1);
- $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
- }
- else
- $input.val( "" );
- }
- }
- );
- }
- if (wasVisible)
- // position cursor at end of input field
- $.Autocompleter.Selection(input, input.value.length, input.value.length);
- };
-
- function receiveData(q, data) {
- if ( data && data.length && hasFocus ) {
- stopLoading();
- select.display(data, q);
- autoFill(q, data[0].value);
- select.show();
- } else {
- hideResultsNow();
- }
- };
-
- function request(term, success, failure) {
- if (!options.matchCase)
- term = term.toLowerCase();
- var data = cache.load(term);
- data = null; // Avoid buggy cache and go to Solr every time
- // recieve the cached data
- if (data && data.length) {
- success(term, data);
- // if an AJAX url has been supplied, try loading the data now
- } else if( (typeof options.url == "string") && (options.url.length > 0) ){
-
- var extraParams = {
- timestamp: +new Date()
- };
- $.each(options.extraParams, function(key, param) {
- extraParams[key] = typeof param == "function" ? param() : param;
- });
-
- $.ajax({
- // try to leverage ajaxQueue plugin to abort previous requests
- mode: "abort",
- // limit abortion to this input
- port: "autocomplete" + input.name,
- dataType: options.dataType,
- url: options.url,
- data: $.extend({
- q: lastWord(term),
- limit: options.max
- }, extraParams),
- success: function(data) {
- var parsed = options.parse && options.parse(data) || parse(data);
- cache.add(term, parsed);
- success(term, parsed);
- }
- });
- } else {
- // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
- select.emptyList();
- failure(term);
- }
- };
-
- function parse(data) {
- var parsed = [];
- var rows = data.split("\n");
- for (var i=0; i < rows.length; i++) {
- var row = $.trim(rows[i]);
- if (row) {
- row = row.split("|");
- parsed[parsed.length] = {
- data: row,
- value: row[0],
- result: options.formatResult && options.formatResult(row, row[0]) || row[0]
- };
- }
- }
- return parsed;
- };
-
- function stopLoading() {
- $input.removeClass(options.loadingClass);
- };
+ var KEY = {
+ UP: 38,
+ DOWN: 40,
+ DEL: 46,
+ TAB: 9,
+ RETURN: 13,
+ ESC: 27,
+ COMMA: 188,
+ PAGEUP: 33,
+ PAGEDOWN: 34,
+ BACKSPACE: 8
+ };
+
+ // Create $ object for input element
+ var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
+
+ var timeout;
+ var previousValue = "";
+ var cache = $.Autocompleter.Cache(options);
+ var hasFocus = 0;
+ var lastKeyPressCode;
+ var config = {
+ mouseDownOnSelect: false
+ };
+ var select = $.Autocompleter.Select(options, input, selectCurrent, config);
+
+ var blockSubmit;
+
+ // prevent form submit in opera when selecting with return key
+ $.browser.opera && $(input.form).bind("submit.autocomplete", function() {
+ if (blockSubmit) {
+ blockSubmit = false;
+ return false;
+ }
+ });
+
+ // only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all
+ $input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) {
+ // track last key pressed
+ lastKeyPressCode = event.keyCode;
+ switch(event.keyCode) {
+
+ case KEY.UP:
+ event.preventDefault();
+ if ( select.visible() ) {
+ select.prev();
+ } else {
+ onChange(0, true);
+ }
+ break;
+
+ case KEY.DOWN:
+ event.preventDefault();
+ if ( select.visible() ) {
+ select.next();
+ } else {
+ onChange(0, true);
+ }
+ break;
+
+ case KEY.PAGEUP:
+ event.preventDefault();
+ if ( select.visible() ) {
+ select.pageUp();
+ } else {
+ onChange(0, true);
+ }
+ break;
+
+ case KEY.PAGEDOWN:
+ event.preventDefault();
+ if ( select.visible() ) {
+ select.pageDown();
+ } else {
+ onChange(0, true);
+ }
+ break;
+
+ // matches also semicolon
+ case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
+ case KEY.TAB:
+ case KEY.RETURN:
+ if( selectCurrent() ) {
+ // stop default to prevent a form submit, Opera needs special handling
+ event.preventDefault();
+ blockSubmit = true;
+ return false;
+ }
+ break;
+
+ case KEY.ESC:
+ select.hide();
+ break;
+
+ default:
+ clearTimeout(timeout);
+ timeout = setTimeout(onChange, options.delay);
+ break;
+ }
+ }).focus(function(){
+ // track whether the field has focus, we shouldn't process any
+ // results if the field no longer has focus
+ hasFocus++;
+ }).blur(function() {
+ hasFocus = 0;
+ if (!config.mouseDownOnSelect) {
+ hideResults();
+ }
+ }).click(function() {
+ // show select when clicking in a focused field
+ if ( hasFocus++ > 1 && !select.visible() ) {
+ onChange(0, true);
+ }
+ }).bind("search", function() {
+ // TODO why not just specifying both arguments?
+ var fn = (arguments.length > 1) ? arguments[1] : null;
+ function findValueCallback(q, data) {
+ var result;
+ if( data && data.length ) {
+ for (var i=0; i < data.length; i++) {
+ if( data[i].result.toLowerCase() == q.toLowerCase() ) {
+ result = data[i];
+ break;
+ }
+ }
+ }
+ if( typeof fn == "function" ) fn(result);
+ else $input.trigger("result", result && [result.data, result.value]);
+ }
+ $.each(trimWords($input.val()), function(i, value) {
+ request(value, findValueCallback, findValueCallback);
+ });
+ }).bind("flushCache", function() {
+ cache.flush();
+ }).bind("setOptions", function() {
+ $.extend(options, arguments[1]);
+ // if we've updated the data, repopulate
+ if ( "data" in arguments[1] )
+ cache.populate();
+ }).bind("unautocomplete", function() {
+ select.unbind();
+ $input.unbind();
+ $(input.form).unbind(".autocomplete");
+ });
+
+
+ function selectCurrent() {
+ var selected = select.selected();
+ if( !selected )
+ return false;
+
+ var v = selected.result;
+ previousValue = v;
+
+ if ( options.multiple ) {
+ var words = trimWords($input.val());
+ if ( words.length > 1 ) {
+ v = words.slice(0, words.length - 1).join( options.multipleSeparator ) + options.multipleSeparator + v;
+ }
+ v += options.multipleSeparator;
+ }
+
+ $input.val(v);
+ hideResultsNow();
+ $input.trigger("result", [selected.data, selected.value]);
+ return true;
+ }
+
+ function onChange(crap, skipPrevCheck) {
+ if( lastKeyPressCode == KEY.DEL ) {
+ select.hide();
+ return;
+ }
+
+ var currentValue = $input.val();
+
+ if ( !skipPrevCheck && currentValue == previousValue )
+ return;
+
+ previousValue = currentValue;
+
+ currentValue = lastWord(currentValue);
+ if ( currentValue.length >= options.minChars) {
+ $input.addClass(options.loadingClass);
+ if (!options.matchCase)
+ currentValue = currentValue.toLowerCase();
+ request(currentValue, receiveData, hideResultsNow);
+ } else {
+ stopLoading();
+ select.hide();
+ }
+ };
+
+ function trimWords(value) {
+ if ( !value ) {
+ return [""];
+ }
+ var words = value.split( options.multipleSeparator );
+ var result = [];
+ $.each(words, function(i, value) {
+ if ( $.trim(value) )
+ result[i] = $.trim(value);
+ });
+ return result;
+ }
+
+ function lastWord(value) {
+ if ( !options.multiple )
+ return value;
+ var words = trimWords(value);
+ return words[words.length - 1];
+ }
+
+ // fills in the input box w/the first match (assumed to be the best match)
+ // q: the term entered
+ // sValue: the first matching result
+ function autoFill(q, sValue){
+ // autofill in the complete box w/the first match as long as the user hasn't entered in more data
+ // if the last user key pressed was backspace, don't autofill
+ if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
+ // fill in the value (keep the case the user has typed)
+ $input.val($input.val() + sValue.substring(lastWord(previousValue).length));
+ // select the portion of the value not typed by the user (so the next character will erase)
+ $.Autocompleter.Selection(input, previousValue.length, previousValue.length + sValue.length);
+ }
+ };
+
+ function hideResults() {
+ clearTimeout(timeout);
+ timeout = setTimeout(hideResultsNow, 200);
+ };
+
+ function hideResultsNow() {
+ var wasVisible = select.visible();
+ select.hide();
+ clearTimeout(timeout);
+ stopLoading();
+ if (options.mustMatch) {
+ // call search and run callback
+ $input.search(
+ function (result){
+ // if no value found, clear the input box
+ if( !result ) {
+ if (options.multiple) {
+ var words = trimWords($input.val()).slice(0, -1);
+ $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
+ }
+ else
+ $input.val( "" );
+ }
+ }
+ );
+ }
+ if (wasVisible)
+ // position cursor at end of input field
+ $.Autocompleter.Selection(input, input.value.length, input.value.length);
+ };
+
+ function receiveData(q, data) {
+ if ( data && data.length && hasFocus ) {
+ stopLoading();
+ select.display(data, q);
+ autoFill(q, data[0].value);
+ select.show();
+ } else {
+ hideResultsNow();
+ }
+ };
+
+ function request(term, success, failure) {
+ if (!options.matchCase)
+ term = term.toLowerCase();
+ var data = cache.load(term);
+ data = null; // Avoid buggy cache and go to Solr every time
+ // recieve the cached data
+ if (data && data.length) {
+ success(term, data);
+ // if an AJAX url has been supplied, try loading the data now
+ } else if( (typeof options.url == "string") && (options.url.length > 0) ){
+
+ var extraParams = {
+ timestamp: +new Date()
+ };
+ $.each(options.extraParams, function(key, param) {
+ extraParams[key] = typeof param == "function" ? param() : param;
+ });
+
+ $.ajax({
+ // try to leverage ajaxQueue plugin to abort previous requests
+ mode: "abort",
+ // limit abortion to this input
+ port: "autocomplete" + input.name,
+ dataType: options.dataType,
+ url: options.url,
+ data: $.extend({
+ q: lastWord(term),
+ limit: options.max
+ }, extraParams),
+ success: function(data) {
+ var parsed = options.parse && options.parse(data) || parse(data);
+ cache.add(term, parsed);
+ success(term, parsed);
+ }
+ });
+ } else {
+ // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
+ select.emptyList();
+ failure(term);
+ }
+ };
+
+ function parse(data) {
+ var parsed = [];
+ var rows = data.split("\n");
+ for (var i=0; i < rows.length; i++) {
+ var row = $.trim(rows[i]);
+ if (row) {
+ row = row.split("|");
+ parsed[parsed.length] = {
+ data: row,
+ value: row[0],
+ result: options.formatResult && options.formatResult(row, row[0]) || row[0]
+ };
+ }
+ }
+ return parsed;
+ };
+
+ function stopLoading() {
+ $input.removeClass(options.loadingClass);
+ };
};
$.Autocompleter.defaults = {
- inputClass: "ac_input",
- resultsClass: "ac_results",
- loadingClass: "ac_loading",
- minChars: 1,
- delay: 400,
- matchCase: false,
- matchSubset: true,
- matchContains: false,
- cacheLength: 10,
- max: 100,
- mustMatch: false,
- extraParams: {},
- selectFirst: false,
- formatItem: function(row) { return row[0]; },
- formatMatch: null,
- autoFill: false,
- width: 0,
- multiple: false,
- multipleSeparator: ", ",
- highlight: function(value, term) {
- return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
- },
+ inputClass: "ac_input",
+ resultsClass: "ac_results",
+ loadingClass: "ac_loading",
+ minChars: 1,
+ delay: 400,
+ matchCase: false,
+ matchSubset: true,
+ matchContains: false,
+ cacheLength: 10,
+ max: 100,
+ mustMatch: false,
+ extraParams: {},
+ selectFirst: false,
+ formatItem: function(row) { return row[0]; },
+ formatMatch: null,
+ autoFill: false,
+ width: 0,
+ multiple: false,
+ multipleSeparator: ", ",
+ highlight: function(value, term) {
+ return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
+ },
scroll: true,
scrollHeight: 180
};
$.Autocompleter.Cache = function(options) {
- var data = {};
- var length = 0;
-
- function matchSubset(s, sub) {
- if (!options.matchCase)
- s = s.toLowerCase();
- var i = s.indexOf(sub);
- if (options.matchContains == "word"){
- i = s.toLowerCase().search("\\b" + sub.toLowerCase());
- }
- if (i == -1) return false;
- return i == 0 || options.matchContains;
- };
-
- function add(q, value) {
- if (length > options.cacheLength){
- flush();
- }
- if (!data[q]){
- length++;
- }
- data[q] = value;
- }
-
- function populate(){
- if( !options.data ) return false;
- // track the matches
- var stMatchSets = {},
- nullData = 0;
-
- // no url was specified, we need to adjust the cache length to make sure it fits the local data store
- if( !options.url ) options.cacheLength = 1;
-
- // track all options for minChars = 0
- stMatchSets[""] = [];
-
- // loop through the array and create a lookup structure
- for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
- var rawValue = options.data[i];
- // if rawValue is a string, make an array otherwise just reference the array
- rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
-
- var value = options.formatMatch(rawValue, i+1, options.data.length);
- if ( value === false )
- continue;
-
- var firstChar = value.charAt(0).toLowerCase();
- // if no lookup array for this character exists, look it up now
- if( !stMatchSets[firstChar] )
- stMatchSets[firstChar] = [];
-
- // if the match is a string
- var row = {
- value: value,
- data: rawValue,
- result: options.formatResult && options.formatResult(rawValue) || value
- };
-
- // push the current match into the set list
- stMatchSets[firstChar].push(row);
-
- // keep track of minChars zero items
- if ( nullData++ < options.max ) {
- stMatchSets[""].push(row);
- }
- };
-
- // add the data items to the cache
- $.each(stMatchSets, function(i, value) {
- // increase the cache size
- options.cacheLength++;
- // add to the cache
- add(i, value);
- });
- }
-
- // populate any existing data
- setTimeout(populate, 25);
-
- function flush(){
- data = {};
- length = 0;
- }
-
- return {
- flush: flush,
- add: add,
- populate: populate,
- load: function(q) {
- if (!options.cacheLength || !length)
- return null;
- /*
- * if dealing w/local data and matchContains than we must make sure
- * to loop through all the data collections looking for matches
- */
- if( !options.url && options.matchContains ){
- // track all matches
- var csub = [];
- // loop through all the data grids for matches
- for( var k in data ){
- // don't search through the stMatchSets[""] (minChars: 0) cache
- // this prevents duplicates
- if( k.length > 0 ){
- var c = data[k];
- $.each(c, function(i, x) {
- // if we've got a match, add it to the array
- if (matchSubset(x.value, q)) {
- csub.push(x);
- }
- });
- }
- }
- return csub;
- } else
- // if the exact item exists, use it
- if (data[q]){
- return data[q];
- } else
- if (options.matchSubset) {
- for (var i = q.length - 1; i >= options.minChars; i--) {
- var c = data[q.substr(0, i)];
- if (c) {
- var csub = [];
- $.each(c, function(i, x) {
- if (matchSubset(x.value, q)) {
- csub[csub.length] = x;
- }
- });
- return csub;
- }
- }
- }
- return null;
- }
- };
+ var data = {};
+ var length = 0;
+
+ function matchSubset(s, sub) {
+ if (!options.matchCase)
+ s = s.toLowerCase();
+ var i = s.indexOf(sub);
+ if (options.matchContains == "word"){
+ i = s.toLowerCase().search("\\b" + sub.toLowerCase());
+ }
+ if (i == -1) return false;
+ return i == 0 || options.matchContains;
+ };
+
+ function add(q, value) {
+ if (length > options.cacheLength){
+ flush();
+ }
+ if (!data[q]){
+ length++;
+ }
+ data[q] = value;
+ }
+
+ function populate(){
+ if( !options.data ) return false;
+ // track the matches
+ var stMatchSets = {},
+ nullData = 0;
+
+ // no url was specified, we need to adjust the cache length to make sure it fits the local data store
+ if( !options.url ) options.cacheLength = 1;
+
+ // track all options for minChars = 0
+ stMatchSets[""] = [];
+
+ // loop through the array and create a lookup structure
+ for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
+ var rawValue = options.data[i];
+ // if rawValue is a string, make an array otherwise just reference the array
+ rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
+
+ var value = options.formatMatch(rawValue, i+1, options.data.length);
+ if ( value === false )
+ continue;
+
+ var firstChar = value.charAt(0).toLowerCase();
+ // if no lookup array for this character exists, look it up now
+ if( !stMatchSets[firstChar] )
+ stMatchSets[firstChar] = [];
+
+ // if the match is a string
+ var row = {
+ value: value,
+ data: rawValue,
+ result: options.formatResult && options.formatResult(rawValue) || value
+ };
+
+ // push the current match into the set list
+ stMatchSets[firstChar].push(row);
+
+ // keep track of minChars zero items
+ if ( nullData++ < options.max ) {
+ stMatchSets[""].push(row);
+ }
+ };
+
+ // add the data items to the cache
+ $.each(stMatchSets, function(i, value) {
+ // increase the cache size
+ options.cacheLength++;
+ // add to the cache
+ add(i, value);
+ });
+ }
+
+ // populate any existing data
+ setTimeout(populate, 25);
+
+ function flush(){
+ data = {};
+ length = 0;
+ }
+
+ return {
+ flush: flush,
+ add: add,
+ populate: populate,
+ load: function(q) {
+ if (!options.cacheLength || !length)
+ return null;
+ /*
+ * if dealing w/local data and matchContains than we must make sure
+ * to loop through all the data collections looking for matches
+ */
+ if( !options.url && options.matchContains ){
+ // track all matches
+ var csub = [];
+ // loop through all the data grids for matches
+ for( var k in data ){
+ // don't search through the stMatchSets[""] (minChars: 0) cache
+ // this prevents duplicates
+ if( k.length > 0 ){
+ var c = data[k];
+ $.each(c, function(i, x) {
+ // if we've got a match, add it to the array
+ if (matchSubset(x.value, q)) {
+ csub.push(x);
+ }
+ });
+ }
+ }
+ return csub;
+ } else
+ // if the exact item exists, use it
+ if (data[q]){
+ return data[q];
+ } else
+ if (options.matchSubset) {
+ for (var i = q.length - 1; i >= options.minChars; i--) {
+ var c = data[q.substr(0, i)];
+ if (c) {
+ var csub = [];
+ $.each(c, function(i, x) {
+ if (matchSubset(x.value, q)) {
+ csub[csub.length] = x;
+ }
+ });
+ return csub;
+ }
+ }
+ }
+ return null;
+ }
+ };
};
$.Autocompleter.Select = function (options, input, select, config) {
- var CLASSES = {
- ACTIVE: "ac_over"
- };
-
- var listItems,
- active = -1,
- data,
- term = "",
- needsInit = true,
- element,
- list;
-
- // Create results
- function init() {
- if (!needsInit)
- return;
- element = $("<div/>")
- .hide()
- .addClass(options.resultsClass)
- .css("position", "absolute")
- .appendTo(document.body);
-
- list = $("<ul/>").appendTo(element).mouseover( function(event) {
- if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
- active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
- $(target(event)).addClass(CLASSES.ACTIVE);
- }
- }).click(function(event) {
- $(target(event)).addClass(CLASSES.ACTIVE);
- select();
- // TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
- input.focus();
- return false;
- }).mousedown(function() {
- config.mouseDownOnSelect = true;
- }).mouseup(function() {
- config.mouseDownOnSelect = false;
- });
-
- if( options.width > 0 )
- element.css("width", options.width);
-
- needsInit = false;
- }
-
- function target(event) {
- var element = event.target;
- while(element && element.tagName != "LI")
- element = element.parentNode;
- // more fun with IE, sometimes event.target is empty, just ignore it then
- if(!element)
- return [];
- return element;
- }
-
- function moveSelect(step) {
- listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
- movePosition(step);
+ var CLASSES = {
+ ACTIVE: "ac_over"
+ };
+
+ var listItems,
+ active = -1,
+ data,
+ term = "",
+ needsInit = true,
+ element,
+ list;
+
+ // Create results
+ function init() {
+ if (!needsInit)
+ return;
+ element = $("<div/>")
+ .hide()
+ .addClass(options.resultsClass)
+ .css("position", "absolute")
+ .appendTo(document.body);
+
+ list = $("<ul/>").appendTo(element).mouseover( function(event) {
+ if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
+ active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
+ $(target(event)).addClass(CLASSES.ACTIVE);
+ }
+ }).click(function(event) {
+ $(target(event)).addClass(CLASSES.ACTIVE);
+ select();
+ // TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
+ input.focus();
+ return false;
+ }).mousedown(function() {
+ config.mouseDownOnSelect = true;
+ }).mouseup(function() {
+ config.mouseDownOnSelect = false;
+ });
+
+ if( options.width > 0 )
+ element.css("width", options.width);
+
+ needsInit = false;
+ }
+
+ function target(event) {
+ var element = event.target;
+ while(element && element.tagName != "LI")
+ element = element.parentNode;
+ // more fun with IE, sometimes event.target is empty, just ignore it then
+ if(!element)
+ return [];
+ return element;
+ }
+
+ function moveSelect(step) {
+ listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
+ movePosition(step);
var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
if(options.scroll) {
var offset = 0;
listItems.slice(0, active).each(function() {
- offset += this.offsetHeight;
- });
+ offset += this.offsetHeight;
+ });
if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
} else if(offset < list.scrollTop()) {
list.scrollTop(offset);
}
}
- };
-
- function movePosition(step) {
- active += step;
- if (active < 0) {
- active = listItems.size() - 1;
- } else if (active >= listItems.size()) {
- active = 0;
- }
- }
-
- function limitNumberOfItems(available) {
- return options.max && options.max < available
- ? options.max
- : available;
- }
-
- function fillList() {
- list.empty();
- var max = limitNumberOfItems(data.length);
- for (var i=0; i < max; i++) {
- if (!data[i])
- continue;
- var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
- if ( formatted === false )
- continue;
- var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
- $.data(li, "ac_data", data[i]);
- }
- listItems = list.find("li");
- if ( options.selectFirst ) {
- listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
- active = 0;
- }
- // apply bgiframe if available
- if ( $.fn.bgiframe )
- list.bgiframe();
- }
-
- return {
- display: function(d, q) {
- init();
- data = d;
- term = q;
- fillList();
- },
- next: function() {
- moveSelect(1);
- },
- prev: function() {
- moveSelect(-1);
- },
- pageUp: function() {
- if (active != 0 && active - 8 < 0) {
- moveSelect( -active );
- } else {
- moveSelect(-8);
- }
- },
- pageDown: function() {
- if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
- moveSelect( listItems.size() - 1 - active );
- } else {
- moveSelect(8);
- }
- },
- hide: function() {
- element && element.hide();
- listItems && listItems.removeClass(CLASSES.ACTIVE);
- active = -1;
- },
- visible : function() {
- return element && element.is(":visible");
- },
- current: function() {
- return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
- },
- show: function() {
- var offset = $(input).offset();
- element.css({
- width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
- top: offset.top + input.offsetHeight,
- left: offset.left
- }).show();
+ };
+
+ function movePosition(step) {
+ active += step;
+ if (active < 0) {
+ active = listItems.size() - 1;
+ } else if (active >= listItems.size()) {
+ active = 0;
+ }
+ }
+
+ function limitNumberOfItems(available) {
+ return options.max && options.max < available
+ ? options.max
+ : available;
+ }
+
+ function fillList() {
+ list.empty();
+ var max = limitNumberOfItems(data.length);
+ for (var i=0; i < max; i++) {
+ if (!data[i])
+ continue;
+ var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
+ if ( formatted === false )
+ continue;
+ var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
+ $.data(li, "ac_data", data[i]);
+ }
+ listItems = list.find("li");
+ if ( options.selectFirst ) {
+ listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
+ active = 0;
+ }
+ // apply bgiframe if available
+ if ( $.fn.bgiframe )
+ list.bgiframe();
+ }
+
+ return {
+ display: function(d, q) {
+ init();
+ data = d;
+ term = q;
+ fillList();
+ },
+ next: function() {
+ moveSelect(1);
+ },
+ prev: function() {
+ moveSelect(-1);
+ },
+ pageUp: function() {
+ if (active != 0 && active - 8 < 0) {
+ moveSelect( -active );
+ } else {
+ moveSelect(-8);
+ }
+ },
+ pageDown: function() {
+ if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
+ moveSelect( listItems.size() - 1 - active );
+ } else {
+ moveSelect(8);
+ }
+ },
+ hide: function() {
+ element && element.hide();
+ listItems && listItems.removeClass(CLASSES.ACTIVE);
+ active = -1;
+ },
+ visible : function() {
+ return element && element.is(":visible");
+ },
+ current: function() {
+ return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
+ },
+ show: function() {
+ var offset = $(input).offset();
+ element.css({
+ width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
+ top: offset.top + input.offsetHeight,
+ left: offset.left
+ }).show();
if(options.scroll) {
list.scrollTop(0);
list.css({
- maxHeight: options.scrollHeight,
- overflow: 'auto'
- });
-
+ maxHeight: options.scrollHeight,
+ overflow: 'auto'
+ });
+
if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
- var listHeight = 0;
- listItems.each(function() {
- listHeight += this.offsetHeight;
- });
- var scrollbarsVisible = listHeight > options.scrollHeight;
+ var listHeight = 0;
+ listItems.each(function() {
+ listHeight += this.offsetHeight;
+ });
+ var scrollbarsVisible = listHeight > options.scrollHeight;
list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
- if (!scrollbarsVisible) {
- // IE doesn't recalculate width when scrollbar disappears
- listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
- }
+ if (!scrollbarsVisible) {
+ // IE doesn't recalculate width when scrollbar disappears
+ listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
+ }
}
}
- },
- selected: function() {
- var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
- return selected && selected.length && $.data(selected[0], "ac_data");
- },
- emptyList: function (){
- list && list.empty();
- },
- unbind: function() {
- element && element.remove();
- }
- };
+ },
+ selected: function() {
+ var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
+ return selected && selected.length && $.data(selected[0], "ac_data");
+ },
+ emptyList: function (){
+ list && list.empty();
+ },
+ unbind: function() {
+ element && element.remove();
+ }
+ };
};
$.Autocompleter.Selection = function(field, start, end) {
- if( field.createTextRange ){
- var selRange = field.createTextRange();
- selRange.collapse(true);
- selRange.moveStart("character", start);
- selRange.moveEnd("character", end);
- selRange.select();
- } else if( field.setSelectionRange ){
- field.setSelectionRange(start, end);
- } else {
- if( field.selectionStart ){
- field.selectionStart = start;
- field.selectionEnd = end;
- }
- }
- field.focus();
+ if( field.createTextRange ){
+ var selRange = field.createTextRange();
+ selRange.collapse(true);
+ selRange.moveStart("character", start);
+ selRange.moveEnd("character", end);
+ selRange.select();
+ } else if( field.setSelectionRange ){
+ field.setSelectionRange(start, end);
+ } else {
+ if( field.selectionStart ){
+ field.selectionStart = start;
+ field.selectionEnd = end;
+ }
+ }
+ field.focus();
};
})(jQuery);
\ No newline at end of file
Modified: lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/xslt/example_rss.xsl
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/xslt/example_rss.xsl?rev=1700800&r1=1700799&r2=1700800&view=diff
==============================================================================
--- lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/xslt/example_rss.xsl (original)
+++ lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/xslt/example_rss.xsl Wed Sep 2 13:06:13 2015
@@ -32,7 +32,7 @@
<xsl:template match='/'>
<rss version="2.0">
<channel>
- <title>Example Solr RSS 2.0 Feed</title>
+ <title>Example Solr RSS 2.0 Feed</title>
<link>http://localhost:8983/solr</link>
<description>
This has been formatted by the sample "example_rss.xsl" transform -
Modified: lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/xslt/updateXml.xsl
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/xslt/updateXml.xsl?rev=1700800&r1=1700799&r2=1700800&view=diff
==============================================================================
--- lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/xslt/updateXml.xsl (original)
+++ lucene/dev/branches/lucene6699/solr/server/solr/configsets/sample_techproducts_configs/conf/xslt/updateXml.xsl Wed Sep 2 13:06:13 2015
@@ -49,10 +49,10 @@
<xsl:variable name="fn" select="@name"/>
<xsl:for-each select="*">
- <xsl:element name="field">
- <xsl:attribute name="name"><xsl:value-of select="$fn"/></xsl:attribute>
- <xsl:value-of select="."/>
- </xsl:element>
+ <xsl:element name="field">
+ <xsl:attribute name="name"><xsl:value-of select="$fn"/></xsl:attribute>
+ <xsl:value-of select="."/>
+ </xsl:element>
</xsl:for-each>
</xsl:template>
@@ -60,10 +60,10 @@
<xsl:template match="doc/*">
<xsl:variable name="fn" select="@name"/>
- <xsl:element name="field">
- <xsl:attribute name="name"><xsl:value-of select="$fn"/></xsl:attribute>
+ <xsl:element name="field">
+ <xsl:attribute name="name"><xsl:value-of select="$fn"/></xsl:attribute>
<xsl:value-of select="."/>
- </xsl:element>
+ </xsl:element>
</xsl:template>
<xsl:template match="*"/>
Modified: lucene/dev/branches/lucene6699/solr/site/index.xsl
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6699/solr/site/index.xsl?rev=1700800&r1=1700799&r2=1700800&view=diff
==============================================================================
--- lucene/dev/branches/lucene6699/solr/site/index.xsl (original)
+++ lucene/dev/branches/lucene6699/solr/site/index.xsl Wed Sep 2 13:06:13 2015
@@ -81,8 +81,8 @@
<xsl:call-template name="modules"/>
</body>
</html>
- </xsl:template>
-
+ </xsl:template>
+
<xsl:template name="modules">
<ul>
<xsl:for-each select="str:split($buildfiles,'|')">
Modified: lucene/dev/branches/lucene6699/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6699/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java?rev=1700800&r1=1700799&r2=1700800&view=diff
==============================================================================
--- lucene/dev/branches/lucene6699/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java (original)
+++ lucene/dev/branches/lucene6699/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java Wed Sep 2 13:06:13 2015
@@ -61,6 +61,7 @@ import java.net.ConnectException;
import java.net.SocketException;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -74,11 +75,16 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import static org.apache.solr.common.params.CommonParams.AUTHC_PATH;
+import static org.apache.solr.common.params.CommonParams.AUTHZ_PATH;
+import static org.apache.solr.common.params.CommonParams.COLLECTIONS_HANDLER_PATH;
+import static org.apache.solr.common.params.CommonParams.CONFIGSETS_HANDLER_PATH;
+import static org.apache.solr.common.params.CommonParams.CORES_HANDLER_PATH;
+
/**
* SolrJ client class to communicate with SolrCloud.
* Instances of this class communicate with Zookeeper to discover
@@ -799,6 +805,12 @@ public class CloudSolrClient extends Sol
collection = (reqParams != null) ? reqParams.get("collection", getDefaultCollection()) : getDefaultCollection();
return requestWithRetryOnStaleState(request, 0, collection);
}
+ private static final Set<String> ADMIN_PATHS = new HashSet<>(Arrays.asList(
+ CORES_HANDLER_PATH,
+ COLLECTIONS_HANDLER_PATH,
+ CONFIGSETS_HANDLER_PATH,
+ AUTHC_PATH,
+ AUTHZ_PATH));
/**
* As this class doesn't watch external collections on the client side,
@@ -816,7 +828,8 @@ public class CloudSolrClient extends Sol
// collections is stale and needs to be refreshed ... this code has no impact on internal collections
String stateVerParam = null;
List<DocCollection> requestedCollections = null;
- if (collection != null && !request.getPath().startsWith("/admin")) { // don't do _stateVer_ checking for admin requests
+ boolean isAdmin = ADMIN_PATHS.contains(request.getPath());
+ if (collection != null && !isAdmin) { // don't do _stateVer_ checking for admin requests
Set<String> requestedCollectionNames = getCollectionNames(getZkStateReader().getClusterState(), collection);
StringBuilder stateVerParamBuilder = null;
@@ -871,7 +884,7 @@ public class CloudSolrClient extends Sol
Throwable rootCause = SolrException.getRootCause(exc);
// don't do retry support for admin requests or if the request doesn't have a collection specified
- if (collection == null || request.getPath().startsWith("/admin")) {
+ if (collection == null || isAdmin) {
if (exc instanceof SolrServerException) {
throw (SolrServerException)exc;
} else if (exc instanceof IOException) {
@@ -979,7 +992,7 @@ public class CloudSolrClient extends Sol
reqParams = new ModifiableSolrParams();
}
List<String> theUrlList = new ArrayList<>();
- if (request.getPath().startsWith("/admin/")) {
+ if (ADMIN_PATHS.contains(request.getPath())) {
Set<String> liveNodes = clusterState.getLiveNodes();
for (String liveNode : liveNodes) {
theUrlList.add(zkStateReader.getBaseUrlForNodeName(liveNode));
@@ -1094,7 +1107,7 @@ public class CloudSolrClient extends Sol
Set<String> collectionNames = new HashSet<>();
// validate collections
for (String collectionName : rawCollectionsList) {
- if (!clusterState.getCollections().contains(collectionName)) {
+ if (!clusterState.hasCollection(collectionName)) {
Aliases aliases = zkStateReader.getAliases();
String alias = aliases.getCollectionAlias(collectionName);
if (alias != null) {
Modified: lucene/dev/branches/lucene6699/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Krb5HttpClientConfigurer.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6699/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Krb5HttpClientConfigurer.java?rev=1700800&r1=1700799&r2=1700800&view=diff
==============================================================================
--- lucene/dev/branches/lucene6699/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Krb5HttpClientConfigurer.java (original)
+++ lucene/dev/branches/lucene6699/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Krb5HttpClientConfigurer.java Wed Sep 2 13:06:13 2015
@@ -34,6 +34,7 @@ import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
+import org.apache.http.auth.AuthSchemeRegistry;
import org.apache.http.impl.auth.SPNegoSchemeFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HttpContext;
@@ -77,7 +78,10 @@ public class Krb5HttpClientConfigurer ex
}
javax.security.auth.login.Configuration.setConfiguration(jaasConfig);
- httpClient.getAuthSchemes().register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory(true, false));
+ //Enable only SPNEGO authentication scheme.
+ AuthSchemeRegistry registry = new AuthSchemeRegistry();
+ registry.register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory(true, false));
+ httpClient.setAuthSchemes(registry);
// Get the credentials from the JAAS configuration rather than here
Credentials useJaasCreds = new Credentials() {
public String getPassword() {
Modified: lucene/dev/branches/lucene6699/solr/solrj/src/java/org/apache/solr/common/cloud/ClusterState.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6699/solr/solrj/src/java/org/apache/solr/common/cloud/ClusterState.java?rev=1700800&r1=1700799&r2=1700800&view=diff
==============================================================================
--- lucene/dev/branches/lucene6699/solr/solrj/src/java/org/apache/solr/common/cloud/ClusterState.java (original)
+++ lucene/dev/branches/lucene6699/solr/solrj/src/java/org/apache/solr/common/cloud/ClusterState.java Wed Sep 2 13:06:13 2015
@@ -17,13 +17,6 @@ package org.apache.solr.common.cloud;
* limitations under the License.
*/
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.SolrException.ErrorCode;
-import org.apache.solr.common.util.Utils;
-import org.noggit.JSONWriter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -33,6 +26,14 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+import org.apache.solr.common.util.Utils;
+import org.apache.zookeeper.Watcher;
+import org.noggit.JSONWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
/**
* Immutable state of the cloud. Normally you can get the state by using
* {@link ZkStateReader#getClusterState()}.
@@ -110,8 +111,16 @@ public class ClusterState implements JSO
return null;
}
- public boolean hasCollection(String coll) {
- return collectionStates.containsKey(coll) ;
+ /**
+ * Returns true if the specified collection name exists, false otherwise.
+ *
+ * Implementation note: This method resolves the collection reference by calling
+ * {@link CollectionRef#get()} which can make a call to ZooKeeper. This is necessary
+ * because the semantics of how collection list is loaded have changed in SOLR-6629.
+ * Please javadocs in {@link ZkStateReader#refreshCollectionList(Watcher)}
+ */
+ public boolean hasCollection(String collectionName) {
+ return getCollectionOrNull(collectionName) != null;
}
/**
@@ -170,19 +179,38 @@ public class ClusterState implements JSO
return collectionStates.get(coll);
}
- public DocCollection getCollectionOrNull(String coll) {
- CollectionRef ref = collectionStates.get(coll);
- return ref == null? null:ref.get();
+ /**
+ * Returns the corresponding {@link DocCollection} object for the given collection name
+ * if such a collection exists. Returns null otherwise.
+ *
+ * Implementation note: This method resolves the collection reference by calling
+ * {@link CollectionRef#get()} which can make a call to ZooKeeper. This is necessary
+ * because the semantics of how collection list is loaded have changed in SOLR-6629.
+ * Please javadocs in {@link ZkStateReader#refreshCollectionList(Watcher)}
+ */
+ public DocCollection getCollectionOrNull(String collectionName) {
+ CollectionRef ref = collectionStates.get(collectionName);
+ return ref == null ? null : ref.get();
}
/**
* Get collection names.
+ *
+ * Implementation note: This method resolves the collection reference by calling
+ * {@link CollectionRef#get()} which can make a call to ZooKeeper. This is necessary
+ * because the semantics of how collection list is loaded have changed in SOLR-6629.
+ * Please javadocs in {@link ZkStateReader#refreshCollectionList(Watcher)}
*/
public Set<String> getCollections() {
- return collectionStates.keySet();
+ Set<String> result = new HashSet<>();
+ for (Entry<String, CollectionRef> entry : collectionStates.entrySet()) {
+ if (entry.getValue().get() != null) {
+ result.add(entry.getKey());
+ }
+ }
+ return result;
}
-
/**
* Get names of the currently live nodes.
*/
Modified: lucene/dev/branches/lucene6699/solr/solrj/src/java/org/apache/solr/common/cloud/ZkConfigManager.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene6699/solr/solrj/src/java/org/apache/solr/common/cloud/ZkConfigManager.java?rev=1700800&r1=1700799&r2=1700800&view=diff
==============================================================================
--- lucene/dev/branches/lucene6699/solr/solrj/src/java/org/apache/solr/common/cloud/ZkConfigManager.java (original)
+++ lucene/dev/branches/lucene6699/solr/solrj/src/java/org/apache/solr/common/cloud/ZkConfigManager.java Wed Sep 2 13:06:13 2015
@@ -29,6 +29,7 @@ import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
/**
* Class that manages named configs in Zookeeper
@@ -142,4 +143,81 @@ public class ZkConfigManager {
throw new IOException("Error listing configs", SolrZkClient.checkInterrupted(e));
}
}
+
+ /**
+ * Check whether a config exists in Zookeeper
+ *
+ * @param configName the config to check existance on
+ * @return whether the config exists or not
+ * @throws IOException if an I/O error occurs
+ */
+ public Boolean configExists(String configName) throws IOException {
+ try {
+ return zkClient.exists(ZkConfigManager.CONFIGS_ZKNODE + "/" + configName, true);
+ } catch (KeeperException | InterruptedException e) {
+ throw new IOException("Error checking whether config exists",
+ SolrZkClient.checkInterrupted(e));
+ }
+ }
+
+ /**
+ * Delete a config in ZooKeeper
+ *
+ * @param configName the config to delete
+ * @throws IOException if an I/O error occurs
+ */
+ public void deleteConfigDir(String configName) throws IOException {
+ try {
+ zkClient.clean(ZkConfigManager.CONFIGS_ZKNODE + "/" + configName);
+ } catch (KeeperException | InterruptedException e) {
+ throw new IOException("Error checking whether config exists",
+ SolrZkClient.checkInterrupted(e));
+ }
+ }
+
+ private void copyConfigDirFromZk(String fromZkPath, String toZkPath, Set<String> copiedToZkPaths) throws IOException {
+ try {
+ List<String> files = zkClient.getChildren(fromZkPath, null, true);
+ for (String file : files) {
+ List<String> children = zkClient.getChildren(fromZkPath + "/" + file, null, true);
+ if (children.size() == 0) {
+ final String toZkFilePath = toZkPath + "/" + file;
+ logger.info("Copying zk node {} to {}",
+ fromZkPath + "/" + file, toZkFilePath);
+ byte[] data = zkClient.getData(fromZkPath + "/" + file, null, null, true);
+ zkClient.makePath(toZkFilePath, data, true);
+ if (copiedToZkPaths != null) copiedToZkPaths.add(toZkFilePath);
+ } else {
+ copyConfigDirFromZk(fromZkPath + "/" + file, toZkPath + "/" + file, copiedToZkPaths);
+ }
+ }
+ } catch (KeeperException | InterruptedException e) {
+ throw new IOException("Error copying nodes from zookeeper path " + fromZkPath + " to " + toZkPath,
+ SolrZkClient.checkInterrupted(e));
+ }
+ }
+
+ /**
+ * Copy a config in ZooKeeper
+ *
+ * @param fromConfig the config to copy from
+ * @param toConfig the config to copy to
+ * @throws IOException if an I/O error occurs
+ */
+ public void copyConfigDir(String fromConfig, String toConfig) throws IOException {
+ copyConfigDir(CONFIGS_ZKNODE + "/" + fromConfig, CONFIGS_ZKNODE + "/" + toConfig, null);
+ }
+
+ /**
+ * Copy a config in ZooKeeper
+ *
+ * @param fromConfig the config to copy from
+ * @param toConfig the config to copy to
+ * @param copiedToZkPaths should be an empty Set, will be filled in by function
+ with the paths that were actually copied to.
+ * @throws IOException if an I/O error occurs
+ */
+ public void copyConfigDir(String fromConfig, String toConfig, Set<String> copiedToZkPaths) throws IOException {
+ copyConfigDirFromZk(CONFIGS_ZKNODE + "/" + fromConfig, CONFIGS_ZKNODE + "/" + toConfig, copiedToZkPaths);
+ }
}