From e96baeb6e14545fd64e2e9f2ff19d9fc766f70d5 Mon Sep 17 00:00:00 2001 From: JanDornbusch Date: Sat, 11 Jul 2020 01:45:15 +0200 Subject: [PATCH 1/9] WIP Tested against solr and fixed cloud/http clients. Started to comment. Added tests. Changed output behavior. Allowed child documents to be added. Sorted connections into own folder. Renamed response ns to converter. Added default connection to ease the use (reduce with-connection requirement). --- .lein-failures | 1 + .nrepl-port | 1 + README.md | 184 +++++++++++++++++++++++++ project.clj | 23 +++- src/flux/client.clj | 55 ++++++++ src/flux/collections.clj | 168 ++++++++++++++++++++++ src/flux/connections/cloud.clj | 15 ++ src/flux/connections/embedded.clj | 29 ++++ src/flux/connections/http.clj | 6 + src/flux/converter.clj | 74 ++++++++++ src/flux/core.clj | 32 +++++ src/flux/query.clj | 47 +++++++ src/flux/update.clj | 46 +++++++ test/src/flux/connections/cloud.clj | 39 ++++++ test/src/flux/connections/embedded.clj | 32 +++++ test/src/flux/connections/http.clj | 26 ++++ test/src/flux/converter.clj | 13 ++ test/src/flux/core.clj | 50 +++++++ test/src/flux/query.clj | 16 +++ test/src/flux/update.clj | 21 +++ 20 files changed, 877 insertions(+), 1 deletion(-) create mode 100644 .lein-failures create mode 100644 .nrepl-port create mode 100644 src/flux/client.clj create mode 100644 src/flux/collections.clj create mode 100644 src/flux/connections/cloud.clj create mode 100644 src/flux/connections/embedded.clj create mode 100644 src/flux/connections/http.clj create mode 100644 src/flux/converter.clj create mode 100644 src/flux/core.clj create mode 100644 src/flux/query.clj create mode 100644 src/flux/update.clj create mode 100644 test/src/flux/connections/cloud.clj create mode 100644 test/src/flux/connections/embedded.clj create mode 100644 test/src/flux/connections/http.clj create mode 100644 test/src/flux/converter.clj create mode 100644 test/src/flux/core.clj create mode 100644 test/src/flux/query.clj create mode 100644 test/src/flux/update.clj diff --git a/.lein-failures b/.lein-failures new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/.lein-failures @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/.nrepl-port b/.nrepl-port new file mode 100644 index 0000000..f0552f7 --- /dev/null +++ b/.nrepl-port @@ -0,0 +1 @@ +55603 \ No newline at end of file diff --git a/README.md b/README.md index ddeec20..78078f2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # star +<<<<<<< Updated upstream A Clojure library designed to ... well, that part is up to you. ## Usage @@ -9,5 +10,188 @@ FIXME ## License Copyright © 2013 FIXME +======= +A Clojure based Solr client. The latest stable version of flux is 0.6.3, which supports Solr `8.5.2`. + +## Installation (Leiningen) + +To include the Flux library, add the following to your `:dependencies`: + + [com.codesignals/flux "0.6.3"] + +## Usage + +### Http + +```clojure +(require '[flux.connection.http :as http]) + +(def conn (http/create "http://localhost:8983/solr" :name-of-collection)) +``` + +### Cloud +Create a connection to SolrCloud using one zk host: + +```clojure +(require '[flux.connection.cloud :as cloud] + '[flux.collections :as cloud-api) + +(def conn (cloud/create ["zk1:2181"])) +``` + +Create a connection to SolrCloud using multiple zk hosts (for failover): + +```clojure +(def conn (cloud/create ["zk1:2181" "zk2:2181" "zk3:2181"])) +``` + +Create a connection to SolrCloud using multiple zk hosts (for failover) and a chroot: + +```clojure +(def conn (cloud/create ["zk1:2181" "zk2:2181" "zk3:2181"] "mysolrchroot")) +``` + +Create a connection to SolrCloud using zk and specify a default collection + +```clojure +(def conn (cloud/create ["zk1:2181" "zk2:2181" "zk3:2181"] "mysolrchroot" :name-of-collection)) +``` + +Create a new SolrCloud collection with 4 shards: + +```clojure +(cloud-api/create-collection conn "my-solr-collection" 4) +``` + +Create a new SolrCloud collection with 4 shards and replication factor of 2: + +```clojure +(cloud-api/create-collection conn "my-solr-collection" 4 2) +``` + +Create a new SolrCloud collection with 4 shards and replication factor of 2 and additional parameters: + +```clojure +(cloud-api/create-collection conn "my-solr-collection" 4 2 { "collection.configName" "schemaless" + "router.name" "implicit" + "shards" "x,y,z,p" + "maxShardsPerNode" 10}) +``` + +The SolrCloud connection is thread-safe and it is recommended that you create just one +and re-use it across all requests. + +Remember to shutdown the connection on exit: + +```clojure +(flux/with-connection conn (flux/shutdown)) +``` + +Delete a SolrCloud collection: + +```clojure +(cloud-api/delete-collection conn :name-of-collection) +``` + +Get a list of active replicas of a collection: + +```clojure +(filter active? (all-replicas conn :name-of-collection)) +``` + +Get a list of not-active replicas (either down or recovering) of a collection: + +```clojure +(filter not-active? (all-replicas conn :name-of-collection)) +``` + +Get a list of down replicas of a collection: + +```clojure +(filter down? (all-replicas conn :name-of-collection)) +``` + +Get a list of recovering replicas of a collection: + +```clojure +(filter recovering? (all-replicas conn :name-of-collection)) +``` + +Get a list of replicas of a collection hosted by host/port: + +```clojure +(filter (fn [x] (hosted-by? x "127.0.1.1:8983")) (all-replicas conn :name-of-collection)) +``` + +Get a list of leaders of a particular collection: + +```clojure +(filter leader? (all-replicas conn :name-of-collection)) +``` + +Wait for all replicas of a given collection hosted by a particular host/port to be in 'active' state: + +```clojure +(wait-until-active conn :name-of-collection "host1:8983") +``` + +### Embedded + +Currently not usable - preferred method is http interface in this case. See [https://cwiki.apache.org/confluence/display/solr/EmbeddedSolr](https://cwiki.apache.org/confluence/display/solr/EmbeddedSolr) too. + +```clojure +(require '[flux.connection.embedded :as embedded]) + +(def cc (embedded/create-core-container)) +``` + +#### Core auto-discovery +Flux also supports `core.properties`. Just give `create-core` the solr-home path as the only argument. + + Note: It's important to call the `load` method on the resulting `CoreContainer` instance: + +```clojure +(def cc (doto (embedded/create-core-container "path/to/solr-home") + (.load)) +``` + +Now create the embedded server instance: + +```clojure +(def conn (embedded/create cc :name-of-collection)) +``` + +### Client +Once a connection as been created, use the `with-connection` macro to wrap client calls: + +```clojure +(require '[flux.core :as flux] + '[flux.query :as q]) + +(flux/with-connection conn + (flux/add [{:id 1} {:id 2}]) + (flux/commit) + (flux/query "*:*") + ;; Or set the path and/or method: + (flux/request + (q/create-query-request :post "/docs" {:q "etc"})) +``` + +#### Query options +You can also specify additional query options (filter queries, fields, ...): +```clojure + (let [options {:fq {:id 1 :range_field [1 5]} :sort "id asc" :fl ["id" "another_field"]}] + (flux/query "*:*" options) +``` + +### Test +```shell +lein midje +``` + +## License + +Copyright © 2013-2020 Matt Mitchell +>>>>>>> Stashed changes Distributed under the Eclipse Public License, the same as Clojure. diff --git a/project.clj b/project.clj index b5e648d..0a69e97 100644 --- a/project.clj +++ b/project.clj @@ -1,11 +1,32 @@ +<<<<<<< Updated upstream (defproject star "0.1.0-SNAPSHOT" +======= +(defproject com.codesignals/flux "0.6.3" +>>>>>>> Stashed changes :description "A clojure client library for Solr" - :url "http://example.com/FIXME" + :url "https://github.com/mwmitchell/flux" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} +<<<<<<< Updated upstream :dependencies [[org.clojure/clojure "1.5.1"] [org.apache.solr/solr-core "4.2.0" :exclusions [javax.servlet/servlet-api]] [org.apache.solr/solr-solrj "4.2.0"] [com.vividsolutions/jts "1.13"] [ring/ring-core "1.1.8"]]) +======= + :dependencies [[org.clojure/clojure "1.10.1"] + [org.apache.solr/solr-core "8.5.2"] + [org.apache.solr/solr-solrj "8.5.2"]] + :plugins [[lein-codox "0.10.7"]] + :profiles {:dev {:dependencies [[midje "1.9.9"] + [org.slf4j/slf4j-jdk14 "1.7.30"] + ;[org.slf4j/slf4j-log4j12 "1.7.30"] + ;[org.slf4j/slf4j-api "1.7.30"] + ;[org.slf4j/jcl-over-slf4j "1.7.30"] + [commons-logging "1.2"]] + :resource-paths ["dev-resources"] + :plugins [[lein-midje "3.2.1"]]}} + :source-paths ["src"] + :test-paths ["test"]) +>>>>>>> Stashed changes diff --git a/src/flux/client.clj b/src/flux/client.clj new file mode 100644 index 0000000..4006616 --- /dev/null +++ b/src/flux/client.clj @@ -0,0 +1,55 @@ +(ns flux.client + (:require [flux.update :refer [create-doc]] + [flux.query :refer [create-query]] + [flux.converter :refer [->clojure]]) + (:import [org.apache.solr.client.solrj SolrClient])) + +(defn query [^SolrClient solr-server query & [options]] + (->clojure (.query solr-server (create-query query options)))) + +(defn request [^SolrClient solr-server request] + (->clojure (.request solr-server request))) + +(defmulti add + (fn [_ input & _] + (cond + (map? input) :one + :else :default))) + +(defmethod add :one [^SolrClient client doc & {:as opts}] + (->clojure (.add client (create-doc doc)))) + +(defmethod add :default [^SolrClient client docs & {:as opts}] + (->clojure (.add client ^java.util.Collection (map create-doc docs)))) + +(defn commit [^SolrClient client & {:as opts}] + (->clojure (.commit client))) + +(letfn [(v [x] + (cond (keyword? x) (name x) :else (str x)))] + (defn delete-by-id [^SolrClient client ids & {:as opts}] + (->clojure + (let [ids (if (coll? ids) (map v ids) (v ids))] + (.deleteById ^SolrClient client ^java.util.List ids))))) + +(defn delete-by-query [^SolrClient client q & {:as opts}] + (->clojure (.deleteByQuery client q))) + +(defn optimize + ([^SolrClient client] + (->clojure (.optimize client))) + ([^SolrClient client wait-flush wait-searcher] + (->clojure (.optimize client wait-flush wait-searcher))) + ([^SolrClient client wait-flush wait-searcher max-segments] + (->clojure + (.optimize client wait-flush wait-searcher max-segments)))) + +(defn rollback [^SolrClient client] + (->clojure (.rollback client))) + +(defn shutdown [^SolrClient client] + (->clojure (.shutdown client))) + +(defn ping [^SolrClient client] + (->clojure (.ping client))) + diff --git a/src/flux/collections.clj b/src/flux/collections.clj new file mode 100644 index 0000000..8cb9b2d --- /dev/null +++ b/src/flux/collections.clj @@ -0,0 +1,168 @@ +(ns flux.collections + (:require [flux.client :as client] + [flux.query :as q])) + +(defn all-replicas + "Returns all replicas of given collection + by quering the clusterstatus of collections + + connection + : given a connection created by create-collection + + collection + : the collection to search for" + [connection collection] + (let [request (q/create-query-request :get "/admin/collections" + {:action "clusterstatus" :collection (name collection)}) + response (client/request connection request) + shards (get-in response [:cluster :collections "shards"])] + (into {} (map (fn [[_ v]] (get v "replicas")) shards)))) + +;; # Filter FNs + +(defn active? + "Filter-FN: + Returns true if replica is in active state" + [[_ {:strs [state]}]] + (= "active" state)) + +(defn recovering? + "Filter-FN: + Returns true if replica is in recovery state" + [[_ {:strs [state]}]] + (= "recovery" state)) + +(defn down? + "Filter-FN: + Returns true if replica is in down state" + [[_ {:strs [state]}]] + (= "down" state)) + +(defn not-active? + "Filter-FN: + Returns true if replica is not in active state" + [replica] + (not (active? replica))) + +(defn node-name + "Filter-FN: + Returns the node name of the replica" + [[_ {:strs [node_name]}]] + node_name) + +(letfn [ ;; Returns the solr node name for given host/port + (solr-node-name [host-port] + (str host-port "_solr"))] + + (defn hosted-by? + "Special Filter-FN: + Returns true if given replica is hosted locally by given host/port + used to generate lists of replicas of a collection hosted by host/port + + replica + : x or % - the value to compare against, usually a list generated by all-replicas + + host-port + : string host:port to search for" + [replica host-port] + (= (node-name replica) (solr-node-name host-port)))) + +(defn leader? + "Filter-FN: + Returns true if given replica is a leader" + [[_ {:keys [leader]}]] + (= "true" leader)) + +(letfn [(try-all-replicas [connection collection] + (try + (all-replicas connection collection) + ;; TODO: catch specific exc.. not throwable + (catch Throwable _ {})))] + + (defn wait-until-active + "Waits (infinitely) until all solr replicas hosted by given host/port belonging to given collection are in active state + + connection + : given a connection created by create-collection + + collection + : the collection to search for + + host-port + : string host:port to search for" + + [connection collection host-port] + (loop [replicas (try-all-replicas connection collection)] + (when-not (->> replicas + (filter active?) + (filter #(hosted-by? %1 host-port)) + (seq)) + ;; TODO: allow optional timeout val + (Thread/sleep 1000) + (recur (try-all-replicas connection collection)))))) + +;; # Collection management + +(defn create-collection + "Create a SolrCloud collection + + connection + : given a connection created by create-collection + + collection-name + : string. Name of collection to create + + num-shards + : integer. The number of shards this collection will be split into + + replication-factor + : [optional] integer. The number of replicas to be created for each shard. + + params + : [optional] map. Additional configuration to crate a collection. + Reserved keys: action, name, numShards and replicationFactor + Possible keys: router.name, shards, nrtReplicas, tlogReplicas, + pullReplicas, maxShardsPerNode, createNodeSet, createNodeSet.shuffle, + collection.configName, router.field, property.name=value (core.properties), + autoAddReplicas, async, rule, snitch, policy, waitForFinalState, withCollection, + alias + + See https://lucene.apache.org/solr/guide/8_5/collection-management.html#create for more info." + ([connection collection-name num-shards] + (create-collection connection collection-name num-shards 1 {})) + ([connection collection-name num-shards replication-factor] + (create-collection connection collection-name num-shards replication-factor {})) + ([connection collection-name num-shards replication-factor params] + (let [with-params (assoc params + "action" "create" + "name" (name collection-name) + "numShards" num-shards + "replicationFactor" replication-factor)] + (client/request + connection + (q/create-query-request :get "/admin/collections" with-params))))) + +(defn delete-collection + "Delete a SolrCloud collection + + connection + : given a connection created by create-collection + + collection-name + : name of collection to delete" + [connection collection-name] + (let [with-params {"action" "delete" "name" (name collection-name)}] + (client/request + connection + (q/create-query-request :get "/admin/collections" with-params)))) + +(defn changeDefaultCollection + "Change the default collection to work with + + connection + : given a connection created by create-collection + + collection-name + : name of collection to use" + [connection collection-name] + (. connection setDefaultCollection (name collection-name))) \ No newline at end of file diff --git a/src/flux/connections/cloud.clj b/src/flux/connections/cloud.clj new file mode 100644 index 0000000..93b5f12 --- /dev/null +++ b/src/flux/connections/cloud.clj @@ -0,0 +1,15 @@ +(ns flux.connections.cloud + (:import [org.apache.solr.client.solrj.impl CloudSolrClient$Builder])) + +(defn create + ([zk-hosts] + (.build + (CloudSolrClient$Builder. zk-hosts (java.util.Optional/empty)))) + ([zk-hosts chroot] + (.build + (CloudSolrClient$Builder. zk-hosts (java.util.Optional/of chroot)))) + ([zk-hosts chroot default-collection] + (let [client (-> (CloudSolrClient$Builder. zk-hosts (java.util.Optional/of chroot)) + (.build))] + (. client setDefaultCollection (name default-collection)) + client))) \ No newline at end of file diff --git a/src/flux/connections/embedded.clj b/src/flux/connections/embedded.clj new file mode 100644 index 0000000..cc022b4 --- /dev/null +++ b/src/flux/connections/embedded.clj @@ -0,0 +1,29 @@ +(ns flux.embedded + (:import [java.io File] + [org.apache.solr.client.solrj.embedded EmbeddedSolrServer] + [org.apache.solr.core CoreContainer] + [java.nio.file Paths])) + +;; (defn- str->path [str-path] +;; (-> str-path File. .toURI Paths/get)) + +;; (defn create-core-container +;; "Creates a CoreContainer from a solr-home path and solr-config.xml path +;; OR just a solr-home path. +;; If the latter is used, $home/$solr.xml works well, as the new +;; core.properties discovery mode. +;; See: org.apache.solr.core.CoreLocator +;; and +;; http://wiki.apache.org/solr/Core%20Discovery%20(4.4%20and%20beyond) +;; Note: If using core.properties only, it is required to call (.load core-container) +;; before creating the EmbeddedSolrServer instance." +;; ([^String solr-home] +;; (CoreContainer. solr-home)) +;; ([^String solr-home-path ^String solr-config-path] +;; (CoreContainer/createAndLoad +;; (str->path solr-home-path) +;; (str->path solr-config-path)))) + +;; (defn create [^CoreContainer core-container core-name] +;; {:pre [(some #(% core-name) [string? keyword?])]} +;; (EmbeddedSolrServer. core-container (name core-name))) diff --git a/src/flux/connections/http.clj b/src/flux/connections/http.clj new file mode 100644 index 0000000..6b8f32f --- /dev/null +++ b/src/flux/connections/http.clj @@ -0,0 +1,6 @@ +(ns flux.connections.http + (:import [org.apache.solr.client.solrj.impl HttpSolrClient$Builder])) + +(defn create [base-url core-name] + (-> (HttpSolrClient$Builder. (str base-url "/" (name core-name))) + (.build))) diff --git a/src/flux/converter.clj b/src/flux/converter.clj new file mode 100644 index 0000000..6fa541d --- /dev/null +++ b/src/flux/converter.clj @@ -0,0 +1,74 @@ +(ns flux.converter + (:import [org.apache.solr.client.solrj SolrResponse] + [org.apache.solr.common.util NamedList] + [org.apache.solr.common SolrDocumentList SolrDocument] + [org.apache.solr.common SolrInputDocument] + [java.util ArrayList])) + +(defonce string-only? (atom false)) + +(defn set-stringoutput-only + "Set to true if you prefer string output + instead of Longs, Doubles and Booleans in return + + bool + : true = strings only; false = try to parse output" + [bool] + (when (boolean? bool) + (reset! string-only? bool))) + +(defmulti ->clojure class) + +(defmethod ->clojure NamedList [^NamedList obj] + (into {} (for [[k v] obj] [(keyword k) (->clojure v)]))) + +(defmethod ->clojure ArrayList [obj] + (mapv ->clojure obj)) + +(defmethod ->clojure SolrDocumentList [^SolrDocumentList obj] + (merge + {:numFound (.getNumFound obj) + :start (.getStart obj) + :docs (mapv ->clojure (iterator-seq (.iterator obj)))} + (when-let [ms (.getMaxScore obj)] + {:maxScore ms}))) + +(defmethod ->clojure SolrDocument [^SolrDocument obj] + (reduce + (fn [acc f] + (assoc acc (keyword f) (->clojure (.getFieldValue obj f)))) + {} + (.getFieldNames obj))) + +(defmethod ->clojure SolrResponse [^SolrResponse obj] + (->clojure (.getResponse obj))) + +(defmethod ->clojure SolrInputDocument [^SolrInputDocument obj] + (reduce + (fn [acc o] + (assoc acc (keyword o) (.getFieldValue obj o))) + {} + (.getFieldNames obj))) + +(defmethod ->clojure java.util.LinkedHashMap [obj] + (into {} (for [[k v] obj] [(keyword k) (->clojure v)]))) + +(defmethod ->clojure String [obj] + (if string-only? + obj + (try + (Long/parseLong obj) + (catch NumberFormatException _ + (try + (Double/parseDouble obj) + (catch NumberFormatException _ + (cond + (= obj "false") + false + (= obj "true") + true + :else + obj))))))) + +(defmethod ->clojure :default [obj] + obj) diff --git a/src/flux/core.clj b/src/flux/core.clj new file mode 100644 index 0000000..e319d02 --- /dev/null +++ b/src/flux/core.clj @@ -0,0 +1,32 @@ +(ns flux.core + (:require [flux.client])) + +;; This will be used to store a default connection to query against +(defonce _default (atom nil)) + +(def ^:dynamic *connection* nil) + +(defn set-default-connection + "Set the solr connection that flux should use by default when no + alternative is specified." + [conn] + (reset! _default conn)) + +(defmacro with-connection [connection & body] + `(binding [*connection* ~connection] + ~@body)) + +(defmacro create-fn [name] + `(defn ~name [& args#] + (apply (ns-resolve 'flux.client '~name) (or *connection* @_default) args#))) + +(create-fn ping) +(create-fn add) +(create-fn query) +(create-fn request) +(create-fn commit) +(create-fn optimize) +(create-fn rollback) +(create-fn delete-by-id) +(create-fn delete-by-query) +(create-fn shutdown) diff --git a/src/flux/query.clj b/src/flux/query.clj new file mode 100644 index 0000000..d4cccb9 --- /dev/null +++ b/src/flux/query.clj @@ -0,0 +1,47 @@ +(ns flux.query + (:import [org.apache.solr.common.params MultiMapSolrParams] + [org.apache.solr.client.solrj.request QueryRequest] + [org.apache.solr.client.solrj SolrRequest$METHOD])) + +(def method-map + {:get SolrRequest$METHOD/GET + :post SolrRequest$METHOD/POST}) + +(defn- format-param [p] + (if (keyword? p) (name p) (str p))) + +(defn- format-values [v] + (into-array (mapv format-param (if (coll? v) v [v])))) + +(defn- format-range [coll] + (str "[" (format-param (first coll)) " TO " (format-param (second coll)) "]")) + +(defn- format-fq [[k v]] + (str (format-param k) ":" (if (coll? v) (format-range v) (format-param v)))) + +(defn format-filter-queries [hm] + (mapv format-fq hm)) + +(defn- create-solr-params [m] + (MultiMapSolrParams. + (reduce-kv (fn [^java.util.HashMap hm k v] + (doto hm + (.put (format-param k) + (format-values v)))) + (java.util.HashMap.) m))) + +(defn create-query [query options] + (let [filter-queries (format-filter-queries (:fq options))] + (create-solr-params (assoc (assoc options :fq filter-queries) :q query)))) + +(defn create-query-request + ([params] + (create-query-request nil params)) + ([path params] + (create-query-request :get path params)) + ([method path params] + {:pre [(or (nil? path) (re-find #"^\/" (str path))) + (get method-map method)]} + (doto + (QueryRequest. (create-solr-params params) (get method-map method)) + (.setPath path)))) diff --git a/src/flux/update.clj b/src/flux/update.clj new file mode 100644 index 0000000..cd74ff7 --- /dev/null +++ b/src/flux/update.clj @@ -0,0 +1,46 @@ +(ns flux.update + (:import [org.apache.solr.common SolrInputDocument])) + +;; Checking the docs the SolrInputDocument required a none +;; empty ctor already since 7.0.0 +;; https://lucene.apache.org/solr/8_5_2/solr-solrj/index.html?org/apache/solr/common/SolrInputDocument.html + +;; NOTE: The result of this function is a SolrInputDocument +;; which throws an exception when printed! +(defn create-doc ^SolrInputDocument + "creates the inputDocument for solr. + + document-map + : takes a map to convert + + examples + {:id 1 :map {:int 22 :string \"abc\"}} + will create + \"SolrInputDocument(fields: [id=1, map={string=abc, int=22}])\" + + or + {:id 1 :_childDocuments_ [{:id 2} {:id 3}]} + will create + \"SolrInputDocument(fields: [id=1], + children: [SolrInputDocument(fields: [id=2]), SolrInputDocument(fields: [id=3])])\" + " + + [document-map] + (reduce-kv (fn [^SolrInputDocument doc k v] + (cond + (= k :_childDocuments_) + (doto doc (.addChildDocuments (map create-doc v))) + + (map? v) + (let [m (java.util.HashMap.)] + (doseq [[kv fv] v] + (doto m + (.put (name kv) fv))) + (doto doc (.addField (name k) m)) + doc) + + :else + (doto doc (.addField (name k) v)))) + + (SolrInputDocument. (java.util.LinkedHashMap.)) + document-map)) diff --git a/test/src/flux/connections/cloud.clj b/test/src/flux/connections/cloud.clj new file mode 100644 index 0000000..f788138 --- /dev/null +++ b/test/src/flux/connections/cloud.clj @@ -0,0 +1,39 @@ +(ns src.flux.connections.cloud + (:require + [clojure.test :refer [deftest is]] + [flux.core :refer :all] + [flux.collections :refer [changeDefaultCollection]] + [flux.connections.cloud :refer [create]])) + +(deftest cloud-solr + (let [conn (create ["localhost:2181"])] + (is (= org.apache.solr.client.solrj.impl.CloudSolrClient + (type (set-default-connection conn))) + "Created correct connection type will fail intentionally + if there is no zookeeper running to connect to solr with + collection flux_test") + (is (nil? (changeDefaultCollection conn :flux_test)) + "Failed to change the default collection")) + (is (= 0 (get-in (add [{:id 1} {:id 2}]) [:responseHeader :status])) + "Was able to drop in 2 test ids") + (is (= 0 (get-in (commit) [:responseHeader :status])) + "Commited successfully") + (is (some + #(= 1 %) + (map :id + (get-in + (query "*:*") + [:response :docs]))) + "Testing if we can find back id 1") + (is (= 0 (get-in (delete-by-query "*:*") [:responseHeader :status])) + "Deleting everything") + (is (= 0 (get-in (commit) [:responseHeader :status])) + "Commited successfully")) + +(deftest cloud-solr-ctor + (is (= org.apache.solr.client.solrj.impl.CloudSolrClient + (type (set-default-connection (create ["localhost:2181"] "/")))) + "Created correct connection type ") + (is (= org.apache.solr.client.solrj.impl.CloudSolrClient + (type (set-default-connection (create ["localhost:2181"] "/" :new_core)))) + "Created correct connection type ")) diff --git a/test/src/flux/connections/embedded.clj b/test/src/flux/connections/embedded.clj new file mode 100644 index 0000000..7cb861c --- /dev/null +++ b/test/src/flux/connections/embedded.clj @@ -0,0 +1,32 @@ +;; (ns src.flux.connections.embedded +;; (:require +;; [clojure.test :refer [deftest is]] +;; [flux.core :refer :all] +;; [flux.connections.embedded :refer :all])) + +;; (deftest embedded-solr +;; (let [cc (create-core-container)] +;; (is (= org.apache.solr.client.solrj.embedded.EmbeddedSolrServer +;; (type (set-default-connection (create cc :flux_test)))) +;; "Created correct connection type")) +;; (is (= 0 (get-in (add [{:id 1} {:id 2}]) [:responseHeader :status])) +;; "Was able to drop in 2 test ids") +;; (is (= 0 (get-in (commit) [:responseHeader :status])) +;; "Commited successfully") +;; (is (some +;; #(= 1 %) +;; (map :id +;; (get-in +;; (query "*:*") +;; [:response :docs]))) +;; "Testing if we can find back id 1") +;; (is (= 0 (get-in (delete-by-query "*:*") [:responseHeader :status])) +;; "Deleting everything") +;; (is (= 0 (get-in (commit) [:responseHeader :status])) +;; "Commited successfully")) + +;; (with-connection +;; (let [cc (create-core-container)] +;; (create cc :flux_test)) +;; (add [{:id 1} {:id 2}])) + \ No newline at end of file diff --git a/test/src/flux/connections/http.clj b/test/src/flux/connections/http.clj new file mode 100644 index 0000000..de1aa18 --- /dev/null +++ b/test/src/flux/connections/http.clj @@ -0,0 +1,26 @@ +(ns src.flux.connections.http + (:require + [clojure.test :refer [deftest is]] + [flux.core :refer :all] + [flux.connections.http :refer [create]])) + +(deftest http-solr + (is (= org.apache.solr.client.solrj.impl.HttpSolrClient + (type (set-default-connection (create "http://localhost:8983/solr" :flux_test)))) + "Created correct connection type - will fail if no solr is running on your localhost + with a collection flux_test") + (is (= 0 (get-in (add [{:id 1} {:id 2}]) [:responseHeader :status])) + "Was able to drop in 2 test ids") + (is (= 0 (get-in (commit) [:responseHeader :status])) + "Commited successfully") + (is (some + #(= 1 %) + (map :id + (get-in + (query "*:*") + [:response :docs]))) + "Testing if we can find back id 1") + (is (= 0 (get-in (delete-by-query "*:*") [:responseHeader :status])) + "Deleting everything") + (is (= 0 (get-in (commit) [:responseHeader :status])) + "Commited successfully")) diff --git a/test/src/flux/converter.clj b/test/src/flux/converter.clj new file mode 100644 index 0000000..e2074d9 --- /dev/null +++ b/test/src/flux/converter.clj @@ -0,0 +1,13 @@ +(ns src.flux.converter + (:require [flux.update :as update] + [flux.converter :refer :all] + [midje.sweet :refer :all])) + +(fact "create-map-from-document" + (let [doc (update/create-doc {:id 1})] + (->clojure doc) => {:id 1}) + (->clojure "2.2") => 2.2 + (->clojure "2") => 2 + (->clojure "2,2") => "2,2" + (->clojure "false") => false + (->clojure "true") => true) diff --git a/test/src/flux/core.clj b/test/src/flux/core.clj new file mode 100644 index 0000000..3fc4322 --- /dev/null +++ b/test/src/flux/core.clj @@ -0,0 +1,50 @@ +(ns src.flux.core + (:require + [flux.client :as client] + [flux.core :refer :all] + [midje.sweet :refer :all])) + +(fact "query" + (with-connection ..connection.. + (query ..query..) => anything + (provided (client/query ..connection.. ..query..) => ..anything..))) + +(fact "query-default" + (set-default-connection ..connection..) + (query ..query..) => anything + (provided (client/query ..connection.. ..query..) => ..anything..)) + +(fact "delete-by-id" + (with-connection ..connection.. + (delete-by-id ..id..) => anything + (provided (client/delete-by-id ..connection.. ..id..) => ..anything..))) + +(fact "delete-by-id" + (with-connection ..connection.. + (delete-by-query ..query..) => anything + (provided (client/delete-by-query ..connection.. ..query..) => ..anything..))) + +(fact "commit" + (with-connection ..connection.. + (commit) => anything + (provided (client/commit ..connection..) => ..anything..))) + +(fact "optimize" + (with-connection ..connection.. + (optimize) => anything + (provided (client/optimize ..connection..) => ..anything..))) + +(fact "add" + (with-connection ..connection.. + (add ..doc..) => anything + (provided (client/add ..connection.. ..doc..) => ..anything..))) + +(fact "rollback" + (with-connection ..connection.. + (rollback) => anything + (provided (client/rollback ..connection..) => ..anything..))) + +(fact "shutdown" + (with-connection ..connection.. + (shutdown) => anything + (provided (client/shutdown ..connection..) => ..anything..))) \ No newline at end of file diff --git a/test/src/flux/query.clj b/test/src/flux/query.clj new file mode 100644 index 0000000..9375b7b --- /dev/null +++ b/test/src/flux/query.clj @@ -0,0 +1,16 @@ +(ns src.flux.query + (:require [flux.query :refer :all] + [midje.sweet :refer :all])) + +(fact "create-query-request" + (create-query-request {:q "*:*"}) => anything) + +(fact "create-query-request w/path" + (create-query-request "/docs" {:q "*:*"}) => anything) + +(fact "create-query-request w/path and method" + (create-query-request :post "/docs" {:q "*:*"}) => anything) + +(fact "format-filter-queries" + (let [fqs {:country "IT" :range ["1" "6"]}] + (format-filter-queries fqs) => ["country:IT" "range:[1 TO 6]"])) \ No newline at end of file diff --git a/test/src/flux/update.clj b/test/src/flux/update.clj new file mode 100644 index 0000000..f87eeec --- /dev/null +++ b/test/src/flux/update.clj @@ -0,0 +1,21 @@ +(ns src.flux.update + (:require [flux.update :refer :all] + [midje.sweet :refer :all]) + (:import [org.apache.solr.common SolrInputDocument])) + +(fact "create-doc" + (class (create-doc {:id 1})) => org.apache.solr.common.SolrInputDocument + (.toString + (create-doc {:id 1 :map {:int 2 :string "abc"}})) => "SolrInputDocument(fields: [id=1, map={string=abc, int=2}])") + + +(fact "create-doc-with-children" + (let [doc {:id 1 + :_childDocuments_ + [{:id "1.1" + :title "a title..." + :_childDocuments_ + [{:id "1.1.1"}]}]} + solr-doc (create-doc doc)] + (.hasChildDocuments solr-doc) => true + (-> solr-doc (.getChildDocuments) first (.hasChildDocuments)) => true)) \ No newline at end of file From af4fac077910e3ed200c3b12f468229e8af91577 Mon Sep 17 00:00:00 2001 From: JanDornbusch Date: Sat, 11 Jul 2020 01:51:41 +0200 Subject: [PATCH 2/9] WIP merged into dev --- README.md | 14 +- project.clj | 17 +- solr/solr.xml | 1381 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1383 insertions(+), 29 deletions(-) create mode 100644 solr/solr.xml diff --git a/README.md b/README.md index 78078f2..0510733 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,5 @@ -# star +# flux -<<<<<<< Updated upstream -A Clojure library designed to ... well, that part is up to you. - -## Usage - -FIXME - -## License - -Copyright © 2013 FIXME -======= A Clojure based Solr client. The latest stable version of flux is 0.6.3, which supports Solr `8.5.2`. ## Installation (Leiningen) @@ -192,6 +181,5 @@ lein midje ## License Copyright © 2013-2020 Matt Mitchell ->>>>>>> Stashed changes Distributed under the Eclipse Public License, the same as Clojure. diff --git a/project.clj b/project.clj index 0a69e97..7a30632 100644 --- a/project.clj +++ b/project.clj @@ -1,32 +1,17 @@ -<<<<<<< Updated upstream -(defproject star "0.1.0-SNAPSHOT" -======= (defproject com.codesignals/flux "0.6.3" ->>>>>>> Stashed changes :description "A clojure client library for Solr" :url "https://github.com/mwmitchell/flux" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} -<<<<<<< Updated upstream - :dependencies [[org.clojure/clojure "1.5.1"] - [org.apache.solr/solr-core "4.2.0" - :exclusions [javax.servlet/servlet-api]] - [org.apache.solr/solr-solrj "4.2.0"] - [com.vividsolutions/jts "1.13"] - [ring/ring-core "1.1.8"]]) -======= :dependencies [[org.clojure/clojure "1.10.1"] [org.apache.solr/solr-core "8.5.2"] [org.apache.solr/solr-solrj "8.5.2"]] :plugins [[lein-codox "0.10.7"]] :profiles {:dev {:dependencies [[midje "1.9.9"] [org.slf4j/slf4j-jdk14 "1.7.30"] - ;[org.slf4j/slf4j-log4j12 "1.7.30"] - ;[org.slf4j/slf4j-api "1.7.30"] - ;[org.slf4j/jcl-over-slf4j "1.7.30"] + ;[org.slf4j/slf4j-log4j12 "1.7.30"] [commons-logging "1.2"]] :resource-paths ["dev-resources"] :plugins [[lein-midje "3.2.1"]]}} :source-paths ["src"] :test-paths ["test"]) ->>>>>>> Stashed changes diff --git a/solr/solr.xml b/solr/solr.xml new file mode 100644 index 0000000..54b7627 --- /dev/null +++ b/solr/solr.xml @@ -0,0 +1,1381 @@ + + + + + + + + + 8.5.2 + + + + + + + + + + + + + + + + + + + + + + ${solr.data.dir:} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${solr.lock.type:native} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${solr.ulog.dir:} + + + + + 15000 + false + + + + + + + + + + + + + + + + ${solr.max.booleanClauses:1024} + + + + + + + + + + + + + + + + + + + + + + true + + + + + + 20 + + + 200 + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + explicit + 10 + + + + + + + + + + + + + + + + explicit + json + true + + + + + + + + + + + _text_ + + + + + + files-update-processor + + + + + + + /xhtml:html/xhtml:body/descendant:node() + content + attr_meta_ + attr_ + true + + + + + + + + text_general + + + + + + default + text + solr.DirectSolrSpellChecker + + internal + + 0.5 + + 2 + + 1 + + 5 + + 4 + + 0.01 + + + + + + wordbreak + solr.WordBreakSolrSpellChecker + name + true + true + 10 + + + + + + + + + + + + + + + + + default + wordbreak + on + true + 10 + 5 + 5 + true + true + 10 + 5 + + + spellcheck + + + + + + + + + + true + + + tvComponent + + + + + + + + + + + + true + false + + + terms + + + + + + + + string + elevate.xml + + + + + + explicit + + + elevator + + + + + + + + + + + 100 + + + + + + + + 70 + + 0.5 + + [-\w ,/\n\"']{20,200} + + + + + + + ]]> + ]]> + + + + + + + + + + + + + + + + + + + + + + + + ,, + ,, + ,, + ,, + ,]]> + ]]> + + + + + + 10 + .,!? + + + + + + + WORD + + + en + US + + + + + + + + + + + + + + [^\w-\.] + _ + + + + + + + yyyy-MM-dd['T'[HH:mm[:ss[.SSS]][z + yyyy-MM-dd['T'[HH:mm[:ss[,SSS]][z + yyyy-MM-dd HH:mm[:ss[.SSS]][z + yyyy-MM-dd HH:mm[:ss[,SSS]][z + [EEE, ]dd MMM yyyy HH:mm[:ss] z + EEEE, dd-MMM-yy HH:mm:ss z + EEE MMM ppd HH:mm:ss [z ]yyyy + + + + strings + + java.lang.Boolean + booleans + + + java.util.Date + pdates + + + java.lang.Long + java.lang.Integer + plongs + + + java.lang.Number + pdoubles + + + + + + + content + language + + + + + update-script.js + + + + + + + + + + + + + + + + + + + + + + + + + + text/plain; charset=UTF-8 + + + + + ${velocity.template.base.dir:} + + + + + 5 + + + + + + + + + + + + + + + From 1ec0f806044be5ff2bfdf0b3d4da816b4aa874e4 Mon Sep 17 00:00:00 2001 From: JanDornbusch Date: Sat, 11 Jul 2020 01:59:26 +0200 Subject: [PATCH 3/9] Merge cleanup --- src/star/client.clj | 54 --------------------------------- src/star/core.clj | 36 ---------------------- src/star/embedded.clj | 11 ------- src/star/http.clj | 5 ---- src/star/response.clj | 66 ----------------------------------------- src/star/util.clj | 28 ----------------- test/star/core_test.clj | 7 ----- 7 files changed, 207 deletions(-) delete mode 100644 src/star/client.clj delete mode 100644 src/star/core.clj delete mode 100644 src/star/embedded.clj delete mode 100644 src/star/http.clj delete mode 100644 src/star/response.clj delete mode 100644 src/star/util.clj delete mode 100644 test/star/core_test.clj diff --git a/src/star/client.clj b/src/star/client.clj deleted file mode 100644 index 2b874a4..0000000 --- a/src/star/client.clj +++ /dev/null @@ -1,54 +0,0 @@ -(ns star.client - (require [star.util :refer [create-doc create-query]] - [star.response :as response])) - -(defn query [solr-server query & [options]] - (response/response-base (.query solr-server (create-query query options)))) - -(defmulti add - (fn [_ input & _] - (cond - (map? input) :one - :else :default))) - -(defmethod add :one [client doc & {:as opts}] - (response/update-response - (.add client (create-doc doc)))) - -(defmethod add :default [client docs & {:as opts}] - (response/update-response - (.add client (map create-doc docs)))) - -(defn commit [client & {:as opts}] - (response/update-response (.commit client))) - -(letfn [(v [x] - (cond (keyword? x) (name x) :else (str x)))] - (defn delete-by-id [client ids & {:as opts}] - (response/update-response - (.deleteById client (if (coll? ids) (map v ids) (v ids)))))) - -(defn delete-by-query [client q & {:as opts}] - (response/update-response - (.deleteByQuery client q))) - -(defn optimize - ([client] - (response/update-response - (.optimize client))) - ([client wait-flush wait-searcher] - (response/update-response - (.optimize client wait-flush wait-searcher))) - ([client wait-flush wait-searcher max-segments] - (response/update-response - (.optimize client wait-flush wait-searcher max-segments)))) - -(defn rollback [client] - (response/update-response - (.rollback client))) - -(defn shutdown [client] - (.shutdown client)) - -(defn ping [client] - (.ping client)) diff --git a/src/star/core.clj b/src/star/core.clj deleted file mode 100644 index dd495a1..0000000 --- a/src/star/core.clj +++ /dev/null @@ -1,36 +0,0 @@ -(ns star.core - (require - [star [client :as client] - [embedded :as embedded] - [http :as http]])) - -(def ^:dynamic *connection*) - -(defmacro with-connection [connection & body] - `(binding [*connection* ~connection] - ~@body)) - -(defmacro create-fn [name] - `(defn ~name [& args#] - (apply (ns-resolve 'star.client '~name) *connection* args#))) - -(create-fn ping) -(create-fn add) -(create-fn query) -(create-fn commit) -(create-fn optimize) -(create-fn rollback) -(create-fn delete-by-id) -(create-fn delete-by-query) -(create-fn shutdown) - -(def cc (embedded/create-core-container "solr-home/home" - "solr-home/home/solr.xml")) - -(def hotels - (embedded/create cc "books")) - -(with-connection hotels - (delete-by-query "*:*") - (rollback) - (query "*:*")) diff --git a/src/star/embedded.clj b/src/star/embedded.clj deleted file mode 100644 index bf06869..0000000 --- a/src/star/embedded.clj +++ /dev/null @@ -1,11 +0,0 @@ -(ns star.embedded - (import [java.io File] - [org.apache.solr.client.solrj.embedded EmbeddedSolrServer] - [org.apache.solr.core CoreContainer])) - -(defn create-core-container - ([solr-home-path solr-config-path] - (CoreContainer. solr-home-path (File. solr-config-path)))) - -(defn create [core-container core-name] - (EmbeddedSolrServer. core-container core-name)) diff --git a/src/star/http.clj b/src/star/http.clj deleted file mode 100644 index 47b171f..0000000 --- a/src/star/http.clj +++ /dev/null @@ -1,5 +0,0 @@ -(ns star.http - (import [org.apache.solr.client.solrj.impl HttpSolrServer])) - -(defn create [base-url core-name] - (HttpSolrServer. (str base-url "/" (name core-name)))) diff --git a/src/star/response.clj b/src/star/response.clj deleted file mode 100644 index 35e7542..0000000 --- a/src/star/response.clj +++ /dev/null @@ -1,66 +0,0 @@ -(ns star.response - (:import [org.apache.solr.client.solrj.response SolrResponseBase] - [org.apache.solr.common.util NamedList SimpleOrderedMap] - [org.apache.solr.common SolrDocumentList] - [java.util ArrayList])) - -(defn create-map-from-document [document] - (into {} - (for [fld (.getFieldNames document)] - {(keyword fld) (.getFieldValue document fld)}))) - -(declare convert-value) - -(defn convert-key - [k] - (if (string? k) - (keyword k) - k)) - -(defn remap-response - [l] - {:maxScore (.getMaxScore l) - :numFound (.getNumFound l) - :start (.getStart l) - :docs (map create-map-from-document (iterator-seq (.iterator l)))}) - -(defn convert-map-entry - "Converts a MapEntry from a NamedList into a vector with 2 values. Nested NamedLists are recursively converted. " - [map-entry] - (let [k (convert-key (.getKey map-entry)) - v (convert-value (.getValue map-entry))] - [k v])) - -(defn- uniquify-paired-seq - "removes values when dup-key-in-paired-seq? returns true" - [coll] - (map #(first (val %1)) (group-by first coll))) - -(defn convert-named-list - "Converts a NamedList into a array-map. Nested NamedLists are recursively converted. " - [named-list] - (let [itrseq (iterator-seq (.iterator named-list)) - converted (map convert-map-entry itrseq) - reduced (uniquify-paired-seq converted) - flattened (apply concat reduced) - result (apply array-map flattened)] - result)) - -(defn convert-value - [v] - (cond - (instance? SimpleOrderedMap v) (convert-named-list v) - (instance? SolrDocumentList v) (remap-response v) - (instance? NamedList v) (convert-named-list v) - (instance? ArrayList v) (vec (map convert-value v)) - :else v)) - -(defn update-response [ur] - {:status (.getStatus ur) - :elaspsed-time (.getElapsedTime ur) - :q-time (.getQTime ur) - :request-url (.getRequestUrl ur)}) - -(defn response-base - [^SolrResponseBase r] - (convert-named-list (.getResponse r))) diff --git a/src/star/util.clj b/src/star/util.clj deleted file mode 100644 index fc13279..0000000 --- a/src/star/util.clj +++ /dev/null @@ -1,28 +0,0 @@ -(ns star.util - (import [org.apache.solr.common.params MultiMapSolrParams] - [org.apache.solr.common SolrInputDocument])) - -(defn- format-param [p] - (if (keyword? p) (name p) (str p))) - -(defn- format-values [v] - (into-array (mapv format-param (if (coll? v) v [v])))) - -(defn- create-solr-params [m] - (MultiMapSolrParams. - (reduce-kv #(doto %1 - (.put (format-param %2) - (format-values %3))) - (java.util.HashMap.) m))) - -(defn create-doc [document-map] - (reduce-kv #(if (map? %3) - (let [m (doto (java.util.HashMap.) - (.put (name (key (first %3))) (val (first %3))))] - (doto %1 (.addField (name %2) m)) - %1) - (doto %1 (.addField (name %2) %3))) - (SolrInputDocument.) document-map)) - -(defn create-query [query options] - (create-solr-params (assoc options :q query))) diff --git a/test/star/core_test.clj b/test/star/core_test.clj deleted file mode 100644 index f6deb2f..0000000 --- a/test/star/core_test.clj +++ /dev/null @@ -1,7 +0,0 @@ -(ns star.core-test - (:use clojure.test - star.core)) - -(deftest a-test - (testing "FIXME, I fail." - (is (= 0 1)))) From 7117a3afd5bc68a92cbfcb148041aac289ee0d2b Mon Sep 17 00:00:00 2001 From: JanDornbusch Date: Sat, 11 Jul 2020 14:58:13 +0200 Subject: [PATCH 4/9] v. 0.7.0 fixes for Solr. 8.5.2 Added tests which points out when you breakl something, but you will have to setup zk and solr for development now. Added docstrings and generated codox doc. Moved the connection ns into folder connections. Added and fixed support for childdocs. (PR: 16) Added and fixed support to create fq as hashmaps (PR: 12) --- .gitignore | 1 + .nrepl-port | 2 +- README.md | 47 +- doc/css/default.css | 551 ++++++++ doc/css/highlight.css | 97 ++ doc/flux.client.html | 61 + doc/flux.collections.html | 55 + doc/flux.connections.cloud.html | 13 + doc/flux.connections.embedded.html | 29 + doc/flux.connections.http.html | 9 + doc/flux.converter.html | 11 + doc/flux.core.html | 13 + doc/flux.query.html | 19 + doc/flux.update.html | 11 + doc/index.html | 3 + doc/intro.md | 3 - doc/js/highlight.min.js | 2 + doc/js/jquery.min.js | 4 + doc/js/page_effects.js | 112 ++ project.clj | 8 +- solr/flux_test/core.properties | 3 + solr/flux_test/data/index/segments_d | Bin 0 -> 135 bytes solr/flux_test/data/index/write.lock | 0 solr/flux_test/lang/contractions_ca.txt | 8 + solr/flux_test/lang/contractions_fr.txt | 15 + solr/flux_test/lang/contractions_ga.txt | 5 + solr/flux_test/lang/contractions_it.txt | 23 + solr/flux_test/lang/hyphenations_ga.txt | 5 + solr/flux_test/lang/stemdict_nl.txt | 6 + solr/flux_test/lang/stoptags_ja.txt | 420 ++++++ solr/flux_test/lang/stopwords_ar.txt | 125 ++ solr/flux_test/lang/stopwords_bg.txt | 193 +++ solr/flux_test/lang/stopwords_ca.txt | 220 ++++ solr/flux_test/lang/stopwords_cz.txt | 172 +++ solr/flux_test/lang/stopwords_da.txt | 110 ++ solr/flux_test/lang/stopwords_de.txt | 294 +++++ solr/flux_test/lang/stopwords_el.txt | 78 ++ solr/flux_test/lang/stopwords_en.txt | 54 + solr/flux_test/lang/stopwords_es.txt | 356 +++++ solr/flux_test/lang/stopwords_et.txt | 1603 +++++++++++++++++++++++ solr/flux_test/lang/stopwords_eu.txt | 99 ++ solr/flux_test/lang/stopwords_fa.txt | 313 +++++ solr/flux_test/lang/stopwords_fi.txt | 97 ++ solr/flux_test/lang/stopwords_fr.txt | 186 +++ solr/flux_test/lang/stopwords_ga.txt | 110 ++ solr/flux_test/lang/stopwords_gl.txt | 161 +++ solr/flux_test/lang/stopwords_hi.txt | 235 ++++ solr/flux_test/lang/stopwords_hu.txt | 211 +++ solr/flux_test/lang/stopwords_hy.txt | 46 + solr/flux_test/lang/stopwords_id.txt | 359 +++++ solr/flux_test/lang/stopwords_it.txt | 303 +++++ solr/flux_test/lang/stopwords_ja.txt | 127 ++ solr/flux_test/lang/stopwords_lv.txt | 172 +++ solr/flux_test/lang/stopwords_nl.txt | 119 ++ solr/flux_test/lang/stopwords_no.txt | 194 +++ solr/flux_test/lang/stopwords_pt.txt | 253 ++++ solr/flux_test/lang/stopwords_ro.txt | 233 ++++ solr/flux_test/lang/stopwords_ru.txt | 243 ++++ solr/flux_test/lang/stopwords_sv.txt | 133 ++ solr/flux_test/lang/stopwords_th.txt | 119 ++ solr/flux_test/lang/stopwords_tr.txt | 212 +++ solr/flux_test/lang/userdict_ja.txt | 29 + solr/flux_test/managed-schema | 1024 +++++++++++++++ solr/flux_test/protwords.txt | 21 + solr/flux_test/solrconfig.xml | 1226 +++++++++++++++++ solr/flux_test/stopwords.txt | 14 + solr/flux_test/synonyms.txt | 29 + solr/solr.xml | 1380 +------------------ src/flux/client.clj | 101 +- src/flux/collections.clj | 2 +- src/flux/connections/cloud.clj | 20 +- src/flux/connections/embedded.clj | 75 +- src/flux/connections/http.clj | 14 +- src/flux/converter.clj | 26 +- src/flux/core.clj | 35 +- src/flux/query.clj | 72 +- src/flux/update.clj | 18 +- test/src/flux/connections/cloud.clj | 52 +- test/src/flux/connections/embedded.clj | 70 +- test/src/flux/connections/http.clj | 35 +- test/src/flux/update.clj | 11 +- 81 files changed, 11399 insertions(+), 1521 deletions(-) create mode 100644 doc/css/default.css create mode 100644 doc/css/highlight.css create mode 100644 doc/flux.client.html create mode 100644 doc/flux.collections.html create mode 100644 doc/flux.connections.cloud.html create mode 100644 doc/flux.connections.embedded.html create mode 100644 doc/flux.connections.http.html create mode 100644 doc/flux.converter.html create mode 100644 doc/flux.core.html create mode 100644 doc/flux.query.html create mode 100644 doc/flux.update.html create mode 100644 doc/index.html delete mode 100644 doc/intro.md create mode 100644 doc/js/highlight.min.js create mode 100644 doc/js/jquery.min.js create mode 100644 doc/js/page_effects.js create mode 100644 solr/flux_test/core.properties create mode 100644 solr/flux_test/data/index/segments_d create mode 100644 solr/flux_test/data/index/write.lock create mode 100644 solr/flux_test/lang/contractions_ca.txt create mode 100644 solr/flux_test/lang/contractions_fr.txt create mode 100644 solr/flux_test/lang/contractions_ga.txt create mode 100644 solr/flux_test/lang/contractions_it.txt create mode 100644 solr/flux_test/lang/hyphenations_ga.txt create mode 100644 solr/flux_test/lang/stemdict_nl.txt create mode 100644 solr/flux_test/lang/stoptags_ja.txt create mode 100644 solr/flux_test/lang/stopwords_ar.txt create mode 100644 solr/flux_test/lang/stopwords_bg.txt create mode 100644 solr/flux_test/lang/stopwords_ca.txt create mode 100644 solr/flux_test/lang/stopwords_cz.txt create mode 100644 solr/flux_test/lang/stopwords_da.txt create mode 100644 solr/flux_test/lang/stopwords_de.txt create mode 100644 solr/flux_test/lang/stopwords_el.txt create mode 100644 solr/flux_test/lang/stopwords_en.txt create mode 100644 solr/flux_test/lang/stopwords_es.txt create mode 100644 solr/flux_test/lang/stopwords_et.txt create mode 100644 solr/flux_test/lang/stopwords_eu.txt create mode 100644 solr/flux_test/lang/stopwords_fa.txt create mode 100644 solr/flux_test/lang/stopwords_fi.txt create mode 100644 solr/flux_test/lang/stopwords_fr.txt create mode 100644 solr/flux_test/lang/stopwords_ga.txt create mode 100644 solr/flux_test/lang/stopwords_gl.txt create mode 100644 solr/flux_test/lang/stopwords_hi.txt create mode 100644 solr/flux_test/lang/stopwords_hu.txt create mode 100644 solr/flux_test/lang/stopwords_hy.txt create mode 100644 solr/flux_test/lang/stopwords_id.txt create mode 100644 solr/flux_test/lang/stopwords_it.txt create mode 100644 solr/flux_test/lang/stopwords_ja.txt create mode 100644 solr/flux_test/lang/stopwords_lv.txt create mode 100644 solr/flux_test/lang/stopwords_nl.txt create mode 100644 solr/flux_test/lang/stopwords_no.txt create mode 100644 solr/flux_test/lang/stopwords_pt.txt create mode 100644 solr/flux_test/lang/stopwords_ro.txt create mode 100644 solr/flux_test/lang/stopwords_ru.txt create mode 100644 solr/flux_test/lang/stopwords_sv.txt create mode 100644 solr/flux_test/lang/stopwords_th.txt create mode 100644 solr/flux_test/lang/stopwords_tr.txt create mode 100644 solr/flux_test/lang/userdict_ja.txt create mode 100644 solr/flux_test/managed-schema create mode 100644 solr/flux_test/protwords.txt create mode 100644 solr/flux_test/solrconfig.xml create mode 100644 solr/flux_test/stopwords.txt create mode 100644 solr/flux_test/synonyms.txt diff --git a/.gitignore b/.gitignore index f706bde..5a5e967 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ pom.xml .lein-deps-sum .lein-repl-history /target/ +/solr/flux_test/data/tlog/ \ No newline at end of file diff --git a/.nrepl-port b/.nrepl-port index f0552f7..0569d78 100644 --- a/.nrepl-port +++ b/.nrepl-port @@ -1 +1 @@ -55603 \ No newline at end of file +60545 \ No newline at end of file diff --git a/README.md b/README.md index 0510733..7603ff4 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ # flux -A Clojure based Solr client. The latest stable version of flux is 0.6.3, which supports Solr `8.5.2`. +A Clojure based Solr client. The latest stable version of flux is 0.7.0, which supports Solr `8.5.2` and java 14. +For Java 12 support change the `slf4j` in `project.clj`. ## Installation (Leiningen) To include the Flux library, add the following to your `:dependencies`: - [com.codesignals/flux "0.6.3"] + [com.codesignals/flux "0.7.0"] ## Usage @@ -166,6 +167,18 @@ Once a connection as been created, use the `with-connection` macro to wrap clien (q/create-query-request :post "/docs" {:q "etc"})) ``` +new with version `0.7.0` you can now use the `set-default-connection`. All client calls will use this connection if no other connection is given by `with-connection`. + +```clojure +(require '[flux.core :as flux] + '[flux.query :as q]) + +(flux/set-default-connection conn) +(flux/add [{:id 1} {:id 2}]) +(flux/commit) +(flux/query "*:*") +``` + #### Query options You can also specify additional query options (filter queries, fields, ...): ```clojure @@ -173,7 +186,37 @@ You can also specify additional query options (filter queries, fields, ...): (flux/query "*:*" options) ``` +#### Special options + +In past all values returned by Solr were strings, flux now tries to convert them back into Doubles or Longs too. +If you prefer the old way the `->clojure` function worked or need a possible better performance you can change back to the old way by: + +```clojure +(require '[flux.converter :refer [set-stringoutput-only]]) + +;; Only strings in return +(set-stringoutput-only true) +;; Allow to try to parse +(set-stringoutput-only false) +``` + ### Test + +Prepare a local zookeeper and solr with collection flux_test then run the tests. + +Usually it's downloading both programms, configure zookeeper and start them, +then create the collection or use parametered start to do so. + +Help config zk: [zookeeper minimum config](https://lucene.apache.org/solr/guide/8_5/setting-up-an-external-zookeeper-ensemble.html#initial-configuration) + +Help start zk: [zookeeper startup](https://lucene.apache.org/solr/guide/8_5/setting-up-an-external-zookeeper-ensemble.html#starting-and-stopping-zookeeper) + +Solr cloud start: [solr sstartup](https://lucene.apache.org/solr/guide/8_5/solr-tutorial.html#launch-solr-in-solrcloud-mode) + +remember one node is enough in dev-test! + +After you are done verify with the following command that nothing is broken. Do not forget to write docstrings and tests! + ```shell lein midje ``` diff --git a/doc/css/default.css b/doc/css/default.css new file mode 100644 index 0000000..33f78fe --- /dev/null +++ b/doc/css/default.css @@ -0,0 +1,551 @@ +body { + font-family: Helvetica, Arial, sans-serif; + font-size: 15px; +} + +pre, code { + font-family: Monaco, DejaVu Sans Mono, Consolas, monospace; + font-size: 9pt; + margin: 15px 0; +} + +h1 { + font-weight: normal; + font-size: 29px; + margin: 10px 0 2px 0; + padding: 0; +} + +h2 { + font-weight: normal; + font-size: 25px; +} + +h5.license { + margin: 9px 0 22px 0; + color: #555; + font-weight: normal; + font-size: 12px; + font-style: italic; +} + +.document h1, .namespace-index h1 { + font-size: 32px; + margin-top: 12px; +} + +#header, #content, .sidebar { + position: fixed; +} + +#header { + top: 0; + left: 0; + right: 0; + height: 22px; + color: #f5f5f5; + padding: 5px 7px; +} + +#content { + top: 32px; + right: 0; + bottom: 0; + overflow: auto; + background: #fff; + color: #333; + padding: 0 18px; +} + +.sidebar { + position: fixed; + top: 32px; + bottom: 0; + overflow: auto; +} + +.sidebar.primary { + background: #e2e2e2; + border-right: solid 1px #cccccc; + left: 0; + width: 250px; +} + +.sidebar.secondary { + background: #f2f2f2; + border-right: solid 1px #d7d7d7; + left: 251px; + width: 200px; +} + +#content.namespace-index, #content.document { + left: 251px; +} + +#content.namespace-docs { + left: 452px; +} + +#content.document { + padding-bottom: 10%; +} + +#header { + background: #3f3f3f; + box-shadow: 0 0 8px rgba(0, 0, 0, 0.4); + z-index: 100; +} + +#header h1 { + margin: 0; + padding: 0; + font-size: 18px; + font-weight: lighter; + text-shadow: -1px -1px 0px #333; +} + +#header h1 .project-version { + font-weight: normal; +} + +.project-version { + padding-left: 0.15em; +} + +#header a, .sidebar a { + display: block; + text-decoration: none; +} + +#header a { + color: #f5f5f5; +} + +.sidebar a { + color: #333; +} + +#header h2 { + float: right; + font-size: 9pt; + font-weight: normal; + margin: 4px 3px; + padding: 0; + color: #bbb; +} + +#header h2 a { + display: inline; +} + +.sidebar h3 { + margin: 0; + padding: 10px 13px 0 13px; + font-size: 19px; + font-weight: lighter; +} + +.sidebar h3 a { + color: #444; +} + +.sidebar h3.no-link { + color: #636363; +} + +.sidebar ul { + padding: 7px 0 6px 0; + margin: 0; +} + +.sidebar ul.index-link { + padding-bottom: 4px; +} + +.sidebar li { + display: block; + vertical-align: middle; +} + +.sidebar li a, .sidebar li .no-link { + border-left: 3px solid transparent; + padding: 0 10px; + white-space: nowrap; +} + +.sidebar li .no-link { + display: block; + color: #777; + font-style: italic; +} + +.sidebar li .inner { + display: inline-block; + padding-top: 7px; + height: 24px; +} + +.sidebar li a, .sidebar li .tree { + height: 31px; +} + +.depth-1 .inner { padding-left: 2px; } +.depth-2 .inner { padding-left: 6px; } +.depth-3 .inner { padding-left: 20px; } +.depth-4 .inner { padding-left: 34px; } +.depth-5 .inner { padding-left: 48px; } +.depth-6 .inner { padding-left: 62px; } + +.sidebar li .tree { + display: block; + float: left; + position: relative; + top: -10px; + margin: 0 4px 0 0; + padding: 0; +} + +.sidebar li.depth-1 .tree { + display: none; +} + +.sidebar li .tree .top, .sidebar li .tree .bottom { + display: block; + margin: 0; + padding: 0; + width: 7px; +} + +.sidebar li .tree .top { + border-left: 1px solid #aaa; + border-bottom: 1px solid #aaa; + height: 19px; +} + +.sidebar li .tree .bottom { + height: 22px; +} + +.sidebar li.branch .tree .bottom { + border-left: 1px solid #aaa; +} + +.sidebar.primary li.current a { + border-left: 3px solid #a33; + color: #a33; +} + +.sidebar.secondary li.current a { + border-left: 3px solid #33a; + color: #33a; +} + +.namespace-index h2 { + margin: 30px 0 0 0; +} + +.namespace-index h3 { + font-size: 16px; + font-weight: bold; + margin-bottom: 0; +} + +.namespace-index .topics { + padding-left: 30px; + margin: 11px 0 0 0; +} + +.namespace-index .topics li { + padding: 5px 0; +} + +.namespace-docs h3 { + font-size: 18px; + font-weight: bold; +} + +.public h3 { + margin: 0; + float: left; +} + +.usage { + clear: both; +} + +.public { + margin: 0; + border-top: 1px solid #e0e0e0; + padding-top: 14px; + padding-bottom: 6px; +} + +.public:last-child { + margin-bottom: 20%; +} + +.members .public:last-child { + margin-bottom: 0; +} + +.members { + margin: 15px 0; +} + +.members h4 { + color: #555; + font-weight: normal; + font-variant: small-caps; + margin: 0 0 5px 0; +} + +.members .inner { + padding-top: 5px; + padding-left: 12px; + margin-top: 2px; + margin-left: 7px; + border-left: 1px solid #bbb; +} + +#content .members .inner h3 { + font-size: 12pt; +} + +.members .public { + border-top: none; + margin-top: 0; + padding-top: 6px; + padding-bottom: 0; +} + +.members .public:first-child { + padding-top: 0; +} + +h4.type, +h4.dynamic, +h4.added, +h4.deprecated { + float: left; + margin: 3px 10px 15px 0; + font-size: 15px; + font-weight: bold; + font-variant: small-caps; +} + +.public h4.type, +.public h4.dynamic, +.public h4.added, +.public h4.deprecated { + font-size: 13px; + font-weight: bold; + margin: 3px 0 0 10px; +} + +.members h4.type, +.members h4.added, +.members h4.deprecated { + margin-top: 1px; +} + +h4.type { + color: #717171; +} + +h4.dynamic { + color: #9933aa; +} + +h4.added { + color: #508820; +} + +h4.deprecated { + color: #880000; +} + +.namespace { + margin-bottom: 30px; +} + +.namespace:last-child { + margin-bottom: 10%; +} + +.index { + padding: 0; + font-size: 80%; + margin: 15px 0; + line-height: 16px; +} + +.index * { + display: inline; +} + +.index p { + padding-right: 3px; +} + +.index li { + padding-right: 5px; +} + +.index ul { + padding-left: 0; +} + +.type-sig { + clear: both; + color: #088; +} + +.type-sig pre { + padding-top: 10px; + margin: 0; +} + +.usage code { + display: block; + color: #008; + margin: 2px 0; +} + +.usage code:first-child { + padding-top: 10px; +} + +p { + margin: 15px 0; +} + +.public p:first-child, .public pre.plaintext { + margin-top: 12px; +} + +.doc { + margin: 0 0 26px 0; + clear: both; +} + +.public .doc { + margin: 0; +} + +.namespace-index .doc { + margin-bottom: 20px; +} + +.namespace-index .namespace .doc { + margin-bottom: 10px; +} + +.markdown p, .markdown li, .markdown dt, .markdown dd, .markdown td { + line-height: 22px; +} + +.markdown li { + padding: 2px 0; +} + +.markdown h2 { + font-weight: normal; + font-size: 25px; + margin: 30px 0 10px 0; +} + +.markdown h3 { + font-weight: normal; + font-size: 20px; + margin: 30px 0 0 0; +} + +.markdown h4 { + font-size: 15px; + margin: 22px 0 -4px 0; +} + +.doc, .public, .namespace .index { + max-width: 680px; + overflow-x: visible; +} + +.markdown pre > code { + display: block; + padding: 10px; +} + +.markdown pre > code, .src-link a { + border: 1px solid #e4e4e4; + border-radius: 2px; +} + +.markdown code:not(.hljs), .src-link a { + background: #f6f6f6; +} + +pre.deps { + display: inline-block; + margin: 0 10px; + border: 1px solid #e4e4e4; + border-radius: 2px; + padding: 10px; + background-color: #f6f6f6; +} + +.markdown hr { + border-style: solid; + border-top: none; + color: #ccc; +} + +.doc ul, .doc ol { + padding-left: 30px; +} + +.doc table { + border-collapse: collapse; + margin: 0 10px; +} + +.doc table td, .doc table th { + border: 1px solid #dddddd; + padding: 4px 6px; +} + +.doc table th { + background: #f2f2f2; +} + +.doc dl { + margin: 0 10px 20px 10px; +} + +.doc dl dt { + font-weight: bold; + margin: 0; + padding: 3px 0; + border-bottom: 1px solid #ddd; +} + +.doc dl dd { + padding: 5px 0; + margin: 0 0 5px 10px; +} + +.doc abbr { + border-bottom: 1px dotted #333; + font-variant: none; + cursor: help; +} + +.src-link { + margin-bottom: 15px; +} + +.src-link a { + font-size: 70%; + padding: 1px 4px; + text-decoration: none; + color: #5555bb; +} diff --git a/doc/css/highlight.css b/doc/css/highlight.css new file mode 100644 index 0000000..d0cdaa3 --- /dev/null +++ b/doc/css/highlight.css @@ -0,0 +1,97 @@ +/* +github.com style (c) Vasily Polovnyov +*/ + +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + color: #333; + background: #f8f8f8; +} + +.hljs-comment, +.hljs-quote { + color: #998; + font-style: italic; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-subst { + color: #333; + font-weight: bold; +} + +.hljs-number, +.hljs-literal, +.hljs-variable, +.hljs-template-variable, +.hljs-tag .hljs-attr { + color: #008080; +} + +.hljs-string, +.hljs-doctag { + color: #d14; +} + +.hljs-title, +.hljs-section, +.hljs-selector-id { + color: #900; + font-weight: bold; +} + +.hljs-subst { + font-weight: normal; +} + +.hljs-type, +.hljs-class .hljs-title { + color: #458; + font-weight: bold; +} + +.hljs-tag, +.hljs-name, +.hljs-attribute { + color: #000080; + font-weight: normal; +} + +.hljs-regexp, +.hljs-link { + color: #009926; +} + +.hljs-symbol, +.hljs-bullet { + color: #990073; +} + +.hljs-built_in, +.hljs-builtin-name { + color: #0086b3; +} + +.hljs-meta { + color: #999; + font-weight: bold; +} + +.hljs-deletion { + background: #fdd; +} + +.hljs-addition { + background: #dfd; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} diff --git a/doc/flux.client.html b/doc/flux.client.html new file mode 100644 index 0000000..f60604d --- /dev/null +++ b/doc/flux.client.html @@ -0,0 +1,61 @@ + +flux.client documentation

