stub_solr 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bfdc89260913319a1d4c3939ee3dfba95feae29d
4
- data.tar.gz: 3ebd59479e416bb97557a990302b310eb3e6636a
3
+ metadata.gz: 3d7ccf1ca8364cbe78d549c7adee20fc57e6657b
4
+ data.tar.gz: e7373b1aad31088dd937d28cf6c7559acc1bc1fb
5
5
  SHA512:
6
- metadata.gz: d763e18b42519ef78114699d73b4f95c537210db9e10934ef9253410f154fcb7c4ff2c0434a96fe478e0220af37ed4b56a4ad7647041330de3ce085991ccd2a6
7
- data.tar.gz: f0d4c8fb320660ace148fb9b9b626d8c8adfb042df3f7f5b82f4b2c6d7274f0cae4be36424ce75e35d3469a6bdb8175da4792c2b36f9714a42f18cd0a49541f2
6
+ metadata.gz: c63a06b47829d41038bb6f581742ba546ea37aa3a0e12d5c9accadd8a371abae23b04c8c5daf860eb753ad30397b2c95d204404227d5c9e066bdeef5e60c6ab2
7
+ data.tar.gz: cd6bfc624908d13a46bdbc8d53c3f350f4823eed7c3a3e4ca5535e21a4d600a931fd66c30bf81fc189518e17612822fdaf79c239bf7e25493bf0ff87e9813536
data/.codeclimate.yml ADDED
@@ -0,0 +1,23 @@
1
+ engines:
2
+ duplication:
3
+ enabled: true
4
+ config:
5
+ languages:
6
+ - ruby
7
+ fixme:
8
+ enabled: true
9
+ rubocop:
10
+ enabled: true
11
+ ratings:
12
+ paths:
13
+ - "**.js"
14
+ - "**.jsx"
15
+ - "**.module"
16
+ - "**.rb"
17
+ exclude_paths:
18
+ - test/
19
+ - Rakefile
20
+ - gemfiles/
21
+ - dev_tasks/
22
+ - bin/
23
+ - tmp/
data/.rubocop.yml CHANGED
@@ -6,6 +6,7 @@ AllCops:
6
6
  - gemfiels/*
7
7
  - test/**/*
8
8
  - tmp/**/*
9
+ - Rakefile
9
10
  TargetRubyVersion: 2.3
10
11
 
11
12
  Rails:
@@ -25,3 +26,6 @@ Style/EmptyLinesAroundClassBody:
25
26
 
26
27
  Style/EmptyLinesAroundMethodBody:
27
28
  Enabled: false
29
+
30
+ Style/FrozenStringLiteralComment:
31
+ Enabled: false
data/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  `Sunspot.session = Sunspot::Rails::StubSessionProxy.new(Sunspot.session)` is enough for most of cases but if you want more than `allow_any_instance_of(Sunspot::Rails::StubSessionProxy::Search).to receive(:results).and_return(myExpectedResults)` this gem can be helpful. `kaminari` for pagination and plain AR to mimic resutls.
7
7
 
8
- this gem depends on `2.2.6` version of sunspot, sunspot_rails.
8
+ this gem depends on `2.2.x` version of sunspot, sunspot_rails.
9
9
 
10
10
  ## Installation
11
11
 
@@ -15,17 +15,49 @@ Add this line to your application's Gemfile:
15
15
  gem 'stub_solr'
16
16
  ```
17
17
 
18
- And then execute:
19
-
20
- $ bundle
21
-
22
- Or install it yourself as:
18
+ ## Usage
23
19
 
24
- $ gem install stub_solr
20
+ `test_helper` file should have `require 'stub_solr'`. Given a sunspot search block as following codes,
25
21
 
26
- ## Usage
22
+ ```ruby
23
+ class ActivitiesController < ApplicationController
24
+ def search
25
+ ...
26
+ search = Sunspot.search Excercise, Activity do
27
+ any_of do
28
+ with :blocked, false
29
+ without :exhausted, true
30
+ end
31
+
32
+ if min && max
33
+ any_of do
34
+ all_of do
35
+ with :start_limit_number, nil
36
+ with(:end_limit_number).less_than_or_equal_to max
37
+ end
38
+ all_of do
39
+ with :end_limit_number, nil
40
+ with(:start_limit_number).greater_than_or_equal_to min
41
+ end
42
+ all_of do
43
+ without(:start_limit_number, nil)
44
+ without(:end_limit_number, nil)
45
+ with(:start_limit_number).greater_than_or_equal_to min
46
+ with(:end_limit_number).less_than_or_equal_to max
47
+ end
48
+ end
49
+ end
50
+ ## search by text
51
+ fulltext params[:search] if params[:search].present?
52
+ order_by :created_at, :asc
53
+ paginate page: page, per_page: per_page
54
+ end
55
+ ...
56
+ end
57
+ end
58
+ ```
27
59
 
28
- `test_helper` file should have `require 'stub_solr'`. In your test file, add setup and teardown like this
60
+ You can write expected results like this. use `concat` for `any_of` block and `array_1 & array_2` for `all_of` block.
29
61
 
30
62
  ```ruby
