sunspot_stats 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -50,6 +50,11 @@ Usually Suspot use the dismax for searches, here we extend to edismax to manage
50
50
 
51
51
  a OR (b AND c) works.
52
52
 
53
+ ## TODO
54
+
55
+ Some more test!
56
+
57
+
53
58
  ## Contributing
54
59
 
55
60
  1. Fork it
@@ -13,7 +13,7 @@ module Sunspot
13
13
 
14
14
  def stat(field_name, options = {})
15
15
  field = @setup.field(field_name)
16
- options[:facet] = @setup.field(options[:facet]) if options[:facet].present?
16
+ options[:facet] = @setup.field(options[:facet]) if !options[:facet].nil?
17
17
  stat = @query.add_stat(Sunspot::Query::FieldStat.new(field, options))
18
18
  @search.add_field_stat(field, options)
19
19
  end
@@ -23,7 +23,7 @@ module Sunspot
23
23
  :stats => 'true',
24
24
  :"stats.field" => @field.indexed_name
25
25
  }
26
- params.merge!({:"stats.facet" => @options[:facet].indexed_name}) if @options[:facet].present?
26
+ params.merge!({:"stats.facet" => @options[:facet].indexed_name}) if !@options[:facet].nil?
27
27
  params
28
28
  end
29
29
  end
