load_balanced_tire 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. data/.gitignore +14 -0
  2. data/.travis.yml +29 -0
  3. data/Gemfile +4 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.markdown +760 -0
  6. data/Rakefile +78 -0
  7. data/examples/rails-application-template.rb +249 -0
  8. data/examples/tire-dsl.rb +876 -0
  9. data/lib/tire.rb +55 -0
  10. data/lib/tire/alias.rb +296 -0
  11. data/lib/tire/configuration.rb +30 -0
  12. data/lib/tire/dsl.rb +43 -0
  13. data/lib/tire/http/client.rb +62 -0
  14. data/lib/tire/http/clients/curb.rb +61 -0
  15. data/lib/tire/http/clients/faraday.rb +71 -0
  16. data/lib/tire/http/response.rb +27 -0
  17. data/lib/tire/index.rb +361 -0
  18. data/lib/tire/logger.rb +60 -0
  19. data/lib/tire/model/callbacks.rb +40 -0
  20. data/lib/tire/model/import.rb +26 -0
  21. data/lib/tire/model/indexing.rb +128 -0
  22. data/lib/tire/model/naming.rb +100 -0
  23. data/lib/tire/model/percolate.rb +99 -0
  24. data/lib/tire/model/persistence.rb +71 -0
  25. data/lib/tire/model/persistence/attributes.rb +143 -0
  26. data/lib/tire/model/persistence/finders.rb +66 -0
  27. data/lib/tire/model/persistence/storage.rb +69 -0
  28. data/lib/tire/model/search.rb +307 -0
  29. data/lib/tire/results/collection.rb +114 -0
  30. data/lib/tire/results/item.rb +86 -0
  31. data/lib/tire/results/pagination.rb +54 -0
  32. data/lib/tire/rubyext/hash.rb +8 -0
  33. data/lib/tire/rubyext/ruby_1_8.rb +7 -0
  34. data/lib/tire/rubyext/symbol.rb +11 -0
  35. data/lib/tire/search.rb +188 -0
  36. data/lib/tire/search/facet.rb +74 -0
  37. data/lib/tire/search/filter.rb +28 -0
  38. data/lib/tire/search/highlight.rb +37 -0
  39. data/lib/tire/search/query.rb +186 -0
  40. data/lib/tire/search/scan.rb +114 -0
  41. data/lib/tire/search/script_field.rb +23 -0
  42. data/lib/tire/search/sort.rb +25 -0
  43. data/lib/tire/tasks.rb +135 -0
  44. data/lib/tire/utils.rb +17 -0
  45. data/lib/tire/version.rb +22 -0
  46. data/test/fixtures/articles/1.json +1 -0
  47. data/test/fixtures/articles/2.json +1 -0
  48. data/test/fixtures/articles/3.json +1 -0
  49. data/test/fixtures/articles/4.json +1 -0
  50. data/test/fixtures/articles/5.json +1 -0
  51. data/test/integration/active_model_indexing_test.rb +51 -0
  52. data/test/integration/active_model_searchable_test.rb +114 -0
  53. data/test/integration/active_record_searchable_test.rb +446 -0
  54. data/test/integration/boolean_queries_test.rb +43 -0
  55. data/test/integration/count_test.rb +34 -0
  56. data/test/integration/custom_score_queries_test.rb +88 -0
  57. data/test/integration/dis_max_queries_test.rb +68 -0
  58. data/test/integration/dsl_search_test.rb +22 -0
  59. data/test/integration/explanation_test.rb +44 -0
  60. data/test/integration/facets_test.rb +259 -0
  61. data/test/integration/filtered_queries_test.rb +66 -0
  62. data/test/integration/filters_test.rb +63 -0
  63. data/test/integration/fuzzy_queries_test.rb +20 -0
  64. data/test/integration/highlight_test.rb +64 -0
  65. data/test/integration/index_aliases_test.rb +122 -0
  66. data/test/integration/index_mapping_test.rb +43 -0
  67. data/test/integration/index_store_test.rb +96 -0
  68. data/test/integration/index_update_document_test.rb +111 -0
  69. data/test/integration/mongoid_searchable_test.rb +309 -0
  70. data/test/integration/percolator_test.rb +111 -0
  71. data/test/integration/persistent_model_test.rb +130 -0
  72. data/test/integration/prefix_query_test.rb +43 -0
  73. data/test/integration/query_return_version_test.rb +70 -0
  74. data/test/integration/query_string_test.rb +52 -0
  75. data/test/integration/range_queries_test.rb +36 -0
  76. data/test/integration/reindex_test.rb +46 -0
  77. data/test/integration/results_test.rb +39 -0
  78. data/test/integration/scan_test.rb +56 -0
  79. data/test/integration/script_fields_test.rb +38 -0
  80. data/test/integration/sort_test.rb +36 -0
  81. data/test/integration/text_query_test.rb +39 -0
  82. data/test/models/active_model_article.rb +31 -0
  83. data/test/models/active_model_article_with_callbacks.rb +49 -0
  84. data/test/models/active_model_article_with_custom_document_type.rb +7 -0
  85. data/test/models/active_model_article_with_custom_index_name.rb +7 -0
  86. data/test/models/active_record_models.rb +122 -0
  87. data/test/models/article.rb +15 -0
  88. data/test/models/mongoid_models.rb +97 -0
  89. data/test/models/persistent_article.rb +11 -0
  90. data/test/models/persistent_article_in_namespace.rb +12 -0
  91. data/test/models/persistent_article_with_casting.rb +28 -0
  92. data/test/models/persistent_article_with_defaults.rb +11 -0
  93. data/test/models/persistent_articles_with_custom_index_name.rb +10 -0
  94. data/test/models/supermodel_article.rb +17 -0
  95. data/test/models/validated_model.rb +11 -0
  96. data/test/test_helper.rb +93 -0
  97. data/test/unit/active_model_lint_test.rb +17 -0
  98. data/test/unit/configuration_test.rb +74 -0
  99. data/test/unit/http_client_test.rb +76 -0
  100. data/test/unit/http_response_test.rb +49 -0
  101. data/test/unit/index_alias_test.rb +275 -0
  102. data/test/unit/index_test.rb +894 -0
  103. data/test/unit/logger_test.rb +125 -0
  104. data/test/unit/model_callbacks_test.rb +116 -0
  105. data/test/unit/model_import_test.rb +71 -0
  106. data/test/unit/model_persistence_test.rb +528 -0
  107. data/test/unit/model_search_test.rb +913 -0
  108. data/test/unit/results_collection_test.rb +281 -0
  109. data/test/unit/results_item_test.rb +162 -0
  110. data/test/unit/rubyext_test.rb +66 -0
  111. data/test/unit/search_facet_test.rb +153 -0
  112. data/test/unit/search_filter_test.rb +42 -0
  113. data/test/unit/search_highlight_test.rb +46 -0
  114. data/test/unit/search_query_test.rb +301 -0
  115. data/test/unit/search_scan_test.rb +113 -0
  116. data/test/unit/search_script_field_test.rb +26 -0
  117. data/test/unit/search_sort_test.rb +50 -0
  118. data/test/unit/search_test.rb +499 -0
  119. data/test/unit/tire_test.rb +126 -0
  120. data/tire.gemspec +90 -0
  121. metadata +549 -0