31
63
  class ActivitiesControllerTest < ActionDispatch::IntegrationTest
@@ -41,9 +73,15 @@ class ActivitiesControllerTest < ActionDispatch::IntegrationTest
41
73
  super
42
74
  end
43
75
 
44
- test 'search with filter' do
45
- get search_activities_url(params: { visible: true, search: 'beer' })
46
- expected = Activity.where(visible: true).where("title LIKE ?", "%beer%")
76
+ test 'filter with number range' do
77
+ pool_1 = Activity.where(blocked: false).to_a.concat(Activity.where(exhausted: false).to_a).uniq
78
+ target_1 = Activity.where("end_limit_number <= ? AND start_limit_number = ?", 18, nil).to_a
79
+ target_2 = Activity.where("start_limit_number >= ? AND end_limit_number = ?", 3, nil).to_a
80
+ target_3 = Activity.where("start_limit_number >= ?", 3).where("end_limit_number <= ?", 18).to_a
81
+ pool_2 = target_1.concat(target_2).concat(target_3)
82
+ expected = pool_1 & pool_2
83
+
84
+ get search_activities_url(params: { limits: [3, 18] })
47
85
  res = ActiveSupport::JSON.decode(response.body)["activities"]
48
86
  res.size.must_equal expected.size
49
87
  end
