Skip to content

Commit

Permalink
readme and fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
bibendi committed Apr 18, 2013
1 parent 91806b2 commit 3a38070
Show file tree
Hide file tree
Showing 10 changed files with 176 additions and 67 deletions.
137 changes: 128 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,135 @@
# Sphinx Integration
# Sphinx::Integration

Набор надстроек на thinking sphinx.
Надор надстроек над ThinkingSphinx и Riddle

Гем служит для использования real time индексов, а также для более хитрого написания sql запросов при описании индекса.

## Возможности:
# Возможности:

## Запуск сфинкса и индексатора

Всё стандартно, как и в thinking sphinx, т.к. все его rake таски перекрыты

## Contributing
+ rake ts:conf
+ rake ts:start
+ rake ts:stop
+ rake ts:in
+ rake ts:rebuild

*Внимание* при офлайн индексации rt индексы не очищаются. Рекоммендуется в этом случае использовать ts:rebuild

## Поддержка RT индексов
```ruby
define_index('model') do
set_property :rt => true
end
```

RT индексы используются как дельта. Таким образом мы избежим существенного замедления поисковых запросов из-за фрагментации памяти
т.к. основная часть запросов будет как и раньше обслуживаться дисковым индексом

Workflow:
+ первоначальная индексация, все данные попадают в дисковый core индекс
+ далее при обновлении записи, она попадает в rt индекс
+ и помечается в core как удалённая

Когда запускается очередная полная индексация:
+ начинает наполнятся core индекс сфинксовым индексатором
+ но в этот момент данные могут обновляться, записываться в rt индекс они будут, но потом всё равно удаляться после завершения полной индексации
+ для того, чтобы не потерять обновления данные начинают попадать в дополнительный delta_rt индекс
+ после завершения полной индексации, очищается основной rt индекс
+ и в него перетекают данные из delta rt индекса

Если у модели существую MVA атрибуты, которые наполняются отдельными запросами (ranged-query), то необходимо определить методы,
которые будут возвращать их значения при сохранении модели. Существую определённые правила именования таких методов.
Метод должен начинаться с mva_sphinx_attributes_for_NAME, например:
```ruby
def mva_sphinx_attributes_for_rubrics
{:rubrics => rubrics.map(&:rubric_id)}
end
```

## Дополнительные возможности конфигурировани индекса

Предполагается, что весь код в примерах будет выполнятся в блоке `define_index('model') do ... end`

### Наполнение определённого индекса из другой базы, например со слэйва

Реквизиты базы из ключа {production}_slave
```ruby
set_property :use_slave_db => true
```

Реквизиты базы из ключа {production}_my-sphinx-slave
```ruby
set_property :use_slave_db => 'my-sphinx-slave'
```

### Common Table Expressions or CTEs
```ruby
set_property :source_cte => {
:_rubrics => <<-SQL,
select companies.id as company_id, array_agg(company_rubrics.rubric_id) as rubrics_array
from companies
inner join company_rubrics on company_rubrics.company_id = companies.id
where {{where}}
group by companies.id
SQL
}
```

Условие {{where}} будет заменено на нужное из основного запроса

### Дополнительные joins, например с заданым CTE
```ruby
set_property :source_joins => {
:_rubrics => {
:as => :_rubrics,
:type => :left,
:on => '_rubrics.company_id = companies.id'
}
```

### Отключение группировки GROUP BY, которая делается по-умолчанию
```ruby
set_property :source_no_grouping => true
```

### Указание LIMIT
```ruby
set_property :sql_query_limit => 1000
```

### Отключение индексации пачками
```ruby
set_property :disable_range => true
```

### Указание своих минимального и максимального предела индексации
```ruby
set_property :sql_query_range => "SELECT 1::int, COALESCE(MAX(id), 1::int) FROM rubrics"
```

