thinking-sphinx 4.1.0 → 4.2.0

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 (28) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +9 -6
  3. data/Appraisals +7 -1
  4. data/CHANGELOG.markdown +20 -0
  5. data/README.textile +7 -7
  6. data/lib/thinking_sphinx/active_record/callbacks/delete_callbacks.rb +3 -1
  7. data/lib/thinking_sphinx/active_record/callbacks/update_callbacks.rb +3 -1
  8. data/lib/thinking_sphinx/active_record/database_adapters/mysql_adapter.rb +7 -1
  9. data/lib/thinking_sphinx/active_record/depolymorph/overridden_reflection.rb +17 -2
  10. data/lib/thinking_sphinx/active_record/property_query.rb +2 -1
  11. data/lib/thinking_sphinx/active_record/source_joins.rb +12 -1
  12. data/lib/thinking_sphinx/middlewares/glazier.rb +12 -1
  13. data/lib/thinking_sphinx/middlewares/sphinxql.rb +13 -13
  14. data/lib/thinking_sphinx/railtie.rb +4 -0
  15. data/lib/thinking_sphinx/scopes.rb +4 -0
  16. data/lib/thinking_sphinx/search.rb +5 -4
  17. data/lib/thinking_sphinx/settings.rb +2 -1
  18. data/spec/acceptance/attribute_access_spec.rb +9 -0
  19. data/spec/acceptance/remove_deleted_records_spec.rb +12 -0
  20. data/spec/acceptance/specifying_sql_spec.rb +36 -0
  21. data/spec/thinking_sphinx/active_record/callbacks/delete_callbacks_spec.rb +2 -2
  22. data/spec/thinking_sphinx/active_record/callbacks/update_callbacks_spec.rb +1 -1
  23. data/spec/thinking_sphinx/active_record/database_adapters/mysql_adapter_spec.rb +13 -1
  24. data/spec/thinking_sphinx/active_record/filter_reflection_spec.rb +14 -2
  25. data/spec/thinking_sphinx/middlewares/glazier_spec.rb +6 -5
  26. data/spec/thinking_sphinx/scopes_spec.rb +4 -0
  27. data/thinking-sphinx.gemspec +2 -2
  28. metadata +5 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f97dda977df8957bc00495056f23faf9e5a7f5c15e72b92f18f7c8e28d8385c2
4
- data.tar.gz: 7364500f75d1edbc579b4f96bba8be9a3629197ece4cb8e0647077b991c03cfa
3
+ metadata.gz: 02fc0ce9894c728fcc2b8bdb3bcb867a6ef9a40653269cbc213d2a7d25ee94d3
4
+ data.tar.gz: 230c83de8b1a5cdbdbb2667b5823ffb6707ad8dfdda15c4c1f8b396642ed1c15
5
5
  SHA512:
6
- metadata.gz: 0d928656c4ca161ad37f7ecea1a3a0435b1259016dc77e3ea65717d135f2bde6f6b2a9a38390e5a5eaf3768fc152334310bcb4c166747611670856e3cd0107b1
7
- data.tar.gz: 4faefa5f695f14da95e564b97d5f29844befb36ff77888330051367da3fe5d5d48b10328713493fbffb914b4b167d750ed6e591d9a24fb6498a97b6e4f5d8ed5
6
+ metadata.gz: f679238131cebcfa2eda04479696065205505e211d583c70c70efe18059d8ddfee29bf6e129070cb82e7b52928dc6f60d88662e6f1e0298e0e5eb6613ff7682e
7
+ data.tar.gz: 2f09c08fc40e1a0052e06b790c5bbcc48a8ff3e3d700debcf2089e1504b4d04652fa4d23bd27f2354d8bd29bf2cca3aae9dd86b96aeed12e49036b7542ba8d4b
@@ -3,7 +3,7 @@ rvm:
3
3
  - 2.3.8
4
4
  - 2.4.5
5
5
  - 2.5.3
6
- - 2.6.0
6
+ - 2.6.1
7
7
  addons:
8
8
  apt:
9
9
  packages:
@@ -13,7 +13,8 @@ addons:
13
13
  before_install:
14
14
  - pip install --upgrade --user awscli
15
15
  - gem update --system