@@ -0,0 +1,144 @@
1
+ class Sunspot::Rails::StubSessionProxy
2
+ # with and fulltext can be called multiple times
3
+ # based on the block. that's why i am mutating @results
4
+ #
5
+ # for all and any block, we need to consult
6
+ # sunspot/lib/sunspot/dsl/standard_query.rb
7
+ #
8
+ class Search
9
+ def hits
10
+ self
11
+ end
12
+
13
+ # Added to pass .group_by
14
+ # Idea is save grouped hash result seperately and then
15
+ # access it with [] method.
16
+ #
17
+ # initial_grouped_hits = initial.hits.group_by(&:class_name)
18
+ # initial_grouped_hits["Activity"]
19
+ def [](key)
20
+ return if @grouped_results.empty? || @grouped_results[key].nil?
21
+ @group_key = key
22
+ self
23
+ end
24
+
25
+ # possible situation
26
+ #
27
+ # initial_grouped_hits = initial.hits.group_by(&:class_name)
28
+ # initial_grouped_hits["Activity"].map(&:primary_key)
29
+ def map(&_pr)
30
+ return unless @grouped_results[@group_key]
31
+ @grouped_results[@group_key].map(&:id)
32
+ end
33
+
34
+ # possible situation
35
+ #
36
+ # initial_grouped_hits = initial.hits.group_by(&:class_name)
37
+ def group_by(_ = nil)
38
+ return self if final_results.empty?
39
+ @types.each do |type|
40
+ @grouped_results[type.name] = sorted_temp_result.group_by do |i|
41
+ i.class.name == type.name
42
+ end[true]
43
+ end
44
+ self
45
+ end
46
+
47
+ def phrase_fields(arg)
48
+ @operation_context.first[:textsearch_priorities] = arg
49
+ return self if @operation_context.first[:search_term].empty?
50
+ fulltext(@operation_context.first[:search_term])
51
+ end
52
+
53
+ def query_phrase_slop(_)
54
+ fulltext(@operation_context.first[:search_term])
55
+ end
56
+
57
+ # with default `StubSessionProxy`, the results is blank whatever
58
+ # argument or options you put in the search block
59
+ def fulltext(term, _opt = {}, &bl)
60
+ matches = []
61
+ @operation_context.first[:search_term] = term
62
+ Sunspot::Util::ContextBoundDelegate
63
+ .instance_eval_with_context(self, &bl) unless bl.nil?
64
+ # if there is phrase_fields option then we only search for the field
65
+ # to mimic the priority search. #string_text_fields
66
+ @types.each do |type|
67
+ string_text_fields(type).each do |field|
68
+ if type.has_attribute?(field.to_sym)
69
+ matches << type.where(type.arel_table[field.to_sym].
70
+ matches("%#{term}%")).to_a
71
+ end
72
+ end
73
+ end
74
+ calculated = (result_for_current_context & matches.flatten.uniq)
75
+ operation_context_result(calculated)
76
+ self
77
+ end
78
+
79
+ def order_by(attribute, direction)
80
+ @operation_context.first[:order_key] = attribute
81
+ @operation_context.first[:order_direction] = direction
82
+ self
83
+ end
84
+
85
+ def in_radius(*args)
86
+ calculated = result_for_current_context.select do |r|
87
+ distance = r&.location&.distance_from([args[0], args[1]])
88
+ distance && distance < args[2].to_i
89
+ end
90
+ operation_context_result(calculated)
91
+ self
92
+ end
93
+
94
+ def greater_than(time)
95
+ calculated = filter(result_for_current_context) do |x|
96
+ x.read_attribute(@range_search_field) > time
97
+ end
98
+ operation_context_result(calculated)
99
+ self
100
+ end
101
+
102
+ def greater_than_or_equal_to(time)
103
+ calculated = filter(result_for_current_context) do |x|
104
+ x.read_attribute(@range_search_field) >= time
105
+ end
106
+ operation_context_result(calculated)
107
+ self
108
+ end
109
+
110
+ def less_than(time)
111
+ calculated = filter(result_for_current_context) do |x|
112
+ x.read_attribute(@range_search_field) < time
113
+ end
114
+ operation_context_result(calculated)
115
+ self
116
+ end
117
+
118
+ def less_than_or_equal_to(time)
119
+ calculated = filter(result_for_current_context) do |x|
120
+ x.read_attribute(@range_search_field) <= time
121
+ end
122
+ operation_context_result(calculated)
123
+ self
124
+ end
125
+
126
+ def paginate(args = {})
127
+ @operation_context.first[:page] = args[:page]
128
+ @operation_context.first[:per_page] = args[:page]
129
+ self
130
+ end
131
+
132
+ def total
133
+ @operation_context.last[:result].size
134
+ end
135
+
136
+ def filter(array)
137
+ array.select do |i|
138
+ i.read_attribute(@range_search_field) &&
139
+ !i.read_attribute(@range_search_field).nil? &&
140
+ yield(i)
141
+ end.uniq
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,107 @@
1
+ require File.expand_path('search_helper', File.dirname(__FILE__))
2
+ require File.expand_path('extra_dsl', File.dirname(__FILE__))
3
+ class Sunspot::Rails::StubSessionProxy
4
+ # with and fulltext can be called multiple times
5
+ # based on the block. that's why i am mutating @results
6
+ #
7
+ # for all and any block, we need to consult
8
+ # sunspot/lib/sunspot/dsl/standard_query.rb
9
+ #
10
+ class Search
11
+ attr_reader :block, :types, :page, :per_page, :grouped_results
12
+
13
+ def initialize(results = [], types = [], &block)
14
+ @types = types
15
+ @grouped_results= {}
16
+ @group_key = ''
17
+ @range_search_field = nil
18
+ @operation_context = [{
19
+ operation: 'all', result: results, order_key: nil, order_direction: nil,
20
+ page: 1, per_page: 30, search_term: '', textsearch_priorities: {}
21
+ }]
22
+ @current_index = 0
23
+ run_proc_block(&block)
24
+ end
25
+
26
+ def final_results
27
+ @operation_context.last[:result]
28
+ end
29
+
30
+ def results
31
+ return PaginatedCollection.new if sorted_temp_result.empty?
32
+ if final_results.is_a? Kaminari::PaginatableArray
33
+ return sorted_temp_result
34
+ end
35
+ Kaminari.paginate_array(sorted_temp_result).
36
+ page(@operation_context.first[:page]).
37
+ per(@operation_context.first[:per_page])
38
+ end
39
+
40
+ def any_of(&bl)
41
+ previous_result = result_for_current_context
42
+ push(previous_result, :any)
43
+ Sunspot::Util::ContextBoundDelegate.instance_eval_with_context(self, &bl)
44
+ after_results = temp_result_for_current_context
45
+ pop unless @current_index == 0
46
+ # parent is now current context
47
+ if parent_temp_result
48
+ @operation_context[@current_index][:temp_result] =
49
+ parent_temp_result.concat(after_results)
50
+ else
51
+ @operation_context[@current_index][:result] =
52
+ after_results
53
+ end
54
+ self
55
+ end
56
+
57
+ def all_of(&bl)
58
+ previous_result = result_for_current_context
59
+ push(previous_result, :all)
60
+ Sunspot::Util::ContextBoundDelegate.instance_eval_with_context(self, &bl)
61
+ after_results = result_for_current_context
62
+ parent_temp_result =
63
+ @operation_context[@current_index - 1][:temp_result]
64
+ pop unless @current_index == 0
65
+ # parent is now current context
66
+ if parent_temp_result
67
+ @operation_context[@current_index][:temp_result] =
68
+ parent_temp_result.concat(after_results)
69
+ else
70
+ @operation_context[@current_index][:result] =
71
+ (previous_result & after_results).uniq
72
+ end
73
+ self
74
+ end
75
+
76
+ def with(att, val = 'no_value')
77
+ # return_early_for_with_blocks(att, val, :with)
78
+ return self if range_no_value?(att, val) && skip_it(att, val)
79
+ return self if range_value_nil?(val, att) && deal_with_nil(att, :with)
80
+ return self if val == 'no_value' || att == :location
81
+ return self if (att == :search_class) && filter_class
82
+ matches = if val.class.name == 'Array'
83
+ select_result_for_array_val(att)
84
+ elsif @types.map{ |x| x.has_attribute?(att)}.include?(true)
85
+ select_result_attr_is_val(att, val)
86
+ else
87
+ result_for_current_context.select { |x| x.send(att) == val }
88
+ end
89
+ operation_context_result(matches)
90
+ self
91
+ end
92
+
93
+ def without(att, val = 'no_value')
94
+ # return_early_for_with_blocks(att, val, :without)
95
+ return self if range_no_value?(att, val) && skip_it(att, val)
96
+ return self if range_value_nil?(val, att) && deal_with_nil(att, :without)
97
+ return self if val == 'no_value' || att == :location
98
+ matches = if val.class.name == 'Array'
99
+ select_result_attr_cant_send_val(att, val)
100
+ else
101
+ select_result_attr_is_not_value(att, val)
102
+ end
103
+ operation_context_result(matches)
104
+ self
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,168 @@
1
+ # serach helper
2
+
3
+ class Sunspot::Rails::StubSessionProxy
4
+ # with and fulltext can be called multiple times
5
+ # based on the block. that's why i am mutating @results
6
+ #
7
+ # for all and any block, we need to consult
8
+ # sunspot/lib/sunspot/dsl/standard_query.rb
9
+ #
10
+ class Search
11
+
12
+ private
13
+
14
+ def instance_eval_with_context(&block)
15
+ Sunspot::Util::ContextBoundDelegate.
16
+ instance_eval_with_context(self, &block)
17
+ end
18
+
19
+ # iterate the given block and change @operation_context
20
+ # block may cotain with, without, fulltext, paginate, in_raius, order_by
21
+ def run_proc_block(&block)
22
+ result = @operation_context.last[:result]
23
+ return PaginatedCollection.new if result.empty?
24
+ instance_eval_with_context(&block)
25
+ self
26
+ end
27
+
28
+ def current_operation
29
+ @operation_context[@current_index][:operation]
30
+ end
31
+
32
+ def result_for_current_context
33
+ @operation_context[@current_index][:result]
34
+ end
35
+
36
+ def temp_result_for_current_context
37
+ @operation_context[@current_index][:temp_result]
38
+ end
39
+
40
+ def parent_temp_result
41
+ @operation_context[@current_index - 1][:temp_result]
42
+ end
43
+
44
+ def operation_context_result(after_results)
45
+ previous_result = result_for_current_context
46
+ unless current_operation == 'any'
47
+ return @operation_context[@current_index][:result] =
48
+ (previous_result & after_results).uniq
49
+ end
50
+ @operation_context[@current_index][:result] =
51
+ previous_result.concat(after_results).uniq
52
+ if temp_result_for_current_context
53
+ @operation_context[@current_index][:temp_result] =
54
+ temp_result_for_current_context.concat(after_results)
55
+ end
56
+ end
57
+
58
+ def pop
59
+ @current_index -= 1
60
+ @operation_context.pop
61
+ end
62
+
63
+ def push(data, type)
64
+ if type == :any
65
+ @operation_context.push(operation: 'any', result: data, temp_result: [])
66
+ else
67
+ @operation_context.push(operation: 'all', result: data)
68
+ end
69
+ @current_index += 1
70
+ end
71
+
72
+ def filter_class
73
+ matches = result_for_current_context.select { |x| x.class.name == value }
74
+ operation_context_result(matches)
75
+ end
76
+
77
+ def range_value_nil?(value, attribute)
78
+ value.nil? && attribute_is_about_range?(attribute)
79
+ end
80
+
81
+ def range_no_value?(attribute, value)
82
+ (value == 'no_value') && attribute_is_about_range?(attribute)
83
+ end
84
+
85
+ def skip_it(attribute, value)
86
+ matches = result_for_current_context.select do |x|
87
+ x.has_attribute?(attribute)
88
+ end
89
+ operation_context_result(matches)
90
+ end
91
+
92
+ def deal_with_nil(attribute, operation)
93
+ matches = if operation == :with
94
+ selelct_result_for_nil(attribute)
95
+ else
96
+ select_result_for_not_nil(attribute)
97
+ end
98
+ operation_context_result(matches)
99
+ end
100
+
101
+ def string_text_fields(type)
102
+ pr = @operation_context.first[:textsearch_priorities]
103
+ return pr.keys unless pr.keys.empty?
104
+ type.columns_hash.select { |k,v| [:string, :text].include? v.type }.keys
105
+ end
106
+
107
+ def attribute_is_about_range?(attribute)
108
+ matched = nil
109
+ Sunspot.session.range_fields.each do |x|
110
+ if attribute.to_s.include? x
111
+ matched = true
112
+ @range_search_field = attribute
113
+ next
114
+ end
115
+ end
116
+ matched
117
+ end
118
+
119
+ def sorted_temp_result
120
+ key = @operation_context.first[:order_key]
121
+ direction = @operation_context.first[:order_direction]
122
+ return final_results unless direction
123
+ asc = final_results.sort_by { |x| x[key] }
124
+ return asc if direction.to_s.downcase.include?('asc')
125
+ asc.reverse
126
+ end
127
+
128
+ def selelct_result_for_nil(attribute)
129
+ result_for_current_context.select do |x|
130
+ x.has_attribute?(attribute) &&
131
+ x.read_attribute(attribute).nil?
132
+ end
133
+ end
134
+
135
+ def select_result_for_not_nil(attribute)
136
+ result_for_current_context.select do |x|
137
+ x.has_attribute?(attribute) &&
138
+ !x.read_attribute(attribute).nil?
139
+ end
140
+ end
141
+
142
+ def select_result_for_array_val(attribute)
143
+ result_for_current_context.select do |x|
144
+ value.include?(x.send(attribute))
145
+ end
146
+ end
147
+
148
+ def select_result_attr_is_val(attribute, value)
149
+ result_for_current_context.select do |x|
150
+ x.read_attribute(attribute) == value
151
+ end
152
+ end
153
+
154
+ def select_result_attr_cant_send_val(attribute, value)
155
+ result_for_current_context.select do |x|
156
+ x.has_attribute?(attribute) && !value.include?(x.send(attribute))
157
+ end
158
+ end
159
+
160
+ def select_result_attr_is_not_value(attribute, value)
161
+ result_for_current_context.select do |x|
162
+ x.has_attribute?(attribute) &&
163
+ !x.read_attribute(attribute).nil? &&
164
+ (x.read_attribute(attribute) != value)
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,27 @@
1
+ require File.expand_path('search', File.dirname(__FILE__))
2
+ # stub solr with "dara" array for testing.
3
+ # if there is no data then it will behave like default blank search
4
+ # this stub uses Kaminari for pagination
5
+ # once the data is given, solr search block will call search
6
+ # and that will create a new Search with arguments.
7
+ # With the search, it passes the given data as @operation_context.
8
+ # And "run_proc_block" method will go through each search conditions
9
+ # in the block. while going through the block, it will change @operation_context
10
+ #
11
+ class Sunspot::Rails::StubSessionProxy
12
+ attr_reader :block, :data, :range_fields
13
+
14
+ def initialize(original_session, data = [])
15
+ @original_session = original_session
16
+ @data = data
17
+ @range_fields = %w[time month created_at start end]
18
+ end
19
+
20
+ def set_range_fields(arr)
21
+ @range_fields = arr
22
+ end
23
+
24
+ def search(*types, &block)
25
+ Search.new(@data, types, &block)
26
+ end
27
+ end
@@ -1,3 +1,3 @@
1
1
  module StubSolr
