mongoid-giza 0.5.1 → 0.6.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.
@@ -1,16 +1,19 @@
1
1
  module Mongoid
2
2
  module Giza
3
-
4
- # Defines a dynamic index which is used to generate a index for each object of the class
3
+ # Defines a dynamic index which is used to generate a index for each object
4
+ # of the class
5
5
  class DynamicIndex
6
6
  attr_reader :klass, :settings, :block
7
7
 
8
8
  # Creates a new dynamic index for the supplied class
9
9
  #
10
- # @param klass [Class] a class which each object will generate an {Mongoid::Giza::Index}
10
+ # @param klass [Class] a class which each object will generate an
11
+ # {Mongoid::Giza::Index}
11
12
  # after the evaluation of the block
12
- # @param settings [Hash] a hash of settings to be defined on every generated index
13
- # @param block [Proc] the routine that will be evaluated for each object from the class
13
+ # @param settings [Hash] a hash of settings to be defined on every
14
+ # generated index
15
+ # @param block [Proc] the routine that will be evaluated for each object
16
+ # from the class
14
17
  def initialize(klass, settings, block)
15
18
  @klass = klass
16
19
  @settings = settings
@@ -21,29 +24,32 @@ module Mongoid
21
24
  # The name of the index is unique so in case of a name collision,
22
25
  # the last index to be generated is the one that will persist
23
26
  #
24
- # @return [Hash<Symbol, Mongoid::Giza::Index>] an hash with every key being the index name
27
+ # @return [Hash<Symbol, Mongoid::Giza::Index>] an hash with every key
28
+ # being the index name
25
29
  # and the value the index itself
26
30
  def generate!
27
31
  indexes = {}
28
32
  klass.all.each do |object|
29
33
  index = generate_index(object)
30
- indexes[index.name] = index if !index.nil?
34
+ indexes[index.name] = index if index
31
35
  end
32
36
  indexes
33
37
  end
34
38
 
35
39
  # Generates the index for the object passed as parameter.
36
- # It is only generated if the object's class is the class or a subclass of the index's class
40
+ # It is only generated if the object's class is the class or a subclass of
41
+ # the index's class
37
42
  #
38
- # @param object [Mongoid::Document] the object which the index block wil be evaluated for
43
+ # @param object [Mongoid::Document] the object which the index block wil
44
+ # be evaluated for
39
45
  #
40
- # @return [Mongoid::Giza::Index, NilClass] the resulting index from the evaluation
41
- # or nil if the object's class is not the index's class or a subclass of it
46
+ # @return [Mongoid::Giza::Index, NilClass] the resulting index from the
47
+ # evaluation or nil if the object's class is not the index's class or a
48
+ # subclass of it
42
49
  def generate_index(object)
43
- if object.is_a?(klass)
44
- index = Mongoid::Giza::Index.new(klass, settings)
45
- Docile.dsl_eval(index, object, &block)
46
- end
50
+ return unless object.is_a?(klass)
51
+ index = Mongoid::Giza::Index.new(klass, settings)
52
+ Docile.dsl_eval(index, object, &block)
47
53
  end
48
54
  end
49
55
  end
@@ -1,9 +1,7 @@
1
1
  module Mongoid
2
2
  module Giza
3
-
4
3
  # Represents a Sphinx index
5
4
  class Index
6
-
7
5
  # Hash in which each key is a class accepted by +Mongoid+
8
6
  # and its value is a compatible Sphix attribute type
9
7
  TYPES_MAP = {
@@ -17,21 +15,24 @@ module Mongoid
17
15
  Time => :timestamp,
18
16
  BigDecimal => :float,
19
17
  Float => :float,
20
- Array => :multi,
18
+ Array => :json,
21
19
  Range => :multi,
22
20
  Hash => :json,
23
- Moped::BSON::ObjectId => :string,
24
- ActiveSupport::TimeWithZone => :timestamp,
21
+ BSON::ObjectId => :string,
22
+ ActiveSupport::TimeWithZone => :timestamp
25
23
  }
