datastax_rails 1.1.0.3 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/README.rdoc +13 -13
  3. data/Rakefile +1 -0
  4. data/config/schema.xml.erb +0 -1
  5. data/config/{solrconfig.xml → solrconfig.xml.erb} +1 -1
  6. data/lib/blankslate.rb +1 -1
  7. data/lib/datastax_rails/associations/collection_proxy.rb +6 -2
  8. data/lib/datastax_rails/attribute_assignment.rb +114 -0
  9. data/lib/datastax_rails/attribute_methods/definition.rb +8 -2
  10. data/lib/datastax_rails/attribute_methods/typecasting.rb +2 -5
  11. data/lib/datastax_rails/attribute_methods.rb +9 -7
  12. data/lib/datastax_rails/base.rb +127 -109
  13. data/lib/datastax_rails/callbacks.rb +11 -7
  14. data/lib/datastax_rails/cassandra_only_model.rb +27 -0
  15. data/lib/datastax_rails/collection.rb +3 -1
  16. data/lib/datastax_rails/cql/base.rb +4 -0
  17. data/lib/datastax_rails/cql/select.rb +12 -2
  18. data/lib/datastax_rails/identity/abstract_key_factory.rb +1 -0
  19. data/lib/datastax_rails/identity/custom_key_factory.rb +1 -0
  20. data/lib/datastax_rails/identity/natural_key_factory.rb +1 -0
  21. data/lib/datastax_rails/identity/uuid_key_factory.rb +4 -0
  22. data/lib/datastax_rails/identity.rb +2 -1
  23. data/lib/datastax_rails/inheritance.rb +61 -0
  24. data/lib/datastax_rails/payload_model.rb +2 -5
  25. data/lib/datastax_rails/persistence.rb +63 -20
  26. data/lib/datastax_rails/railtie.rb +5 -1
  27. data/lib/datastax_rails/relation/batches.rb +2 -2
  28. data/lib/datastax_rails/relation/facet_methods.rb +56 -5
  29. data/lib/datastax_rails/relation/finder_methods.rb +55 -1
  30. data/lib/datastax_rails/relation/search_methods.rb +103 -32
  31. data/lib/datastax_rails/relation/spawn_methods.rb +3 -1
  32. data/lib/datastax_rails/relation/stats_methods.rb +1 -1
  33. data/lib/datastax_rails/relation.rb +166 -30
  34. data/lib/datastax_rails/schema/cassandra.rb +165 -0
  35. data/lib/datastax_rails/schema/migrator.rb +85 -193
  36. data/lib/datastax_rails/schema/solr.rb +158 -0
  37. data/lib/datastax_rails/schema.rb +2 -30
  38. data/lib/datastax_rails/scoping/default.rb +142 -0
  39. data/lib/datastax_rails/scoping/named.rb +200 -0
  40. data/lib/datastax_rails/scoping.rb +106 -349
  41. data/lib/datastax_rails/tasks/ds.rake +41 -42
  42. data/lib/datastax_rails/types/array_type.rb +1 -1
  43. data/lib/datastax_rails/types/base_type.rb +2 -2
  44. data/lib/datastax_rails/types/binary_type.rb +1 -1
  45. data/lib/datastax_rails/types/boolean_type.rb +1 -1
  46. data/lib/datastax_rails/types/date_type.rb +1 -1
  47. data/lib/datastax_rails/types/float_type.rb +4 -4
  48. data/lib/datastax_rails/types/integer_type.rb +3 -3
  49. data/lib/datastax_rails/types/string_type.rb +1 -1
  50. data/lib/datastax_rails/types/text_type.rb +1 -1
  51. data/lib/datastax_rails/types/time_type.rb +3 -3
  52. data/lib/datastax_rails/validations/uniqueness.rb +1 -1
  53. data/lib/datastax_rails/version.rb +1 -1
  54. data/lib/datastax_rails/wide_storage_model.rb +44 -0
  55. data/lib/datastax_rails.rb +16 -18
  56. data/spec/datastax_rails/associations_spec.rb +7 -3
  57. data/spec/datastax_rails/attribute_methods_spec.rb +23 -0
  58. data/spec/datastax_rails/base_spec.rb +1 -6
  59. data/spec/datastax_rails/inheritance_spec.rb +41 -0
  60. data/spec/datastax_rails/persistence_spec.rb +13 -3
  61. data/spec/datastax_rails/relation/batches_spec.rb +1 -1
  62. data/spec/datastax_rails/relation/facet_methods_spec.rb +52 -0
  63. data/spec/datastax_rails/relation/finder_methods_spec.rb +22 -1
  64. data/spec/datastax_rails/relation/search_methods_spec.rb +51 -1
  65. data/spec/datastax_rails/relation_spec.rb +14 -3
  66. data/spec/datastax_rails/schema/migrator_spec.rb +92 -0
  67. data/spec/datastax_rails/schema/solr_spec.rb +34 -0
  68. data/spec/datastax_rails/scoping/default_spec.rb +17 -0
  69. data/spec/datastax_rails/types/float_type_spec.rb +5 -9
  70. data/spec/datastax_rails/types/integer_type_spec.rb +5 -9
  71. data/spec/datastax_rails/types/time_type_spec.rb +28 -0
  72. data/spec/datastax_rails/validations/uniqueness_spec.rb +3 -1
  73. data/spec/dummy/config/application.rb +1 -4
  74. data/spec/dummy/config/datastax.yml +1 -1
  75. data/spec/dummy/config/environments/test.rb +2 -0
  76. data/spec/dummy/config/solr/articles-schema.xml.erb +1 -0
  77. data/spec/dummy/db/test.sqlite3 +0 -0
  78. data/spec/dummy/log/development.log +2 -0
  79. data/spec/dummy/log/production.log +2 -0
  80. data/spec/dummy/log/test.log +523 -0
  81. data/spec/spec_helper.rb +11 -0
  82. data/spec/support/models.rb +14 -0
  83. metadata +66 -22
  84. data/lib/datastax_rails/log_subscriber.rb +0 -37
  85. data/lib/datastax_rails/migrations/migration.rb +0 -15
  86. data/lib/datastax_rails/migrations.rb +0 -36
  87. data/lib/datastax_rails/mocking.rb +0 -15
  88. data/lib/datastax_rails/schema/migration.rb +0 -106
  89. data/lib/datastax_rails/schema/migration_proxy.rb +0 -25
  90. data/lib/datastax_rails/tasks/column_family.rb +0 -329
  91. data/lib/datastax_rails/tasks/keyspace.rb +0 -57
  92. data/spec/support/connection_double.rb +0 -6