data/Rakefile ADDED
@@ -0,0 +1,78 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ task :default => :test
5
+
6
+ require 'rake/testtask'
7
+ Rake::TestTask.new(:test) do |test|
8
+ test.libs << 'lib' << 'test'
9
+ test.test_files = FileList['test/unit/*_test.rb', 'test/integration/*_test.rb']
10
+ test.verbose = true
11
+ # test.warning = true
12
+ end
13
+
14
+ namespace :test do
15
+ Rake::TestTask.new(:unit) do |test|
16
+ test.libs << 'lib' << 'test'
17
+ test.test_files = FileList["test/unit/*_test.rb"]
18
+ test.verbose = true
19
+ end
20
+ Rake::TestTask.new(:integration) do |test|
21
+ test.libs << 'lib' << 'test'
22
+ test.test_files = FileList["test/integration/*_test.rb"]
23
+ test.verbose = true
24
+ end
25
+ end
26
+
27
+ # Generate documentation
28
+ begin
29
+ require 'rdoc'
30
+ require 'rdoc/task'
31
+ Rake::RDocTask.new do |rdoc|
32
+ rdoc.rdoc_dir = 'rdoc'
33
+ rdoc.title = "Tire"
34
+ rdoc.rdoc_files.include('README.markdown')
35
+ rdoc.rdoc_files.include('lib/**/*.rb')
36
+ end
37
+ rescue LoadError
38
+ task :rdoc do
39
+ abort "[!] RDoc gem is not available."
40
+ end
41
+ end
42
+
43
+ # Generate coverage reports
44
+ begin
45
+ require 'rcov/rcovtask'
46
+ Rcov::RcovTask.new do |test|
47
+ test.libs << 'test'
48
+ test.rcov_opts = ['--exclude', 'gems/*']
49
+ test.pattern = 'test/**/*_test.rb'
50
+ test.verbose = true
51
+ end
52
+ rescue LoadError
53
+ task :rcov do
54
+ abort "[!] RCov gem is not available."
55
+ end
56
+ end
57
+
58
+ namespace :web do
59
+
60
+ desc "Update the Github website"
61
+ task :update => :generate do
62
+ current_branch = `git branch --no-color`.split("\n").select { |line| line =~ /^\* / }.to_s.gsub(/\* (.*)/, '\1')
63
+ (puts "Unable to determine current branch"; exit(1) ) unless current_branch
64
+ system "git checkout web"
65
+ system "cp examples/tire-dsl.html index.html"
66
+ system "git add index.html && git co -m 'Updated Tire website'"
67
+ system "git push origin web:gh-pages -f"
68
+ system "git checkout #{current_branch}"
69
+ end
70
+
71
+ desc "Generate the Rocco documentation page"
72
+ task :generate do
73
+ system "rocco examples/tire-dsl.rb"
74
+ html = File.read('examples/tire-dsl.html').gsub!(/>tire\-dsl\.rb</, '>tire.rb<')
75
+ File.open('examples/tire-dsl.html', 'w') { |f| f.write html }
76
+ system "open examples/tire-dsl.html"
77
+ end
78
+ end
@@ -0,0 +1,249 @@
1
+ # ===================================================================================================================
2
+ # Template for generating a no-frills Rails application with support for ElasticSearch full-text search via Tire
3
+ # ===================================================================================================================
4
+ #
5
+ # This file creates a basic, fully working Rails application with support for ElasticSearch full-text search
6
+ # via the Tire gem [http://github.com/karmi/tire].
7
+ #
8
+ # You DON'T NEED ELASTICSEARCH INSTALLED, it is installed and launched automatically by this script.
9
+ #
10
+ # Requirements
11
+ # ------------
12
+ #
13
+ # * Git
14
+ # * Ruby >= 1.8.7
15
+ # * Rubygems
16
+ # * Rails >= 3.0.7
17
+ # * Sun Java 6 (for ElasticSearch)
18
+ #
19
+ #
20
+ # Usage
21
+ # -----
22
+ #
23
+ # $ rails new tired -m https://github.com/karmi/tire/raw/master/examples/rails-application-template.rb
24
+ #
25
+ # ===================================================================================================================
26
+
27
+ require 'rubygems'
28
+
29
+ begin
30
+ require 'restclient'
31
+ rescue LoadError
32
+ puts "\n"
33
+ say_status "ERROR", "Rubygem 'rest-client' not installed\n", :red
34
+ puts '-'*80
35
+ say_status "", "gem install rest-client"
36
+ puts "\n"
37
+
38
+ if yes?("Should I install it for you?", :bold)
39
+ say_status "gem", "install rest-client", :yellow
40
+ system "gem install rest-client"
41
+ else
42
+ exit(1)
43
+ end
44
+ end
45
+
46
+ at_exit do
47
+ pid = File.read("#{destination_root}/tmp/pids/elasticsearch.pid") rescue nil
48
+ if pid
49
+ say_status "Stop", "ElasticSearch", :yellow
50
+ run "kill #{pid}"
51
+ end
52
+ end
53
+
54
+ run "rm public/index.html"
55
+ run "rm public/images/rails.png"
56
+ run "touch tmp/.gitignore log/.gitignore vendor/.gitignore"
57
+
58
+ run "rm -f .gitignore"
59
+ file ".gitignore", <<-END.gsub(/ /, '')
60
+ .DS_Store
61
+ log/*.log
62
+ tmp/**/*
63
+ config/database.yml
64
+ db/*.sqlite3
65
+ vendor/elasticsearch-0.19.0/
66
+ END
67
+
68
+ git :init
69
+ git :add => '.'
70
+ git :commit => "-m 'Initial commit: Clean application'"
71
+
72
+ unless (RestClient.get('http://localhost:9200') rescue false)
73
+ COMMAND = <<-COMMAND.gsub(/^ /, '')
74
+ curl -k -L -# -o elasticsearch-0.19.0.tar.gz \
75
+ "http://github.com/downloads/elasticsearch/elasticsearch/elasticsearch-0.19.0.tar.gz"
76
+ tar -zxf elasticsearch-0.19.0.tar.gz
77
+ rm -f elasticsearch-0.19.0.tar.gz
78
+ ./elasticsearch-0.19.0/bin/elasticsearch -p #{destination_root}/tmp/pids/elasticsearch.pid
79
+ COMMAND
80
+
81
+ puts "\n"
82
+ say_status "ERROR", "ElasticSearch not running!\n", :red
83
+ puts '-'*80
84
+ say_status '', "It appears that ElasticSearch is not running on this machine."
85
+ say_status '', "Is it installed? Do you want me to install it for you with this command?\n\n"
86
+ COMMAND.each_line { |l| say_status '', "$ #{l}" }
87
+ puts
88
+ say_status '', "(To uninstall, just remove the generated application directory.)"
89
+ puts '-'*80, ''
90
+
91
+ if yes?("Install ElasticSearch?", :bold)
92
+ puts
93
+ say_status "Install", "ElasticSearch", :yellow
94
+
95
+ commands = COMMAND.split("\n")
96
+ exec = commands.pop
97
+ inside("vendor") do
98
+ commands.each { |command| run command }
99
+ run "(#{exec})" # Launch ElasticSearch in subshell
100
+ end
101
+ end
102
+ end
103
+
104
+ puts
105
+ say_status "Rubygems", "Adding Rubygems into Gemfile...\n", :yellow
106
+ puts '-'*80, ''; sleep 1
107
+
108
+ gem 'tire'
109
+ gem 'will_paginate', '~> 3.0'
110
+
111
+ git :add => '.'
112
+ git :commit => "-m 'Added gems'"
113
+
114
+ puts
115
+ say_status "Rubygems", "Installing Rubygems...", :yellow
116
+ puts '-'*80, ''
117
+
118
+ puts "********************************************************************************"
119
+ puts " Running `bundle install`. Let's watch a movie!"
120
+ puts "********************************************************************************", ""
121
+
122
+ run "bundle install"
123
+
124
+ puts
125
+ say_status "Model", "Adding the Article resource...", :yellow
126
+ puts '-'*80, ''; sleep 1
127
+
128
+ generate :scaffold, "Article title:string content:text published_on:date"
129
+ route "root :to => 'articles#index'"
130
+ rake "db:migrate"
131
+
132
+ git :add => '.'
133
+ git :commit => "-m 'Added the Article resource'"
134
+
135
+ puts
136
+ say_status "Database", "Seeding the database with data...", :yellow
137
+ puts '-'*80, ''; sleep 0.25
138
+
139
+ run "rm -f db/seeds.rb"
140
+ file 'db/seeds.rb', <<-CODE
141
+ contents = [
142
+ 'Lorem ipsum dolor sit amet.',
143
+ 'Consectetur adipisicing elit, sed do eiusmod tempor incididunt.',
144
+ 'Labore et dolore magna aliqua.',
145
+ 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.',
146
+ 'Excepteur sint occaecat cupidatat non proident.'
147
+ ]
148
+
149
+ puts "Deleting all articles..."
150
+ Article.delete_all
151
+
152
+ puts "Creating articles..."
153
+ %w[ One Two Three Four Five ].each_with_index do |title, i|
154
+ Article.create :title => title, :content => contents[i], :published_on => i.days.ago.utc
155
+ end
156
+ CODE
157
+
158
+ rake "db:seed"
159
+
160
+ git :add => "db/seeds.rb"
161
+ git :commit => "-m 'Added database seeding script'"
162
+
163
+ puts
164
+ say_status "Model", "Adding search support into the Article model...", :yellow
165
+ puts '-'*80, ''; sleep 1
166
+
167
+ run "rm -f app/models/article.rb"
168
+ file 'app/models/article.rb', <<-CODE
169
+ class Article < ActiveRecord::Base
170
+ include Tire::Model::Search
171
+ include Tire::Model::Callbacks
172
+ end
173
+ CODE
174
+
175
+ initializer 'tire.rb', <<-CODE
176
+ Tire.configure do
177
+ logger STDERR
178
+ end
179
+ CODE
180
+
181
+ git :commit => "-a -m 'Added Tire support into the Article class and an initializer'"
182
+
183
+ puts
184
+ say_status "Controller", "Adding controller action, route, and HTML for search...", :yellow
185
+ puts '-'*80, ''; sleep 1
186
+
187
+ gsub_file 'app/controllers/articles_controller.rb', %r{# GET /articles/1$}, <<-CODE
188
+ # GET /articles/search
189
+ def search
190
+ @articles = Article.search params[:q]
191
+
192
+ render :action => "index"
193
+ end
194
+
195
+ # GET /articles/1
196
+ CODE
197
+
198
+ gsub_file 'app/views/articles/index.html.erb', %r{<h1>Listing articles</h1>}, <<-CODE
199
+ <h1>Listing articles</h1>
200
+
201
+ <%= form_tag search_articles_path, :method => 'get' do %>
202
+ <%= label_tag :query %>
203
+ <%= text_field_tag :q, params[:q] %>
204
+ <%= submit_tag :search %>
205
+ <% end %>
206
+
207
+ <hr>
208
+ CODE
209
+
210
+ gsub_file 'app/views/articles/index.html.erb', %r{<%= link_to 'New Article', new_article_path %>}, <<-CODE
211
+ <%= link_to 'New Article', new_article_path %>
212
+ <%= link_to 'Back', articles_path if params[:q] %>
213
+ CODE
214
+
215
+ gsub_file 'config/routes.rb', %r{resources :articles}, <<-CODE
216
+ resources :articles do
217
+ collection { get :search }
218
+ end
219
+ CODE
220
+
221
+ git :commit => "-a -m 'Added Tire support into the frontend of application'"
222
+
223
+ puts
224
+ say_status "Index", "Indexing the database...", :yellow
225
+ puts '-'*80, ''; sleep 0.5
226
+
227
+ rake "environment tire:import CLASS='Article' FORCE=true"
228
+
229
+ puts
230
+ say_status "Git", "Details about the application:", :yellow
231
+ puts '-'*80, ''
232
+
233
+ run "git log --reverse --pretty=format:'%Cblue%h%Creset | %s'"
234
+
235
+ if (begin; RestClient.get('http://localhost:3000'); rescue Errno::ECONNREFUSED; false; rescue Exception; true; end)
236
+ puts "\n"
237
+ say_status "ERROR", "Some other application is running on port 3000!\n", :red
238
+ puts '-'*80
239
+
240
+ port = ask("Please provide free port:", :bold)
241
+ else
242
+ port = '3000'
243
+ end
244
+
245
+ puts "", "="*80
246
+ say_status "DONE", "\e[1mStarting the application. Open http://localhost:#{port}\e[0m", :yellow
247
+ puts "="*80, ""
248
+
249
+ run "rails server --port=#{port}"
@@ -0,0 +1,876 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # **Tire** provides rich and comfortable Ruby API for the
4
+ # [_ElasticSearch_](http://www.elasticsearch.org/) search engine/database.
5
+ #
6
+ # _ElasticSearch_ is a scalable, distributed, cloud-ready, highly-available
7
+ # full-text search engine and database, communicating by JSON over RESTful HTTP,
8
+ # based on [Lucene](http://lucene.apache.org/), written in Java.
9
+ #
10
+ # <img src="http://github.com/favicon.ico" style="position:relative; top:2px">
11
+ # _Tire_ is open source, and you can download or clone the source code
12
+ # from <https://github.com/karmi/tire>.
13
+ #
14
+ # By following these instructions you should have the search running
15
+ # on a sane operating system in less then 10 minutes.
16
+
17
+ # Note, that this file can be executed directly:
18
+ #
19
+ # ruby -I lib examples/tire-dsl.rb
20
+ #
21
+
22
+
23
+ #### Installation
24
+
25
+ # Install _Tire_ with _Rubygems_:
26
+
27
+ #
28
+ # gem install tire
29
+ #
30
+ require 'rubygems'
31
+
32
+ # _Tire_ uses the [_multi_json_](https://github.com/intridea/multi_json) gem as a generic JSON library.
33
+ # We want to use the [_yajl-ruby_](https://github.com/brianmario/yajl-ruby) gem in its full on mode here.
34
+ #
35
+ require 'yajl/json_gem'
36
+
37
+ # Now, let's require the _Tire_ gem itself, and we're ready to go.
38
+ #
39
+ require 'tire'
40
+
41
+ #### Prerequisites
42
+
43
+ # We'll need a working and running _ElasticSearch_ server, of course. Thankfully, that's easy.
44
+ ( puts <<-"INSTALL" ; exit(1) ) unless (RestClient.get('http://localhost:9200') rescue false)
45
+
46
+ [ERROR] You don’t appear to have ElasticSearch installed. Please install and launch it with the following commands:
47
+
48
+ curl -k -L -o elasticsearch-0.19.0.tar.gz http://github.com/downloads/elasticsearch/elasticsearch/elasticsearch-0.19.0.tar.gz
49
+ tar -zxvf elasticsearch-0.19.0.tar.gz
50
+ ./elasticsearch-0.19.0/bin/elasticsearch -f
51
+ INSTALL
52
+
53
+ ### Storing and indexing documents
54
+
55
+ # Let's initialize an index named “articles”.
56
+ #
57
+ Tire.index 'articles' do
58
+ # To make sure it's fresh, let's delete any existing index with the same name.
59
+ #
60
+ delete
61
+ # And then, let's create it.
62
+ #
63
+ create
64
+
65
+ # We want to store and index some articles with `title`, `tags` and `published_on` properties.
66
+ # Simple Hashes are OK. The default type is „document”.
67
+ #
68
+ store :title => 'One', :tags => ['ruby'], :published_on => '2011-01-01'
69
+ store :title => 'Two', :tags => ['ruby', 'python'], :published_on => '2011-01-02'
70
+
71
+ # We usually want to set a specific _type_ for the document in _ElasticSearch_.
72
+ # Simply setting a `type` property is OK.
73
+ #
74
+ store :type => 'article',
75
+ :title => 'Three',
76
+ :tags => ['java'],
77
+ :published_on => '2011-01-02'
78
+
79
+ # We may want to wrap your data in a Ruby class, and use it when storing data.
80
+ # The contract required of such a class is very simple.
81
+ #
82
+ class Article
83
+
84
+ #
85
+ attr_reader :title, :tags, :published_on
86
+ def initialize(attributes={})
87
+ @attributes = attributes
88
+ @attributes.each_pair { |name,value| instance_variable_set :"@#{name}", value }
89
+ end
90
+
91
+ # It must provide a `type`, `_type` or `document_type` method for propper mapping.
92
+ #
93
+ def type
94
+ 'article'
95
+ end
96
+
97
+ # And it must provide a `to_indexed_json` method for conversion to JSON.
98
+ #
99
+ def to_indexed_json
100
+ @attributes.to_json
101
+ end
102
+ end
103
+
104
+ # Note: Since our class takes a Hash of attributes on initialization, we may even
105
+ # wrap the results in instances of this class; we'll see how to do that further below.
106
+ #
107
+ article = Article.new :title => 'Four',
108
+ :tags => ['ruby', 'php'],
109
+ :published_on => '2011-01-03'
110
+
111
+ # Let's store the `article`, now.
112
+ #
113
+ store article
114
+
115
+ # And let's „force refresh“ the index, so we can query it immediately.
116
+ #
117
+ refresh
118
+ end
119
+
120
+ # We may want to define a specific [mapping](http://www.elasticsearch.org/guide/reference/api/admin-indices-create-index.html)
121
+ # for the index.
122
+
123
+ Tire.index 'articles' do
124
+ # To do so, let's just pass a Hash containing the specified mapping to the `Index#create` method.
125
+ #
126
+ create :mappings => {
127
+
128
+ # Let's specify for which _type_ of documents this mapping should be used:
129
+ # „article”, in our case.
130
+ #
131
+ :article => {
132
+ :properties => {
133
+
134
+ # Let's specify the type of the field, whether it should be analyzed, ...
135
+ #
136
+ :id => { :type => 'string', :index => 'not_analyzed', :include_in_all => false },
137
+
138
+ # ... set the boost or analyzer settings for the field, etc. The _ElasticSearch_ guide
139
+ # has [more information](http://elasticsearch.org/guide/reference/mapping/index.html).
140
+ # Don't forget, that proper mapping is key to efficient and effective search.
141
+ # But don't fret about getting the mapping right the first time, you won't.
142
+ # In most cases, the default, dynamic mapping is just fine for prototyping.
143
+ #
144
+ :title => { :type => 'string', :analyzer => 'snowball', :boost => 2.0 },
145
+ :tags => { :type => 'string', :analyzer => 'keyword' },
146
+ :content => { :type => 'string', :analyzer => 'czech' }
147
+ }
148
+ }
149
+ }
150
+ end
151
+
152
+ #### Bulk Indexing
153
+
154
+ # Of course, we may have large amounts of data, and adding them to the index one by one really isn't the best idea.
155
+ # We can use _ElasticSearch's_ [bulk API](http://www.elasticsearch.org/guide/reference/api/bulk.html)
156
+ # for importing the data.
157
+
158
+ # So, for demonstration purposes, let's suppose we have a simple collection of hashes to store.
159
+ #
160
+ articles = [
161
+
162
+ # Notice that such objects must have an `id` property!
163
+ #
164
+ { :id => '1', :type => 'article', :title => 'one', :tags => ['ruby'], :published_on => '2011-01-01' },
165
+
166
+ # And, of course, they should contain the `type` property for the mapping to work!
167
+ #
168
+ { :id => '2', :type => 'article', :title => 'two', :tags => ['ruby', 'python'], :published_on => '2011-01-02' },
169
+ { :id => '3', :type => 'article', :title => 'three', :tags => ['java'], :published_on => '2011-01-02' },
170
+ { :id => '4', :type => 'article', :title => 'four', :tags => ['ruby', 'php'], :published_on => '2011-01-03' }
171
+ ]
172
+
173
+ # We can just push them into the index in one go.
174
+ #
175
+ Tire.index 'articles' do
176
+ import articles
177
+ end
178
+
179
+ # Of course, we can easily manipulate the documents before storing them in the index.
180
+ #
181
+ Tire.index 'articles' do
182
+ delete
183
+
184
+ # ... by passing a block to the `import` method. The collection will
185
+ # be available in the block argument.
186
+ #
187
+ import articles do |documents|
188
+
189
+ # We will capitalize every _title_ and return the manipulated collection
190
+ # back to the `import` method.
191
+ #
192
+ documents.map { |document| document.update(:title => document[:title].capitalize) }
193
+ end
194
+
195
+ refresh
196
+ end
197
+
198
+ ### Searching
199
+
200
+ # With the documents indexed and stored in the _ElasticSearch_ database, we can search them, finally.
201
+ #
202
+ # _Tire_ exposes the search interface via simple domain-specific language.
203
+
204
+ #### Simple Query String Searches
205
+
206
+ # We can do simple searches, like searching for articles containing “One” in their title.
207
+ #
208
+ s = Tire.search('articles') do
209
+ query do
210
+ string "title:one"
211
+ end
212
+ end
213
+
214
+ # The results:
215
+ # * One [tags: ruby]
216
+ #
217
+ s.results.each do |document|
218
+ puts "* #{ document.title } [tags: #{document.tags.join(', ')}]"
219
+ end
220
+
221
+ # Or, we can search for articles published between January, 1st and January, 2nd.
222
+ #
223
+ s = Tire.search('articles') do
224
+ query do
225
+ string "published_on:[2011-01-01 TO 2011-01-02]"
226
+ end
227
+ end
228
+
229
+ # The results:
230
+ # * One [published: 2011-01-01]
231
+ # * Two [published: 2011-01-02]
232
+ # * Three [published: 2011-01-02]
233
+ #
234
+ s.results.each do |document|
235
+ puts "* #{ document.title } [published: #{document.published_on}]"
236
+ end
237
+
238
+ # Notice, that we can access local variables from the _enclosing scope_.
239
+ # (Of course, we may write the blocks in shorter notation.)
240
+
241
+ # We will define the query in a local variable named `q`...
242
+ #
243
+ q = "title:T*"
244
+ # ... and we can use it inside the `query` block.
245
+ #
246
+ s = Tire.search('articles') { query { string q } }
247
+
248
+ # The results:
249
+ # * Two [tags: ruby, python]
250
+ # * Three [tags: java]
251
+ #
252
+ s.results.each do |document|
253
+ puts "* #{ document.title } [tags: #{document.tags.join(', ')}]"
254
+ end
255
+
256
+ # Often, we need to access variables or methods defined in the _outer scope_.
257
+ # To do that, we have to use a slight variation of the DSL.
258
+ #
259
+
260
+ # Let's assume we have a plain Ruby class, named `Article`.
261
+ #
262
+ class Article
263
+
264
+ # We will define the query in a class method...
265
+ #
266
+ def self.q
267
+ "title:T*"
268
+ end
269
+
270
+ # ... and wrap the _Tire_ search method in another one.
271
+ def self.search
272
+
273
+ # Notice how we pass the `search` object around as a block argument.
274
+ #
275
+ Tire.search('articles') do |search|
276
+
277
+ # And we pass the query object in a similar matter.
278
+ #
279
+ search.query do |query|
280
+
281
+ # Which means we can access the `q` class method.
282
+ #
283
+ query.string self.q
284
+ end
285
+ end.results
286
+ end
287
+ end
288
+
289
+ # We may use any valid [Lucene query syntax](http://lucene.apache.org/java/3_0_3/queryparsersyntax.html)
290
+ # for the `query_string` queries.
291
+
292
+ # For debugging our queries, we can display the JSON which is being sent to _ElasticSearch_.
293
+ #
294
+ # {"query":{"query_string":{"query":"title:T*"}}}
295
+ #
296
+ puts "", "Query:", "-"*80
297
+ puts s.to_json
298
+
299
+ # Or better yet, we may display a complete `curl` command to recreate the request in terminal,
300
+ # so we can see the naked response, tweak request parameters and meditate on problems.
301
+ #
302
+ # curl -X POST "http://localhost:9200/articles/_search?pretty=true" \
303
+ # -d '{"query":{"query_string":{"query":"title:T*"}}}'
304
+ #
305
+ puts "", "Try the query in Curl:", "-"*80
306
+ puts s.to_curl
307
+
308
+
309
+ ### Logging
310
+
311
+ # For debugging more complex situations, we can enable logging, so requests and responses
312
+ # will be logged using this `curl`-friendly format.
313
+
314
+ Tire.configure do
315
+
316
+ # By default, at the _info_ level, only the `curl`-format of request and
317
+ # basic information about the response will be logged:
318
+ #
319
+ # # 2011-04-24 11:34:01:150 [CREATE] ("articles")
320
+ # #
321
+ # curl -X POST "http://localhost:9200/articles"
322
+ #
323
+ # # 2011-04-24 11:34:01:152 [200]
324
+ #
325
+ logger 'elasticsearch.log'
326
+
327
+ # For debugging, we can switch to the _debug_ level, which will log the complete JSON responses.
328
+ #
329
+ # That's very convenient if we want to post a recreation of some problem or solution
330
+ # to the mailing list, IRC channel, etc.
331
+ #
332
+ logger 'elasticsearch.log', :level => 'debug'
333
+
334
+ # Note that we can pass any [`IO`](http://www.ruby-doc.org/core/classes/IO.html)-compatible Ruby object as a logging device.
335
+ #
336
+ logger STDERR
337
+ end
338
+
339
+ ### Configuration
340
+
341
+ # As we have just seen with logging, we can configure various parts of _Tire_.
342
+ #
343
+ Tire.configure do
344
+
345
+ # First of all, we can configure the URL for _ElasticSearch_.
346
+ #
347
+ url "http://search.example.com"
348
+
349
+ # Second, we may want to wrap the result items in our own class, for instance
350
+ # the `Article` class set above.
351
+ #
352
+ wrapper Article
353
+
354
+ # Finally, we can reset one or all configuration settings to their defaults.
355
+ #
356
+ reset :url
357
+ reset
358
+
359
+ end
360
+
361
+
362
+ ### Complex Searching
363
+
364
+ # Query strings are convenient for simple searches, but we may want to define our queries more expressively,
365
+ # using the _ElasticSearch_ [Query DSL](http://www.elasticsearch.org/guide/reference/query-dsl/index.html).
366
+ #
367
+ s = Tire.search('articles') do
368
+
369
+ # Let's suppose we want to search for articles with specific _tags_, in our case “ruby” _or_ “python”.
370
+ #
371
+ query do
372
+
373
+ # That's a great excuse to use a [_terms_](http://elasticsearch.org/guide/reference/query-dsl/terms-query.html)
374
+ # query.
375
+ #
376
+ terms :tags, ['ruby', 'python']
377
+ end
378
+ end
379
+
380
+ # The search, as expected, returns three articles, all tagged “ruby” — among other tags:
381
+ #
382
+ # * Two [tags: ruby, python]
383
+ # * One [tags: ruby]
384
+ # * Four [tags: ruby, php]
385
+ #
386
+ s.results.each do |document|
387
+ puts "* #{ document.title } [tags: #{document.tags.join(', ')}]"
388
+ end
389
+
390
+ # What if we wanted to search for articles tagged both “ruby” _and_ “python”?
391
+ #
392
+ s = Tire.search('articles') do
393
+ query do
394
+
395
+ # That's a great excuse to specify `minimum_match` for the query.
396
+ #
397
+ terms :tags, ['ruby', 'python'], :minimum_match => 2
398
+ end
399
+ end
400
+
401
+ # The search, as expected, returns one article, tagged with _both_ “ruby” and “python”:
402
+ #
403
+ # * Two [tags: ruby, python]
404
+ #
405
+ s.results.each do |document|
406
+ puts "* #{ document.title } [tags: #{document.tags.join(', ')}]"
407
+ end
408
+
409
+ #### Boolean Queries
410
+
411
+ # Quite often, we need complex queries with boolean logic.
412
+ # Instead of composing long query strings such as `tags:ruby OR tags:java AND NOT tags:python`,
413
+ # we can use the [_bool_](http://www.elasticsearch.org/guide/reference/query-dsl/bool-query.html)
414
+ # query.
415
+
416
+ s = Tire.search('articles') do
417
+ query do
418
+
419
+ # In _Tire_, we can build `bool` queries declaratively, as usual.
420
+ boolean do
421
+
422
+ # Let's define a `should` (`OR`) query for _ruby_,
423
+ #
424
+ should { string 'tags:ruby' }
425
+
426
+ # as well as for _java_,
427
+ should { string 'tags:java' }
428
+
429
+ # while defining a `must_not` (`AND NOT`) query for _python_.
430
+ must_not { string 'tags:python' }
431
+ end
432
+ end
433
+ end
434
+
435
+ # The search returns these documents:
436
+ #
437
+ # * One [tags: ruby]
438
+ # * Three [tags: java]
439
+ # * Four [tags: ruby, php]
440
+ #
441
+ s.results.each do |document|
442
+ puts "* #{ document.title } [tags: #{document.tags.join(', ')}]"
443
+ end
444
+
445
+ # The best thing about `boolean` queries is that we can very easily save these partial queries as Ruby blocks,
446
+ # to mix and reuse them later, since we can call the `boolean` method multiple times.
447
+ #
448
+
449
+ # Let's define the query for the _tags_ property,
450
+ #
451
+ tags_query = lambda do |boolean|
452
+ boolean.should { string 'tags:ruby' }
453
+ boolean.should { string 'tags:java' }
454
+ end
455
+
456
+ # ... and a query for the _published_on_ property.
457
+ published_on_query = lambda do |boolean|
458
+ boolean.must { string 'published_on:[2011-01-01 TO 2011-01-02]' }
459
+ end
460
+
461
+ # Now, we can use the `tags_query` on its own.
462
+ #
463
+ Tire.search('articles') { query { boolean &tags_query } }
464
+
465
+ # Or, we can combine it with the `published_on` query.
466
+ #
467
+ Tire.search('articles') do
468
+ query do
469
+ boolean &tags_query
470
+ boolean &published_on_query
471
+ end
472
+ end
473
+
474
+ # _ElasticSearch_ supports many types of [queries](http://www.elasticsearch.org/guide/reference/query-dsl/).
475
+ #
476
+ # Eventually, _Tire_ will support all of them. So far, only these are supported:
477
+ #
478
+ # * [string](http://www.elasticsearch.org/guide/reference/query-dsl/query-string-query.html)
479
+ # * [text](http://www.elasticsearch.org/guide/reference/query-dsl/text-query.html)
480
+ # * [term](http://elasticsearch.org/guide/reference/query-dsl/term-query.html)
481
+ # * [terms](http://elasticsearch.org/guide/reference/query-dsl/terms-query.html)
482
+ # * [bool](http://www.elasticsearch.org/guide/reference/query-dsl/bool-query.html)
483
+ # * [custom_score](http://www.elasticsearch.org/guide/reference/query-dsl/custom-score-query.html)
484
+ # * [fuzzy](http://www.elasticsearch.org/guide/reference/query-dsl/fuzzy-query.html)
485
+ # * [all](http://www.elasticsearch.org/guide/reference/query-dsl/match-all-query.html)
486
+ # * [ids](http://www.elasticsearch.org/guide/reference/query-dsl/ids-query.html)
487
+
488
+ #### Faceted Search
489
+
490
+ # _ElasticSearch_ makes it trivial to retrieve complex aggregated data from our index/database,
491
+ # so called [_facets_](http://www.elasticsearch.org/guide/reference/api/search/facets/index.html).
492
+
493
+ # Let's say we want to display article counts for every tag in the database.
494
+ # For that, we'll use a _terms_ facet.
495
+
496
+ #
497
+ s = Tire.search 'articles' do
498
+
499
+ # We will search for articles whose title begins with letter “T”,
500
+ #
501
+ query { string 'title:T*' }
502
+
503
+ # and retrieve the counts “bucketed” by `tags`.
504
+ #
505
+ facet 'tags' do
506
+ terms :tags
507
+ end
508
+ end
509
+
510
+ # As we see, our query has found two articles, and if you recall our articles from above,
511
+ # _Two_ is tagged with “ruby” and “python”, while _Three_ is tagged with “java”.
512
+ #
513
+ # Found 2 articles: Three, Two
514
+ #
515
+ # The counts shouldn't surprise us:
516
+ #
517
+ # Counts by tag:
518
+ # -------------------------
519
+ # ruby 1
520
+ # python 1
521
+ # java 1
522
+ #
523
+ puts "Found #{s.results.count} articles: #{s.results.map(&:title).join(', ')}"
524
+ puts "Counts by tag:", "-"*25
525
+ s.results.facets['tags']['terms'].each do |f|
526
+ puts "#{f['term'].ljust(10)} #{f['count']}"
527
+ end
528
+
529
+ # These counts are based on the scope of our current query.
530
+ # What if we wanted to display aggregated counts by `tags` across the whole database?
531
+
532
+ #
533
+ s = Tire.search 'articles' do
534
+
535
+ # Let's repeat the search for “T”...
536
+ #
537
+ query { string 'title:T*' }
538
+
539
+ facet 'global-tags', :global => true do
540
+
541
+ # ...but set the `global` scope for the facet in this case.
542
+ #
543
+ terms :tags
544
+ end
545
+
546
+ # We can even _combine_ facets scoped to the current query
547
+ # with globally scoped facets — we'll just use a different name.
548
+ #
549
+ facet 'current-tags' do
550
+ terms :tags
551
+ end
552
+ end
553
+
554
+ # Aggregated results for the current query are the same as previously:
555
+ #
556
+ # Current query facets:
557
+ # -------------------------
558
+ # ruby 1
559
+ # python 1
560
+ # java 1
561
+ #
562
+ puts "Current query facets:", "-"*25
563
+ s.results.facets['current-tags']['terms'].each do |f|
564
+ puts "#{f['term'].ljust(10)} #{f['count']}"
565
+ end
566
+
567
+ # On the other hand, aggregated results for the global scope include also
568
+ # tags for articles not matched by the query, such as “java” or “php”:
569
+ #
570
+ # Global facets:
571
+ # -------------------------
572
+ # ruby 3
573
+ # python 1
574
+ # php 1
575
+ # java 1
576
+ #
577
+ puts "Global facets:", "-"*25
578
+ s.results.facets['global-tags']['terms'].each do |f|
579
+ puts "#{f['term'].ljust(10)} #{f['count']}"
580
+ end
581
+
582
+ # _ElasticSearch_ supports many advanced types of facets, such as those for computing statistics or geographical distance.
583
+ #
584
+ # Eventually, _Tire_ will support all of them. So far, only these are supported:
585
+ #
586
+ # * [terms](http://www.elasticsearch.org/guide/reference/api/search/facets/terms-facet.html)
587
+ # * [date](http://www.elasticsearch.org/guide/reference/api/search/facets/date-histogram-facet.html)
588
+ # * [range](http://www.elasticsearch.org/guide/reference/api/search/facets/range-facet.html)
589
+ # * [histogram](http://www.elasticsearch.org/guide/reference/api/search/facets/histogram-facet.html)
590
+ # * [statistical](http://www.elasticsearch.org/guide/reference/api/search/facets/statistical-facet.html)
591
+ # * [terms_stats](http://www.elasticsearch.org/guide/reference/api/search/facets/terms-stats-facet.html)
592
+ # * [query](http://www.elasticsearch.org/guide/reference/api/search/facets/query-facet.html)
593
+
594
+ # We have seen that _ElasticSearch_ facets enable us to fetch complex aggregations from our data.
595
+ #
596
+ # They are frequently used for another feature, „faceted navigation“.
597
+ # We can be combine query and facets with
598
+ # [filters](http://elasticsearch.org/guide/reference/api/search/filter.html),
599
+ # so the returned documents are restricted by certain criteria — for example to a specific category —,
600
+ # but the aggregation calculations are still based on the original query.
601
+
602
+
603
+ #### Filtered Search
604
+
605
+ # So, let's make our search a bit more complex. Let's search for articles whose titles begin
606
+ # with letter “T”, again, but filter the results, so only the articles tagged “ruby”
607
+ # are returned.
608
+ #
609
+ s = Tire.search 'articles' do
610
+
611
+ # We will use just the same **query** as before.
612
+ #
613
+ query { string 'title:T*' }
614
+
615
+ # But we will add a _terms_ **filter** based on tags.
616
+ #
617
+ filter :terms, :tags => ['ruby']
618
+
619
+ # And, of course, our facet definition.
620
+ #
621
+ facet('tags') { terms :tags }
622
+
623
+ end
624
+
625
+ # We see that only the article _Two_ (tagged “ruby” and “python”) is returned,
626
+ # _not_ the article _Three_ (tagged “java”):
627
+ #
628
+ # * Two [tags: ruby, python]
629
+ #
630
+ s.results.each do |document|
631
+ puts "* #{ document.title } [tags: #{document.tags.join(', ')}]"
632
+ end
633
+
634
+ # The _count_ for article _Three_'s tags, “java”, on the other hand, _is_ in fact included:
635
+ #
636
+ # Counts by tag:
637
+ # -------------------------
638
+ # ruby 1
639
+ # python 1
640
+ # java 1
641
+ #
642
+ puts "Counts by tag:", "-"*25
643
+ s.results.facets['tags']['terms'].each do |f|
644
+ puts "#{f['term'].ljust(10)} #{f['count']}"
645
+ end
646
+
647
+ #### Sorting
648
+
649
+ # By default, the results are sorted according to their relevancy.
650
+ #
651
+ s = Tire.search('articles') { query { string 'tags:ruby' } }
652
+
653
+ s.results.each do |document|
654
+ puts "* #{ document.title } " +
655
+ "[tags: #{document.tags.join(', ')}; " +
656
+
657
+ # The score is available as the `_score` property.
658
+ #
659
+ "score: #{document._score}]"
660
+ end
661
+
662
+ # The results:
663
+ #
664
+ # * One [tags: ruby; score: 0.30685282]
665
+ # * Four [tags: ruby, php; score: 0.19178301]
666
+ # * Two [tags: ruby, python; score: 0.19178301]
667
+
668
+ # But, what if we want to sort the results based on some other criteria,
669
+ # such as published date or product price? We can do that.
670
+ #
671
+ s = Tire.search 'articles' do
672
+
673
+ # We will search for articles tagged “ruby”, again, ...
674
+ #
675
+ query { string 'tags:ruby' }
676
+
677
+ # ... but will sort them by their `title`, in descending order.
678
+ #
679
+ sort { by :title, 'desc' }
680
+ end
681
+
682
+ # The results:
683
+ #
684
+ # * Two
685
+ # * One
686
+ # * Four
687
+ #
688
+ s.results.each do |document|
689
+ puts "* #{ document.title }"
690
+ end
691
+
692
+ # Of course, it's possible to combine more fields in the sorting definition.
693
+
694
+ s = Tire.search 'articles' do
695
+
696
+ # We will just get all articles in this case.
697
+ #
698
+ query { all }
699
+
700
+ sort do
701
+
702
+ # We will sort the results by their `published_on` property in _ascending_ order (the default),
703
+ #
704
+ by :published_on
705
+
706
+ # and by their `title` property, in _descending_ order.
707
+ #
708
+ by :title, 'desc'
709
+ end
710
+ end
711
+
712
+ # The results:
713
+ # * One (Published on: 2011-01-01)
714
+ # * Two (Published on: 2011-01-02)
715
+ # * Three (Published on: 2011-01-02)
716
+ # * Four (Published on: 2011-01-03)
717
+ #
718
+ s.results.each do |document|
719
+ puts "* #{ document.title.ljust(10) } (Published on: #{ document.published_on })"
720
+ end
721
+
722
+ #### Highlighting
723
+
724
+ # Often, we want to highlight the snippets matching our query in the displayed results.
725
+ # _ElasticSearch_ provides rich
726
+ # [highlighting](http://www.elasticsearch.org/guide/reference/api/search/highlighting.html)
727
+ # features, and _Tire_ makes them trivial to use.
728
+ #
729
+ s = Tire.search 'articles' do
730
+
731
+ # Let's search for documents containing word “Two” in their titles,
732
+ query { string 'title:Two' }
733
+
734
+ # and instruct _ElasticSearch_ to highlight relevant snippets.
735
+ #
736
+ highlight :title
737
+ end
738
+
739
+ # The results:
740
+ # Title: Two; Highlighted: <em>Two</em>
741
+ #
742
+ s.results.each do |document|
743
+ puts "Title: #{ document.title }; Highlighted: #{document.highlight.title}"
744
+ end
745
+
746
+ # We can configure many options for highlighting, such as:
747
+ #
748
+ s = Tire.search 'articles' do
749
+ query { string 'title:Two' }
750
+
751
+ # • specify the fields to highlight
752
+ #
753
+ highlight :title, :body
754
+
755
+ # • specify their individual options
756
+ #
757
+ highlight :title, :body => { :number_of_fragments => 0 }
758
+
759
+ # • or specify global highlighting options, such as the wrapper tag
760
+ #
761
+ highlight :title, :body, :options => { :tag => '<strong class="highlight">' }
762
+ end
763
+
764
+ #### Percolation
765
+
766
+ # _ElasticSearch_ comes with one very interesting, and rather unique feature:
767
+ # [_percolation_](http://www.elasticsearch.org/guide/reference/api/percolate.html).
768
+
769
+ # It works in a „reverse search“ manner to regular search workflow of adding
770
+ # documents to the index and then querying them.
771
+ # Percolation allows us to register a query, and ask if a specific document
772
+ # matches it, either on demand, or immediately as the document is being indexed.
773
+
774
+ # Let's review an example for an index named _weather_.
775
+ # We will register three queries for percolation against this index.
776
+ #
777
+ index = Tire.index('weather') do
778
+ delete
779
+ create
780
+
781
+ # First, a query named _warning_,
782
+ #
783
+ register_percolator_query('warning', :tags => ['warning']) { string 'warning OR severe OR extreme' }
784
+
785
+ # a query named _tsunami_,
786
+ #
787
+ register_percolator_query('tsunami', :tags => ['tsunami']) { string 'tsunami' }
788
+
789
+ # and a query named _floods_.
790
+ #
791
+ register_percolator_query('floods', :tags => ['floods']) { string 'flood*' }
792
+
793
+ end
794
+
795
+ # Notice, that we have added a _tags_ field to the query document, because it behaves
796
+ # just like any other document in _ElasticSearch_.
797
+
798
+ # We will refresh the `_percolator` index for immediate access.
799
+ #
800
+ Tire.index('_percolator').refresh
801
+
802
+ # Now, let's _percolate_ a document containing some trigger words against all registered queries.
803
+ #
804
+ matches = index.percolate(:message => '[Warning] Extreme flooding expected after tsunami wave.')
805
+
806
+ # The result will contain, unsurprisingly, names of all the three registered queries:
807
+ #
808
+ # Matching queries: ["floods", "tsunami", "warning"]
809
+ #
810
+ puts "Matching queries: " + matches.inspect
811
+
812
+ # We can filter the executed queries with a regular _ElasticSearch_ query passed as a block to
813
+ # the `percolate` method.
814
+ #
815
+ matches = index.percolate(:message => '[Warning] Extreme flooding expected after tsunami wave.') do
816
+ # Let's use a _terms_ query against the `tags` field.
817
+ term :tags, 'tsunami'
818
+ end
819
+
820
+ # In this case, the result will contain only the name of the “tsunami” query.
821
+ #
822
+ # Matching queries: ["tsunami"]
823
+ #
824
+ puts "Matching queries: " + matches.inspect
825
+
826
+ # What if we percolate another document, without the “tsunami” trigger word?
827
+ #
828
+ matches = index.percolate(:message => '[Warning] Extreme temperatures expected.') { term :tags, 'tsunami' }
829
+
830
+ # As expected, we will get an empty array:
831
+ #
832
+ # Matching queries: []
833
+ #
834
+ puts "Matching queries: " + matches.inspect
835
+
836
+ # Well, that's of course immensely useful for real-time search systems. But, there's more.
837
+ # We can _percolate_ a document _at the same time_ it is being stored in the index,
838
+ # getting back a list of matching queries.
839
+
840
+ # Let's store a document with some trigger words in the index, and mark it for percolation.
841
+ #
842
+ response = index.store :message => '[Warning] Severe floods expected after tsunami wave.', :percolate => true
843
+
844
+ # We will get the names of all matching queries in response.
845
+ #
846
+ # Matching queries: ["floods", "tsunami", "warning"]
847
+ #
848
+ puts "Matching queries: " + response['matches'].inspect
849
+
850
+ # As with the _percolate_ example, we can filter the executed queries.
851
+ #
852
+ response = index.store :message => '[Warning] Severe floods expected after tsunami wave.',
853
+ # Let's use a simple string query for the “tsunami” tag.
854
+ :percolate => 'tags:tsunami'
855
+
856
+ # Unsurprisingly, the response will contain just the name of the “tsunami” query.
857
+ #
858
+ # Matching queries: ["tsunami"]
859
+ #
860
+ puts "Matching queries: " + response['matches'].inspect
861
+
862
+ ### ActiveModel Integration
863
+
864
+ # As you can see, [_Tire_](https://github.com/karmi/tire) supports the
865
+ # main features of _ElasticSearch_ in Ruby.
866
+ #
867
+ # It allows you to create and delete indices, add documents, search them, retrieve the facets, highlight the results,
868
+ # and comes with a usable logging facility.
869
+ #
870
+ # Of course, the holy grail of any search library is easy, painless integration with your Ruby classes, and,
871
+ # most importantly, with ActiveRecord/ActiveModel classes.
872
+ #
873
+ # Please, check out the [README](https://github.com/karmi/tire/tree/master#readme) file for instructions
874
+ # how to include _Tire_-based search in your models..
875
+ #
876
+ # Send any feedback via Github issues, or ask questions in the [#elasticsearch](irc://irc.freenode.net/#elasticsearch) IRC channel.