Skip to content

Commit

Permalink
Merge pull request #17 from Sage/feature_index_alias
Browse files Browse the repository at this point in the history
Feature - Index Alias
  • Loading branch information
adamgeorgeson authored Apr 29, 2019
2 parents 76d4b98 + abd5c5c commit 3646ca3
Show file tree
Hide file tree
Showing 13 changed files with 424 additions and 13 deletions.
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
source 'https://rubygems.org'

gem 'json', '2.1.0'

# Specify your gem's dependencies in elastic_search_framework.gemspec
gemspec

Expand Down
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,60 @@ This is used to set the port that should be used for all connection actions.

> DEFAULT = 9200
# IndexAlias
To define an index alias within elasticsearch, create an index alias class that extends from the `ElasticSearchFramework::IndexAlias` module.

Example:


```ruby
class ExampleIndexAlias
extend ElasticSearchFramework::IndexAlias

index ExampleIndex, active: true
index ExampleIndex2, active: false

name :example
end
```

**attributes**

- **index** [Hash] [Required] [Multi] This is used to specify the indexes associated with this alias and which index is the current active index for the alias to point to.
- **name** [Hash] [Required] [Single] This is used to specify the unique name of the index alias.

---

Index Aliases are required to decouple your application from a specific index and allow you to handle index updates without downtime.

To change the mapping of an existing index Create a new version of the index, then associate the new index with the index alias as an inactive index `active: false`. This will allow index writes and deletes to be performed on both indexes so no new data is lost while you perform a `_reindex` operation to move existing data from the old index into the new index.

Once you have `_reindexed` into your new index you can then de-activate the old index `active: false` and activate the new index `active: true` in your index alias. This will swap all requests to the new index.

Doing the above steps should enable you to seamlessly transition between 1 index and another when mapping/analyzer changes are required.

## #create
This method is called to create the index alias within an elastic search instance.
> This method is idempotent and will modify the index alias if it already exists.
> All associated indexes must exist before this method is called.
ExampleIndexAlias.create

## Index operation methods
The following index operation methods are available for an index alias:

- `#get_item`
- `#put_item`
- `#delete_item`
- `#query`

> `#put_item` calls will be performed against all indexes associated with the alias.
> `#delete_item` calls will be performed against all indexes associated with the alias.
> Details for how to use the above index operation methods can be found below.
# Index
To define an index within elasticsearch, create an index definition class that extends from the `ElasticSearchFramework::Index` module.

Expand Down
1 change: 1 addition & 0 deletions lib/elastic_search_framework.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
require_relative 'elastic_search_framework/exceptions'
require_relative 'elastic_search_framework/repository'
require_relative 'elastic_search_framework/index'
require_relative 'elastic_search_framework/index_alias'
require_relative 'elastic_search_framework/query'

module ElasticSearchFramework
Expand Down
11 changes: 8 additions & 3 deletions lib/elastic_search_framework/index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@ module ElasticSearchFramework
module Index
attr_accessor :index_settings

def index(name:)
def index(name:, version: nil)
unless instance_variable_defined?(:@elastic_search_index_def)
instance_variable_set(:@elastic_search_index_def, name: "#{name}")
instance_variable_set(:@elastic_search_index_def, name: "#{name}#{version}")
instance_variable_set(:@elastic_search_index_version, version: version) unless version.nil?
else
raise ElasticSearchFramework::Exceptions::IndexError.new("[#{self.class}] - Duplicate index description. Name: #{name}.")
end
end

def version
instance_variable_defined?(:@elastic_search_index_version) ? instance_variable_get(:@elastic_search_index_version) : 0
end

def id(field)
unless instance_variable_defined?(:@elastic_search_index_id)
instance_variable_set(:@elastic_search_index_id, field)
Expand Down Expand Up @@ -160,7 +165,7 @@ def host
end

def repository
ElasticSearchFramework::Repository.new
@repository ||= ElasticSearchFramework::Repository.new
end