@@ -1,7 +1,7 @@
1
1
  module DatastaxRails
2
2
  module Types
3
3
  class DateType < BaseType
4
- DEFAULTS = {:solr_type => 'date', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false}
4
+ DEFAULTS = {:solr_type => 'date', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false, :cassandra_type => 'timestamp'}
5
5
  FORMAT = '%Y-%m-%dT%H:%M:%SZ'
6
6
 
7
7
  def encode(value)
@@ -1,16 +1,16 @@
1
1
  module DatastaxRails
2
2
  module Types
3
3
  class FloatType < BaseType
4
- DEFAULTS = {:solr_type => 'float', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false}
4
+ DEFAULTS = {:solr_type => 'float', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false, :cassandra_type => 'float'}
5
5
  REGEX = /\A[-+]?(\d+(\.\d+)?|\.\d+)\Z/
6
6
  def encode(float)
7
- return -10191980.0 if float.blank?
7
+ return "-10191980.0" if float.blank?
8
8
  raise ArgumentError.new("#{self} requires a Float. You passed #{float.to_s}") unless float.kind_of?(Float) || (float.kind_of?(String) && float.match(REGEX)) || float.kind_of?(Fixnum)
9
- float.to_f
9
+ float.to_f.to_s
10
10
  end
11
11
 
12
12
  def decode(float)
13
- return nil if float.blank? || (float.to_f < -10191979.9 && float.to_f > -10191980.1)
13
+ return nil if float.blank? || float.to_s == '-10191980.0'
14
14
  float.to_f
15
15
  end
16
16
  end
@@ -1,12 +1,12 @@
1
1
  module DatastaxRails
2
2
  module Types
3
3
  class IntegerType < BaseType
