thinking-sphinx 5.5.1 → 5.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +6 -7
  3. data/Appraisals +6 -0
  4. data/CHANGELOG.markdown +19 -0
  5. data/README.textile +4 -4
  6. data/bin/loadsphinx +15 -4
  7. data/lib/thinking_sphinx/active_record/base.rb +23 -4
  8. data/lib/thinking_sphinx/active_record/filter_reflection.rb +1 -1
  9. data/lib/thinking_sphinx/active_record/log_subscriber.rb +16 -4
  10. data/lib/thinking_sphinx/commands/clear_real_time.rb +1 -1
  11. data/lib/thinking_sphinx/commands/clear_sql.rb +1 -1
  12. data/lib/thinking_sphinx/configuration/minimum_fields.rb +8 -8
  13. data/lib/thinking_sphinx/masks/scopes_mask.rb +6 -0
  14. data/lib/thinking_sphinx/processor.rb +34 -8
  15. data/lib/thinking_sphinx/search/context.rb +1 -0
  16. data/lib/thinking_sphinx/search.rb +2 -2
  17. data/lib/thinking_sphinx/test.rb +1 -1
  18. data/lib/thinking_sphinx.rb +4 -0
  19. data/spec/acceptance/attribute_access_spec.rb +4 -4
  20. data/spec/acceptance/excerpts_spec.rb +2 -2
  21. data/spec/acceptance/grouping_by_attributes_spec.rb +20 -20
  22. data/spec/acceptance/real_time_updates_spec.rb +61 -1
  23. data/spec/acceptance/searching_across_models_spec.rb +7 -0
  24. data/spec/acceptance/searching_with_filters_spec.rb +8 -8
  25. data/spec/acceptance/searching_within_a_model_spec.rb +14 -0
  26. data/spec/acceptance/sorting_search_results_spec.rb +15 -15
  27. data/spec/acceptance/sphinx_scopes_spec.rb +23 -16
  28. data/spec/internal/app/indices/article_index.rb +6 -0
  29. data/spec/internal/app/indices/book_index.rb +1 -1
  30. data/spec/internal/app/models/book.rb +5 -5
  31. data/spec/internal/db/schema.rb +1 -1
  32. data/spec/thinking_sphinx/active_record/callbacks/update_callbacks_spec.rb +1 -1
  33. data/spec/thinking_sphinx/active_record/interpreter_spec.rb +5 -5
  34. data/spec/thinking_sphinx/commands/clear_real_time_spec.rb +2 -2
  35. data/spec/thinking_sphinx/commands/clear_sql_spec.rb +2 -2
  36. data/spec/thinking_sphinx/configuration/minimum_fields_spec.rb +12 -2
  37. data/spec/thinking_sphinx/excerpter_spec.rb +3 -2
  38. data/spec/thinking_sphinx/index_spec.rb +2 -2
  39. data/spec/thinking_sphinx/middlewares/sphinxql_spec.rb +4 -4
  40. data/spec/thinking_sphinx/panes/excerpts_pane_spec.rb +1 -1
  41. data/spec/thinking_sphinx/real_time/interpreter_spec.rb +5 -5
  42. data/spec/thinking_sphinx_spec.rb +2 -2
  43. data/thinking-sphinx.gemspec +3 -3
  44. metadata +6 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 903795814bfdd5627c9fad8c6a9aa075973c29a046e9ea1985244df29023d52b
4
- data.tar.gz: 8e1152802089f3d9e0bce6536ac7a3d6d9bdc7707feee2d72489bf491bab670e
3
+ metadata.gz: 9312857a98ecddb77183d29a5967d0a7b9bd1e5aecd4ba058931281d15ae073c
4
+ data.tar.gz: 2cad1197f8f6f99fd8007cc532d4ae0709bae06e3ecd6b064a443817842753f5
5
5
  SHA512:
6
- metadata.gz: ccad3b3ffc2724ff71fb96707a7140ad44c927f6d7a7059022281056838f11a9fd784739f4bb5a4e1ce8f92f295f8da2af6e3134a32a0f9b5079042f6a225c99
7
- data.tar.gz: f86ffa18c5c01b2a4b6530606e50010eb2bf8309ea18198489d347b9ee73807712c59aa286a14db35fb900d0d55182d415a65b63db645e0f92cd3d0f26173ee5
6
+ metadata.gz: '08c9725de4f436fbcf36b71a91940e5920d03b40f237dacd64a7e6ecd8dceffc917b9da353992e677538ddee12cf34b9065bf70bbbfcb698b9dfa5c68e51305c'
7
+ data.tar.gz: 43a8d688ca74d7e7bdb1d30368aa6ae5908b84250b0e60e37cd484143d91ac2cd2dbde8ab8115aa7538e09ff6619de13ba1c95c603baa224e77c8d46bb7d075f
@@ -4,14 +4,13 @@ on: [push, pull_request]
4
4
 