### Указание набора условий для выборки из базы
```ruby
set_property :use_own_sql_query_range => true
where %[faq_posts.id in (select faq_post_id from faq_post_deltas where ("faq_post_deltas"."id" >= $start AND "faq_post_deltas"."id" <= $end))]
```

### Указание полей при группировке
```ruby
set_property :force_group_by => true
group_by 'search_hints.forms.id', 'search_hints.forms.value',
```

### Указание произвольного названия таблицы, например вьюшки
```ruby
set_property :source_table => 'prepared_table_view'
```

### Наполнение MVA атрибутов из произволного запроса
```ruby
has :regions, :type => :multi, :source => :ranged_query, :query => "SELECT {{product_id}} AS id, region_id AS regions FROM product_regions WHERE id>=$start AND id<=$end; SELECT MIN(id), MAX(id) FROM product_regions"
```
В данно случае `{{product_id}}` заменится на нечто подобное `product_id * 8::INT8 + 5 AS id`, т.е. заменится на вычисление правильного внутреннего сквозного id

1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ module TransmitterCallbacks
end

def transmitter
@transmitter ||= Sphinx::Integration::Transmitter.new(self)
Sphinx::Integration::Transmitter.new(self)
end
end

Expand All @@ -33,9 +33,7 @@ def define_secondary_index(*args, &block)
raise ArgumentError unless name
define_index(name, &block)

self.sphinx_index_blocks << lambda {
self.sphinx_indexes.last.merged_with_core = true
}
self.sphinx_index_blocks << -> { self.sphinx_indexes.last.merged_with_core = true }
end

def reset_indexes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ def initialize_with_query_option(source, columns, options = {})
initialize_without_query_option(source, columns, options)
end

def available?
true
end

def source_value_with_custom_query(offset, delta)
if is_string?
return "#{query_source.to_s.dasherize}; #{columns.first.__name}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@ module Sphinx::Integration::Extensions::ThinkingSphinx::Configuration
extend ActiveSupport::Concern

included do
attr_accessor :remote

alias_method_chain :enforce_common_attribute_types, :rt
end

# Находится ли sphinx на другой машине
#
# Returns Boolean
def remote?
!!remote
end

# Не проверям на валидность RT индексы
# Метод пришлось полностью переписать
def enforce_common_attribute_types_with_rt
Expand Down
9 changes: 1 addition & 8 deletions lib/sphinx/integration/extensions/thinking_sphinx/index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,7 @@ def to_riddle_for_rt(delta = false)
when :multi then :rt_attr_multi
end

#begin
index.send(attr_type) << attr.unique_name
#rescue
# puts rt_name
# puts attr_type.inspect
# puts attr.type.inspect
# raise
#end
index.send(attr_type) << attr.unique_name
end
index
end
Expand Down
6 changes: 3 additions & 3 deletions lib/sphinx/integration/extensions/thinking_sphinx/source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ def set_source_database_settings_with_slave(source)

config ||= @database_configuration

source.sql_host = config[:host] || "localhost"
source.sql_user = config[:username] || config[:user] || ENV['USER']
source.sql_pass = (config[:password].to_s || "").gsub('#', '\#')
source.sql_host = config.fetch(:host, 'localhost')
source.sql_user = config[:username] || config[:user] || ENV['USER']
source.sql_pass = config[:password].to_s.gsub('#', '\#')
source.sql_db = config[:database]
source.sql_port = config[:port]
source.sql_sock = config[:socket]
Expand Down
1 change: 0 additions & 1 deletion lib/sphinx/integration/fast_facet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ module Sphinx::Integration::FastFacet
module ClassMethods