4
- DEFAULTS = {:solr_type => 'int', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false}
4
+ DEFAULTS = {:solr_type => 'int', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false, :cassandra_type => 'int'}
5
5
  REGEX = /\A[-+]?\d+\Z/
6
6
  def encode(int)
7
- return -10191980 if int.blank?
7
+ return "-10191980" if int.blank?
8
8
  raise ArgumentError.new("#{self} requires an Integer. You passed #{int.to_s}") unless int.kind_of?(Integer) || (int.kind_of?(String) && int.match(REGEX))
9
- int.to_i
9
+ int.to_i.to_s
10
10
  end
11
11
 
12
12
  def decode(int)
@@ -1,7 +1,7 @@
1
1
  module DatastaxRails
2
2
  module Types
3
3
  class StringType < BaseType
4
- DEFAULTS = {:solr_type => 'string', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => true}
4
+ DEFAULTS = {:solr_type => 'string', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => true, :cassandra_type => 'text'}
5
5
  def encode(str)
6
6
  str = "" unless str
7
7
  str.to_s
@@ -1,7 +1,7 @@
1
1
  module DatastaxRails
2
2
  module Types
3
3
  class TextType < BaseType
4
- DEFAULTS = {:solr_type => 'text', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => false, :tokenized => true, :fulltext => true}
4
+ DEFAULTS = {:solr_type => 'text', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => false, :tokenized => true, :fulltext => true, :cassandra_type => 'text'}
5
5
  def encode(str)
6
6
  str.to_s.dup
7
7
  end
@@ -1,18 +1,18 @@
1
1
  module DatastaxRails
2
2
  module Types
3
3
  class TimeType < BaseType
4
- DEFAULTS = {:solr_type => 'date', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false}
4
+ DEFAULTS = {:solr_type => 'date', :indexed => :solr, :stored => true, :multi_valued => false, :sortable => true, :tokenized => false, :fulltext => false, :cassandra_type => 'timestamp'}
5
5
  FORMAT = "%Y-%m-%dT%H:%M:%SZ"
6
6
 
7
7
  def encode(time)
8
8
  return unless time
9
9
  raise ArgumentError.new("#{self} requires a Time") unless time.kind_of?(Time)
10
- time.strftime(FORMAT)
10
+ time.utc.strftime(FORMAT)
11
11
  end
12
12
 
13
13
  def decode(str)
14
14
  return str if str.kind_of?(Time)
15
- Time.parse(str) rescue nil
15
+ Time.zone.parse(str) rescue nil
16
16
  end
17
17
 
18
18
  def full_solr_range
@@ -17,7 +17,7 @@ module DatastaxRails
17
17
  # are implemented in datastax_rails (such as STI)
18
18
  finder_class = record.class
19
19
 
20
- scope = finder_class.unscoped.where(attribute => value)
20
+ scope = finder_class.where(attribute => value)
21
21
  scope = scope.where_not(:id => record.id) if record.persisted?
22
22
 
23
23
  Array.wrap(options[:scope]).each do |scope_item|
@@ -1,4 +1,4 @@
1
1
  module DatastaxRails
2
2
  # The current version of the gem
3
- VERSION = "1.1.0.3"
3
+ VERSION = "1.2.3"
4
4
  end
@@ -0,0 +1,44 @@
1
+ module DatastaxRails
2
+ # A special model that is designed to efficiently store very wide data.
3
+ # This model type assumes that you have a unique ID and want to either
4
+ # search or sort on a second piece of data. The store the data as a
5
+ # single, very wide row; however you can safely treat them as if
6
+ # there were multiple rows.
7
+ #
8
+ # CAVEATS:
9
+ # * Wide Storage Models cannot be indexed into Solr.
10
+ # * Once the cluster is set, it cannot be changed as it becomes the column header in Cassandra
11
+ #
12
+ # class AuditLog < DatastaxRails::WideStorageModel
13
+ # self.column_family = 'audit_logs'
14
+ #
15
+ # key :natural, :attributes => [:uuid]
16
+ # cluster_by :created_at => :desc
17
+ #
18
+ # string :uuid
19
+ # string :message
20
+ # timestamps
21
+ # end
22
+ class WideStorageModel < CassandraOnlyModel
23
+ self.abstract_class = true
24
+
25
+ def self.cluster_by(attr = nil)
26
+ @cluster_by ||= attr.is_a?(Hash) ? attr : {attr => :asc}
27
+ end
28
+
29
+ def self.write(key, attributes, options = {})
30
+ attributes = encode_attributes(attributes)
31
+ level = (options[:consistency] || self.default_consistency).to_s.upcase
32
+ if(valid_consistency?(level))
33
+ options[:consistency] = level
34
+ else
35
+ raise ArgumentError, "'#{level}' is not a valid Cassandra consistency level"
36
+ end
37
+ key.tap do |key|
38
+ ActiveSupport::Notifications.instrument("insert.datastax_rails", :column_family => column_family, :key => key, :attributes => attributes) do
39
+ cql.insert.using(level).columns(attributes).execute
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -8,16 +8,19 @@ module DatastaxRails
8
8
  extend ActiveSupport::Autoload