flux.client

add

multimethod

Takes a map or list of maps which are converted into InputDocuments and be added to the server.

+
+
client
+
connection to work with (will be hidden by fn-generator)
+
doc
+
map or maps to be added to solr
+

commit

(commit client & {:as opts})

Commits the pending changes to solr

+
+
client
+
connection to work with (will be hidden by fn-generator)
+

delete-by-id

(delete-by-id client ids & {:as opts})

Deletes a list of documents by unique ID. Doesn’t work for child/nested docs.

+
+
client
+
connection to work with (will be hidden by fn-generator)
+
ids
+
a single id to delete or a list with ids of documents to delete
+

delete-by-query

(delete-by-query client q & {:as opts})

Deletes documents from the index based on a query

+
+
client
+
connection to work with (will be hidden by fn-generator)
+
q
+
string (the query expressing what documents to delete)
+

optimize

(optimize client)(optimize client wait-flush wait-searcher)(optimize client wait-flush wait-searcher max-segments)

Performs an explicit optimize, causing a merge of all segments to one. Note: In most cases it is not required to do explicit optimize

+
+
client
+
connection to work with (will be hidden by fn-generator)
+
waitFlush
+
[Optional ctor2] block until index changes are flushed to disk
+
wait-searcher +
+
[Optional ctor2] block until a new searcher is opened and registered as the main query searcher, making the changes visible
+
max-segments +
+
[Optional ctor3] optimizes down to at most this number of segments
+