26
24
 
27
25
  attr_accessor :klass, :settings, :fields, :attributes
28
26
 
29
- # Creates a new index with a class, which should include Mongoid::Document, and an optional settings hash.
27
+ # Creates a new index with a class, which should include
28
+ # Mongoid::Document, and an optional settings hash.
30
29
  #
31
- # Note that no validations are made on class, so classes that behave like Mongoid::Document should be fine.
30
+ # Note that no validations are made on class, so classes that behave like
31
+ # Mongoid::Document should be fine.
32
32
  #
33
33
  # @param klass [Class] the class whose objects will be indexed
34
- # @param settings [Hash] an optional settings hash to be forwarded to Riddle
34
+ # @param settings [Hash] an optional settings hash to be forwarded to
35
+ # Riddle
35
36
  def initialize(klass, settings = {})
36
37
  @klass = klass
37
38
  @settings = settings
@@ -43,38 +44,48 @@ module Mongoid
43
44
 
44
45
  # Adds a full-text field to the index with the corresponding name
45
46
  #
46
- # If a block is given then it will be evaluated for each instance of the class being indexed
47
- # and the resulting string will be the field value.
48
- # Otherwise the field value will be the value of the corresponding object field
47
+ # If a block is given then it will be evaluated for each instance of the
48
+ # class being indexed and the resulting string will be the field value.
49
+ # Otherwise the field value will be the value of the corresponding object
50
+ # field
49
51
  #
50
52
  # @param name [Symbol] the name of the field
51
53
  # @param options [Hash] options for the field.
52
- # @option options [TrueClass, FalseClass] :attribute whether the field will also be a attribute or not (see {Mongoid::Giza::Index::Field#initialize})
53
- # @param block [Proc] an optional block to be evaluated at the scope of the document on index creation
54
+ # @option options [TrueClass, FalseClass] :attribute whether the field
55
+ # will also be a attribute or not (see
56
+ # {Mongoid::Giza::Index::Field#initialize})
57
+ # @param block [Proc] an optional block to be evaluated at the scope of
58
+ # the document on index creation
54
59
  def field(name, options = {}, &block)
55
- attribute = options[:attribute].nil? ? false : true
56
- @fields << Mongoid::Giza::Index::Field.new(name, attribute, &block)
60
+ attribute = options[:attribute]
61
+ @fields << Field.new(name, attribute, &block)
57
62
  end
58
63
 
59
64
  # Adds an attribute to the index with the corresponding name.
60
65
  #
61
- # If a type is not given then it will try to fetch the type of the corresponding class field,
62
- # falling back to :string
66
+ # If a type is not given then it will try to fetch the type of the
67
+ # corresponding class field, falling back to :string
63
68
  #
64
- # If a block is given then it will be evaluated for each instance of the class being indexed
65
- # and the resulting value will be the attribute value.
66
- # Otherwise the attribute value will be the value of the corresponding object field
69
+ # If a block is given then it will be evaluated for each instance of the
70
+ # class being indexed and the resulting value will be the attribute
71
+ # value.
72
+ # Otherwise the attribute value will be the value of the corresponding
73
+ # object field
67
74
  #
68
75
  # @param name [Symbol] the name of the attribute
69
76
  # @param type [Symbol] an optional attribute type
70
- # @param block [Proc] an optional block to be evaluated at the scope of the document on index creation
71
- def attribute(name, type = nil, &block)
72
- if type.nil?
77
+ # @param block [Proc] an optional block to be evaluated at the scope of
78
+ # the document on index creation
79
+ def attribute(name, type = nil, options = {}, &block)
80
+ unless type
73
81
  field = @klass.fields[name.to_s]
74
- type = field.nil? ? Mongoid::Giza::Index::TYPES_MAP.values.first :
75
- Mongoid::Giza::Index::TYPES_MAP[field.type] || Mongoid::Giza::Index::TYPES_MAP.values.first
82
+ if field
83
+ type = TYPES_MAP[field.type] || :string
84
+ else
85
+ type = :string
86
+ end
76
87
  end
