Commit d146b9c39ec9bd42493af8e5bbc2bac0ee6b1dec

Authored by Andrew Kane
0 parents

First commit

.gitignore 0 → 100644
  1 +++ a/.gitignore
... ... @@ -0,0 +1,17 @@
  1 +*.gem
  2 +*.rbc
  3 +.bundle
  4 +.config
  5 +.yardoc
  6 +Gemfile.lock
  7 +InstalledFiles
  8 +_yardoc
  9 +coverage
  10 +doc/
  11 +lib/bundler/man
  12 +pkg
  13 +rdoc
  14 +spec/reports
  15 +test/tmp
  16 +test/version_tmp
  17 +tmp
... ...
Gemfile 0 → 100644
  1 +++ a/Gemfile
... ... @@ -0,0 +1,4 @@
  1 +source 'https://rubygems.org'
  2 +
  3 +# Specify your gem's dependencies in searchkick.gemspec
  4 +gemspec
... ...
LICENSE.txt 0 → 100644
  1 +++ a/LICENSE.txt
... ... @@ -0,0 +1,22 @@
  1 +Copyright (c) 2013 Andrew Kane
  2 +
  3 +MIT License
  4 +
  5 +Permission is hereby granted, free of charge, to any person obtaining
  6 +a copy of this software and associated documentation files (the
  7 +"Software"), to deal in the Software without restriction, including
  8 +without limitation the rights to use, copy, modify, merge, publish,
  9 +distribute, sublicense, and/or sell copies of the Software, and to
  10 +permit persons to whom the Software is furnished to do so, subject to
  11 +the following conditions:
  12 +
  13 +The above copyright notice and this permission notice shall be
  14 +included in all copies or substantial portions of the Software.
  15 +
  16 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  18 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19 +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  20 +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  21 +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  22 +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
... ...
README.md 0 → 100644
  1 +++ a/README.md