ping

(ping client)

Issues a ping request to check if the server is alive

+
+
client
+
connection to work with (will be hidden by fn-generator)
+

query

(query client query & [options])

query solr

+
+
client
+
connection to work with (will be hidden by fn-generator)
+
+

query : string to send to solr most simple use *:*

request

(request client request)

sending a query request to solr

+
+
client
+
connection to work with (will be hidden by fn-generator)
+
request
+
request generated by create-query-request
+

rollback

(rollback client)

Performs a rollback of all non-committed documents pending. Note that this is not a true rollback as in databases. Content you have previously added may have been committed due to autoCommit, buffer full, other client performing a commit etc. Also note that rollbacks reset changes made by all clients. Use this method carefully when multiple clients, or multithreaded clients are in use.

+
+
client
+
connection to work with (will be hidden by fn-generator)
+

shutdown

(shutdown client)

Only applicable to embedded.

+

Function not more exist in Core and Cloud!
See docs

+
+
client
+
embedded solr to shutdown
+
\ No newline at end of file diff --git a/doc/flux.collections.html b/doc/flux.collections.html new file mode 100644 index 0000000..b2841d0 --- /dev/null +++ b/doc/flux.collections.html @@ -0,0 +1,55 @@ + +flux.collections documentation

flux.collections