2
- VERSION = "0.0.5"
2
+ VERSION = '0.0.6'
3
3
  end
data/lib/stub_solr.rb CHANGED
@@ -1,9 +1,14 @@
1
+ ## requires all necessary fiels
1
2
  require 'kaminari'
3
+ ##
4
+ # previous_page method doesn't exist in Kaminari
5
+ # but we use it for pagination
2
6
  module Kaminari::PageScopeMethods
3
7
  alias_method :previous_page, :prev_page
4
8
  end
5
9
  require 'sunspot'
6
10
  require 'sunspot_rails'
7
11
  require 'rails'
8
- require "stub_solr/version"
9
- require File.expand_path('stub_solr/sunspot_rails_stub_session_proxy', File.dirname(__FILE__))
12
+ require 'stub_solr/version'
13
+ require File.expand_path('stub_solr/stub_session_proxy',
14
+ File.dirname(__FILE__))
data/stub_solr.gemspec CHANGED
@@ -20,9 +20,7 @@ Gem::Specification.new do |gem|
20
20
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
21
21
 
22
22
  gem.add_dependency 'kaminari', '~> 0.17.0'
23
- gem.add_dependency 'sunspot', '~> 2.2.6'
24
- gem.add_dependency 'sunspot_solr', '~> 2.2.6'
25
- gem.add_dependency 'sunspot_rails', '~> 2.2.6'
23
+ gem.add_dependency 'sunspot_rails', '~> 2.2.5'
26
24
  gem.add_dependency 'rails', '>= 4'