5
5
  jobs:
6
6
  sphinx:
7
- runs-on: ${{ matrix.os }}
7
+ runs-on: ubuntu-22.04
8
8
 
9
9
  strategy:
10
10
  fail-fast: false
11
11
  matrix:
12
- os: [ 'ubuntu-18.04' ]
13
12
  ruby: [ '2.7', '3.0', '3.1', '3.2' ]
14
- rails: [ '5_0', '5_1', '5_2', '6_0', '6_1', '7_0' ]
13
+ rails: [ '5_0', '5_1', '5_2', '6_0', '6_1', '7_0', '7_1' ]
15
14
  database: [ 'mysql2', 'postgresql' ]
16
15
  sphinx_version: [ '2.2.11', '3.4.1' ]
17
16
  sphinx_engine: [ 'sphinx' ]
@@ -58,7 +57,7 @@ jobs:
58
57
  options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
59
58
 
60
59
  steps:
61
- - uses: actions/checkout@v2
60
+ - uses: actions/checkout@v4
62
61
  - uses: ./.github/actions/test
63
62
  with:
64
63
  ruby-version: ${{ matrix.ruby }}
@@ -69,15 +68,15 @@ jobs:
69
68
  timeout-minutes: 12
70
69
 
71
70
  manticore:
72
- runs-on: ubuntu-20.04
71
+ runs-on: ubuntu-22.04
73
72
 
74
73
  strategy:
75
74
  fail-fast: false
76
75
  matrix:
77
76
  ruby: [ '2.7', '3.0', '3.1', '3.2' ]
78
- rails: [ '5_0', '5_1', '5_2', '6_0', '6_1', '7_0' ]
77
+ rails: [ '5_0', '5_1', '5_2', '6_0', '6_1', '7_0', '7_1' ]
79
78
  database: [ 'mysql2', 'postgresql' ]
80
- sphinx_version: [ '3.5.4', '4.0.2' ]
79
+ sphinx_version: [ '4.0.2', '6.0.0' ]
81
80
  sphinx_engine: [ 'manticore' ]
82
81
  exclude:
83
82
  - ruby: '3.0'
data/Appraisals CHANGED
@@ -45,3 +45,9 @@ appraise 'rails_7_0' do
45
45
  gem 'mysql2', '~> 0.5.0', :platform => :ruby
46
46
  gem 'pg', '~> 1.0', :platform => :ruby
47
47
  end if RUBY_PLATFORM != 'java' && RUBY_VERSION.to_f >= 2.7
48
+
49
+ appraise 'rails_7_1' do
50
+ gem 'rails', '~> 7.1.0'
51
+ gem 'mysql2', '~> 0.5.0', :platform => :ruby
52
+ gem 'pg', '~> 1.0', :platform => :ruby
53
+ end if RUBY_PLATFORM != 'java' && RUBY_VERSION.to_f >= 2.7
data/CHANGELOG.markdown CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  All notable changes to this project (at least, from v3.0.0 onwards) are documented in this file.
4
4
 