16
- - gem install bundler
16
+ - gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true
17
+ - gem install bundler -v '< 2'
17
18
  before_script:
18
19
  - mysql -e 'create database thinking_sphinx;' > /dev/null
19
20
  - psql -c 'create database thinking_sphinx;' -U postgres >/dev/null
@@ -32,10 +33,12 @@ env:
32
33
  - DATABASE=mysql2 SPHINX_VERSION=3.0.3 SPHINX_ENGINE=sphinx
33
34
  - DATABASE=postgresql SPHINX_VERSION=3.0.3 SPHINX_ENGINE=sphinx
34
35
  - DATABASE=mysql2 SPHINX_VERSION=3.1.1 SPHINX_ENGINE=sphinx
35
- - DATABASE=mysql2 SPHINX_VERSION=2.6.3 SPHINX_ENGINE=manticore
36
- - DATABASE=postgresql SPHINX_VERSION=2.6.3 SPHINX_ENGINE=manticore
37
- - DATABASE=mysql2 SPHINX_VERSION=2.7.4 SPHINX_ENGINE=manticore
38
- - DATABASE=postgresql SPHINX_VERSION=2.7.4 SPHINX_ENGINE=manticore
36
+ - DATABASE=mysql2 SPHINX_VERSION=2.6.4 SPHINX_ENGINE=manticore
37
+ - DATABASE=postgresql SPHINX_VERSION=2.6.4 SPHINX_ENGINE=manticore
38
+ - DATABASE=mysql2 SPHINX_VERSION=2.7.5 SPHINX_ENGINE=manticore
39
+ - DATABASE=postgresql SPHINX_VERSION=2.7.5 SPHINX_ENGINE=manticore
40
+ - DATABASE=mysql2 SPHINX_VERSION=2.8.1 SPHINX_ENGINE=manticore
41
+ - DATABASE=postgresql SPHINX_VERSION=2.8.1 SPHINX_ENGINE=manticore
39
42
  # - DATABASE=postgresql SPHINX_VERSION=3.1.1 SPHINX_ENGINE=sphinx
40
43
  sudo: false
41
44
  addons:
data/Appraisals CHANGED
@@ -41,4 +41,10 @@ appraise 'rails_5_2' do
41
41
  gem 'rails', '~> 5.2.0'
42
42
  gem 'mysql2', '~> 0.5.0', :platform => :ruby
43
43
  gem 'pg', '~> 1.0', :platform => :ruby