9
9
 
10
10
  autoload :Associations
11
+ autoload :AttributeAssignment
11
12
  autoload :AttributeMethods
12
13
  autoload :Base
13
14
  autoload :Batches
14
15
  autoload :Callbacks
16
+ autoload :CassandraOnlyModel
15
17
  autoload :Collection
16
18
  autoload :Connection
17
19
  autoload :Cql
18
20
  autoload :GroupedCollection
19
21
  autoload :Identity
20
- autoload :Migrations
22
+ autoload :Index
23
+ autoload :Inheritance
21
24
  autoload :PayloadModel
22
25
  autoload :Persistence
23
26
  autoload :Reflection
@@ -30,6 +33,7 @@ module DatastaxRails
30
33
  autoload :SpawnMethods
31
34
  autoload :StatsMethods
32
35
  autoload :Batches
36
+ autoload :FacetMethods
33
37
  end
34
38
 
35
39
  autoload :RSolrClientWrapper, 'datastax_rails/rsolr_client_wrapper'
@@ -42,6 +46,8 @@ module DatastaxRails
42
46
  autoload :SolrRepair
43
47
  end
44
48
  autoload :Validations
49
+ autoload :Version
50
+ autoload :WideStorageModel
45
51
 
46
52
  module AttributeMethods
47
53
  extend ActiveSupport::Autoload
@@ -52,6 +58,15 @@ module DatastaxRails
52
58
  autoload :Typecasting
53
59
  end
54
60
  end
61
+
62
+ module Scoping
63
+ extend ActiveSupport::Autoload
64
+
65
+ eager_autoload do
66
+ autoload :Named
67
+ autoload :Default
68
+ end
69
+ end
55
70
 
56
71
  module Tasks
57
72
  extend ActiveSupport::Autoload
@@ -77,23 +92,6 @@ module DatastaxRails
77
92
  end
78
93
  end
79
94
 
80
- require "thrift"
81
- # Thrift is how we communicate with Cassandra. We need to do a little fixup
82
- # work to handle UTF-8 properly in Ruby 1.8.6.
83
- # module Thrift
84
- # class BinaryProtocol
85
- # def write_string(str)
86
- # if(str.respond_to?(:bytesize))
87
- # size = str.bytesize
88
- # else
89
- # size = str.size
90
- # end
91
- # write_i32(size)
92
- # trans.write(str)
93
- # end
94
- # end
95
- # end
96
-
97
95
  require 'datastax_rails/railtie' if defined?(Rails)
98
96
  require 'datastax_rails/errors'
99
97
 
@@ -1,18 +1,22 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe DatastaxRails::Base do
4
- describe "Relations" do
4
+ describe "Associations" do
5
5
  describe "belongs_to" do
6
6
  it "should set the id when setting the object" do
7
7
  person = Person.create(:name => "Jason")
8
8
  job = Job.create(:title => "Developer")
9
+ Person.commit_solr
9
10
  job.person = person
10
11
  job.person_id.should == person.id
11
12
  end
12
13
 
13
14
  it "should look up the owning model by id" do
14
- person = Person.create(:name => "John")
15
- job = Job.create(:title => "Developer", :person_id => person.id)
15
+ Job.truncate
16
+ Job.commit_solr
17
+ person = Person.create!(:name => "John")
18
+ job = Job.create!(:title => "Developer", :person_id => person.id)
19
+ Person.commit_solr
16
20
  Person.commit_solr