77
- @attributes << Mongoid::Giza::Index::Attribute.new(name, type, &block)
88
+ @attributes << Attribute.new(name, type, options, &block)
78
89
  end
79
90
 
80
91
  # Retrieves and optionally sets the index name
@@ -83,22 +94,25 @@ module Mongoid
83
94
  #
84
95
  # @return [Symbol] The name of the index
85
96
  def name(new_name = nil)
86
- @name = new_name.to_sym if !new_name.nil?
97
+ @name = new_name.to_sym if new_name
87
98
  @name
88
99
  end
89
100
 
90
- # Defines the Mongoid::Criteria that will be used to retrive objects when indexing.
101
+ # Defines the Mongoid::Criteria that will be used to retrive objects when
102
+ # indexing.
91
103
  # Use this to filter what objects from the class will be indexed.
92
104
  # When an index is created the criteria is defined as class.all
93
105
  #
94
- #@param new_criteria [Mongoid::Criteria] the criteria to be used
106
+ # @param new_criteria [Mongoid::Criteria] the criteria to be used
95
107
  def criteria(new_criteria = nil)
96
108
  @criteria = new_criteria || @criteria
97
109
  end
98
110
 
99
- # Generates a XML document according to the XMLPipe2 specification from Sphinx
111
+ # Generates a XML document according to the XMLPipe2 specification from
112
+ # Sphinx
100
113
  #
101
- # @param buffer [#<<] any IO object that supports appending content using <<
114
+ # @param buffer [#<<] any IO object that supports appending content using
115
+ # <<
102
116
  def xmlpipe2(buffer)
103
117
  Mongoid::Giza::XMLPipe2.new(self, buffer).generate!
104
118
  end
@@ -1,37 +1,42 @@
1
1
  module Mongoid
2
2
  module Giza
3
3
  class Index
4
-
5
- # Represents an Sphinx index {http://sphinxsearch.com/docs/current.html#attributes attribute}
4
+ # Represents a Sphinx index attribute
6
5
  class Attribute
7
-
8
6
  # Defines the array of currently supported Sphix attribute types
9
7
  TYPES = [
10
- :uint, :bool, :bigint, :timestamp, :str2ordinal,
11
- :float, :multi, :string, :json, :str2wordcount
8
+ :uint, :bool, :bigint, :timestamp, :float,
9
+ :multi, :multi_64, :string, :json
12
10
  ]
13
11
 
14
- attr_accessor :name, :type, :block
12
+ attr_accessor :name, :type, :default, :bits, :block
15
13
 
16
14
  # Creates a new attribute with name, type and an optional block
17
15
  #
18
- # If a block is given then it will be evaluated for each instance of the class being indexed
19
- # and the resulting value will be the attribute value.
20
- # Otherwise the attribute value will be the value of the corresponding object field
16
+ # If a block is given then it will be evaluated for each instance of the
17
+ # class being indexed and the resulting value will be the attribute
18
+ # value.
19
+ # Otherwise the attribute value will be the value of the corresponding
20
+ # object field
21
21
  #
22
22
  # @param name [Symbol] the name of the attribute
23
- # @param type [Symbol] the type of the attribute. Must be one of the types defined in {Mongoid::Giza::Index::Attribute::TYPES}
24
- # @param block [Proc] an optional block to be evaluated at the scope of the document on index creation
23
+ # @param type [Symbol] the type of the attribute. Must be one of the
24
+ # types defined in {Mongoid::Giza::Index::Attribute::TYPES}
25
+ # @param block [Proc] an optional block to be evaluated at the scope of
26
+ # the document on index creation
25
27
  #