27
25
 
28
26
  # gem.add_development_dependency 'simplecov', '~> 0.12.0'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stub_solr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jaigouk Kim
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-13 00:00:00.000000000 Z
11
+ date: 2016-09-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kaminari
@@ -24,48 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.17.0
27
- - !ruby/object:Gem::Dependency
28
- name: sunspot
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: 2.2.6
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: 2.2.6
41
- - !ruby/object:Gem::Dependency
42
- name: sunspot_solr
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: 2.2.6
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: 2.2.6
55
27
  - !ruby/object:Gem::Dependency
56
28
  name: sunspot_rails
57
29
  requirement: !ruby/object:Gem::Requirement
58
30
  requirements:
59
31
  - - "~>"
60
32
  - !ruby/object:Gem::Version
61
- version: 2.2.6
33
+ version: 2.2.5
62
34
  type: :runtime
63
35
  prerelease: false
64
36
  version_requirements: !ruby/object:Gem::Requirement
65
37
  requirements:
66
38
  - - "~>"
67
39
  - !ruby/object:Gem::Version
68
- version: 2.2.6
40
+ version: 2.2.5
69
41
  - !ruby/object:Gem::Dependency
70
42
  name: rails
71
43
  requirement: !ruby/object:Gem::Requirement
@@ -129,6 +101,7 @@ executables: []
129
101
  extensions: []