17
21
  Job.commit_solr
18
22
  Job.first.person.should == person
@@ -5,6 +5,11 @@ class AttributeMethodsTester < DatastaxRails::Base
5
5
  string :non_search_string, :searchable => false
6
6
  end
7
7
 
8
+ class CassandraOnlyTester < DatastaxRails::CassandraOnlyModel
9
+ string :test_string
10
+ string :test_string2, :indexed => :both
11
+ end
12
+
8
13
  describe DatastaxRails::Base do
9
14
  def tester
10
15
  @tester ||= AttributeMethodsTester.new
@@ -20,4 +25,22 @@ describe DatastaxRails::Base do
20
25
  end
21
26
  end
22
27
 
28
+ describe "#attribute" do
29
+ context "Cassandra-only models" do
30
+ it "does not index columns by default" do
31
+ expect(CassandraOnlyTester.attribute_definitions[:test_string].coder.options[:indexed]).to be_false
32
+ end
33
+
34
+ it "does not index into solr" do
35
+ expect(CassandraOnlyTester.attribute_definitions[:test_string2].coder.options[:indexed]).to eq(:cassandra)
36
+ end
37
+ end
38
+
39
+ context "Normal models" do
40
+ it "indexes data into solr by default" do
41
+ expect(AttributeMethodsTester.attribute_definitions[:test_string].coder.options[:indexed]).to eq(:solr)
42
+ end
43
+ end
44
+ end
45
+
23
46
  end
@@ -8,6 +8,7 @@ describe DatastaxRails::Base do
8
8
  end
9
9
 
10
10
  it "should run after_save" do
11
+ Person.commit_solr
11
12
  p = Person.new(:name => "Jason")
12
13
  p.save!
13
14
  p.instance_variable_get(:@after_save_ran).should == "yup"
@@ -16,10 +17,4 @@ describe DatastaxRails::Base do
16
17
  it "should raise RecordNotFound when finding a bogus ID" do
17
18
  lambda { Person.find("xyzzy") }.should raise_exception(DatastaxRails::RecordNotFound)
18
19
  end
19
-
20
- xit "should skip records that are missing dsr in cassandra" do
21
- p = Person.create(:name => 'Jason')
22
- Person.cql.delete(p.id).columns(['dsr']).execute
23
- Person.find_by_name('Jason').should be_nil
24
- end
25
20
  end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ class InheritanceTesterBase < DatastaxRails::Base
4
+ self.abstract_class = true
5
+ end
6
+
7
+ class InheritanceTesterChild < InheritanceTesterBase
8
+
9
+ end
10
+
11
+ class InheritanceTesterGrandChild < InheritanceTesterChild
12
+
13
+ end
14
+
15
+ class InheritanceTesterNonAbstract < DatastaxRails::Base
16
+
17
+ end
18
+
19
+ describe DatastaxRails::Base do
20
+ context "Inheritance" do
21
+ it "raises NotImplementedError if DatastaxRails::Base is instantiated" do
22
+ expect { DatastaxRails::Base.new }.to raise_error(NotImplementedError)
23
+ end
24
+
25
+ it "raises NotImplementedError if an abstract class is instantiated" do
26
+ expect { InheritanceTesterBase.new }.to raise_error(NotImplementedError)
27
+ end
28
+
29
+ it "identifies the abstract class as base for direct decendants" do
30
+ expect(InheritanceTesterChild.base_class).to eq(InheritanceTesterBase)
31
+ end
32
+
33
+ it "identifies the abstract class as base for its indirect decendants" do
34
+ expect(InheritanceTesterGrandChild.base_class).to eq(InheritanceTesterBase)
35
+ end
36
+
37
+ it "identifies a child of DatastaxRails::Base as base" do
38
+ expect(InheritanceTesterNonAbstract.base_class).to eq(InheritanceTesterNonAbstract)
39
+ end
40
+ end
41
+ end
@@ -2,6 +2,16 @@ require 'spec_helper'
2
2
 
3
3
  describe "DatastaxRails::Base" do
4
4
  describe "persistence" do