26
- # @raise [TypeError] if the type is not valid. (see {Mongoid::Giza::Index::Attribute::TYPES})
27
- def initialize(name, type, &block)
28
- raise TypeError,
29
- "attribute type not supported. " \
30
- "It must be one of the following: " \
31
- "#{Mongoid::Giza::Index::Attribute::TYPES.join(", ")}" unless Mongoid::Giza::Index::Attribute::TYPES.include? type
28
+ # @raise [TypeError] if the type is not valid. (see
29
+ # {Mongoid::Giza::Index::Attribute::TYPES})
30
+ def initialize(name, type, options = {}, &block)
31
+ fail TypeError,
32
+ "Attribute type not supported. " \
33
+ "It must be one of the following: " \
34
+ "#{TYPES.join(', ')}" unless TYPES.include? type
32
35
  @name = name.to_s.mb_chars.downcase.to_sym
33
36
  @type = type
34
37
  @block = block
38
+ @default = options[:default]
39
+ @bits = options[:bits] if type == :uint
35
40
  end
36
41
  end
37
42
  end
@@ -1,22 +1,24 @@
1
1
  module Mongoid
2
2
  module Giza
3
3
  class Index
4
-
5
- # Represents a Sphinx index {http://sphinxsearch.com/docs/current.html#fields full-text field}
4
+ # Represents a Sphinx indexed field
6
5
  class Field
7
6
  attr_accessor :name, :attribute, :block
8
7
 
9
8
  # Creates a full-text field with a name and an optional block
10
9
  #
11
- # If a block is given then it will be evaluated for each instance of the class being indexed
10
+ # If a block is given then it will be evaluated for each instance of the
11
+ # class being indexed
12
12
  # and the resulting string will be the field value.
13
- # Otherwise the field value will be the value of the corresponding object field
13
+ # Otherwise the field value will be the value of the corresponding
14
+ # object field
14
15
  #
15
16
  # @param name [Symbol] the name of the field
16
- # @param attribute [TrueClass, FalseClass] whether this field will also be stored as an string attribute
17
- # (see {http://sphinxsearch.com/docs/current.html#conf-xmlpipe-field-string})
18
- # @param block [Proc] an optional block to be evaluated at the scope of the document on index creation
19
- def initialize(name, attribute = false, &block)
17
+ # @param attribute [TrueClass, FalseClass] whether this field will also
18
+ # be stored as an string attribute
19
+ # @param block [Proc] an optional block to be evaluated at the scope of
20
+ # the document on index creation
21
+ def initialize(name, attribute = nil, &block)
20
22
  @name = name.to_s.mb_chars.downcase.to_sym
21
23
  @attribute = attribute
22
24
  @block = block
@@ -1,6 +1,5 @@
1
1
  module Mongoid
2
2
  module Giza
3
-
4
3
  # Routines related to creating the defined indexes in sphinx
5
4
  class Indexer
6
5
  include Singleton
@@ -8,13 +7,14 @@ module Mongoid
8
7
  # Creates the Indexer instance
9
8
  def initialize
10
9
  @configuration = Mongoid::Giza::Configuration.instance
11
- @controller = Riddle::Controller.new(@configuration, @configuration.file.output_path)
10
+ @controller = Riddle::Controller.new(@configuration,
11
+ @configuration.file.output_path)
12
12
  end
13
13
 
14
14
  # Index everything, regenerating all dynamic indexes from all classes
15
15
  def full_index!
16
16
  @configuration.clear_generated_indexes
17
- giza_classes.each { |klass| klass.regenerate_sphinx_indexes }
17
+ giza_classes.each(&:regenerate_sphinx_indexes)
18
18
  @configuration.render
19
19
  index!
20
20
  end
@@ -23,13 +23,16 @@ module Mongoid
23
23
  #
24
24
  # @param names [Array<Symbol>] name of the indexes that should be indexed.
25
25
  # If not provided all indexes from the configuration file are indexed
26
- # @param options [Hash] additional options to pass to Riddle::Controller#index
27
- # @option options [TrueClass, FalseClass] :verbose shows the indexer output
26
+ # @param options [Hash] additional options to pass to
27
+ # Riddle::Controller#index
28
+ # @option options [TrueClass, FalseClass] :verbose shows the indexer
29
+ # output
28
30
  def index!(*names)
29
31
  @controller.index(*names)
30
32
  end
31
33
 