def get_item(id:, type: 'default')
Expand Down
146 changes: 146 additions & 0 deletions lib/elastic_search_framework/index_alias.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
module ElasticSearchFramework
module IndexAlias
def index(klass, active:)
unless instance_variable_defined?(:@elastic_search_indexes)
instance_variable_set(:@elastic_search_indexes, [])
end
indexes = self.instance_variable_get(:@elastic_search_indexes)
indexes << {klass: klass, active: active}
instance_variable_set(:@elastic_search_indexes, indexes)
end

def indexes
self.instance_variable_get(:@elastic_search_indexes)
end

def name(name)
unless instance_variable_defined?(:@elastic_search_index_alias_name)
instance_variable_set(:@elastic_search_index_alias_name, "#{name}")
else
raise ElasticSearchFramework::Exceptions::IndexError.new("[#{self.class}] - Duplicate index alias name: #{name}.")
end
end

def valid?
indexes.select { |i| i[:active] == true }.length == 1
end

def create
if !valid?
raise ElasticSearchFramework::Exceptions::IndexError.new("[#{self.class}] - Invalid Index alias.")
end

uri = URI("#{host}/_aliases")

payload = {
actions: [],
}

indexes.each do |index|
action = nil
if exists?(index: index[:klass])
action = "remove" if !index[:active]
else
action = "add" if index[:active]
end
next if action.nil?

payload[:actions] << {action => {index: index[:klass].full_name, alias: self.full_name}}
end

request = Net::HTTP::Post.new(uri.request_uri)
request.body = JSON.dump(payload)
request.content_type = "application/json"

response = repository.with_client do |client|
client.request(request)
end

is_valid_response?(response.code) || Integer(response.code) == 404
end

def delete
uri = URI("#{host}/_all/_aliases/#{full_name}")

request = Net::HTTP::Delete.new(uri.request_uri)

response = repository.with_client do |client|
client.request(request)
end

is_valid_response?(response.code) || Integer(response.code) == 404
end

def exists?(index:)
uri = URI("#{host}/#{index.full_name}/_alias/#{full_name}")

request = Net::HTTP::Get.new(uri.request_uri)

response = repository.with_client do |client|
client.request(request)
end

return false if response.code == "404"

result = nil
if is_valid_response?(response.code)
result = JSON.parse(response.body)
end

return true if !result.nil? && result[index.full_name]["aliases"] != nil
return false
end

def is_valid_response?(code)
[200, 201, 202].include?(Integer(code))
end

def full_name
name = instance_variable_get(:@elastic_search_index_alias_name)
if ElasticSearchFramework.namespace != nil
"#{ElasticSearchFramework.namespace}#{ElasticSearchFramework.namespace_delimiter}#{name.downcase}"
else
name.downcase
end
end

def description
index = indexes.last[:klass]
hash = index.instance_variable_get(:@elastic_search_index_def)
if index.instance_variable_defined?(:@elastic_search_index_id)
hash[:id] = index.instance_variable_get(:@elastic_search_index_id)
else
hash[:id] = :id
end
hash
end

def host
"#{ElasticSearchFramework.host}:#{ElasticSearchFramework.port}"
end

def repository
@repository ||= ElasticSearchFramework::Repository.new
end

def get_item(id:, type: "default")
repository.get(index: self, id: id, type: type)
end

def put_item(type: "default", item:)
indexes.each do |index|
repository.set(entity: item, index: index[:klass], type: type)
end
end

def delete_item(id:, type: "default")
indexes.each do |index|
repository.drop(index: index[:klass], id: id, type: type)
end
end

def query
ElasticSearchFramework::Query.new(index: self)
end
end
end
2 changes: 1 addition & 1 deletion lib/elastic_search_framework/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module ElasticSearchFramework
VERSION = '2.1.0'
VERSION = '2.2.0'
end
2 changes: 1 addition & 1 deletion script/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: '2.1'

services:
elasticsearch:
image: elasticsearch:alpine
image: elasticsearch:5.6-alpine
container_name: elasticsearch
environment:
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
Expand Down
Loading

0 comments on commit 3646ca3

Please sign in to comment.