5
+ describe "#update_attributes" do
6
+ it "only overwrites attributes that are passed in as part of the hash" do
7
+ person = Person.create(:name => 'Jason', :birthdate => Date.parse("Oct 19, 1981"), :nickname => 'Jas')
8
+ person.birthdate = Date.parse("Oct 19, 1980")
9
+ person.update_attributes(:nickname => 'Jace')
10
+ person.birthdate.should eql(Date.parse("Oct 19, 1980"))
11
+ person.nickname.should eql('Jace')
12
+ end
13
+ end
14
+
5
15
  describe "with cql" do
6
16
  describe "#create" do
7
17
  it "should persist at the given consistency level" do
@@ -62,19 +72,19 @@ describe "DatastaxRails::Base" do
62
72
  end
63
73
 
64
74
  describe "#store_file" do
65
- it "should store a file" do
75
+ it "should store a file", :slow => true do
66
76
  file = "abcd"*1.megabyte
67
77
  CarPayload.create(:digest => 'limo', :payload => file)
68
78
  CarPayload.find('limo').payload.should == file
69
79
  end
70
80
 
71
- it "should store really large files" do
81
+ it "should store really large files", :slow => true do
72
82
  file = IO.read("/dev/zero", 25.megabyte)
73
83
  CarPayload.create(:digest => 'limo', :payload => file)
74
84
  CarPayload.find('limo').payload.should == file
75
85
  end
76
86
 
77
- it "should successfully overwrite a larger file with a smaller one" do
87
+ it "should successfully overwrite a larger file with a smaller one", :slow => true do
78
88
  file = "abcd"*1.megabyte
79
89
  car = CarPayload.create(:digest => 'limo', :payload => file)
80
90
  smallfile = "e"*1.kilobyte
@@ -8,12 +8,12 @@ describe DatastaxRails::Relation do
8
8
  sleep(1) if idx % 5 == 4 # Performance hack
9
9
  end
10
10
  Hobby.commit_solr
11
- Hobby.commit_solr
12
11
  end
13
12
 
14
13
  ['cassandra', 'solr'].each do |method|
15
14
  describe "#find_each" do
16
15
  it "returns each record one at a time with #{method}" do
16
+ sleep(1)
17
17
  missed_hobbies = ('a'..'l').to_a
18
18
  @relation.send('with_'+method).find_each(:batch_size => 5) do |hobby|
19
19
  missed_hobbies.delete_if {|h| h == hobby.name}
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ describe DatastaxRails::Relation do
4
+ before(:each) do
5
+ @relation = DatastaxRails::Relation.new(Hobby, "hobbies")
6
+ end
7
+
8
+ describe "#field_facet" do
9
+
10
+ it "should return facets on a field" do
11
+ Hobby.create(:name => 'skiing')
12
+ Hobby.create(:name => 'boating')
13
+ Hobby.create(:name => 'fishing')
14
+ Hobby.create(:name => 'skiing')
15
+ Hobby.commit_solr
16
+ @relation.field_facet(:name).all.facets['name'].should == ["skiing", 2, "boating", 1, "fishing", 1]
17
+ end
18
+
19
+ it "should allow options to be specified" do
20
+ Hobby.create(:name => 'skiing')
21
+ Hobby.create(:name => 'singing')
22
+ Hobby.create(:name => 'reading')
23
+ Hobby.commit_solr
24
+ @relation.field_facet(:name, :prefix => 's').all.facets['name'].should == ["singing", 1, "skiing", 1]
25
+ end
26
+
27
+ end
28
+
29
+ describe "#range_facet" do
30
+
31
+ it "should return facets on a field" do
32
+ Hobby.create(:complexity => 1.0)
33
+ Hobby.create(:complexity => 5.0)
34
+ Hobby.create(:complexity => 8.0)
35
+ Hobby.create(:complexity => 9.0)
36
+ Hobby.create(:complexity => 10.0)
37
+ Hobby.commit_solr
38
+ @relation.range_facet(:complexity, 1.0, 10.0, 2.0).all.facets['complexity'].should == {"counts"=>["1.0", 1, "3.0", 0, "5.0", 1, "7.0", 1, "9.0", 2], "gap"=>2.0, "start"=>1.0, "end"=>11.0}
39
+ end
40
+
41
+ it "should allow options to be specified" do
42
+ Hobby.create(:complexity => 1.0)
43
+ Hobby.create(:complexity => 5.0)
44
+ Hobby.create(:complexity => 8.0)
45
+ Hobby.create(:complexity => 9.0)
46
+ Hobby.create(:complexity => 10.0)
47
+ Hobby.commit_solr
48
+ @relation.range_facet(:complexity, 1.0, 10.0, 2.0, :include => 'upper').all.facets['complexity'].should == {"counts"=>["1.0", 0, "3.0", 1, "5.0", 0, "7.0", 2, "9.0", 1], "gap"=>2.0, "start"=>1.0, "end"=>11.0}
49
+ end
50
+
51
+ end
52
+ end
@@ -3,6 +3,7 @@ require 'spec_helper'
3
3
  describe DatastaxRails::Relation do