32
- # @return [Array<Class>] all Mongoid models that include the {Mongoid::Giza} module
34
+ # @return [Array<Class>] all Mongoid models that include the
35
+ # {Mongoid::Giza} module
33
36
  def giza_classes
34
37
  Mongoid.models.select { |model| model.include?(Mongoid::Giza) }
35
38
  end
@@ -1,24 +1,22 @@
1
1
  module Mongoid
2
2
  module Giza
3
-
4
3
  # MongoDB counter collection to generate ids compatible with sphinx
5
- class GizaID
4
+ class ID
6
5
  include Mongoid::Document
7
6
 
8
7
  field :_id, type: Symbol
9
8
  field :seq, type: Integer, default: 0
10
9
 
11
- attr_accessible :id
12
-
13
10
  class << self
14
-
15
11
  # Gets the next id in the sequence to assign to an object
16
12
  #
17
- # @param klass [Symbol] the name of the class which next id will be retrived for
13
+ # @param klass [Symbol] the name of the class which next id will be
14
+ # retrived for
18
15
  #
19
16
  # @return [Integer] the next id in the sequence
20
- def next_id(klass)
21
- giza_id = where(id: klass).find_and_modify({"$inc" => {seq: 1}}, new: true)
17
+ def next(klass)
18
+ giza_id = where(id: klass).find_and_modify({"$inc" => {seq: 1}},
19
+ new: true)
22
20
  giza_id.seq
23
21
  end
24
22
  end
@@ -2,15 +2,18 @@ require "rails"
2
2
 
3
3
  module Mongoid
4
4
  module Giza
5
+ # :nodoc:
5
6
  class Railtie < Rails::Railtie
6
7
  configuration = Mongoid::Giza::Configuration.instance
7
8
 
8
9
  initializer "mongoid-giza.load-configuration" do
9
10
  # Sets the default xmlpipe_command
10
- configuration.source.xmlpipe_command = "rails r '<%= index.klass %>.sphinx_indexes[:<%= index.name %>].xmlpipe2(STDOUT)'"
11
+ configuration.source.xmlpipe_command =
12
+ "rails r '<%= index.klass %>.sphinx_indexes[:<%= index.name %>]" \
13
+ ".xmlpipe2(STDOUT)'"
11
14
  # Loads the configuration file
12
- file = Rails.root.join("config", "giza.yml")
13
- configuration.load(file, Rails.env) if file.file?
15
+ giza_yml = Rails.root.join("config", "giza.yml")
16
+ configuration.load(giza_yml, Rails.env) if giza_yml.file?
14
17
  end
15
18
  end
16
19
  end
@@ -1,82 +1,96 @@
1
1
  module Mongoid
2
2
  module Giza
3
-
4
3
  # Executes queries on Sphinx
5
4
  class Search
6
- attr_accessor :indexes
5
+ attr_accessor :indexes, :query_string
7
6
  attr_reader :client
8
7
 
8
+ alias_method :fulltext, :query_string=
9
+
9
10
  # Creates a new search
10
11
  #
11
12
  # @param host [String] the host address of sphinxd
12
13
  # @param port [Fixnum] the TCP port of sphinxd
13
- # @param names [Array] an optional array defining the indexes that the search will run on.
14
+ # @param names [Array] an optional array defining the indexes that the
15
+ # search will run on.
14
16
  # Defaults to "[]" which means all indexes
15
17
  def initialize(host, port, names = [])
16
18
  @client = Riddle::Client.new(host, port)
17
19
  @indexes = names
18
20
  end
19
21
 
20
- # Sets the search criteria on full-text fields
21
- #
22
- # @param query [String] a sphinx query string based on the current {http://sphinxsearch.com/docs/current.html#matching-modes matching mode}
23
- def fulltext(query)
24
- index = indexes.length > 0 ? indexes.join(" ") : "*"
25
- @client.append_query(query, index)
26
- end
27
-
28
22
  # Sets a filter based on an attribute.