5
+ ## 5.6.0 - 2024-07-07
6
+
7
+ ### Added
8
+
9
+ * Support for Manticore 6.0 ([#1242](https://github.com/pat/thinking-sphinx/pull/1242))
10
+ * `sphinx`-prefixed search methods, in case the standard `search` is overridden from something unrelated. ([#1265](https://github.com/pat/thinking-sphinx/pull/1265))
11
+ * `none` / `search_none` scopes that can be chained to searches and will return no results.
12
+ * Added `ThinkingSphinx::Processor#sync` to synchronise updates/deletions based on a real-time index's scope, by @akostadinov in [@1258](https://github.com/pat/thinking-sphinx/pull/1258).
13
+
14
+ ### Changed
15
+
16
+ * Improved Rails 7.1 support, by @jdelstrother in [#1252](https://github.com/pat/thinking-sphinx/pull/1252).
17
+
18
+ ### Fixed
19
+
20
+ * Handle both SQL and RT indices correctly for inheritance column checks, by @akostadinov in [#1249](https://github.com/pat/thinking-sphinx/pull/1249).
21
+ * Ensure tests and CI work with recent Manticore versions, by @jdelstrother in [#1263](https://github.com/pat/thinking-sphinx/pull/1263).
22
+ * Use `rm -rf` to delete test and temporary directories (instead of `rm -r`).
23
+
5
24
  ## 5.5.1 - 2022-12-31
6
25
 
7
26
  [Release Notes](https://github.com/pat/thinking-sphinx/releases/tag/v5.5.1)
data/README.textile CHANGED
@@ -1,6 +1,6 @@
1
1
  h1. Thinking Sphinx
2
2
 
3
- Thinking Sphinx is a library for connecting ActiveRecord to the Sphinx full-text search tool, and integrates closely with Rails (but also works with other Ruby web frameworks). The current release is v5.5.1.
3
+ Thinking Sphinx is a library for connecting ActiveRecord to the Sphinx full-text search tool, and integrates closely with Rails (but also works with other Ruby web frameworks). The current release is v5.6.0.
4
4
 
5
5
  h2. Upgrading
6
6
 
@@ -31,7 +31,7 @@ The current release of Thinking Sphinx works with the following versions of its
31
31
  |_. Library |_. Minimum |_. Tested Against |
32
32
  | Ruby | v2.4 | v2.4, v2.5, v2.6, v2.7, v3.0, v3.1, v3.2 |
33
33
  | Sphinx | v2.2.11 | v2.2.11, v3.4.1 |
34
- | Manticore | v2.8 | v3.5, v4.0 |
34
+ | Manticore | v2.8 | v4.0, v6.0 |
35
35
  | ActiveRecord | v4.2 | v4.2..v7.0 |
36
36
 
37
37
  It _might_ work with older versions of Ruby, but it's highly recommended to update to a supported release.
@@ -42,7 +42,7 @@ h3. Sphinx or Manticore
42
42
 
43
43
  If you're using Sphinx, v2.2.11 is recommended even though it's quite old, as it works well with PostgreSQL databases (but if you're using MySQL - or real-time indices - then v3.3.1 should also be fine).
44
44
 
45
- If you're opting for Manticore instead, v2.8 or newer works, but v3 or newer is recommended as that's what is actively tested against.
45
+ If you're opting for Manticore instead, v2.8 or newer works, but v4 or newer is recommended as that's what is actively tested against. The v4.2 and 5.0 releases had bugs with facet searching, but that's been fixed in Manticore v6.0.
46
46
 
47
47
  h3. Rails and ActiveRecord
48
48
 
@@ -81,4 +81,4 @@ You can then run the unit tests with @rake spec:unit@, the acceptance tests with
81
81
 
82
82
  h2. Licence
83
83
 
84
- Copyright (c) 2007-2022, Thinking Sphinx is developed and maintained by Pat Allan, and is released under the open MIT Licence. Many thanks to "all who have contributed patches":https://github.com/pat/thinking-sphinx/contributors.
84
+ Copyright (c) 2007-2024, Thinking Sphinx is developed and maintained by Pat Allan, and is released under the open MIT Licence. Many thanks to "all who have contributed patches":https://github.com/pat/thinking-sphinx/contributors.
data/bin/loadsphinx CHANGED
@@ -70,15 +70,26 @@ load_manticore () {
70
70
  url="https://repo.manticoresearch.com/repository/manticoresearch_focal/dists/focal/main/binary-amd64/manticore_3.5.4-210107-f70faec5_amd64.deb";;
71
71
  4.0.2)
72
72
  url="https://repo.manticoresearch.com/repository/manticoresearch_focal/dists/focal/main/binary-amd64/manticore_4.0.2-210921-af497f245_amd64.deb";;
73
+ 4.2.0)
74
+ url="https://repo.manticoresearch.com/repository/manticoresearch_focal/dists/focal/main/binary-amd64/manticore_4.2.0-211223-15e927b28_amd64.deb";;
75
+ 6.0.0)
76
+ url="skipped";;
73
77
  *)
74
78
  echo "No Manticore version $version available"
75
79
  exit 1;;
76
80
  esac
77
81
 
78
- sudo apt-get install default-libmysqlclient-dev
79
- curl --location $url -o manticore.deb
80
- sudo dpkg -i ./manticore.deb
81
- sudo apt-get install -f
82
+ if [ "$version" == "6.0.0" ]; then
83
+ curl --location https://repo.manticoresearch.com/manticore-repo.noarch.deb -o repo.deb
84
+ sudo dpkg -i repo.deb
85
+ sudo apt update
86
+ sudo apt install manticore
87
+ else
88
+ sudo apt-get install default-libmysqlclient-dev
89
+ curl --location $url -o manticore.deb
90
+ sudo dpkg -i ./manticore.deb
91
+ sudo apt-get install -f
92
+ fi
82
93
  }
83
94
 
84
95
  if [ "$engine" == "sphinx" ]; then
@@ -4,6 +4,21 @@ module ThinkingSphinx::ActiveRecord::Base
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
+ # Avoid method collisions for public Thinking Sphinx methods added to all
8
+ # ActiveRecord models. The `sphinx_`-prefixed versions will always exist,
9
+ # and the non-prefixed versions will be added if a method of that name
10
+ # doesn't already exist.
11
+ #
12
+ # If a method is overwritten later by something else, that's also fine - the
13
+ # prefixed versions will still be there.
14
+ class_module = ThinkingSphinx::ActiveRecord::Base::ClassMethods
15
+ class_module.public_instance_methods.each do |method_name|
16
+ short_method = method_name.to_s.delete_prefix("sphinx_").to_sym
17
+ next if methods.include?(short_method)
18
+
19
+ define_singleton_method(short_method, method(method_name))
20
+ end
21
+
7
22
  if ActiveRecord::VERSION::STRING.to_i >= 5
8
23
  [
9
24
  ::ActiveRecord::Reflection::HasManyReflection,
@@ -25,24 +40,28 @@ module ThinkingSphinx::ActiveRecord::Base
25
40
  end
26
41
 
27
42
  module ClassMethods
28
- def facets(query = nil, options = {})
43
+ def sphinx_facets(query = nil, options = {})
29
44
  merge_search ThinkingSphinx.facets, query, options
30
45
  end
31
46
 
32
- def search(query = nil, options = {})
47
+ def sphinx_search(query = nil, options = {})
33
48
  merge_search ThinkingSphinx.search, query, options
34
49
  end
35
50
 
36
- def search_count(query = nil, options = {})
51
+ def sphinx_search_count(query = nil, options = {})
37
52
  search_for_ids(query, options).total_entries
38
53
  end
39
54
 
40
- def search_for_ids(query = nil, options = {})
55
+ def sphinx_search_for_ids(query = nil, options = {})
41
56
  ThinkingSphinx::Search::Merger.new(
42
57
  search(query, options)
43
58
  ).merge! nil, :ids_only => true
44
59
  end
45
60
 
61
+ def sphinx_search_none
62
+ merge_search ThinkingSphinx.search, nil, none: true
63
+ end
64
+
46
65
  private
47
66
 
48
67
  def default_sphinx_scope?
@@ -2,7 +2,7 @@
2
2
 
3
3
  class ThinkingSphinx::ActiveRecord::FilterReflection
4
4
  ReflectionGenerator = case ActiveRecord::VERSION::STRING.to_f
5
- when 5.2..7.0
5
+ when 5.2..7.1
6
6
  ThinkingSphinx::ActiveRecord::Depolymorph::OverriddenReflection
7
7
  when 4.1..5.1
8
8
  ThinkingSphinx::ActiveRecord::Depolymorph::AssociationReflection
@@ -2,24 +2,36 @@
2
2
 
3
3
  class ThinkingSphinx::ActiveRecord::LogSubscriber < ActiveSupport::LogSubscriber
4
4
  def guard(event)
5
- identifier = color 'Sphinx', GREEN, true
5
+ identifier = colored_text "Sphinx"
6
6
  warn " #{identifier} #{event.payload[:guard]}"
7
7
  end
8
8
 
9
9
  def message(event)
10
- identifier = color 'Sphinx', GREEN, true
10
+ identifier = colored_text "Sphinx"
11
11
  debug " #{identifier} #{event.payload[:message]}"
12
12
  end
13
13
 
14
14
  def query(event)
15
- identifier = color('Sphinx Query (%.1fms)' % event.duration, GREEN, true)
15
+ identifier = colored_text("Sphinx Query (%.1fms)" % event.duration)
16
16
  debug " #{identifier} #{event.payload[:query]}"
17
17
  end
18
18
 
19
19
  def caution(event)
20
- identifier = color 'Sphinx', GREEN, true
20
+ identifier = colored_text "Sphinx"
21
21
  warn " #{identifier} #{event.payload[:caution]}"
22
22
  end
23
+
24
+ private
25
+
26
+ if Rails.gem_version >= Gem::Version.new("7.1.0")
27
+ def colored_text(text)
28
+ color text, GREEN, bold: true
29
+ end
30
+ else
31
+ def colored_text(text)
32
+ color text, GREEN, true
33
+ end
34
+ end
23
35
  end
24
36
 
25
37
  ThinkingSphinx::ActiveRecord::LogSubscriber.attach_to :thinking_sphinx
@@ -7,7 +7,7 @@ class ThinkingSphinx::Commands::ClearRealTime < ThinkingSphinx::Commands::Base
7
7
  Dir["#{index.path}.*"].each { |path| FileUtils.rm path }
8
8
  end
9
9
 
10
- FileUtils.rm_r(binlog_path) if File.exist?(binlog_path)
10
+ FileUtils.rm_rf(binlog_path) if File.exist?(binlog_path)
11
11
  end
12
12
 
13
13
  private
@@ -7,7 +7,7 @@ class ThinkingSphinx::Commands::ClearSQL < ThinkingSphinx::Commands::Base
7
7
  Dir["#{index.path}.*"].each { |path| FileUtils.rm path }
8
8
  end
9
9
 
10
- FileUtils.rm_r Dir["#{configuration.indices_location}/ts-*.tmp"]
10
+ FileUtils.rm_rf Dir["#{configuration.indices_location}/ts-*.tmp"]
11
11
  end
12
12
 
13
13
  private
@@ -18,19 +18,19 @@ class ThinkingSphinx::Configuration::MinimumFields
18
18
  attr_reader :indices
19
19
 
20
20
  def field_collections
21
- plain_indices_without_inheritance.collect(&:sources).flatten +
22
- indices_of_type('rt')
23
- end
24
-
25
- def indices_of_type(type)
26
- indices.select { |index| index.type == type }
21
+ indices_without_inheritance_of_type('plain').collect(&:sources).flatten +
22
+ indices_without_inheritance_of_type('rt')
27
23
  end
28
24
 
29
25
  def inheritance_columns?(index)
30
26
  index.model.table_exists? && index.model.column_names.include?(index.model.inheritance_column)
31
27
  end
32
28
 
33
- def plain_indices_without_inheritance
34
- indices_of_type('plain').reject(&method(:inheritance_columns?))
29
+ def indices_without_inheritance_of_type(type)
30
+ indices_without_inheritance.select { |index| index.type == type }
31
+ end
32
+
33
+ def indices_without_inheritance
34
+ indices.reject(&method(:inheritance_columns?))
35
35
  end
36
36
  end
@@ -26,6 +26,12 @@ class ThinkingSphinx::Masks::ScopesMask
26
26
  search query, options.merge(:ids_only => true)
27
27
  end
28
28
 
29
+ def none
30
+ ThinkingSphinx::Search::Merger.new(@search).merge! nil, :none => true
31
+ end
32
+
33
+ alias_method :search_none, :none
34
+
29
35
  private
30
36
 
31
37
  def apply_scope(scope, *args)
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ThinkingSphinx::Processor
4
+ # @param instance [ActiveRecord::Base] an ActiveRecord object
5
+ # @param model [Class] the ActiveRecord model of the instance
6
+ # @param id [Integer] the instance indices primary key (might be different from model primary key)
4
7
  def initialize(instance: nil, model: nil, id: nil)
5
8
  raise ArgumentError if instance.nil? && (model.nil? || id.nil?)
6
9
 
@@ -12,16 +15,27 @@ class ThinkingSphinx::Processor
12
15
  def delete
13
16
  return if instance&.new_record?
14
17
 
15
- indices.each { |index|
16
- ThinkingSphinx::Deletion.perform(
17
- index, id || instance.public_send(index.primary_key)
18
- )
19
- }
18
+ indices.each { |index| perform_deletion(index) }
20
19
  end
21
20
 
21
+ # Will insert instance into all matching indices
22
22
  def upsert
23
23
  real_time_indices.each do |index|
24
- ThinkingSphinx::RealTime::Transcriber.new(index).copy loaded_instance
24
+ found = loaded_instance(index)
25
+ ThinkingSphinx::RealTime::Transcriber.new(index).copy found if found
26
+ end
27
+ end
28
+
29
+ # Will upsert or delete instance into all matching indices based on index scope
30
+ def sync
31
+ real_time_indices.each do |index|
32
+ found = find_in(index)
33
+
34
+ if found
35
+ ThinkingSphinx::RealTime::Transcriber.new(index).copy found
36
+ else
37
+ ThinkingSphinx::Deletion.perform(index, index_id(index))
38
+ end
25
39
  end
26
40
  end
27
41
 
@@ -35,11 +49,23 @@ class ThinkingSphinx::Processor
35
49
  ).to_a
36
50
  end
37
51
 
38
- def loaded_instance
39
- @loaded_instance ||= instance || model.find(id)
52
+ def find_in(index)
53
+ index.scope.find_by(index.primary_key => index_id(index))
54
+ end
55
+
56
+ def loaded_instance(index)
57
+ instance || find_in(index)
40
58
  end
41
59
 
42
60
  def real_time_indices
43
61
  indices.select { |index| index.is_a? ThinkingSphinx::RealTime::Index }
44
62
  end
63
+
64
+ def perform_deletion(index)
65
+ ThinkingSphinx::Deletion.perform(index, index_id(index))
66
+ end
67
+
68
+ def index_id(index)
69
+ id || instance.public_send(index.primary_key)
70
+ end
45
71
  end
@@ -7,6 +7,7 @@ class ThinkingSphinx::Search::Context
7
7
  @search = search
8
8
  @configuration = configuration || ThinkingSphinx::Configuration.instance
9
9
  @memory = {
10
+ :raw => [],
10
11
  :results => [],
11
12
  :panes => ThinkingSphinx::Configuration::Defaults::PANES.clone
12
13
  }
@@ -12,7 +12,7 @@ class ThinkingSphinx::Search < Array
12
12
  [
13
13
  :classes, :conditions, :excerpts, :geo, :group_by, :ids_only,
14
14
  :ignore_scopes, :indices, :limit, :masks, :max_matches, :middleware,
15
- :offset, :order, :order_group_by, :page, :per_page, :populate,
15
+ :none, :offset, :order, :order_group_by, :page, :per_page, :populate,
16
16
  :retry_stale, :select, :skip_sti, :sql, :star, :with, :with_all, :without,
17
17
  :without_ids
18
18
  ] +
@@ -92,7 +92,7 @@ class ThinkingSphinx::Search < Array
92
92
  def populate
93
93
  return self if @populated
94
94
 
95
- middleware.call [context]
95
+ middleware.call [context] unless options[:none]
96
96
  @populated = true
97
97
 
98
98
  self
@@ -42,7 +42,7 @@ class ThinkingSphinx::Test
42
42
  config.indices_location,
43
43
  config.searchd.binlog_path
44
44
  ].each do |path|
45
- FileUtils.rm_r(path) if File.exist?(path)
45
+ FileUtils.rm_rf(path) if File.exist?(path)
46
46
  end
47
47
  end
48
48
 
@@ -34,6 +34,10 @@ module ThinkingSphinx
34
34
  ThinkingSphinx::Search::Merger.new(search).merge! nil, :ids_only => true
35
35
  end
36
36
 
37
+ def self.none
38
+ ThinkingSphinx::Search.new nil, :none => true
39
+ end
40
+
37
41
  def self.before_index_hooks
38
42
  @before_index_hooks
39
43
  end
@@ -4,17 +4,17 @@ require 'acceptance/spec_helper'
4
4
 
5
5
  describe 'Accessing attributes directly via search results', :live => true do
6
6
  it "allows access to attribute values" do
7
- Book.create! :title => 'American Gods', :year => 2001
7
+ Book.create! :title => 'American Gods', :publishing_year => 2001
8
8
  index
9
9
 
10
10
  search = Book.search('gods')
11
11
  search.context[:panes] << ThinkingSphinx::Panes::AttributesPane
12
12
 
13
- expect(search.first.sphinx_attributes['year']).to eq(2001)
13
+ expect(search.first.sphinx_attributes['publishing_year']).to eq(2001)
14
14
  end
15
15
 
16
16
  it "provides direct access to the search weight/relevance scores" do
17
- Book.create! :title => 'American Gods', :year => 2001
17
+ Book.create! :title => 'American Gods', :publishing_year => 2001
18
18
  index
19
19
 
20
20
  search = Book.search 'gods', :select => "*, weight()"
@@ -37,7 +37,7 @@ describe 'Accessing attributes directly via search results', :live => true do
37
37
  end
38
38
 
39
39
  it "can enumerate with the weight" do
40
- gods = Book.create! :title => 'American Gods', :year => 2001
40
+ gods = Book.create! :title => 'American Gods', :publishing_year => 2001
41
41
  index
42
42
 
43
43
  search = Book.search 'gods', :select => "*, weight()"
@@ -5,7 +5,7 @@ require 'acceptance/spec_helper'
5
5
 
6
6
  describe 'Accessing excerpts for methods on a search result', :live => true do
7
7
  it "returns excerpts for a given method" do
8
- Book.create! :title => 'American Gods', :year => 2001
8
+ Book.create! :title => 'American Gods', :publishing_year => 2001
9
9
  index
10
10
 
11
11
  search = Book.search('gods')
@@ -16,7 +16,7 @@ describe 'Accessing excerpts for methods on a search result', :live => true do
16
16
  end
17
17
 
18
18
  it "handles UTF-8 text for excerpts" do
19
- Book.create! :title => 'Война и миръ', :year => 1869
19
+ Book.create! :title => 'Война и миръ', :publishing_year => 1869
20
20
  index
21
21
 
22
22
  search = Book.search 'миръ'
@@ -4,36 +4,36 @@ require 'acceptance/spec_helper'
4
4
 
5
5
  describe 'Grouping search results by attributes', :live => true do
6
6
  it "groups by the provided attribute" do
7
- snuff = Book.create! :title => 'Snuff', :year => 2011
8
- earth = Book.create! :title => 'The Long Earth', :year => 2012
9
- dodger = Book.create! :title => 'Dodger', :year => 2012
7
+ snuff = Book.create! :title => 'Snuff', :publishing_year => 2011
8
+ earth = Book.create! :title => 'The Long Earth', :publishing_year => 2012
9
+ dodger = Book.create! :title => 'Dodger', :publishing_year => 2012
10
10
 
11
11
  index
12
12
 
13
- expect(Book.search(:group_by => :year).to_a).to eq([snuff, earth])
13
+ expect(Book.search(:group_by => :publishing_year).to_a).to eq([snuff, earth])
14
14
  end
15
15
 
16
16
  it "allows sorting within the group" do
17
- snuff = Book.create! :title => 'Snuff', :year => 2011
18
- earth = Book.create! :title => 'The Long Earth', :year => 2012
19
- dodger = Book.create! :title => 'Dodger', :year => 2012
17
+ snuff = Book.create! :title => 'Snuff', :publishing_year => 2011
18
+ earth = Book.create! :title => 'The Long Earth', :publishing_year => 2012
19
+ dodger = Book.create! :title => 'Dodger', :publishing_year => 2012
20
20
 
21
21
  index
22
22
 
23
- expect(Book.search(:group_by => :year, :order_group_by => 'title ASC').to_a).
23
+ expect(Book.search(:group_by => :publishing_year, :order_group_by => 'title ASC').to_a).
24
24
  to eq([snuff, dodger])
25
25
  end
26
26
 
27
27
  it "allows enumerating by count" do
28
- snuff = Book.create! :title => 'Snuff', :year => 2011
29
- earth = Book.create! :title => 'The Long Earth', :year => 2012
30
- dodger = Book.create! :title => 'Dodger', :year => 2012
28
+ snuff = Book.create! :title => 'Snuff', :publishing_year => 2011
29
+ earth = Book.create! :title => 'The Long Earth', :publishing_year => 2012
30
+ dodger = Book.create! :title => 'Dodger', :publishing_year => 2012
31
31
 
32
32
  index
33
33
 
34
34
  expectations = [[snuff, 1], [earth, 2]]
35
35
 
36
- Book.search(:group_by => :year).each_with_count do |book, count|
36
+ Book.search(:group_by => :publishing_year).each_with_count do |book, count|
37
37
  expectation = expectations.shift
38
38
 
39
39
  expect(book).to eq(expectation.first)
@@ -42,15 +42,15 @@ describe 'Grouping search results by attributes', :live => true do
42
42
  end
43
43
 
44
44
  it "allows enumerating by group" do
45
- snuff = Book.create! :title => 'Snuff', :year => 2011
46
- earth = Book.create! :title => 'The Long Earth', :year => 2012
47
- dodger = Book.create! :title => 'Dodger', :year => 2012
45
+ snuff = Book.create! :title => 'Snuff', :publishing_year => 2011
46
+ earth = Book.create! :title => 'The Long Earth', :publishing_year => 2012
47
+ dodger = Book.create! :title => 'Dodger', :publishing_year => 2012
48
48
 
49
49
  index
50
50
 
51
51
  expectations = [[snuff, 2011], [earth, 2012]]
52
52
 
53
- Book.search(:group_by => :year).each_with_group do |book, group|
53
+ Book.search(:group_by => :publishing_year).each_with_group do |book, group|
54
54
  expectation = expectations.shift
55
55
 
56
56
  expect(book).to eq(expectation.first)
@@ -59,14 +59,14 @@ describe 'Grouping search results by attributes', :live => true do
59
59
  end
60
60
 
61
61
  it "allows enumerating by group and count" do
62
- snuff = Book.create! :title => 'Snuff', :year => 2011
63
- earth = Book.create! :title => 'The Long Earth', :year => 2012
64
- dodger = Book.create! :title => 'Dodger', :year => 2012
62
+ snuff = Book.create! :title => 'Snuff', :publishing_year => 2011
63
+ earth = Book.create! :title => 'The Long Earth', :publishing_year => 2012
64
+ dodger = Book.create! :title => 'Dodger', :publishing_year => 2012
65
65
 
66
66
  index
67
67
 
68
68
  expectations = [[snuff, 2011, 1], [earth, 2012, 2]]
69
- search = Book.search(:group_by => :year)
69
+ search = Book.search(:group_by => :publishing_year)
70
70
 
71
71
  search.each_with_group_and_count do |book, group, count|
72
72
  expectation = expectations.shift
@@ -28,7 +28,7 @@ describe 'Updates to records in real-time indices', :live => true do
28
28
  expect(Admin::Person.search('Mort').to_a).to eq([person])
29
29
  end
30
30
 
31
- it "can use a direct interface for processing records" do
31
+ it "can use direct interface for upserting records" do
32
32
  Admin::Person.connection.execute <<~SQL
33
33
  INSERT INTO admin_people (name, created_at, updated_at)
34
34
  VALUES ('Pat', now(), now());
@@ -52,4 +52,64 @@ describe 'Updates to records in real-time indices', :live => true do
52
52
 
53
53
  expect(Admin::Person.search('Patrick').to_a).to eq([instance])
54
54
  end
55
+
56
+ it "can use direct interface for processing records outside scope" do
57
+ Article.connection.execute <<~SQL
58
+ INSERT INTO articles (title, published, created_at, updated_at)
59
+ VALUES ('Nice Title', TRUE, now(), now());
60
+ SQL
61
+
62
+ article = Article.last
63
+
64
+ ThinkingSphinx::Processor.new(model: article.class, id: article.id).sync
65
+
66
+ expect(ThinkingSphinx.search('Nice', :indices => ["published_articles_core"])).to include(article)
67
+
68
+ Article.connection.execute <<~SQL
69
+ UPDATE articles SET published = FALSE WHERE title = 'Nice Title';
70
+ SQL
71
+ ThinkingSphinx::Processor.new(model: article.class, id: article.id).sync
72
+
73
+ expect(ThinkingSphinx.search('Nice', :indices => ["published_articles_core"])).to be_empty
74
+ end
75
+
76
+ it "can use direct interface for processing deleted records" do
77
+ Article.connection.execute <<~SQL
78
+ INSERT INTO articles (title, published, created_at, updated_at)
79
+ VALUES ('Nice Title', TRUE, now(), now());
80
+ SQL
81
+
82
+ article = Article.last
83
+ ThinkingSphinx::Processor.new(:instance => article).sync
84
+
85
+ expect(ThinkingSphinx.search('Nice', :indices => ["published_articles_core"])).to include(article)
86
+
87
+ Article.connection.execute <<~SQL
88
+ DELETE FROM articles where title = 'Nice Title';
89
+ SQL
90
+
91
+ ThinkingSphinx::Processor.new(:instance => article).sync
92
+
93
+ expect(ThinkingSphinx.search('Nice', :indices => ["published_articles_core"])).to be_empty
94
+ end
95
+
96
+ it "syncs records in real-time index with alternate ids" do
97
+ Album.connection.execute <<~SQL
98
+ INSERT INTO albums (id, name, artist, integer_id)
99
+ VALUES ('#{("a".."z").to_a.sample}', 'Sing to the Moon', 'Laura Mvula', #{rand(10000)});
100
+ SQL
101
+
102
+ album = Album.last
103
+ ThinkingSphinx::Processor.new(:model => Album, id: album.integer_id).sync
104
+
105
+ expect(ThinkingSphinx.search('Laura', :indices => ["album_real_core"])).to include(album)
106
+
107
+ Article.connection.execute <<~SQL
108
+ DELETE FROM albums where id = '#{album.id}';
109
+ SQL
110
+
111
+ ThinkingSphinx::Processor.new(:instance => album).sync
112
+
113
+ expect(ThinkingSphinx.search('Laura', :indices => ["album_real_core"])).to be_empty
114
+ end
55
115
  end