4
4
  before(:each) do
5
5
  @relation = DatastaxRails::Relation.new(Hobby, "hobbies")
6
+ Hobby.commit_solr
6
7
  end
7
8
 
8
9
  describe "#first" do
@@ -56,7 +57,7 @@ describe DatastaxRails::Relation do
56
57
  it "finds a record by an attribute" do
57
58
  Boat.create(:name => 'Spooner')
58
59
  Boat.commit_solr
59
- Boat.find_by_name('Spooner').should_not be_nil
60
+ Boat.find_all_by_name('Spooner').should_not be_nil
60
61
  end
61
62
 
62
63
  it "finds a record by an attribute with a space in it" do
@@ -71,4 +72,24 @@ describe DatastaxRails::Relation do
71
72
  Boat.find_by_name('Dumb: Name').should_not be_nil
72
73
  end
73
74
  end
75
+
76
+ describe "#find_by" do
77
+ it "finds a record by an attribute" do
78
+ Boat.create(:name => 'Spooner')
79
+ Boat.commit_solr
80
+ Boat.find_by(name: 'Spooner').should_not be_nil
81
+ end
82
+
83
+ it "finds a record by an attribute with a space in it" do
84
+ Boat.create(:name => 'Water Lily')
85
+ Boat.commit_solr
86
+ Boat.find_by(name: 'Water Lily').should_not be_nil
87
+ end
88
+
89
+ it "finds a record by an attribute with a colon in it" do
90
+ Boat.create(:name => 'Dumb: Name')
91
+ Boat.commit_solr
92
+ Boat.find_by(name: 'Dumb: Name').should_not be_nil
93
+ end
94
+ end
74
95
  end
@@ -58,13 +58,29 @@ describe DatastaxRails::Relation do
58
58
 
59
59
  it "should return items in descending order" do
60
60
  %w[fishing hiking boating jogging swimming chess].each do |word|
61
- Hobby.create(:name => word)
61
+ Hobby.create!(:name => word)
62
62
  end
63
63
  @relation.commit_solr
64
64
  @relation.order(:name => :desc).collect {|h| h.name}.should == %w[swimming jogging hiking fishing chess boating]
65
65
  end
66
66
  end
67
67
 
68
+ describe "#slow_order" do
69
+ it "should manually order items coming from Cassandra" do
70
+ %w[john jason michael tony billy jim phil].each_with_index do |name,i|
71
+ AuditLog.create!(:uuid => "c1401540-f092-11e2-9001-6a5ab73a986#{i}", :user => name, :message => 'changed')
72
+ end
73
+ AuditLog.unscoped.slow_order(:user => :asc).collect {|log| log.user}.should == %w[billy jason jim john michael phil tony]
74
+ end
75
+
76
+ it "should manually order items coming from Cassandra in descending order" do
77
+ %w[john jason michael tony billy jim phil].each_with_index do |name,i|
78
+ AuditLog.create!(:uuid => "c1401540-f092-11e2-9001-6a5ab73a986#{i}", :user => name, :message => 'changed')
79
+ end
80
+ AuditLog.unscoped.slow_order(:user => :desc).collect {|log| log.user}.should == %w[tony phil michael john jim jason billy]
81
+ end
82
+ end
83
+
68
84
  describe "#where" do
69
85
  it "should return documents where a field is nil (does not exist)" do
70
86
  Hobby.create(:name => 'Swimming')
@@ -186,4 +202,38 @@ describe DatastaxRails::Relation do
186
202
  @relation.fulltext("swimming").should_not be_empty
187
203
  end
188
204
  end