@@ -1,3 +1,3 @@
1
1
  module SunspotStats
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -0,0 +1 @@
1
+ require File.expand_path('spec_helper', File.join(File.dirname(__FILE__), '..'))
@@ -0,0 +1,14 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+
3
+ describe "stats component" do
4
+ it "sends stats parameters to solr" do
5
+ session.search Content do
6
+ stat :visibility, :facet => :published_at
7
+ end
8
+
9
+ connection.should have_last_search_including(:stats, "true")
10
+ connection.should have_last_search_including(:"stats.field", "visibility_f")
11
+ connection.should have_last_search_including(:"stats.facet", "published_at_d")
12
+ end
13
+
14
+ end
@@ -0,0 +1 @@
1
+ require File.expand_path('spec_helper', File.join(File.dirname(__FILE__), '..'))
@@ -0,0 +1,13 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+
3
+ describe 'stats', :type => :search do
4
+ it 'returns field name for facet' do
5
+ #stub_stat(:visibility)
6
+ result = session.search Content do
7
+ stat :visibility, :facet => :published_at
8
+ end
9
+
10
+ result.stat(:visibility).field_name.should == :visibility
11
+ end
12
+
13
+ end
@@ -0,0 +1,3 @@
1
+ require File.expand_path('spec_helper', File.join(File.dirname(__FILE__), '..'))
2
+
3
+ Dir.glob(File.join(File.dirname(__FILE__), '**', '*_examples.rb')).each { |shared| require(shared) }
data/spec/ext.rb ADDED
@@ -0,0 +1,11 @@
1
+ module Solr
2
+ class Document
3
+ def field_by_name(field_name)
4
+ @fields.find { |field| field.name.to_s == field_name.to_s }
5
+ end
6
+
7
+ def fields_by_name(field_name)
8
+ @fields.select { |field| field.name.to_s == field_name.to_s }
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ module IndexerHelper
2
+ def post(attrs = {})
3
+ @post ||= Post.new(attrs)
4
+ end
5
+
6
+ def last_add
7
+ @connection.adds.last
8
+ end
9
+
10
+ def value_in_last_document_for(field_name)
11
+ @connection.adds.last.last.field_by_name(field_name).value
12
+ end
13
+
14
+ def values_in_last_document_for(field_name)
15
+ @connection.adds.last.last.fields_by_name(field_name).map { |field| field.value }
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ module IntegrationHelper
2
+ def self.included(base)
3
+ base.before(:all) do
4
+ Sunspot.config.solr.url = ENV['SOLR_URL'] || 'http://localhost:8983/solr'
5
+ Sunspot.reset!(true)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,13 @@
1
+ module MockSessionHelper
2
+ def config
3
+ @config ||= Sunspot::Configuration.build
4
+ end
5
+
6
+ def connection
7
+ @connection ||= Mock::Connection.new
8
+ end
9
+
10
+ def session
11
+ @session ||= Sunspot::Session.new(config, connection)
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ module QueryHelper
2
+ def get_filter_tag(boolean_query)
3
+ connection.searches.last[:fq].each do |fq|
4
+ if match = fq.match(/^\{!tag=(.+)\}#{Regexp.escape(boolean_query)}$/)
5
+ return match[1]
6
+ end
7
+ end
8
+ nil
9
+ end
10
+
11
+ def subqueries(param)
12
+ q = connection.searches.last[:q]
13
+ subqueries = []
14
+ subqueries = q.scan(%r(_query_:"\{!dismax (.*?)\}(.*?)"))
15
+ subqueries.map do |subquery|
16
+ params = {}
17
+ subquery[0].scan(%r((\S+?)='(.+?)')) do |key, value|
18
+ params[key.to_sym] = value
19
+ end
20
+ unless subquery[1].empty?
21
+ params[:v] = subquery[1]
22
+ end
23
+ params
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,55 @@
1
+ module SearchHelper
2
+ def stub_nil_results
3
+ connection.response = { 'response' => nil }
4
+ end
5
+
6
+ def stub_full_results(*results)
7
+ count =
8
+ if results.last.is_a?(Integer) then results.pop
9
+ else results.length
10
+ end
11
+ docs = results.map do |result|
12
+ instance = result.delete('instance')
13
+ result.merge('id' => "#{instance.class.name} #{instance.id}")
14
+ end
15
+ response = {
16
+ 'response' => {
17
+ 'docs' => docs,
18
+ 'numFound' => count
19
+ }
20
+ }
21
+ connection.response = response
22
+ response
23
+ end
24
+
25
+ def stub_results(*results)
26
+ stub_full_results(
27
+ *results.map do |result|
28
+ if result.is_a?(Integer)
29
+ result
30
+ else
31
+ { 'instance' => result }
32
+ end
33
+ end
34
+ )
35
+ end
36
+
37
+ def stub_stat(name, values)
38
+ connection.response = {
39
+ 'facet_counts' => {
40
+ 'facet_fields' => {
41
+ name.to_s => values.to_a.sort_by { |value, count| -count }.flatten
42
+ }
43
+ }
44
+ }
45
+ end
46
+
47
+ def stat_field_name(result, field_name)
48
+ result.stat(field_name).rows.map { |row| row.field_name }
49
+ end
50
+
51
+ def values(result, field_name)
52
+ result.stat(field_name).rows.map { |row| row.value }
53
+ end
54
+
55
+ end
@@ -0,0 +1,32 @@
1
+ class AbstractModel
2
+ end
3
+
4
+ class Model < AbstractModel
5
+ end
6
+
7
+ class AbstractModelInstanceAdapter < Sunspot::Adapters::InstanceAdapter
8
+ end
9
+
10
+ class AbstractModelDataAccessor < Sunspot::Adapters::DataAccessor
11
+ end
12
+
13
+ Sunspot::Adapters::InstanceAdapter.register(AbstractModelInstanceAdapter, AbstractModel)
14
+ Sunspot::Adapters::DataAccessor.register(AbstractModelDataAccessor, AbstractModel)
15
+
16
+
17
+ module MixInModel
18
+ end
19
+
20
+ class MixModel
21
+ include MixInModel
22
+ end
23
+
24
+ class MixInModelInstanceAdapter < Sunspot::Adapters::InstanceAdapter
25
+ end
26
+
27
+ class MixInModelDataAccessor < Sunspot::Adapters::DataAccessor
28
+ end
29
+
30
+ Sunspot::Adapters::InstanceAdapter.register(MixInModelInstanceAdapter, MixInModel)
31
+ Sunspot::Adapters::DataAccessor.register(MixInModelDataAccessor, MixInModel)
32
+
@@ -0,0 +1,126 @@
1
+ module Mock
2
+ class ConnectionFactory
3
+ def connect(opts)
4
+ if @instance
5
+ raise('Factory can only create an instance once!')
6
+ else
7
+ @instance = Connection.new(opts)
8
+ end
9
+ end
10
+
11
+ def new(url = nil)
12
+ if @instance
13
+ raise('Factory can only create an instance once!')
14
+ else
15
+ @instance = Connection.new(url)
16
+ end
17
+ end
18
+
19
+ def instance
20
+ @instance ||= Connection.new
21
+ end
22
+ end
23
+
24
+ class Connection
25
+ attr_reader :adds, :commits, :optims, :searches, :message, :opts, :deletes_by_query
26
+ attr_accessor :response
27
+ attr_writer :expected_handler
28
+ undef_method :select # annoyingly defined on Object
29
+
30
+ def initialize(opts = {})
31
+ @opts = opts
32
+ @message = OpenStruct.new
33
+ @adds, @deletes, @deletes_by_query, @commits, @optims, @searches = Array.new(6) { [] }
34
+ @expected_handler = :select
35
+ end
36
+
37
+ def add(documents)
38
+ @adds << Array(documents)
39
+ end
40
+
41
+ def delete_by_id(ids)
42
+ @deletes << Array(ids)
43
+ end
44
+
45
+ def delete_by_query(query)
46
+ @deletes_by_query << query
47
+ end
48
+
49
+ def commit
50
+ @commits << Time.now
51
+ end
52
+
53
+ def optimize
54
+ @optims << Time.now
55
+ end
56
+
57
+ def post(path, params)
58
+ unless path == "#{@expected_handler}"
59
+ raise ArgumentError, "Expected request to #{@expected_handler} request handler"
60
+ end
61
+ @searches << @last_search = params[:data]
62
+ @response || {}
63
+ end
64
+
65
+ def method_missing(method, *args, &block)
66
+ get("#{method}", *args)
67
+ end
68
+
69
+ def has_add_with?(*documents)
70
+ @adds.any? do |add|
71
+ documents.all? do |document|
72
+ add.any? do |added|
73
+ if document.is_a?(Hash)
74
+ document.all? do |field, value|
75
+ added.fields_by_name(field).map do |field|
76
+ field.value.to_s
77
+ end == Array(value).map { |v| v.to_s }
78
+ end
79
+ else
80
+ !added.fields_by_name(document).empty?
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ def has_delete?(*ids)
88
+ @deletes.any? do |delete|
89
+ delete & ids == ids
90
+ end
91
+ end
92
+
93
+ def has_delete_by_query?(query)
94
+ @deletes_by_query.include?(query)
95
+ end
96
+
97
+ def has_last_search_with?(params)
98
+ with?(@last_search, params) if @last_search
99
+ end
100
+
101
+ def has_last_search_including?(key, *values)
102
+ return unless @last_search
103
+ if @last_search.has_key?(key)
104
+ if @last_search[key].is_a?(Array)
105
+ (@last_search[key] & values).length == values.length
106
+ elsif values.length == 1
107
+ @last_search[key] == values.first
108
+ end
109
+ end
110
+ end
111
+
112
+ private
113
+
114
+ def with?(request, params)
115
+ if params.respond_to?(:all?)
116
+ params.all? do |key, value|
117
+ if request.has_key?(key)
118
+ request[key] == value
119
+ end
120
+ end
121
+ else
122
+ request.has_key?(params)
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,12 @@
1
+ require File.join(File.dirname(__FILE__), 'super_class')
2
+
3
+ class Content < SuperClass
4
+ attr_accessor :body, :visibility, :published_at
5
+ end
6
+
7
+ Sunspot.setup(Content) do
8
+ text :body
9
+ float :visibility
10
+ time :published_at
11
+ end
12
+
@@ -0,0 +1,30 @@
1
+ require File.join(File.dirname(__FILE__), 'content')
2
+
3
+ module MockAdapter
4
+ class InstanceAdapter < Sunspot::Adapters::InstanceAdapter
5
+ def id
6
+ @instance.id
7
+ end
8
+ end
9
+
10
+ class DataAccessor < Sunspot::Adapters::DataAccessor
11
+ def load(id)
12
+ @clazz.get(id.to_i)
13
+ end
14
+
15
+ def load_all(ids)
16
+ all = @clazz.get_all(ids.map { |id| id.to_i })
17
+ if @custom_title
18
+ all.each { |item| item.title = @custom_title }
19
+ end
20
+ all
21
+ end
22
+
23
+ def custom_title=(custom_title)
24
+ @custom_title = custom_title
25
+ end
26
+ end
27
+ end
28
+
29
+ Sunspot::Adapters::DataAccessor.register(MockAdapter::DataAccessor, MockRecord)
30
+ Sunspot::Adapters::InstanceAdapter.register(MockAdapter::InstanceAdapter, MockRecord)
@@ -0,0 +1,52 @@
1
+ class MockRecord
2
+ IDS = Hash.new { |h, k| h[k] = 0 }
3
+ QUERY_COUNTS = Hash.new { |h, k| h[k] = 0 }
4
+ INSTANCES = Hash.new { |h, k| h[k] = {} }
5
+
6
+ attr_reader :id
7
+
8
+ class <<self
9
+ def reset!
10
+ IDS[name.to_sym] = 0
11
+ INSTANCES[name.to_sym] = {}
12
+ end
13
+ end
14
+
15
+ def initialize(attrs = {})
16
+ @id = attrs.delete(:id) || IDS[self.class.name.to_sym] += 1
17
+ INSTANCES[self.class.name.to_sym][@id] = self
18
+ attrs.each_pair do |name, value|
19
+ send(:"#{name}=", value)
20
+ end
21
+ end
22
+
23
+ def self.inherited(base)
24
+ base.extend(ClassMethods)
25
+ end
26
+
27
+ module ClassMethods
28
+ def get(id)
29
+ QUERY_COUNTS[self.name.to_sym] += 1
30
+ get_instance(id)
31
+ end
32
+
33
+ def get_all(ids)
34
+ QUERY_COUNTS[self.name.to_sym] += 1
35
+ ids.map { |id| get_instance(id) }.compact.sort_by { |instance| instance.id }
36
+ end
37
+
38
+ def query_count
39
+ QUERY_COUNTS[self.name.to_sym]
40
+ end
41
+
42
+ private
43
+
44
+ def get_instance(id)
45
+ INSTANCES[self.name.to_sym][id]
46
+ end
47
+ end
48
+
49
+ def destroy
50
+ INSTANCES[self.class.name.to_sym].delete(@id)
51
+ end
52
+ end
@@ -0,0 +1,2 @@
1
+ class SuperClass < MockRecord
2
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+
3
+ require 'ostruct'
4
+ require 'sunspot'
5
+ require 'sunspot_stats'
6
+
7
+ require File.join(File.dirname(__FILE__), 'mocks', 'mock_record.rb')
8
+ Dir.glob(File.join(File.dirname(__FILE__), 'mocks', '**', '*.rb')).each do |file|
9
+ require file unless File.basename(file) == 'mock_record.rb'
10
+ end
11
+ Dir.glob(File.join(File.dirname(__FILE__), "helpers", "*.rb")).each do |file|
12
+ require file
13
+ end
14
+ require File.join(File.dirname(__FILE__), 'ext')
15
+
16
+ RSpec.configure do |config|
17
+ # Mock session available to all spec/api tests
18
+ config.include MockSessionHelper,
19
+ :type => :api,
20
+ :example_group => {:file_path => /spec[\\\/]api/}
21
+
22
+ # Real Solr instance is available to integration tests
23
+ config.include IntegrationHelper,
24
+ :type => :integration,
25
+ :example_group => {:file_path => /spec[\\\/]integration/}
26
+
27
+ # Nested under spec/api
28
+ [:indexer, :query, :search].each do |spec_type|
29
+ helper_name = "#{spec_type}_helper"
30
+
31
+ config.include Sunspot::Util.full_const_get(Sunspot::Util.camel_case(helper_name)),
32
+ :type => spec_type,
33
+ :example_group => {:file_path => /spec[\\\/]api[\\\/]#{spec_type}/}
34
+ end
35
+ end
36
+
37
+ def without_class(clazz)
38
+ Object.class_eval { remove_const(clazz.name.to_sym) }
39
+ yield
40
+ Object.class_eval { const_set(clazz.name.to_sym, clazz) }
41
+ end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 2
9
- version: 0.0.2
8
+ - 3
9
+ version: 0.0.3
10
10
  platform: ruby
11
11
  authors:
12
12
  - duccio giovannelli
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2012-07-14 00:00:00 +02:00
17
+ date: 2012-07-16 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -69,6 +69,24 @@ files:
69
69
  - lib/search/stat_search.rb
70
70
  - lib/sunspot_stats.rb
71
71
  - lib/sunspot_stats/version.rb
72
+ - spec/api/query/spec_helper.rb
73
+ - spec/api/query/stats_spec.rb
74
+ - spec/api/search/spec_helper.rb
75
+ - spec/api/search/stats_spec.rb
76
+ - spec/api/spec_helper.rb
77
+ - spec/ext.rb
78
+ - spec/helpers/indexer_helper.rb
79
+ - spec/helpers/integration_helper.rb
80
+ - spec/helpers/mock_session_helper.rb
81
+ - spec/helpers/query_helper.rb
82
+ - spec/helpers/search_helper.rb
83
+ - spec/mocks/adapters.rb
84
+ - spec/mocks/connection.rb
85
+ - spec/mocks/content.rb
86
+ - spec/mocks/mock_adapter.rb
87
+ - spec/mocks/mock_record.rb
88
+ - spec/mocks/super_class.rb
89
+ - spec/spec_helper.rb
72
90
  - sunspot_stats.gemspec
73
91
  has_rdoc: true
74
92
  homepage: https://github.com/giovannelli/sunspot_stats
@@ -104,5 +122,22 @@ rubygems_version: 1.3.6
104
122
  signing_key:
105
123
  specification_version: 3
106
124
  summary: Added the statsComponent to sunspot
107
- test_files: []
108
-
125
+ test_files:
126
+ - spec/api/query/spec_helper.rb
127
+ - spec/api/query/stats_spec.rb
128
+ - spec/api/search/spec_helper.rb
129
+ - spec/api/search/stats_spec.rb
130
+ - spec/api/spec_helper.rb
131
+ - spec/ext.rb
132
+ - spec/helpers/indexer_helper.rb
133
+ - spec/helpers/integration_helper.rb
134
+ - spec/helpers/mock_session_helper.rb
135
+ - spec/helpers/query_helper.rb
136
+ - spec/helpers/search_helper.rb
137
+ - spec/mocks/adapters.rb
138
+ - spec/mocks/connection.rb
139
+ - spec/mocks/content.rb
140
+ - spec/mocks/mock_adapter.rb
141
+ - spec/mocks/mock_record.rb
142
+ - spec/mocks/super_class.rb
143
+ - spec/spec_helper.rb