def fast_facet_ts_args(facet, ts_args = {})
facet = facet.to_sym if facet.is_a?(String)
ts_args.merge(:group => facet,
:limit => ts_args[:limit] || max_matches,
:page => 1,
Expand Down
37 changes: 12 additions & 25 deletions lib/sphinx/integration/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,13 @@
module Sphinx::Integration
class Helper
module ClassMethods
def remote_sphinx?
sphinx_addr = ThinkingSphinx::Configuration.instance.address
local_addrs = internal_ips

!local_addrs.include?(sphinx_addr)
end

def internal_ips
@internal_ips ||= Socket.ip_address_list.map do |addr|
IPAddr.new(addr.ip_address.sub(/\%.*$/, ''))
end
end

def config_file
ThinkingSphinx::Configuration.instance.config_file
def config
ThinkingSphinx::Configuration.instance
end

def sphinx_running?
if remote_sphinx?
if config.remote?
`#{Rails.root}/script/sphinx --status`.present?
else
ThinkingSphinx.sphinx_running?
Expand All @@ -33,18 +21,18 @@ def run_command(command)
end

def stop
if remote_sphinx?
if config.remote?
run_command("#{Rails.root}/script/sphinx --stop")
else
run_command "searchd --config #{config_file} --stop"
run_command "searchd --config #{config.config_file} --stop"
end
end

def start
if remote_sphinx?
if config.remote?
run_command("#{Rails.root}/script/sphinx --start")
else
run_command "searchd --config #{config_file}"
run_command "searchd --config #{config.config_file}"
end
end

Expand All @@ -55,12 +43,12 @@ def running_start

def index(online = true)
Redis::Mutex.with_lock(:full_reindex, :expire => 3.hours) do
if remote_sphinx?
if config.remote?
run_command("#{Rails.root}/script/sphinx --reindex-offline") unless online
run_command("#{Rails.root}/script/sphinx --reindex-online") if online
else
run_command "indexer --config #{config_file} --all" unless online
run_command "indexer --config #{config_file} --rotate --all" if online
run_command "indexer --config #{config.config_file} --all" unless online
run_command "indexer --config #{config.config_file} --rotate --all" if online
end
end

Expand Down Expand Up @@ -95,16 +83,15 @@ def prepare_rt(only_index = nil)
end

def configure
config = ThinkingSphinx::Configuration.instance
puts "Generating Configuration to #{config.config_file}"
config.build
end

def rebuild
configure

if remote_sphinx?
run_command("#{Rails.root}/script/sphinx --copy-config #{config_file}")
if config.remote?
run_command("#{Rails.root}/script/sphinx --copy-config #{config.config_file}")
end

stop if sphinx_running?
Expand Down
32 changes: 20 additions & 12 deletions lib/sphinx/integration/spec/support/thinking_sphinx.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,32 @@

require 'thinking_sphinx/test'
require 'database_cleaner'
require 'benchmark'

class Sphinx::Integration::Spec::Support::ThinkingSphinx
def reindex
time = Benchmark.realtime do
if remote?
Core::SphinxHelper.index
else
ThinkingSphinx::Test.index
end

class << self
attr_reader :instance

def instance
@@instance ||= ThinkingSphinx::Support.new
end
end

def reindex(opts = {})
options = {
:sleep => 0.25
}.merge(opts)

if remote?
Sphinx::Integration::Helper.index
else
ThinkingSphinx::Test.index
end
Rails.logger.info "Sphinx reindexed (#{time})"
sleep(options[:sleep])
end

def remote?
sphinx_addr = ThinkingSphinx::Configuration.instance.address
local_addrs = Core::IpTools.internal_ips
!local_addrs.include?(sphinx_addr)
ThinkingSphinx::Configuration.instance.remote?
end
end

Expand Down
2 changes: 1 addition & 1 deletion sphinx-integration.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Gem::Specification.new do |gem|
gem.add_development_dependency 'rake'
gem.add_development_dependency 'bundler'
gem.add_development_dependency 'rspec'
gem.add_development_dependency 'rails', '~> 3.0.19'
gem.add_development_dependency 'rails', '>= 3.0.3'
gem.add_development_dependency 'rspec-rails'
gem.add_development_dependency 'combustion'
gem.add_development_dependency 'mock_redis'
Expand Down

0 comments on commit 3a38070

Please sign in to comment.