205
+
206
+ describe '#highlight' do
207
+ let(:hl) { @relation.highlight(:name, :description, :snippet => 3, :fragsize => 200) }
208
+
209
+ it { expect(hl.highlight_options[:fields]).to eq [:name, :description] }
210
+ it { expect(hl.highlight_options[:snippet]).to eq 3 }
211
+ it { expect(hl.highlight_options[:fragsize]).to eq 200 }
212
+
213
+ context 'with duplicate fields' do
214
+ let(:hl) { @relation.highlight(:name, :description, :name) }
215
+
216
+ it { expect(hl.highlight_options[:fields]).to eq [:name, :description] }
217
+ end
218
+ end
219
+
220
+ describe '#solr_format' do
221
+ context 'when formatting Time' do
222
+ let(:time) { Time.new 2011, 10, 9, 8, 7, 6, "-05:00" }
223
+
224
+ it { expect(@relation.solr_format(time)).to eq '2011-10-09T13:07:06Z' }
225
+ end
226
+
227
+ context 'when formatting Date' do
228
+ let(:date) { Date.new 2001, 2, 3 }
229
+
230
+ it { expect(@relation.solr_format(date)).to eq '2001-02-03T00:00:00Z' }
231
+ end
232
+
233
+ context 'when formatting DateTime' do
234
+ let(:datetime) { DateTime.new 2001, 2, 3, 4, 5, 6, "-07:00" }
235
+
236
+ it { expect(@relation.solr_format(datetime)).to eq '2001-02-03T11:05:06Z' }
237
+ end
238
+ end
189
239
  end
@@ -3,6 +3,8 @@ require 'spec_helper'
3
3
  describe DatastaxRails::Relation do
4
4
  before(:each) do
5
5
  @relation = DatastaxRails::Relation.new(Hobby, "hobbies")
6
+ @relation.default_scoped = true
7
+ @relation.commit_solr
6
8
  end
7
9
 
8
10
  describe "#==" do
@@ -30,14 +32,16 @@ describe DatastaxRails::Relation do
30
32
  end
31
33
 
32
34
  it "should cache the total count on any solr query" do
33
- @relation.should_receive(:query_via_solr).and_return(mock("ResultSet", :total_entries => 42))
35
+ @relation = @relation.with_solr
36
+ @relation.should_receive(:query_via_solr).and_return(double("ResultSet", :total_entries => 42))
34
37
  @relation.all
35
38
  @relation.count.should == 42
36
39
  end
37
40
 
38
41
  it "should execute a fast search to determine the count" do
39
- mock_relation = mock(DatastaxRails::Relation)
42
+ mock_relation = double(DatastaxRails::Relation)
40
43
  mock_relation.stub_chain(:select, :to_a, :total_entries).and_return(37)
44
+ @relation = @relation.with_solr
41
45
  @relation.should_receive(:limit).with(1).and_return(mock_relation)
42
46
  @relation.count.should == 37
43
47
  end
@@ -62,6 +66,12 @@ describe DatastaxRails::Relation do
62
66
  relation.count.should == 0
63
67
  relation.default_scope.count.should == 1
64
68
  end
69
+
70
+ it "should return a relation that has a default scope set" do
71
+ relation = DatastaxRails::Relation.new(Boat, "boats")
72
+ relation.default_scoped = true
73
+ relation.default_scope.order_values.should_not be_empty
74
+ end
65
75
  end
66
76
 
67
77
  describe "#empty?" do
@@ -130,6 +140,7 @@ describe DatastaxRails::Relation do
130
140
 
131
141
  describe "grouped queries" do
132
142
  before(:each) do
143
+ Person.commit_solr
133
144
  Person.create(:name => 'John', :nickname => 'J')
134
145
  Person.create(:name => 'Jason', :nickname => 'J')
135
146
  Person.create(:name => 'James', :nickname => 'J')
@@ -146,7 +157,7 @@ describe DatastaxRails::Relation do
146
157
  results['steve'].should have(1).item
147
158
  end
148
159
 
149
- it "should return total_entires as the highest value of any group" do
160
+ it "should return total_entries as the highest value of any group" do
150
161
  results = Person.group(:nickname).all
151
162
  results.total_entries.should eq(3)
152
163
  end