... ... @@ -0,0 +1,55 @@
  1 +# Searchkick
  2 +
  3 +Search made easy
  4 +
  5 +## Usage
  6 +
  7 +### Reindex with Zero Downtime
  8 +
  9 +Elasticsearch has a feature called aliases that allows you to reindex with no downtime.
  10 +
  11 +```ruby
  12 +Book.tire.reindex
  13 +```
  14 +
  15 +This creates a new index `books_20130714181054` and points the `books` alias to the new index when complete - an atomic operation :)
  16 +
  17 +**First time:** If books is an existing index, it will be replaced by an alias.
  18 +
  19 +Searchkick uses `find_in_batches` to import documents. To filter documents or eagar load associations, use the `tire_import` scope.
  20 +
  21 +```ruby
  22 +class Book < ActiveRecord::Base
  23 + scope :tire_import, where(active: true).includes(:author, :chapters)
  24 +end
  25 +```
  26 +
  27 +There is also a rake task.
  28 +
  29 +```sh
  30 +rake searchkick:reindex CLASS=Book
  31 +```
  32 +
  33 +[Thanks to Jaroslav Kalistsuk for the original source](https://gist.github.com/jarosan/3124884)
  34 +
  35 +## Installation
  36 +
  37 +Add this line to your application's Gemfile:
  38 +
  39 +```ruby
  40 +gem "searchkick"
  41 +```
  42 +
  43 +And then execute:
  44 +
  45 +```sh
  46 +bundle
  47 +```
  48 +
  49 +## Contributing
  50 +
  51 +1. Fork it
  52 +2. Create your feature branch (`git checkout -b my-new-feature`)
  53 +3. Commit your changes (`git commit -am 'Add some feature'`)
  54 +4. Push to the branch (`git push origin my-new-feature`)
  55 +5. Create new Pull Request
... ...
Rakefile 0 → 100644
  1 +++ a/Rakefile
... ... @@ -0,0 +1 @@
  1 +require "bundler/gem_tasks"
... ...
lib/searchkick.rb 0 → 100644
  1 +++ a/lib/searchkick.rb
... ... @@ -0,0 +1,49 @@
  1 +require "searchkick/version"
  2 +require "searchkick/tasks"
  3 +require "tire"
  4 +
  5 +module Searchkick
  6 + module ClassMethods
  7 +
  8 + # https://gist.github.com/jarosan/3124884
  9 + def reindex
  10 + alias_name = klass.tire.index.name
  11 + new_index = alias_name + "_" + Time.now.strftime("%Y%m%d%H%M%S")
  12 +
  13 + # Rake::Task["tire:import"].invoke
  14 + index = Tire::Index.new(new_index)
  15 + Tire::Tasks::Import.create_index(index, klass)
  16 + scope = klass.respond_to?(:tire_import) ? klass.tire_import : klass
  17 + scope.find_in_batches do |batch|
  18 + index.import batch
  19 + end
  20 +
  21 + if a = Tire::Alias.find(alias_name)
  22 + puts "[IMPORT] Alias found: #{Tire::Alias.find(alias_name).indices.to_ary.join(",")}"
  23 + old_indices = Tire::Alias.find(alias_name).indices
  24 + old_indices.each do |index|
  25 + a.indices.delete index
  26 + end
  27 +
  28 + a.indices.add new_index
  29 + a.save
  30 +
  31 + old_indices.each do |index|
  32 + puts "[IMPORT] Deleting index: #{index}"
  33 + i = Tire::Index.new(index)
  34 + i.delete if i.exists?
  35 + end
  36 + else
  37 + puts "[IMPORT] No alias found. Deleting index, creating new one, and setting up alias"
  38 + i = Tire::Index.new(alias_name)
  39 + i.delete if i.exists?
  40 + Tire::Alias.create(name: alias_name, indices: [new_index])
  41 + end
  42 +
  43 + puts "[IMPORT] Saved alias #{alias_name} pointing to #{new_index}"
  44 + end
  45 +
  46 + end
  47 +end
  48 +
  49 +Tire::Model::Search::ClassMethodsProxy.send :include, Searchkick::ClassMethods
... ...
lib/searchkick/tasks.rb 0 → 100644
  1 +++ a/lib/searchkick/tasks.rb
... ... @@ -0,0 +1,9 @@
  1 +require "rake"
  2 +
  3 +namespace :searchkick do
  4 + desc "re-index elasticsearch"
  5 + task :reindex => :environment do
  6 + klass = ENV["CLASS"].constantize
  7 + klass.tire.reindex
  8 + end
  9 +end
... ...
lib/searchkick/version.rb 0 → 100644
  1 +++ a/lib/searchkick/version.rb
... ... @@ -0,0 +1,3 @@
  1 +module Searchkick
  2 + VERSION = "0.0.1"
  3 +end
... ...
searchkick.gemspec 0 → 100644
  1 +++ a/searchkick.gemspec
... ... @@ -0,0 +1,25 @@
  1 +# coding: utf-8
  2 +lib = File.expand_path('../lib', __FILE__)
  3 +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
  4 +require 'searchkick/version'
  5 +
  6 +Gem::Specification.new do |spec|
  7 + spec.name = "searchkick"
  8 + spec.version = Searchkick::VERSION
  9 + spec.authors = ["Andrew Kane"]
  10 + spec.email = ["andrew@chartkick.com"]
  11 + spec.description = %q{Search made easy}
  12 + spec.summary = %q{Search made easy}
  13 + spec.homepage = "https://github.com/ankane/searchkick"
  14 + spec.license = "MIT"
  15 +
  16 + spec.files = `git ls-files`.split($/)
  17 + spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
  18 + spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
  19 + spec.require_paths = ["lib"]
  20 +
  21 + spec.add_dependency "tire"
  22 +
  23 + spec.add_development_dependency "bundler", "~> 1.3"
  24 + spec.add_development_dependency "rake"
  25 +end
... ...