active?

(active? [_ {:strs [state]}])

Filter-FN: Returns true if replica is in active state

all-replicas

(all-replicas connection collection)

Returns all replicas of given collection by quering the clusterstatus of collections

+
+
connection
+
given a connection created by create-collection
+
collection
+
the collection to search for
+

changeDefaultCollection

(changeDefaultCollection connection collection-name)

Change the default collection to work with

+
+
connection
+
given a connection created by create-collection
+
collection-name +
+
name of collection to use
+

create-collection

(create-collection connection collection-name num-shards)(create-collection connection collection-name num-shards replication-factor)(create-collection connection collection-name num-shards replication-factor params)

Create a SolrCloud collection

+
+
connection
+
given a connection created by create-collection
+
collection-name +
+
string. Name of collection to create
+
+

num-shards : integer. The number of shards this collection will be split into

+
+
replication-factor +
+
[optional] integer. The number of replicas to be created for each shard.
+
params
+
[optional] map. Additional configuration to crate a collection. Reserved keys: action, name, numShards and replicationFactor Possible keys: router.name, shards, nrtReplicas, tlogReplicas, pullReplicas, maxShardsPerNode, createNodeSet, createNodeSet.shuffle, collection.configName, router.field, property.name=value (core.properties), autoAddReplicas, async, rule, snitch, policy, waitForFinalState, withCollection, alias
+
+