44
- end if RUBY_PLATFORM != 'java' && RUBY_VERSION.to_f >= 2.3
44
+ end if RUBY_PLATFORM != 'java'
45
+
46
+ appraise 'rails_6_0' do
47
+ gem 'rails', '~> 6.0.0.beta1'
48
+ gem 'mysql2', '~> 0.5.0', :platform => :ruby
49
+ gem 'pg', '~> 1.0', :platform => :ruby
50
+ end if RUBY_PLATFORM != 'java' && RUBY_VERSION.to_f >= 2.5
@@ -2,6 +2,26 @@
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
+ ## 4.2.0 - 2019-03-09
6
+
7
+ ### Added
8
+
9
+ * Allow changing the default encoding for MySQL database connections from utf8 to something else via the `mysql_encoding` setting in `config/thinking_sphinx.yml`. In the next significant release, the default will change to utf8mb4 (which is supported in MySQL 5.5.3 and newer).
10
+ * Added Rails 6.0 and Manticore 2.8 to the test matrix.
11
+
12
+ ### Changed
13
+
14
+ * Use Arel's SQL literals for generated order clauses, to avoid warnings from Rails 6.
15
+
16
+ ### Fixed
17
+
18
+ * Fix usage of alternative primary keys in update and deletion callbacks and attribute access.
19
+ * Ensure `respond_to?` takes Sphinx scopes into account ([Jonathan del Strother](https://github.com/jdelstrother) in [#1124](https://github.com/pat/thinking-sphinx/pull/1124)).
20
+ * Add `:excerpts` as a known option for search requests.
21
+ * Fix depolymorphed association join construction with Rails 6.0.0.beta2.
22
+ * Reset ThinkingSphinx::Configuration's cached values when Rails reloads, to avoid holding onto stale references to ActiveRecord models ([#1125](https://github.com/pat/thinking-sphinx/issues/1125)).
23
+ * Don't join against associations in `sql_query` if they're only used by query-sourced properties ([Hans de Graaff](https://github.com/graaff) in [#1127](https://github.com/pat/thinking-sphinx/pull/1127)).
24
+
5
25
  ## 4.1.0 - 2018-12-28
6
26
 
7
27
  [Release Notes](https://github.com/pat/thinking-sphinx/releases/tag/v4.1.0)
@@ -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 v4.1.0.
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 v4.2.0.
4
4
 
5
5
  h2. Upgrading
6
6
 
@@ -14,7 +14,7 @@ It's a gem, so install it like you would any other gem. You will also need to sp
14
14
 
15
15
  <pre><code>gem 'mysql2', '~> 0.3', :platform => :ruby
16
16
  gem 'jdbc-mysql', '~> 5.1.35', :platform => :jruby
17
- gem 'thinking-sphinx', '~> 4.1'</code></pre>
17
+ gem 'thinking-sphinx', '~> 4.2'</code></pre>
18
18
 
19
19
  The MySQL gems mentioned are required for connecting to Sphinx, so please include it even when you're using PostgreSQL for your database. If you're using JRuby with a version of Sphinx prior to 2.2.11, there is "currently an issue with Sphinx and jdbc-mysql 5.1.36 or newer":http://sphinxsearch.com/forum/view.html?id=13939, so you'll need to stick to nothing more recent than 5.1.35, or upgrade Sphinx.
20
20
 
@@ -29,10 +29,10 @@ h2. Requirements
29
29
  The current release of Thinking Sphinx works with the following versions of its dependencies:
30
30
 
31
31
  |_. Library |_. Minimum |_. Tested Against |
32
- | Ruby | v2.3 | v2.3.8, v2.4.5, v2.5.3, v2.6.0 |
32
+ | Ruby | v2.3 | v2.3.8, v2.4.5, v2.5.3, v2.6.1 |
33
33
  | Sphinx | v2.1.2 | v2.1.9, v2.2.11, v3.0.3, v3.1.1 |
34
- | Manticore | v2.6.3 | v2.6.3, v2.7.4 |
35
- | ActiveRecord | v3.2 | v3.2, v4.0, v4.1, v4.2, v5.0, v5.1, v5.2 |
34
+ | Manticore | v2.6.3 | v2.6.4, v2.7.5, v2.8.1 |
35
+ | ActiveRecord | v3.2 | v3.2..v6.0 |
36
36
 
37
37
  It _might_ work with older versions of Ruby, but it's highly recommended to update to a supported release.
38
38
 
@@ -52,7 +52,7 @@ If you want ActiveRecord 3.1 support, then refer to the 3.0.x releases of Thinki
52
52
 
53
53
  h3. Ruby
54
54
 
55
- You'll need either the standard Ruby (v2.2 or newer) or JRuby (9.1 or newer).
55
+ You'll need either the standard Ruby (v2.3 or newer) or JRuby (9.1 or newer).
56
56
 
57
57
  h3. Database Versions
58
58
 
@@ -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-2018, 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-2019, 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.
@@ -19,7 +19,9 @@ class ThinkingSphinx::ActiveRecord::Callbacks::DeleteCallbacks <
19
19
  return if ThinkingSphinx::Callbacks.suspended? || instance.new_record?
20
20
 
21
21
  indices.each { |index|
22
- ThinkingSphinx::Deletion.perform index, instance.id
22
+ ThinkingSphinx::Deletion.perform(
23
+ index, instance.public_send(index.primary_key)
24
+ )
23
25
  }
24
26
  end
25
27
 
@@ -55,7 +55,9 @@ class ThinkingSphinx::ActiveRecord::Callbacks::UpdateCallbacks <
55
55
  return if attributes.empty?
56
56
 
57
57
  sphinxql = Riddle::Query.update(
58
- index.name, index.document_id_for_key(instance.id), attributes
58
+ index.name,
59
+ index.document_id_for_key(instance.public_send(index.primary_key)),
60
+ attributes
59
61
  )
60
62
  ThinkingSphinx::Connection.take do |connection|
61
63
  connection.execute(sphinxql)
@@ -40,6 +40,12 @@ class ThinkingSphinx::ActiveRecord::DatabaseAdapters::MySQLAdapter <
40
40
  end
41
41
 
42
42
  def utf8_query_pre
43
- ['SET NAMES utf8']
43
+ ["SET NAMES #{settings['mysql_encoding']}"]
44
+ end
45
+
46
+ private
47
+
48
+ def settings
49
+ ThinkingSphinx::Configuration.instance.settings
44
50
  end
45
51
  end
@@ -5,7 +5,7 @@
5
5
  class ThinkingSphinx::ActiveRecord::Depolymorph::OverriddenReflection <
6
6
  ThinkingSphinx::ActiveRecord::Depolymorph::BaseReflection
7
7
 
8
- module JoinConstraint
8
+ module BuildJoinConstraint
9
9
  def build_join_constraint(table, foreign_table)
10
10
  super.and(
11
11
  foreign_table[options[:foreign_type]].eq(
@@ -15,6 +15,16 @@ class ThinkingSphinx::ActiveRecord::Depolymorph::OverriddenReflection <
15
15
  end
16
16
  end
17
17
 
18
+ module JoinScope
19
+ def join_scope(table, foreign_table, foreign_klass)
20
+ super.where(
21
+ foreign_table[options[:foreign_type]].eq(
22
+ options[:class_name].constantize.base_class.name
23
+ )
24
+ )
25
+ end
26
+ end
27
+
18
28
  def self.overridden_classes
19
29
  @overridden_classes ||= {}
20
30
  end
@@ -28,8 +38,13 @@ class ThinkingSphinx::ActiveRecord::Depolymorph::OverriddenReflection <
28
38
  def klass
29
39
  self.class.overridden_classes[reflection.class] ||= begin
30
40
  subclass = Class.new reflection.class
31
- subclass.include JoinConstraint
41
+ subclass.include extension(reflection)
32
42
  subclass
33
43
  end
34
44
  end
45
+
46
+ def extension(reflection)
47
+ reflection.respond_to?(:build_join_constraint) ?
48
+ BuildJoinConstraint : JoinScope
49
+ end
35
50
  end
@@ -27,6 +27,7 @@ primary key.
27
27
  attr_reader :property, :source, :type
28
28
 
29
29
  delegate :unscoped, :to => :base_association_class, :prefix => true
30
+ delegate :sql, :to => Arel
30
31
 
31
32
  def base_association
32
33
  reflections.first
@@ -135,7 +136,7 @@ primary key.
135
136
  relation = relation.joins(joins) if joins.present?
136
137
  relation = relation.where("#{quoted_foreign_key} BETWEEN $start AND $end") if ranged?
137
138
  relation = relation.where("#{quoted_foreign_key} IS NOT NULL")
138
- relation = relation.order("#{quoted_foreign_key} ASC") if type.nil?
139
+ relation = relation.order(sql("#{quoted_foreign_key} ASC")) if type.nil?
139
140
 
140
141
  relation.to_sql
141
142
  end
@@ -27,7 +27,7 @@ class ThinkingSphinx::ActiveRecord::SourceJoins
27
27
  end
28
28
 
29
29
  def append_column_associations(column)
30
- return if column.__stack.empty?
30
+ return if column.__stack.empty? or column_included_in_queries?(column)
31
31
 
32
32
  joins.add_join_to column.__stack if column_exists?(column)
33
33
  end
@@ -54,4 +54,15 @@ class ThinkingSphinx::ActiveRecord::SourceJoins
54
54
  joins
55
55
  end
56
56
  end
57
+
58
+ def source_query_properties
59
+ source.properties.select { |field| field.source_type == :query }
60
+ end
61
+
62
+ # Use "first" here instead of a more intuitive flatten because flatten
63
+ # will also ask each column to become an Array and that will start
64
+ # to retrieve data.
65
+ def column_included_in_queries?(column)
66
+ source_query_properties.collect(&:columns).collect(&:first).include?(column)
67
+ end
57
68
  end
@@ -16,6 +16,7 @@ class ThinkingSphinx::Middlewares::Glazier <
16
16
  class Inner
17
17
  def initialize(context)
18
18
  @context = context
19
+ @indices = {}
19
20
  end
20
21
 
21
22
  def call
@@ -31,10 +32,20 @@ class ThinkingSphinx::Middlewares::Glazier <
31
32
 
32
33
  attr_reader :context
33
34
 
35
+ def indices_for(model)
36
+ @indices[model] ||= context[:indices].select do |index|
37
+ index.model == model
38
+ end
39
+ end
40
+
34
41
  def row_for(result)
42
+ ids = indices_for(result.class).collect do |index|
43
+ result.send index.primary_key
44
+ end
45
+
35
46
  context[:raw].detect { |row|
36
47
  row['sphinx_internal_class'] == result.class.name &&
37
- row['sphinx_internal_id'] == result.id
48
+ ids.include?(row['sphinx_internal_id'])
38
49
  }
39
50
  end
40
51
  end
@@ -82,19 +82,6 @@ class ThinkingSphinx::Middlewares::SphinxQL <
82
82
  end.flatten
83
83
  end
84
84
 
85
- def indices_match_classes?
86
- indices.collect(&:reference).uniq.sort == classes.collect { |klass|
87
- ThinkingSphinx::IndexSet.reference_name(klass)
88
- }.sort
89
- end
90
-
91
- def inheritance_column_select(klass)
92
- <<-SQL
93
- SELECT DISTINCT #{klass.inheritance_column}
94
- FROM #{klass.table_name}
95
- SQL
96
- end
97
-
98
85
  def exclusive_filters
99
86
  @exclusive_filters ||= (options[:without] || {}).tap do |without|
100
87
  without[:sphinx_internal_id] = options[:without_ids] if options[:without_ids].present?
@@ -144,6 +131,19 @@ SQL
144
131
  end
145
132
  end
146
133
 
134
+ def indices_match_classes?
135
+ indices.collect(&:reference).uniq.sort == classes.collect { |klass|
136
+ ThinkingSphinx::IndexSet.reference_name(klass)
137
+ }.sort
138
+ end
139
+
140
+ def inheritance_column_select(klass)
141
+ <<-SQL
142
+ SELECT DISTINCT #{klass.inheritance_column}
143
+ FROM #{klass.table_name}
144
+ SQL
145
+ end
146
+
147
147
  def order_clause
148
148
  order_by = options[:order]
149
149
  order_by = "#{order_by} ASC" if order_by.is_a? Symbol
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ThinkingSphinx::Railtie < Rails::Railtie
4
+ config.to_prepare do
5
+ ThinkingSphinx::Configuration.reset
6
+ end
7
+
4
8
  initializer 'thinking_sphinx.initialisation' do
5
9
  ActiveSupport.on_load(:active_record) do
6
10
  ActiveRecord::Base.send :include, ThinkingSphinx::ActiveRecord::Base
@@ -26,5 +26,9 @@ module ThinkingSphinx::Scopes
26
26
  query, options = sphinx_scopes[method].call(*args)
27
27
  search query, (options || {})
28
28
  end
29
+
30
+ def respond_to_missing?(method, include_private = false)
31
+ super || sphinx_scopes.keys.include?(method)
32
+ end
29
33
  end
30
34
  end
@@ -10,10 +10,11 @@ class ThinkingSphinx::Search < Array
10
10
  send class )
11
11
  KNOWN_OPTIONS = (
12
12
  [
13
- :classes, :conditions, :geo, :group_by, :ids_only, :ignore_scopes,
14
- :indices, :limit, :masks, :max_matches, :middleware, :offset, :order,
15
- :order_group_by, :page, :per_page, :populate, :retry_stale, :select,
16
- :skip_sti, :sql, :star, :with, :with_all, :without, :without_ids
13
+ :classes, :conditions, :excerpts, :geo, :group_by, :ids_only,
14
+ :ignore_scopes, :indices, :limit, :masks, :max_matches, :middleware,
15
+ :offset, :order, :order_group_by, :page, :per_page, :populate,
16
+ :retry_stale, :select, :skip_sti, :sql, :star, :with, :with_all, :without,
17
+ :without_ids
17
18
  ] +
18
19
  ThinkingSphinx::Middlewares::SphinxQL::SELECT_OPTIONS
19
20
  ).uniq
@@ -17,7 +17,8 @@ class ThinkingSphinx::Settings
17
17
  "log" => "log/ENVIRONMENT.searchd.log",
18
18
  "query_log" => "log/ENVIRONMENT.searchd.query.log",
19
19
  "binlog_path" => "tmp/binlog/ENVIRONMENT",
20
- "workers" => "threads"
20
+ "workers" => "threads",
21
+ "mysql_encoding" => "utf8"
21
22
  }.freeze
22
23
 
23
24
  def self.call(configuration)
@@ -23,6 +23,15 @@ describe 'Accessing attributes directly via search results', :live => true do
23
23
  expect(search.first.weight).to eq(2500)
24
24
  end
25
25
 
26
+ it "provides direct access to the weight with alternative primary keys" do
27
+ album = Album.create! :name => 'Sing to the Moon', :artist => 'Laura Mvula'
28
+
29
+ search = Album.search 'sing', :select => "*, weight()"
30
+ search.context[:panes] << ThinkingSphinx::Panes::WeightPane
31
+
32
+ expect(search.first.weight).to be >= 1000
33
+ end
34
+
26
35
  it "can enumerate with the weight" do
27
36
  gods = Book.create! :title => 'American Gods', :year => 2001
28
37
  index
@@ -35,6 +35,18 @@ describe 'Hiding deleted records from search results', :live => true do
35
35
  to be_empty
36
36
  end
37
37
 
38
+ it "removes records from real-time index results with alternate ids" do
39
+ album = Album.create! :name => 'Sing to the Moon', :artist => 'Laura Mvula'
40
+
41
+ expect(Album.search('Sing', :indices => ['album_real_core']).to_a).
42
+ to eq([album])
43
+
44
+ album.destroy
45
+
46
+ expect(Album.search_for_ids('Sing', :indices => ['album_real_core'])).
47
+ to be_empty
48
+ end
49
+
38
50
  it "does not remove real-time results when callbacks are disabled" do
39
51
  original = ThinkingSphinx::Configuration.instance.
40
52
  settings['real_time_callbacks']
@@ -198,6 +198,31 @@ describe 'separate queries for MVAs' do
198
198
  expect(query).to match(/^SELECT .taggings.\..article_id. \* #{count} \+ #{source.offset} AS .id., .taggings.\..tag_id. AS .tag_ids. FROM .taggings.\s? WHERE \(.taggings.\..article_id. IS NOT NULL\)$/)
199
199
  end
200
200
 
201
+ it "does not include attributes sourced via separate queries" do
202
+ index.definition_block = Proc.new {
203
+ indexes title
204
+ has taggings.tag_id, :as => :tag_ids, :source => :query
205
+ }
206
+ index.render
207
+
208
+ # We don't want it in the SELECT, JOIN or GROUP clauses. This should catch
209
+ # them all.
210
+ expect(source.sql_query).not_to include('taggings')
211
+ end
212
+
213
+ it "keeps the joins in for separately queried tables if they're used elsewhere" do
214
+ index.definition_block = Proc.new {
215
+ indexes taggings.tag.name, :as => :tag_names
216
+ has taggings.tag.created_at, :as => :tag_dates, :source => :query
217
+ }
218
+ index.render
219
+
220
+ expect(source.sql_query).to include('taggings')
221
+ expect(source.sql_query).to include('tags')
222
+ expect(source.sql_query).to_not match(/.tags.\..created_at./)
223
+ expect(source.sql_query).to match(/.tags.\..name./)
224
+ end
225
+
201
226
  it "generates a SQL query with joins when appropriate for MVAs" do
202
227
  index.definition_block = Proc.new {
203
228
  indexes title
@@ -434,6 +459,17 @@ describe 'separate queries for field' do
434
459
  expect(range).to match(/^SELECT MIN\(.taggings.\..article_id.\), MAX\(.taggings.\..article_id.\) FROM .taggings.\s?$/)
435
460
  end
436
461
 
462
+ it "does not include fields sourced via separate queries" do
463
+ index.definition_block = Proc.new {
464
+ indexes taggings.tag.name, :as => :tags, :source => :query
465
+ }
466
+ index.render
467
+
468
+ # We don't want it in the SELECT, JOIN or GROUP clauses. This should catch
469
+ # them all.
470
+ expect(source.sql_query).not_to include('tags')
471
+ end
472
+
437
473
  it "respects custom SQL snippets as the query value" do
438
474
  index.definition_block = Proc.new {
439
475
  indexes 'My Custom SQL Query', :as => :tags, :source => :query
@@ -34,7 +34,7 @@ describe ThinkingSphinx::ActiveRecord::Callbacks::DeleteCallbacks do
34
34
 
35
35
  describe '#after_destroy' do
36
36
  let(:index_set) { double 'index set', :to_a => [index] }
37
- let(:index) { double('index', :name => 'foo_core',
37
+ let(:index) { double('index', :name => 'foo_core', :primary_key => :id,
38
38
  :document_id_for_key => 14, :type => 'plain', :distributed? => false) }
39
39
  let(:instance) { double('instance', :id => 7, :new_record? => false) }
40
40
 
@@ -93,7 +93,7 @@ describe ThinkingSphinx::ActiveRecord::Callbacks::DeleteCallbacks do
93
93
 
94
94
  describe '#after_rollback' do
95
95
  let(:index_set) { double 'index set', :to_a => [index] }
96
- let(:index) { double('index', :name => 'foo_core',
96
+ let(:index) { double('index', :name => 'foo_core', :primary_key => :id,
97
97
  :document_id_for_key => 14, :type => 'plain', :distributed? => false) }
98
98
  let(:instance) { double('instance', :id => 7, :new_record? => false) }
99
99
 
@@ -23,7 +23,7 @@ describe ThinkingSphinx::ActiveRecord::Callbacks::UpdateCallbacks do
23
23
  let(:connection) { double('connection', :execute => '') }
24
24
  let(:index) { double 'index', :name => 'article_core',
25
25
  :sources => [source], :document_id_for_key => 3, :distributed? => false,
26
- :type => 'plain'}
26
+ :type => 'plain', :primary_key => :id}
27
27
  let(:source) { double('source', :attributes => []) }
28
28
 
29
29
  before :each do
@@ -48,11 +48,23 @@ describe ThinkingSphinx::ActiveRecord::DatabaseAdapters::MySQLAdapter do
48
48
  end
49
49
  end
50
50
 
51
-
52
51
  describe '#group_concatenate' do
53
52
  it "group concatenates the clause with the given separator" do
54
53
  expect(adapter.group_concatenate('foo', ',')).
55
54
  to eq("GROUP_CONCAT(DISTINCT foo SEPARATOR ',')")
56
55
  end
57
56
  end
57
+
58
+ describe '#utf8_query_pre' do
59
+ it "defaults to using utf8" do
60
+ expect(adapter.utf8_query_pre).to eq(["SET NAMES utf8"])
61
+ end
62
+
63
+ it "allows custom values" do
64
+ ThinkingSphinx::Configuration.instance.settings['mysql_encoding'] =
65
+ 'utf8mb4'
66
+
67
+ expect(adapter.utf8_query_pre).to eq(["SET NAMES utf8mb4"])
68
+ end
69
+ end
58
70
  end
@@ -6,7 +6,8 @@ describe ThinkingSphinx::ActiveRecord::FilterReflection do
6
6
  describe '.call' do
7
7
  let(:reflection) { double('Reflection', :macro => :has_some,
8
8
  :options => options, :active_record => double, :name => 'baz',
9
- :foreign_type => :foo_type, :class => original_klass) }
9
+ :foreign_type => :foo_type, :class => original_klass,
10
+ :build_join_constraint => nil) }
10
11
  let(:options) { {:polymorphic => true} }
11
12
  let(:filtered_reflection) { double 'filtered reflection' }
12
13
  let(:original_klass) { double }
@@ -179,13 +180,24 @@ describe ThinkingSphinx::ActiveRecord::FilterReflection do
179
180
  end
180
181
 
181
182
  it "includes custom behaviour in the subclass" do
182
- expect(subclass).to receive(:include).with(ThinkingSphinx::ActiveRecord::Depolymorph::OverriddenReflection::JoinConstraint)
183
+ expect(subclass).to receive(:include).with(ThinkingSphinx::ActiveRecord::Depolymorph::OverriddenReflection::BuildJoinConstraint)
183
184
 
184
185
  ThinkingSphinx::ActiveRecord::FilterReflection.call(
185
186
  reflection, 'foo_bar', 'Bar'
186
187
  )
187
188
  end if ActiveRecord::VERSION::STRING.to_f > 5.1
188
189
 
190
+ it "includes custom behaviour in the subclass" do
191
+ allow(reflection).to receive(:respond_to?).with(:build_join_constraint).
192
+ and_return(false)
193
+
194
+ expect(subclass).to receive(:include).with(ThinkingSphinx::ActiveRecord::Depolymorph::OverriddenReflection::JoinScope)
195
+
196
+ ThinkingSphinx::ActiveRecord::FilterReflection.call(
197
+ reflection, 'foo_bar', 'Bar'
198
+ )
199
+ end if ActiveRecord::VERSION::STRING.to_f >= 6.0
200
+
189
201
  it "returns the new reflection" do
190
202
  expect(ThinkingSphinx::ActiveRecord::FilterReflection.call(
191
203
  reflection, 'foo_bar', 'Bar'
@@ -12,11 +12,12 @@ describe ThinkingSphinx::Middlewares::Glazier do
12
12
  let(:middleware) { ThinkingSphinx::Middlewares::Glazier.new app }
13
13
  let(:context) { {:results => [result], :indices => [index],
14
14
  :meta => {}, :raw => [raw_result], :panes => []} }
15
- let(:result) { double('result', :id => 10,
16
- :class => double(:name => 'Article')) }
17
- let(:index) { double('index', :name => 'foo_core') }
18
- let(:search) { double('search', :options => {}) }
19
- let(:glazed_result) { double('glazed result') }
15
+ let(:result) { double 'result', :id => 10, :class => model }
16
+ let(:model) { double 'model', :name => 'Article' }
17
+ let(:index) { double 'index', :name => 'foo_core', :model => model,
18
+ :primary_key => :id }
19
+ let(:search) { double 'search', :options => {} }
20
+ let(:glazed_result) { double 'glazed result' }
20
21
  let(:raw_result) {
21
22
  {'sphinx_internal_class' => 'Article', 'sphinx_internal_id' => 10} }
22
23
 
@@ -18,6 +18,10 @@ describe ThinkingSphinx::Scopes do
18
18
  model.sphinx_scopes[:foo] = Proc.new { {:with => {:foo => :bar}} }
19
19
  end
20
20
 
21
+ it "implements respond_to" do
22
+ expect(model).to respond_to(:foo)
23
+ end
24
+
21
25
  it "creates new search" do
22
26
  expect(model.foo.class).to eq(ThinkingSphinx::Search)
23
27
  end
@@ -5,7 +5,7 @@ $:.push File.expand_path('../lib', __FILE__)
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = 'thinking-sphinx'
8
- s.version = '4.1.0'
8
+ s.version = '4.2.0'
9
9
  s.platform = Gem::Platform::RUBY
10
10
  s.authors = ["Pat Allan"]
11
11
  s.email = ["pat@freelancing-gods.com"]
@@ -31,7 +31,7 @@ Gem::Specification.new do |s|
31
31
  s.add_runtime_dependency 'riddle', '~> 2.3'
32
32
 
33
33
  s.add_development_dependency 'appraisal', '~> 1.0.2'
34
- s.add_development_dependency 'combustion', '~> 0.8.0'
34
+ s.add_development_dependency 'combustion', '~> 1.1'
35
35
  s.add_development_dependency 'database_cleaner', '~> 1.6.0'
36
36
  s.add_development_dependency 'rspec', '~> 3.7.0'
37
37
  s.add_development_dependency 'rspec-retry', '~> 0.5.6'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thinking-sphinx
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0
4
+ version: 4.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pat Allan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-12-28 00:00:00.000000000 Z
11
+ date: 2019-03-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -114,14 +114,14 @@ dependencies:
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: 0.8.0
117
+ version: '1.1'
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: 0.8.0
124
+ version: '1.1'
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: database_cleaner
127
127
  requirement: !ruby/object:Gem::Requirement
@@ -520,7 +520,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
520
520
  - !ruby/object:Gem::Version
521
521
  version: '0'
522
522
  requirements: []
523
- rubygems_version: 3.0.1
523
+ rubygems_version: 3.0.3
524
524
  signing_key:
525
525
  specification_version: 4
526
526
  summary: A smart wrapper over Sphinx for ActiveRecord