29
- # Only documents that the attribute value matches will be returned from the search
23
+ # Only documents that the attribute value matches will be returned from
24
+ # the search
30
25
  #
31
26
  # @param attribute [Symbol] the attribute name to set the filter
32
- # @param value [Fixnum, Float, Range] the value (or values) that the attribute must match
27
+ # @param value [Fixnum, Float, Range] the value (or values) that the
28
+ # attribute must match
33
29
  def with(attribute, value)
34
- @client.filters << Riddle::Client::Filter.new(attribute.to_s, value, false)
30
+ @client.filters << Riddle::Client::Filter.new(attribute.to_s, value,
31
+ false)
35
32
  end
36
33
 
37
34
  # Excludes from the search documents that the attribute value matches
38
35
  #
39
36
  # @param attribute [Symbol] the attribute name
40
- # @param value [Fixnum, Float, Range] the value (or values) that the attribute must match
37
+ # @param value [Fixnum, Float, Range] the value (or values) that the
38
+ # attribute must match
41
39
  def without(attribute, value)
42
- @client.filters << Riddle::Client::Filter.new(attribute.to_s, value, true)
40
+ @client.filters << Riddle::Client::Filter.new(attribute.to_s, value,
41
+ true)
43
42
  end
44
43
 
45
44
  # Sets the order in which the results will be returned
46
45
  #
47
46
  # @param attribute [Symbol] the attribute used for sorting
48
- # @param order [Symbol] the order of the sorting. Valid values are :asc and :desc
47
+ # @param order [Symbol] the order of the sorting. Valid values are :asc
48
+ # and :desc
49
49
  def order_by(attribute, order)
50
50
  @client.sort_by = "#{attribute} #{order.to_s.upcase}"
51
51
  end
52
52
 
53
- # Executes the configured queries
53
+ # Executes the configured query
54
54
  #
55
55
  # @return [Array] an Array of Hashes as specified by Riddle::Response
56
56
  def run
57
- @client.run
57
+ index = indexes.length > 0 ? indexes.join(" ") : "*"
58
+ @client.query(query_string, index)
58
59
  end
59
60
 
60
61
  # Checks for methods on Riddle::Client
61
62
  #
62
- # @param method [Symbol, String] the method name that will be checked on Riddle::Client
63
+ # @param method [Symbol, String] the method name that will be checked on
64
+ # Riddle::Client
63
65
  #
64
- # @return [TrueClass, FalseClass] true if either Riddle::Client or Mongoid::Giza::Search respond to the method
66
+ # @return [TrueClass, FalseClass] true if either Riddle::Client or
67
+ # Mongoid::Giza::Search respond to the method
65
68
  def respond_to?(method)
66
- @client.respond_to?("#{method}=") || super
69
+ @client.respond_to?(method) ||
70
+ @client.respond_to?("#{method}=") ||
71
+ super
67
72
  end
68
73
 
69
- # Dynamically dispatches the method call to Riddle::Client if the method is defined in it
74
+ # Dynamically dispatches the method call to Riddle::Client if the method
75
+ # is defined in it
70
76
  #
71
- # @param method [Symbol, String] the method name that will be called on Riddle::Client
72
- # @param args [Array] an argument list that will also be forwarded to the Riddle::Client method
77
+ # @param method [Symbol, String] the method name that will be called on
78
+ # Riddle::Client
79
+ # @param args [Array] an argument list that will also be forwarded to the
80
+ # Riddle::Client method
73
81
  #
74
82
  # @return [Object] the return value of the Riddle::Client method
75
83
  #
76
84
  # @raise [NoMethodError] if the method is also missing on Riddle::Client
77
85
  def method_missing(method, *args)
78
- super if !respond_to?(method)
79
- @client.send "#{method}=", *args
86
+ if args.length == 1
87
+ method_writer = "#{method}="
88
+ super unless respond_to?(method_writer)
89
+ @client.send method_writer, *args
90
+ else
91
+ super unless respond_to?(method)
92
+ @client.send method, *args
93
+ end
80
94
  end
81
95
  end
82
96
  end