See https://lucene.apache.org/solr/guide/8_5/collection-management.html#create for more info.

delete-collection

(delete-collection connection collection-name)

Delete a SolrCloud collection

+
+
connection
+
given a connection created by create-collection
+
collection-name +
+
name of collection to delete
+

down?

(down? [_ {:strs [state]}])

Filter-FN: Returns true if replica is in down state

hosted-by?

(hosted-by? replica host-port)

Special Filter-FN: Returns true if given replica is hosted locally by given host/port used to generate lists of replicas of a collection hosted by host/port

+
+
replica
+
x or % - the value to compare against, usually a list generated by all-replicas
+
host-port +
+
string host:port to search for
+

leader?

(leader? [_ {:keys [leader]}])

Filter-FN: Returns true if given replica is a leader

node-name

(node-name [_ {:strs [node_name]}])

Filter-FN: Returns the node name of the replica

not-active?

(not-active? replica)

Filter-FN: Returns true if replica is not in active state

recovering?

(recovering? [_ {:strs [state]}])

Filter-FN: Returns true if replica is in recovery state

wait-until-active

(wait-until-active connection collection host-port)

Waits (infinitely) until all solr replicas hosted by given host/port belonging to given collection are in active state

+
+
connection
+
given a connection created by create-collection
+
collection
+
the collection to search for
+
host-port +
+
string host:port to search for
+
\ No newline at end of file diff --git a/doc/flux.connections.cloud.html b/doc/flux.connections.cloud.html new file mode 100644 index 0000000..f7b255d --- /dev/null +++ b/doc/flux.connections.cloud.html @@ -0,0 +1,13 @@ + +flux.connections.cloud documentation