130
102
  extra_rdoc_files: []
131
103
  files:
104
+ - ".codeclimate.yml"
132
105
  - ".gitignore"
133
106
  - ".rubocop.yml"
134
107
  - Gemfile
@@ -141,7 +114,10 @@ files:
141
114
  - gemfiles/.bundle/config
142
115
  - gemfiles/rails-5.0.0.1
143
116
  - lib/stub_solr.rb
144
- - lib/stub_solr/sunspot_rails_stub_session_proxy.rb
117
+ - lib/stub_solr/extra_dsl.rb
118
+ - lib/stub_solr/search.rb
119
+ - lib/stub_solr/search_helper.rb
120
+ - lib/stub_solr/stub_session_proxy.rb
145
121
  - lib/stub_solr/version.rb
146
122
  - stub_solr.gemspec
147
123
  homepage: https://github.com/jaigouk/stub_solr
@@ -1,363 +0,0 @@
1
-
2
- # stub solr with "dara" array for testing.
3
- # if there is no data then it will behave like default blank search
4
- # this stub uses Kaminari for pagination
5
- # once the data is given, solr search block will call search
6
- # and that will create a new Search with arguments.
7
- # With the search, it passes the given data as @operation_context.
8
- # And "run_proc_block" method will go through each search conditions
9
- # in the block. while going through the block, it will change @operation_context
10
- #
11
- class Sunspot::Rails::StubSessionProxy
12
- attr_reader :block, :data, :range_fields
13
-
14
- def initialize(original_session, data = [])
15
- @original_session = original_session
16
- @data = data
17
- @range_fields = %w[time month created_at start end]
18
- end
19
-
20
- def set_range_fields(arr)
21
- @range_fields = arr
22
- end
23
-
24
- def search(*types, &block)
25
- Search.new(@data, types, &block)
26
- end
27
-
28
- # with and fulltext can be called multiple times
29
- # based on the block. that's why i am mutating @results
30
- #
31
- # for all and any block, we need to consult sunspot/lib/sunspot/dsl/standard_query.rb
32
- #
33
- class Search
34
- attr_reader :block, :types, :page, :per_page, :grouped_results
35
- def initialize(results = [], types = [], &block)
36
- @facets = {}
37
- @search_data = results
38
- @block = block
39
- @types = types
40
- @page = 1
41
- @per_page = 30
42
- @grouped_results = {}
43
- @group_key = ""
44
- @range_search_field = nil
45
- @operation_context = [{operation: "all", result: @search_data}]
46
- @current_context_index = 0
47
- @order_key = nil
48
- @order_direction = nil
49
- @textsearch_priorities = {}
50
- @search_term = ""
51
- run_proc_block
52
- end
53
-
54
- def final_results
55
- @operation_context.last[:result]
56
- end
57
-
58
- def results
59
- return PaginatedCollection.new if sorted_temp_result.empty?
60
- return sorted_temp_result if final_results.is_a? Kaminari::PaginatableArray
61
- Kaminari.paginate_array(sorted_temp_result).page(@page).per(@per_page)
62
- end
63
-
64
- def any_of(&bl)
65
- previous_result = result_for_current_context
66
- @operation_context.push({operation: "any", result: previous_result, temp_result: []})
67
- @current_context_index += 1
68
-
69
- Sunspot::Util::ContextBoundDelegate.instance_eval_with_context(self, &bl)
70
- after_results = @operation_context[@current_context_index][:temp_result]
71
-
72
- parent_temp_result = @operation_context[@current_context_index - 1][:temp_result]
73
-
74
-
75
- unless @current_context_index == 0
76
- @current_context_index -= 1
77
- @operation_context.pop
78
- end
79
- # parent is now current context
80
- if parent_temp_result
81
- @operation_context[@current_context_index][:temp_result] = parent_temp_result.concat(after_results)
82
- else
83
- @operation_context[@current_context_index][:result] = after_results
84
- end
85
- self
86
- end
87
-
88
- def all_of(&bl)
89
- previous_result = result_for_current_context
90
- @operation_context.push({operation: "all", result: previous_result})
91
- @current_context_index += 1
92
-
93
- Sunspot::Util::ContextBoundDelegate.instance_eval_with_context(self, &bl)
94
- after_results = result_for_current_context
95
- parent_temp_result = @operation_context[@current_context_index - 1][:temp_result]
96
-
97
- unless @current_context_index == 0
98
- @current_context_index -= 1
99
- @operation_context.pop
100
- end
101
-
102
- # parent is now current context
103
- if parent_temp_result
104
- @operation_context[@current_context_index][:temp_result] = parent_temp_result.concat(after_results)
105
- else
106
- @operation_context[@current_context_index][:result] = (previous_result & after_results).uniq
107
- end
108
-
109
- self
110
- end
111
-
112
- def with(attribute, value = "no_value")
113
- skip_no_value(attribute, value)
114
-
115
- if attribute == :search_class
116
- matches = result_for_current_context.select {|x| x.class.name == value }
117
- operation_context_result(matches)
118
- return self
119
- end
120
-
121
- if value == nil && attribute_is_about_range?(attribute)
122
- matches = result_for_current_context.select {|x| x.has_attribute?(attribute) && (x.read_attribute(attribute) == value)}
123
- operation_context_result(matches)
124
- return self
125
- end
126
-
127
- return self if value == "no_value" || attribute == :location
128
-
129
- matches = if value.class.name == "Array"
130
- result_for_current_context.select {|x| value.include?(x.send(attribute)) }
131
- elsif @types.map{|x| x.has_attribute?(attribute)}.include?(true)
132
- result_for_current_context.select {|x| x.read_attribute(attribute) == value }
133
- else
134
- result_for_current_context.select {|x| x.send(attribute) == value }
135
- end
136
- operation_context_result(matches)
137
- self
138
- end
139
-
140
- def without(attribute, value = "no_value")
141
- skip_no_value(attribute, value)
142
- if value == nil && attribute_is_about_range?(attribute)
143
- matches = result_for_current_context.select {|x| x.has_attribute?(attribute) && !(x.read_attribute(attribute) == value)}
144
- operation_context_result(matches)
145
- return self
146
- end
147
-
148
- return self if value == "no_value" || attribute == :location
149
- matches = if value.class.name == "Array"
150
- result_for_current_context.select {|x| x.has_attribute?(attribute) && !value.include?(x.send(attribute)) }
151
- else
152
- result_for_current_context.select {|x| x.has_attribute?(attribute) && !x.read_attribute(attribute).nil? && (x.read_attribute(attribute) != value) }
153
- end
154
- operation_context_result(matches)
155
- self
156
- end
157
-
158
- def hits
159
- self
160
- end
161
-
162
- # Added to pass .group_by
163
- # Idea is save grouped hash result seperately and then
164
- # access it with [] method.
165
- #
166
- # initial_grouped_hits = initial.hits.group_by(&:class_name)
167
- # initial_grouped_hits["Activity"]
168
- def [](key)
169
- return if @grouped_results.empty? || @grouped_results[key].nil?
170
- @group_key = key
171
- self
172
- end
173
-
174
- # possible situation
175
- #
176
- # initial_grouped_hits = initial.hits.group_by(&:class_name)
177
- # initial_grouped_hits["Activity"].map(&:primary_key)
178
- def map(&pr)
179
- return unless @grouped_results[@group_key]
180
- @grouped_results[@group_key].map{|x| x.id}
181
- end
182
-
183
- # possible situation
184
- #
185
- # initial_grouped_hits = initial.hits.group_by(&:class_name)
186
- def group_by(class_name = nil)
187
- return self if final_results.empty?
188
- @types.each do |type|
189
- @grouped_results[type.name] = sorted_temp_result.group_by{|i| i.class.name == type.name }[true]
190
- end
191
- self
192
- end
193
-
194
- def phrase_fields(arg)
195
- @textsearch_priorities = arg
196
- return self if @search_term.empty?
197
- fulltext(@search_term)
198
- end
199
-
200
- def query_phrase_slop(*args)
201
- fulltext(@search_term)
202
- end
203
-
204
- # with default `StubSessionProxy`, the results is blank whatever
205
- # argument or options you put in the search block
206
- def fulltext(term, opt={}, &bl)
207
- matches = []
208
- @search_term = term
209
- Sunspot::Util::ContextBoundDelegate.instance_eval_with_context(self, &bl) if !bl.nil?
210
- # if there is phrase_fields option then we only search for the field
211
- # to mimic the priority search. #string_text_fields
212
- @types.each do |type|
213
- string_text_fields(type).each do |field|
214
- if type.has_attribute?(field.to_sym)
215
- matches << type.where(type.arel_table[field.to_sym].matches("%#{term}%")).to_a
216
- end
217
- end
218
- end
219
- calculated = (result_for_current_context & matches.flatten.uniq)
220
- operation_context_result(calculated)
221
- self
222
- end
223
-
224
- def order_by(attribute, direction)
225
- @order_key = attribute
226
- @order_direction = direction
227
- self
228
- end
229
-
230
- def in_radius(*args)
231
- calculated = result_for_current_context.select do |r|
232
- distance = r&.location&.distance_from([args[0], args[1]])
233
- distance && distance < args[2].to_i
234
- end
235
- operation_context_result(calculated)
236
- self
237
- end
238
-
239
- def greater_than(time)
240
- calculated = result_for_current_context.select do |x|
241
- has_attribute_and_not_nil(x) &&
242
- x.read_attribute(@range_search_field) > time
243
- end.uniq
244
- operation_context_result(calculated)
245
- self
246
- end
247
-
248
- def greater_than_or_equal_to(time)
249
- calculated = result_for_current_context.select do |x|
250
- has_attribute_and_not_nil(x) &&
251
- x.read_attribute(@range_search_field) >= time
252
- end.uniq
253
- operation_context_result(calculated)
254
- self
255
- end
256
-
257
- def less_than(time)
258
- calculated = result_for_current_context.select do |x|
259
- has_attribute_and_not_nil(x) &&
260
- x.read_attribute(@range_search_field) < time
261
- end.uniq
262
- operation_context_result(calculated)
263
- self
264
- end
265
-
266
- def less_than_or_equal_to(time)
267
- calculated = result_for_current_context.select do |x|
268
- has_attribute_and_not_nil(x) &&
269
- x.read_attribute(@range_search_field) <= time
270
- end.uniq
271
- operation_context_result(calculated)
272
- self
273
- end
274
-
275
- def paginate(args={})
276
- @page = args[:page]
277
- @per_page = args[:per_page]
278
- self
279
- end
280
-
281
- def total
282
- @operation_context.last[:result].size
283
- end
284
-
285
- private
286
-
287
- # iterate the given block and change @operation_context
288
- # block may cotain with, without, fulltext, paginate, in_raius, order_by
289
- def run_proc_block
290
- result = @operation_context.last[:result]
291
- return PaginatedCollection.new if result.empty?
292
-
293
- Sunspot::Util::ContextBoundDelegate.instance_eval_with_context(self, &@block)
294
- # @operation_context.last[:result] = @operation_context.last[:result].uniq
295
- self
296
- end
297
-
298
- def current_operation
299
- @operation_context[@current_context_index][:operation]
300
- end
301
-
302
- def result_for_current_context
303
- @operation_context[@current_context_index][:result]
304
- end
305
-
306
- def temp_result_for_current_context
307
- @operation_context[@current_context_index][:temp_result]
308
- end
309
-
310
- def operation_context_result(after_results)
311
- previous_result = result_for_current_context
312
- # parent_result = @operation_context[@current_context_index -1][:result]
313
- temp_result = @operation_context[@current_context_index][:temp_result]
314
- if current_operation == "any"
315
- @operation_context[@current_context_index][:result] = previous_result.concat(after_results).uniq
316
- if temp_result
317
- @operation_context[@current_context_index][:temp_result] = temp_result.concat(after_results)
318
- end
319
- else
320
- @operation_context[@current_context_index][:result] = (previous_result & after_results).uniq
321
- end
322
- end
323
-
324
- def skip_no_value(attribute, value)
325
- if value == "no_value" && attribute_is_about_range?(attribute)
326
- matches = result_for_current_context.select {|x| x.has_attribute?(attribute)}
327
- operation_context_result(matches)
328
- return self
329
- end
330
- end
331
-
332
- def has_attribute_and_not_nil(x)
333
- x.read_attribute(@range_search_field) &&
334
- !x.read_attribute(@range_search_field).nil?
335
- end
336
-
337
- def string_text_fields(type)
338
- return @textsearch_priorities.keys unless @textsearch_priorities.keys.empty?
339
- type.columns.collect { |c| {"#{c.type}":"#{c.name}"} }
340
- .map{|hash| hash.select{|k,v| [:string, :text].include? k} }.uniq
341
- .map{|x| x.values}.flatten
342
- end
343
-
344
- def attribute_is_about_range?(attribute)
345
- matched = nil
346
- Sunspot.session.range_fields.each do |x|
347
- if attribute.to_s.include? x
348
- matched = true
349
- @range_search_field = attribute
350
- break
351
- end
352
- end
353
- matched
354
- end
355
-
356
- def sorted_temp_result
357
- return final_results unless @order_direction
358
- asc = final_results.sort_by{|x| x[@order_key]}
359
- return asc if @order_direction.to_s.downcase.include?("asc")
360
- asc.reverse
361
- end
362
- end
363
- end