flux.connections.cloud

create

(create zk-hosts)(create zk-hosts chroot)(create zk-hosts chroot default-collection)

connect to a zookeeper cloud

+
+
zk-hosts +
+
a list of zookeepers one to connect to and rest as failover minimum like ["localhost:2181"]
+
chroot
+
the path to the root ZooKeeper node containing Solr data. “/” when empty
+
default-collection +
+
change to a give collection on connection already, can be a keyword :flux_test or a string "flux_test"
+
\ No newline at end of file diff --git a/doc/flux.connections.embedded.html b/doc/flux.connections.embedded.html new file mode 100644 index 0000000..c826069 --- /dev/null +++ b/doc/flux.connections.embedded.html @@ -0,0 +1,29 @@ + +flux.connections.embedded documentation

flux.connections.embedded

create

(create core-container core-name)

Connects to embedded server.

+
+
core-container +
+
created by create-core-container to use
+
core-name +
+
change to a give collection on connection already, can be a keyword :flux_test or a string "flux_test"
+

create-core

(create-core core-container core-name)

Creates a new core.

+
+
core-container +
+
created by create-core-container to use
+
core-name +
+
change to a give collection on connection already, can be a keyword :flux_test or a string "flux_test"
+

create-core-container

(create-core-container)(create-core-container solr-home-path)(create-core-container solr-home-path solr-config-path)

Creates a CoreContainer from a default path (solr folder beside .jar?), by a solr-home path or by solr-home path and solr-config.xml path.

+
+
solr-home-path +
+
string to solr home
+
solr-config-path +
+
If the latter is used, $home/$solr.xml works well, as the new core.properties discovery mode.
+
+

See: org.apache.solr.core.CoreLocator and http://wiki.apache.org/solr/Core%20Discovery%20(4.4%20and%20beyond) Note: If using core.properties only, it is required to call (.load core-container) before creating the EmbeddedSolrServer instance.

+
\ No newline at end of file diff --git a/doc/flux.connections.http.html b/doc/flux.connections.http.html new file mode 100644 index 0000000..472a570 --- /dev/null +++ b/doc/flux.connections.http.html @@ -0,0 +1,9 @@ + +flux.connections.http documentation

flux.connections.http

create

(create base-url core-name)

Connect to solr by url and core-name

+

base-url : string like "http://localhost:8983/solr"

+
+
core-name +
+
the core to connect to like :flux_test or "flux_test"
+
\ No newline at end of file diff --git a/doc/flux.converter.html b/doc/flux.converter.html new file mode 100644 index 0000000..5b90b14 --- /dev/null +++ b/doc/flux.converter.html @@ -0,0 +1,11 @@ + +flux.converter documentation

flux.converter

->clojure

multimethod

Converts the solr Data back to clojure collections

+
+
class
+
can be NamedList, SolrDocumentList, SolrDocument, SolrResponse, SolrInputDocument, java.util.LinkedHashMap, String or whatever (whatever is returned only)
+

set-stringoutput-only

(set-stringoutput-only bool)

Set to true if you prefer string output instead of Longs, Doubles and Booleans in return

+
+
bool
+
true = strings only; false = try to parse output
+

string-only?

\ No newline at end of file diff --git a/doc/flux.core.html b/doc/flux.core.html new file mode 100644 index 0000000..585c227 --- /dev/null +++ b/doc/flux.core.html @@ -0,0 +1,13 @@ + +flux.core documentation

flux.core

*connection*

dynamic

_default-connection

add

(add & args__3503__auto__)

FN created by macro! See: flux.client for description!

commit

(commit & args__3503__auto__)

FN created by macro! See: flux.client for description!

create-fn

macro

(create-fn name)

function to create those client functions without need of passsing in a connection all time

+
+
name
+
name of fn existent in flux.client ns
+

delete-by-id

(delete-by-id & args__3503__auto__)

FN created by macro! See: flux.client for description!

delete-by-query

(delete-by-query & args__3503__auto__)

FN created by macro! See: flux.client for description!

optimize

(optimize & args__3503__auto__)

FN created by macro! See: flux.client for description!

ping

(ping & args__3503__auto__)

FN created by macro! See: flux.client for description!

query

(query & args__3503__auto__)

FN created by macro! See: flux.client for description!

request

(request & args__3503__auto__)

FN created by macro! See: flux.client for description!

rollback

(rollback & args__3503__auto__)

FN created by macro! See: flux.client for description!

set-default-connection

(set-default-connection connection)

Set the solr connection that flux should use by default when no alternative is specified.

+
+
connection
+
connection to solr generated by create
+

shutdown

(shutdown & args__3503__auto__)

FN created by macro! See: flux.client for description!

with-connection

macro

(with-connection connection & body)

takes an connection and executes the body with this connection

+

connection : connection to solr generated by create

+

& body : all functions to be executed with given connection

\ No newline at end of file diff --git a/doc/flux.query.html b/doc/flux.query.html new file mode 100644 index 0000000..47de69c --- /dev/null +++ b/doc/flux.query.html @@ -0,0 +1,19 @@ + +flux.query documentation

flux.query

create-query

(create-query query options)

extracts the :fq option (filter query) and adds it to solr query

+
+
query
+
the normal query of solr like *:* added to :q
+
+

options : searches for :fq and converts it into filter conditions

create-query-request

(create-query-request params)(create-query-request path params)(create-query-request method path params)

creates a solr Request of given args

+

method : [Optional ctor3] :get or :post be used to query

+
+
path
+
[Optional ctor2+3] nil or path/directory to query against?
+
params
+
parameters at least {:q “etc”}
+

format-filter-queries

(format-filter-queries hm)

converts input into filter query for solr

+
+
hm
+
fq map of conditions
+

method-map

maps SolrRequest$METHODs to :get and :post

\ No newline at end of file diff --git a/doc/flux.update.html b/doc/flux.update.html new file mode 100644 index 0000000..8b66342 --- /dev/null +++ b/doc/flux.update.html @@ -0,0 +1,11 @@ + +flux.update documentation

flux.update

create-doc

(create-doc document-map)

creates the inputDocument for solr.

+
+
document-map +
+
takes a map to convert, adding children with the keyword :__childDocuments [...].
+
+

examples {:id 1 :map {:int 22 :string "abc"}} will create "SolrInputDocument(fields: [id=1, map={string=abc, int=22}])"

+

or {:id 1 :__childDocuments [{:id 2} {:id 3}]} will create "SolrInputDocument(fields: [id=1], + children: [SolrInputDocument(fields: [id=2]), SolrInputDocument(fields: [id=3])])"

\ No newline at end of file diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..c3d0242 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,3 @@ + +Flux 0.7.0

Flux 0.7.0

Released under the Eclipse Public License

A clojure client library for Solr 8.5.2.

Installation

To install, add the following dependency to your project or build file:

[com.codesignals/flux "0.7.0"]

Namespaces

flux.connections.cloud

Public variables and functions:

flux.connections.http

Public variables and functions:

flux.update

Public variables and functions:

\ No newline at end of file diff --git a/doc/intro.md b/doc/intro.md deleted file mode 100644 index 0d9d9d7..0000000 --- a/doc/intro.md +++ /dev/null @@ -1,3 +0,0 @@ -# Introduction to star - -TODO: write [great documentation](http://jacobian.org/writing/great-documentation/what-to-write/) diff --git a/doc/js/highlight.min.js b/doc/js/highlight.min.js new file mode 100644 index 0000000..6486ffd --- /dev/null +++ b/doc/js/highlight.min.js @@ -0,0 +1,2 @@ +/*! highlight.js v9.6.0 | BSD3 License | git.io/hljslicense */ +!function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function n(e){return e.replace(/[&<>]/gm,function(e){return I[e]})}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0===t.index}function a(e){return k.test(e)}function i(e){var n,t,r,i,o=e.className+" ";if(o+=e.parentNode?e.parentNode.className:"",t=B.exec(o))return R(t[1])?t[1]:"no-highlight";for(o=o.split(/\s+/),n=0,r=o.length;r>n;n++)if(i=o[n],a(i)||R(i))return i}function o(e,n){var t,r={};for(t in e)r[t]=e[t];if(n)for(t in n)r[t]=n[t];return r}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?a+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!==r[0].offset?e[0].offset"}function u(e){l+=""}function c(e){("start"===e.event?o:u)(e.node)}for(var s=0,l="",f=[];e.length||r.length;){var g=i();if(l+=n(a.substr(s,g[0].offset-s)),s=g[0].offset,g===e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g===e&&g.length&&g[0].offset===s);f.reverse().forEach(o)}else"start"===g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return l+n(a.substr(s))}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var u={},c=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");u[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?c("keyword",a.k):E(a.k).forEach(function(e){c(e,a.k[e])}),a.k=u}a.lR=t(a.l||/\w+/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),null==a.r&&(a.r=1),a.c||(a.c=[]);var s=[];a.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(o(e,n))}):s.push("self"===e?a:e)}),a.c=s,a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var l=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=l.length?t(l.join("|"),!0):{exec:function(){return null}}}}r(e)}function l(e,t,a,i){function o(e,n){var t,a;for(t=0,a=n.c.length;a>t;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function g(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function h(e,n,t,r){var a=r?"":y.classPrefix,i='',i+n+o}function p(){var e,t,r,a;if(!E.k)return n(B);for(a="",t=0,E.lR.lastIndex=0,r=E.lR.exec(B);r;)a+=n(B.substr(t,r.index-t)),e=g(E,r),e?(M+=e[1],a+=h(e[0],n(r[0]))):a+=n(r[0]),t=E.lR.lastIndex,r=E.lR.exec(B);return a+n(B.substr(t))}function d(){var e="string"==typeof E.sL;if(e&&!x[E.sL])return n(B);var t=e?l(E.sL,B,!0,L[E.sL]):f(B,E.sL.length?E.sL:void 0);return E.r>0&&(M+=t.r),e&&(L[E.sL]=t.top),h(t.language,t.value,!1,!0)}function b(){k+=null!=E.sL?d():p(),B=""}function v(e){k+=e.cN?h(e.cN,"",!0):"",E=Object.create(e,{parent:{value:E}})}function m(e,n){if(B+=e,null==n)return b(),0;var t=o(n,E);if(t)return t.skip?B+=n:(t.eB&&(B+=n),b(),t.rB||t.eB||(B=n)),v(t,n),t.rB?0:n.length;var r=u(E,n);if(r){var a=E;a.skip?B+=n:(a.rE||a.eE||(B+=n),b(),a.eE&&(B=n));do E.cN&&(k+=C),E.skip||(M+=E.r),E=E.parent;while(E!==r.parent);return r.starts&&v(r.starts,""),a.rE?0:n.length}if(c(n,E))throw new Error('Illegal lexeme "'+n+'" for mode "'+(E.cN||"")+'"');return B+=n,n.length||1}var N=R(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var w,E=i||N,L={},k="";for(w=E;w!==N;w=w.parent)w.cN&&(k=h(w.cN,"",!0)+k);var B="",M=0;try{for(var I,j,O=0;;){if(E.t.lastIndex=O,I=E.t.exec(t),!I)break;j=m(t.substr(O,I.index-O),I[0]),O=I.index+j}for(m(t.substr(O)),w=E;w.parent;w=w.parent)w.cN&&(k+=C);return{r:M,value:k,language:e,top:E}}catch(T){if(T.message&&-1!==T.message.indexOf("Illegal"))return{r:0,value:n(t)};throw T}}function f(e,t){t=t||y.languages||E(x);var r={r:0,value:n(e)},a=r;return t.filter(R).forEach(function(n){var t=l(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}),a.language&&(r.second_best=a),r}function g(e){return y.tabReplace||y.useBR?e.replace(M,function(e,n){return y.useBR&&"\n"===e?"
":y.tabReplace?n.replace(/\t/g,y.tabReplace):void 0}):e}function h(e,n,t){var r=n?L[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function p(e){var n,t,r,o,s,p=i(e);a(p)||(y.useBR?(n=document.createElementNS("http://www.w3.org/1999/xhtml","div"),n.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):n=e,s=n.textContent,r=p?l(p,s,!0):f(s),t=u(n),t.length&&(o=document.createElementNS("http://www.w3.org/1999/xhtml","div"),o.innerHTML=r.value,r.value=c(t,u(o),s)),r.value=g(r.value),e.innerHTML=r.value,e.className=h(e.className,p,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function d(e){y=o(y,e)}function b(){if(!b.called){b.called=!0;var e=document.querySelectorAll("pre code");w.forEach.call(e,p)}}function v(){addEventListener("DOMContentLoaded",b,!1),addEventListener("load",b,!1)}function m(n,t){var r=x[n]=t(e);r.aliases&&r.aliases.forEach(function(e){L[e]=n})}function N(){return E(x)}function R(e){return e=(e||"").toLowerCase(),x[e]||x[L[e]]}var w=[],E=Object.keys,x={},L={},k=/^(no-?highlight|plain|text)$/i,B=/\blang(?:uage)?-([\w-]+)\b/i,M=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,C="
",y={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},I={"&":"&","<":"<",">":">"};return e.highlight=l,e.highlightAuto=f,e.fixMarkup=g,e.highlightBlock=p,e.configure=d,e.initHighlighting=b,e.initHighlightingOnLoad=v,e.registerLanguage=m,e.listLanguages=N,e.getLanguage=R,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|like)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e});hljs.registerLanguage("clojure",function(e){var t={"builtin-name":"def defonce cond apply if-not if-let if not not= = < > <= >= == + / * - rem quot neg? pos? delay? symbol? keyword? true? false? integer? empty? coll? list? set? ifn? fn? associative? sequential? sorted? counted? reversible? number? decimal? class? distinct? isa? float? rational? reduced? ratio? odd? even? char? seq? vector? string? map? nil? contains? zero? instance? not-every? not-any? libspec? -> ->> .. . inc compare do dotimes mapcat take remove take-while drop letfn drop-last take-last drop-while while intern condp case reduced cycle split-at split-with repeat replicate iterate range merge zipmap declare line-seq sort comparator sort-by dorun doall nthnext nthrest partition eval doseq await await-for let agent atom send send-off release-pending-sends add-watch mapv filterv remove-watch agent-error restart-agent set-error-handler error-handler set-error-mode! error-mode shutdown-agents quote var fn loop recur throw try monitor-enter monitor-exit defmacro defn defn- macroexpand macroexpand-1 for dosync and or when when-not when-let comp juxt partial sequence memoize constantly complement identity assert peek pop doto proxy defstruct first rest cons defprotocol cast coll deftype defrecord last butlast sigs reify second ffirst fnext nfirst nnext defmulti defmethod meta with-meta ns in-ns create-ns import refer keys select-keys vals key val rseq name namespace promise into transient persistent! conj! assoc! dissoc! pop! disj! use class type num float double short byte boolean bigint biginteger bigdec print-method print-dup throw-if printf format load compile get-in update-in pr pr-on newline flush read slurp read-line subvec with-open memfn time re-find re-groups rand-int rand mod locking assert-valid-fdecl alias resolve ref deref refset swap! reset! set-validator! compare-and-set! alter-meta! reset-meta! commute get-validator alter ref-set ref-history-count ref-min-history ref-max-history ensure sync io! new next conj set! to-array future future-call into-array aset gen-class reduce map filter find empty hash-map hash-set sorted-map sorted-map-by sorted-set sorted-set-by vec vector seq flatten reverse assoc dissoc list disj get union difference intersection extend extend-type extend-protocol int nth delay count concat chunk chunk-buffer chunk-append chunk-first chunk-rest max min dec unchecked-inc-int unchecked-inc unchecked-dec-inc unchecked-dec unchecked-negate unchecked-add-int unchecked-add unchecked-subtract-int unchecked-subtract chunk-next chunk-cons chunked-seq? prn vary-meta lazy-seq spread list* str find-keyword keyword symbol gensym force rationalize"},r="a-zA-Z_\\-!.?+*=<>&#'",n="["+r+"]["+r+"0-9/;:]*",a="[-+]?\\d+(\\.\\d+)?",o={b:n,r:0},s={cN:"number",b:a,r:0},i=e.inherit(e.QSM,{i:null}),c=e.C(";","$",{r:0}),d={cN:"literal",b:/\b(true|false|nil)\b/},l={b:"[\\[\\{]",e:"[\\]\\}]"},m={cN:"comment",b:"\\^"+n},p=e.C("\\^\\{","\\}"),u={cN:"symbol",b:"[:]{1,2}"+n},f={b:"\\(",e:"\\)"},h={eW:!0,r:0},y={k:t,l:n,cN:"name",b:n,starts:h},b=[f,i,m,p,c,u,l,s,d,o];return f.c=[e.C("comment",""),y,h],h.c=b,l.c=b,{aliases:["clj"],i:/\S/,c:[f,i,m,p,c,u,l,s,d]}});hljs.registerLanguage("clojure-repl",function(e){return{c:[{cN:"meta",b:/^([\w.-]+|\s*#_)=>/,starts:{e:/$/,sL:"clojure"}}]}}); \ No newline at end of file diff --git a/doc/js/jquery.min.js b/doc/js/jquery.min.js new file mode 100644 index 0000000..73f33fb --- /dev/null +++ b/doc/js/jquery.min.js @@ -0,0 +1,4 @@ +/*! jQuery v1.11.0 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k="".trim,l={},m="1.11.0",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(n.isPlainObject(c)||(b=n.isArray(c)))?(b?(b=!1,f=a&&n.isArray(a)?a:[]):f=a&&n.isPlainObject(a)?a:{},g[d]=n.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray||function(a){return"array"===n.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(l.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&n.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:k&&!k.call("\ufeff\xa0")?function(a){return null==a?"":k.call(a)}:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),n.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||n.guid++,e):void 0},now:function(){return+new Date},support:l}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s="sizzle"+-new Date,t=a.document,u=0,v=0,w=eb(),x=eb(),y=eb(),z=function(a,b){return a===b&&(j=!0),0},A="undefined",B=1<<31,C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=D.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",M=L.replace("w","w#"),N="\\["+K+"*("+L+")"+K+"*(?:([*^$|!~]?=)"+K+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+M+")|)|)"+K+"*\\]",O=":("+L+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+N.replace(3,8)+")*)|.*)\\)|)",P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(O),U=new RegExp("^"+M+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L.replace("w","w*")+")"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=/'|\\/g,ab=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),bb=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{G.apply(D=H.call(t.childNodes),t.childNodes),D[t.childNodes.length].nodeType}catch(cb){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function db(a,b,d,e){var f,g,h,i,j,m,p,q,u,v;if((b?b.ownerDocument||b:t)!==l&&k(b),b=b||l,d=d||[],!a||"string"!=typeof a)return d;if(1!==(i=b.nodeType)&&9!==i)return[];if(n&&!e){if(f=Z.exec(a))if(h=f[1]){if(9===i){if(g=b.getElementById(h),!g||!g.parentNode)return d;if(g.id===h)return d.push(g),d}else if(b.ownerDocument&&(g=b.ownerDocument.getElementById(h))&&r(b,g)&&g.id===h)return d.push(g),d}else{if(f[2])return G.apply(d,b.getElementsByTagName(a)),d;if((h=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(h)),d}if(c.qsa&&(!o||!o.test(a))){if(q=p=s,u=b,v=9===i&&a,1===i&&"object"!==b.nodeName.toLowerCase()){m=ob(a),(p=b.getAttribute("id"))?q=p.replace(_,"\\$&"):b.setAttribute("id",q),q="[id='"+q+"'] ",j=m.length;while(j--)m[j]=q+pb(m[j]);u=$.test(a)&&mb(b.parentNode)||b,v=m.join(",")}if(v)try{return G.apply(d,u.querySelectorAll(v)),d}catch(w){}finally{p||b.removeAttribute("id")}}}return xb(a.replace(P,"$1"),b,d,e)}function eb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function fb(a){return a[s]=!0,a}function gb(a){var b=l.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function hb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function ib(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||B)-(~a.sourceIndex||B);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function jb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function kb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function lb(a){return fb(function(b){return b=+b,fb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function mb(a){return a&&typeof a.getElementsByTagName!==A&&a}c=db.support={},f=db.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},k=db.setDocument=function(a){var b,e=a?a.ownerDocument||a:t,g=e.defaultView;return e!==l&&9===e.nodeType&&e.documentElement?(l=e,m=e.documentElement,n=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){k()},!1):g.attachEvent&&g.attachEvent("onunload",function(){k()})),c.attributes=gb(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=gb(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(e.getElementsByClassName)&&gb(function(a){return a.innerHTML="
",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=gb(function(a){return m.appendChild(a).id=s,!e.getElementsByName||!e.getElementsByName(s).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==A&&n){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ab,bb);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ab,bb);return function(a){var c=typeof a.getAttributeNode!==A&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==A?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==A&&n?b.getElementsByClassName(a):void 0},p=[],o=[],(c.qsa=Y.test(e.querySelectorAll))&&(gb(function(a){a.innerHTML="",a.querySelectorAll("[t^='']").length&&o.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||o.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll(":checked").length||o.push(":checked")}),gb(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&o.push("name"+K+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||o.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),o.push(",.*:")})),(c.matchesSelector=Y.test(q=m.webkitMatchesSelector||m.mozMatchesSelector||m.oMatchesSelector||m.msMatchesSelector))&&gb(function(a){c.disconnectedMatch=q.call(a,"div"),q.call(a,"[s!='']:x"),p.push("!=",O)}),o=o.length&&new RegExp(o.join("|")),p=p.length&&new RegExp(p.join("|")),b=Y.test(m.compareDocumentPosition),r=b||Y.test(m.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},z=b?function(a,b){if(a===b)return j=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===t&&r(t,a)?-1:b===e||b.ownerDocument===t&&r(t,b)?1:i?I.call(i,a)-I.call(i,b):0:4&d?-1:1)}:function(a,b){if(a===b)return j=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],k=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:i?I.call(i,a)-I.call(i,b):0;if(f===g)return ib(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)k.unshift(c);while(h[d]===k[d])d++;return d?ib(h[d],k[d]):h[d]===t?-1:k[d]===t?1:0},e):l},db.matches=function(a,b){return db(a,null,null,b)},db.matchesSelector=function(a,b){if((a.ownerDocument||a)!==l&&k(a),b=b.replace(S,"='$1']"),!(!c.matchesSelector||!n||p&&p.test(b)||o&&o.test(b)))try{var d=q.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return db(b,l,null,[a]).length>0},db.contains=function(a,b){return(a.ownerDocument||a)!==l&&k(a),r(a,b)},db.attr=function(a,b){(a.ownerDocument||a)!==l&&k(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!n):void 0;return void 0!==f?f:c.attributes||!n?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},db.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},db.uniqueSort=function(a){var b,d=[],e=0,f=0;if(j=!c.detectDuplicates,i=!c.sortStable&&a.slice(0),a.sort(z),j){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return i=null,a},e=db.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=db.selectors={cacheLength:50,createPseudo:fb,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ab,bb),a[3]=(a[4]||a[5]||"").replace(ab,bb),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||db.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&db.error(a[0]),a},PSEUDO:function(a){var b,c=!a[5]&&a[2];return V.CHILD.test(a[0])?null:(a[3]&&void 0!==a[4]?a[2]=a[4]:c&&T.test(c)&&(b=ob(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ab,bb).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=w[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&w(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==A&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=db.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),t=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&t){k=q[s]||(q[s]={}),j=k[a]||[],n=j[0]===u&&j[1],m=j[0]===u&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[u,n,m];break}}else if(t&&(j=(b[s]||(b[s]={}))[a])&&j[0]===u)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(t&&((l[s]||(l[s]={}))[a]=[u,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||db.error("unsupported pseudo: "+a);return e[s]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?fb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:fb(function(a){var b=[],c=[],d=g(a.replace(P,"$1"));return d[s]?fb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:fb(function(a){return function(b){return db(a,b).length>0}}),contains:fb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:fb(function(a){return U.test(a||"")||db.error("unsupported lang: "+a),a=a.replace(ab,bb).toLowerCase(),function(b){var c;do if(c=n?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===m},focus:function(a){return a===l.activeElement&&(!l.hasFocus||l.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:lb(function(){return[0]}),last:lb(function(a,b){return[b-1]}),eq:lb(function(a,b,c){return[0>c?c+b:c]}),even:lb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:lb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:lb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:lb(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function qb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=v++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[u,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[s]||(b[s]={}),(h=i[d])&&h[0]===u&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function rb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function sb(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function tb(a,b,c,d,e,f){return d&&!d[s]&&(d=tb(d)),e&&!e[s]&&(e=tb(e,f)),fb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||wb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:sb(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=sb(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=sb(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ub(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],i=g||d.relative[" "],j=g?1:0,k=qb(function(a){return a===b},i,!0),l=qb(function(a){return I.call(b,a)>-1},i,!0),m=[function(a,c,d){return!g&&(d||c!==h)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>j;j++)if(c=d.relative[a[j].type])m=[qb(rb(m),c)];else{if(c=d.filter[a[j].type].apply(null,a[j].matches),c[s]){for(e=++j;f>e;e++)if(d.relative[a[e].type])break;return tb(j>1&&rb(m),j>1&&pb(a.slice(0,j-1).concat({value:" "===a[j-2].type?"*":""})).replace(P,"$1"),c,e>j&&ub(a.slice(j,e)),f>e&&ub(a=a.slice(e)),f>e&&pb(a))}m.push(c)}return rb(m)}function vb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,i,j,k){var m,n,o,p=0,q="0",r=f&&[],s=[],t=h,v=f||e&&d.find.TAG("*",k),w=u+=null==t?1:Math.random()||.1,x=v.length;for(k&&(h=g!==l&&g);q!==x&&null!=(m=v[q]);q++){if(e&&m){n=0;while(o=a[n++])if(o(m,g,i)){j.push(m);break}k&&(u=w)}c&&((m=!o&&m)&&p--,f&&r.push(m))}if(p+=q,c&&q!==p){n=0;while(o=b[n++])o(r,s,g,i);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=E.call(j));s=sb(s)}G.apply(j,s),k&&!f&&s.length>0&&p+b.length>1&&db.uniqueSort(j)}return k&&(u=w,h=t),r};return c?fb(f):f}g=db.compile=function(a,b){var c,d=[],e=[],f=y[a+" "];if(!f){b||(b=ob(a)),c=b.length;while(c--)f=ub(b[c]),f[s]?d.push(f):e.push(f);f=y(a,vb(e,d))}return f};function wb(a,b,c){for(var d=0,e=b.length;e>d;d++)db(a,b[d],c);return c}function xb(a,b,e,f){var h,i,j,k,l,m=ob(a);if(!f&&1===m.length){if(i=m[0]=m[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&c.getById&&9===b.nodeType&&n&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(ab,bb),b)||[])[0],!b)return e;a=a.slice(i.shift().value.length)}h=V.needsContext.test(a)?0:i.length;while(h--){if(j=i[h],d.relative[k=j.type])break;if((l=d.find[k])&&(f=l(j.matches[0].replace(ab,bb),$.test(i[0].type)&&mb(b.parentNode)||b))){if(i.splice(h,1),a=f.length&&pb(i),!a)return G.apply(e,f),e;break}}}return g(a,m)(f,b,!n,e,$.test(a)&&mb(b.parentNode)||b),e}return c.sortStable=s.split("").sort(z).join("")===s,c.detectDuplicates=!!j,k(),c.sortDetached=gb(function(a){return 1&a.compareDocumentPosition(l.createElement("div"))}),gb(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||hb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&gb(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||hb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),gb(function(a){return null==a.getAttribute("disabled")})||hb(J,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),db}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return n.inArray(a,b)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;e>b;b++)if(n.contains(d[b],this))return!0}));for(b=0;e>b;b++)n.find(a,d[b],c);return c=this.pushStack(e>1?n.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=a.document,A=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,B=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:A.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:z,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=z.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return y.find(a);this.length=1,this[0]=d}return this.context=z,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};B.prototype=n.fn,y=n(z);var C=/^(?:parents|prev(?:Until|All))/,D={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!n(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b,c=n(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(n.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?n.inArray(this[0],n(a)):n.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function E(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return E(a,"nextSibling")},prev:function(a){return E(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return n.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(D[a]||(e=n.unique(e)),C.test(a)&&(e=e.reverse())),this.pushStack(e)}});var F=/\S+/g,G={};function H(a){var b=G[a]={};return n.each(a.match(F)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?G[a]||H(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&n.each(arguments,function(a,c){var d;while((d=n.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){if(a===!0?!--n.readyWait:!n.isReady){if(!z.body)return setTimeout(n.ready);n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(z,[n]),n.fn.trigger&&n(z).trigger("ready").off("ready"))}}});function J(){z.addEventListener?(z.removeEventListener("DOMContentLoaded",K,!1),a.removeEventListener("load",K,!1)):(z.detachEvent("onreadystatechange",K),a.detachEvent("onload",K))}function K(){(z.addEventListener||"load"===event.type||"complete"===z.readyState)&&(J(),n.ready())}n.ready.promise=function(b){if(!I)if(I=n.Deferred(),"complete"===z.readyState)setTimeout(n.ready);else if(z.addEventListener)z.addEventListener("DOMContentLoaded",K,!1),a.addEventListener("load",K,!1);else{z.attachEvent("onreadystatechange",K),a.attachEvent("onload",K);var c=!1;try{c=null==a.frameElement&&z.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!n.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}J(),n.ready()}}()}return I.promise(b)};var L="undefined",M;for(M in n(l))break;l.ownLast="0"!==M,l.inlineBlockNeedsLayout=!1,n(function(){var a,b,c=z.getElementsByTagName("body")[0];c&&(a=z.createElement("div"),a.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",b=z.createElement("div"),c.appendChild(a).appendChild(b),typeof b.style.zoom!==L&&(b.style.cssText="border:0;margin:0;width:1px;padding:1px;display:inline;zoom:1",(l.inlineBlockNeedsLayout=3===b.offsetWidth)&&(c.style.zoom=1)),c.removeChild(a),a=b=null)}),function(){var a=z.createElement("div");if(null==l.deleteExpando){l.deleteExpando=!0;try{delete a.test}catch(b){l.deleteExpando=!1}}a=null}(),n.acceptData=function(a){var b=n.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(O,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}n.data(a,b,c)}else c=void 0}return c}function Q(a){var b;for(b in a)if(("data"!==b||!n.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function R(a,b,d,e){if(n.acceptData(a)){var f,g,h=n.expando,i=a.nodeType,j=i?n.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||n.guid++:h),j[k]||(j[k]=i?{}:{toJSON:n.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=n.extend(j[k],b):j[k].data=n.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[n.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[n.camelCase(b)])):f=g,f +}}function S(a,b,c){if(n.acceptData(a)){var d,e,f=a.nodeType,g=f?n.cache:a,h=f?a[n.expando]:n.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){n.isArray(b)?b=b.concat(n.map(b,n.camelCase)):b in d?b=[b]:(b=n.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!Q(d):!n.isEmptyObject(d))return}(c||(delete g[h].data,Q(g[h])))&&(f?n.cleanData([a],!0):l.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}n.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?n.cache[a[n.expando]]:a[n.expando],!!a&&!Q(a)},data:function(a,b,c){return R(a,b,c)},removeData:function(a,b){return S(a,b)},_data:function(a,b,c){return R(a,b,c,!0)},_removeData:function(a,b){return S(a,b,!0)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=n.data(f),1===f.nodeType&&!n._data(f,"parsedAttrs"))){c=g.length;while(c--)d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d]));n._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){n.data(this,a)}):arguments.length>1?this.each(function(){n.data(this,a,b)}):f?P(f,a,n.data(f,a)):void 0},removeData:function(a){return this.each(function(){n.removeData(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=n._data(a,b),c&&(!d||n.isArray(c)?d=n._data(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return n._data(a,c)||n._data(a,c,{empty:n.Callbacks("once memory").add(function(){n._removeData(a,b+"queue"),n._removeData(a,c)})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},X=/^(?:checkbox|radio)$/i;!function(){var a=z.createDocumentFragment(),b=z.createElement("div"),c=z.createElement("input");if(b.setAttribute("className","t"),b.innerHTML="
a",l.leadingWhitespace=3===b.firstChild.nodeType,l.tbody=!b.getElementsByTagName("tbody").length,l.htmlSerialize=!!b.getElementsByTagName("link").length,l.html5Clone="<:nav>"!==z.createElement("nav").cloneNode(!0).outerHTML,c.type="checkbox",c.checked=!0,a.appendChild(c),l.appendChecked=c.checked,b.innerHTML="",l.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,a.appendChild(b),b.innerHTML="",l.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,l.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){l.noCloneEvent=!1}),b.cloneNode(!0).click()),null==l.deleteExpando){l.deleteExpando=!0;try{delete b.test}catch(d){l.deleteExpando=!1}}a=b=c=null}(),function(){var b,c,d=z.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(l[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),l[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var Y=/^(?:input|select|textarea)$/i,Z=/^key/,$=/^(?:mouse|contextmenu)|click/,_=/^(?:focusinfocus|focusoutblur)$/,ab=/^([^.]*)(?:\.(.+)|)$/;function bb(){return!0}function cb(){return!1}function db(){try{return z.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=n.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof n===L||a&&n.event.triggered===a.type?void 0:n.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(F)||[""],h=b.length;while(h--)f=ab.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=n.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=n.event.special[o]||{},l=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},i),(m=g[o])||(m=g[o]=[],m.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,l):m.push(l),n.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n.hasData(a)&&n._data(a);if(r&&(k=r.events)){b=(b||"").match(F)||[""],j=b.length;while(j--)if(h=ab.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=m.length;while(f--)g=m[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(m.splice(f,1),g.selector&&m.delegateCount--,l.remove&&l.remove.call(a,g));i&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(k)&&(delete r.handle,n._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,m,o=[d||z],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||z,3!==d.nodeType&&8!==d.nodeType&&!_.test(p+n.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[n.expando]?b:new n.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),k=n.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!n.isWindow(d)){for(i=k.delegateType||p,_.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||z)&&o.push(l.defaultView||l.parentWindow||a)}m=0;while((h=o[m++])&&!b.isPropagationStopped())b.type=m>1?i:k.bindType||p,f=(n._data(h,"events")||{})[b.type]&&n._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&n.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&n.acceptData(d)&&g&&d[p]&&!n.isWindow(d)){l=d[g],l&&(d[g]=null),n.event.triggered=p;try{d[p]()}catch(r){}n.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(n._data(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((n.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?n(c,this).index(i)>=0:n.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h]","i"),ib=/^\s+/,jb=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,kb=/<([\w:]+)/,lb=/\s*$/g,sb={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:l.htmlSerialize?[0,"",""]:[1,"X
","
"]},tb=eb(z),ub=tb.appendChild(z.createElement("div"));sb.optgroup=sb.option,sb.tbody=sb.tfoot=sb.colgroup=sb.caption=sb.thead,sb.th=sb.td;function vb(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==L?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==L?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||n.nodeName(d,b)?f.push(d):n.merge(f,vb(d,b));return void 0===b||b&&n.nodeName(a,b)?n.merge([a],f):f}function wb(a){X.test(a.type)&&(a.defaultChecked=a.checked)}function xb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function yb(a){return a.type=(null!==n.find.attr(a,"type"))+"/"+a.type,a}function zb(a){var b=qb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ab(a,b){for(var c,d=0;null!=(c=a[d]);d++)n._data(c,"globalEval",!b||n._data(b[d],"globalEval"))}function Bb(a,b){if(1===b.nodeType&&n.hasData(a)){var c,d,e,f=n._data(a),g=n._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)n.event.add(b,c,h[c][d])}g.data&&(g.data=n.extend({},g.data))}}function Cb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!l.noCloneEvent&&b[n.expando]){e=n._data(b);for(d in e.events)n.removeEvent(b,d,e.handle);b.removeAttribute(n.expando)}"script"===c&&b.text!==a.text?(yb(b).text=a.text,zb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),l.html5Clone&&a.innerHTML&&!n.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&X.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}n.extend({clone:function(a,b,c){var d,e,f,g,h,i=n.contains(a.ownerDocument,a);if(l.html5Clone||n.isXMLDoc(a)||!hb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(ub.innerHTML=a.outerHTML,ub.removeChild(f=ub.firstChild)),!(l.noCloneEvent&&l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(d=vb(f),h=vb(a),g=0;null!=(e=h[g]);++g)d[g]&&Cb(e,d[g]);if(b)if(c)for(h=h||vb(a),d=d||vb(f),g=0;null!=(e=h[g]);g++)Bb(e,d[g]);else Bb(a,f);return d=vb(f,"script"),d.length>0&&Ab(d,!i&&vb(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k,m=a.length,o=eb(b),p=[],q=0;m>q;q++)if(f=a[q],f||0===f)if("object"===n.type(f))n.merge(p,f.nodeType?[f]:f);else if(mb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(kb.exec(f)||["",""])[1].toLowerCase(),k=sb[i]||sb._default,h.innerHTML=k[1]+f.replace(jb,"<$1>")+k[2],e=k[0];while(e--)h=h.lastChild;if(!l.leadingWhitespace&&ib.test(f)&&p.push(b.createTextNode(ib.exec(f)[0])),!l.tbody){f="table"!==i||lb.test(f)?""!==k[1]||lb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)n.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}n.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),l.appendChecked||n.grep(vb(p,"input"),wb),q=0;while(f=p[q++])if((!d||-1===n.inArray(f,d))&&(g=n.contains(f.ownerDocument,f),h=vb(o.appendChild(f),"script"),g&&Ab(h),c)){e=0;while(f=h[e++])pb.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=n.expando,j=n.cache,k=l.deleteExpando,m=n.event.special;null!=(d=a[h]);h++)if((b||n.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)m[e]?n.event.remove(d,e):n.removeEvent(d,e,g.handle);j[f]&&(delete j[f],k?delete d[i]:typeof d.removeAttribute!==L?d.removeAttribute(i):d[i]=null,c.push(f))}}}),n.fn.extend({text:function(a){return W(this,function(a){return void 0===a?n.text(this):this.empty().append((this[0]&&this[0].ownerDocument||z).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=xb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=xb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(vb(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&Ab(vb(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&n.cleanData(vb(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&n.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return W(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(gb,""):void 0;if(!("string"!=typeof a||nb.test(a)||!l.htmlSerialize&&hb.test(a)||!l.leadingWhitespace&&ib.test(a)||sb[(kb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(jb,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(vb(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(vb(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,k=this.length,m=this,o=k-1,p=a[0],q=n.isFunction(p);if(q||k>1&&"string"==typeof p&&!l.checkClone&&ob.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(k&&(i=n.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=n.map(vb(i,"script"),yb),f=g.length;k>j;j++)d=i,j!==o&&(d=n.clone(d,!0,!0),f&&n.merge(g,vb(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,n.map(g,zb),j=0;f>j;j++)d=g[j],pb.test(d.type||"")&&!n._data(d,"globalEval")&&n.contains(h,d)&&(d.src?n._evalUrl&&n._evalUrl(d.src):n.globalEval((d.text||d.textContent||d.innerHTML||"").replace(rb,"")));i=c=null}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=0,e=[],g=n(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),n(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Db,Eb={};function Fb(b,c){var d=n(c.createElement(b)).appendTo(c.body),e=a.getDefaultComputedStyle?a.getDefaultComputedStyle(d[0]).display:n.css(d[0],"display");return d.detach(),e}function Gb(a){var b=z,c=Eb[a];return c||(c=Fb(a,b),"none"!==c&&c||(Db=(Db||n("