qiita-elasticsearch 0.3.0 → 0.4.0

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: cc0094615455695aa7148b67052af7b154c5e31e
4
- data.tar.gz: 64562e5f4139f14eb727062d7caf39cf706f92eb
3
+ metadata.gz: 133b6b66d4231ea9d97e516812cc58b232981319
4
+ data.tar.gz: b70d9f047d78e461e4cd81a076e5f09418982682
5
5
  SHA512:
6
- metadata.gz: 183fabc234d8480455a5804eb80a8ccca40cc0b0e39fc5f6ee2f75800958875170203063f76303c88ea68e45388f347aa000f14c13d204eb6f2f8317c213b5d7
7
- data.tar.gz: 857a39fb54f660ed9a3edf6c74b45212589ba558586bd0966f2b388454099ce5aad322a7b77949d1a4129b10ae6e755eacf377b8c2eecf3e6a0f43a042637ba6
6
+ metadata.gz: 3d9776c15e61239299ec17682c62930e2fddade09934cccf08f236a8e85f38bb8788fc34703323a4e49d66094bc19c494a4b8e7cd6c3a567bdebab0a5b564e68
7
+ data.tar.gz: 93d80c35c9135c8f3cb0837731fccd55b60cca6a912e9a36e1f641f71984b7f815b1d945c2a9836295fad9389ecec433566a5c457907759f0157c027caeb7b7d
data/.rubocop.yml CHANGED
@@ -39,3 +39,7 @@ Style/StringLiterals:
39
39
 
40
40
  Style/TrailingComma:
41
41
  Enabled: false
42
+
43
+ Metrics/ParameterLists:
44
+ Max: 5
45
+ CountKeywordArgs: false
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## 0.4.0
2
+ - Change range fields to use integer
3
+ - Support date fields
4
+ - Support time_zone parameter
5
+ - Rename range_fields parameter as int_fields
6
+ - Return a null query if an invalid query string is given
7
+
1
8
  ## 0.3.0
2
9
  - Add downcased_fields options and update case rule
3
10
 
data/README.md CHANGED
@@ -60,15 +60,36 @@ query_builder.build("tag:ruby")
60
60
  #=> {"filtered"=>{"filter"=>{"bool"=>{"should"=>[{"prefix"=>{"tag"=>"ruby/"}}, {"term"=>{"tag"=>"ruby"}}]}}}}
61
61
  ```
62
62
 
63
- ### range_fields
64
- Pass `:range_fields` option with `:filterable_fields` to enable range filtered queries.
65
- With this option, `created_at:<2015-04-16` will hit documents created before 2015-04-16.
63
+ ### int_fields
64
+ Pass `:int_fields` option with `:filterable_fields` to enable range filtered queries.
65
+ With this option, `stocks:>100` will hit documents stocked by greater than 100 users.
66
66
 
67
67
  ```rb
68
- query_builder = Qiita::Elasticsearch::QueryBuilder.new(filterable_fields: ["created_at"], range_fields: ["created_at"])
68
+ query_builder = Qiita::Elasticsearch::QueryBuilder.new(filterable_fields: ["stocks"], int_fields: ["stocks"])
69
+
70
+ query_builder.build("stocks:>100")
71
+ #=> {"filtered"=>{"filter"=>{"range"=>{"stocks"=>{"gt"=>100}}}}}
72
+ ```
73
+
74
+ ### date_fields
75
+ Pass `:date_fields` option with `:filterable_fields` to enable date filtered queries.
76
+ With this option, `created_at:<2015-04-01` will hit documents created before 2015-04-01.
77
+
78
+ ```rb
79
+ query_builder = Qiita::Elasticsearch::QueryBuilder.new(filterable_fields: ["created_at"], date_fields: ["created_at"])
80
+
81
+ query_builder.build("created_at:<2015-04-01")
82
+ #=> {"filtered"=>{"filter"=>{"range"=>{"created_at"=>{"lt"=>"2015-04-01"}}}}}
83
+ ```
84
+
85
+ ### time_zone
86
+ Pass `:time_zone` option to tell how move range field's input to UTC time based date.
87
+
88
+ ```rb
89
+ query_builder = Qiita::Elasticsearch::QueryBuilder.new(filterable_fields: ["created_at"], date_fields: ["created_at"], time_zone: "+09:00")
69
90
 
70
91
  query_builder.build("created_at:<2015-04-16")
71
- #=> {"filtered"=>{"filter"=>{"range"=>{"created_at"=>{"lt"=>"2015-04-16"}}}}}
92
+ #=> {"filtered"=>{"filter"=>{"range"=>{"created_at"=>{"lt"=>"2015-04-16","time_zone"=>"+09:00"}}}}}
72
93
  ```
73
94
 
74
95
  ### downcased_fields
@@ -0,0 +1,42 @@
1
+ require "active_support/concern"
2
+
3
+ module Qiita
4
+ module Elasticsearch
5
+ module Concerns
6
+ module RangeOperandIncludable
7
+ extend ActiveSupport::Concern
8
+
9
+ RANGE_TERM_REGEXP = /\A(?<operand>\<=|\<|\>=|\>)(?<query>.*)\z/
10
+
11
+ private
12
+
13
+ # @return [String, nil]
14
+ # @example Suppose @term is "created_at:>=2015-04-16"
15
+ # range_parameter #=> "gte"
16
+ def range_parameter
17
+ range_match[:operand] ? operand_map[range_match[:operand]] : nil
18
+ end
19
+
20
+ # @return [String, nil]
21
+ # @example Suppose @term is "created_at:>=2015-04-16"
22
+ # range_query #=> "2015-04-16"
23
+ def range_query
24
+ range_match[:query]
25
+ end
26
+
27
+ def range_match
28
+ @range_match ||= RANGE_TERM_REGEXP.match(@term) || {}
29
+ end
30
+
31
+ def operand_map
32
+ {
33
+ ">" => "gt",
34
+ ">=" => "gte",
35
+ "<" => "lt",
36
+ "<=" => "lte",
37
+ }
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,93 @@
1
+ require "active_support/core_ext/date"
2
+ require "active_support/core_ext/integer"
3
+ require "qiita/elasticsearch/concerns/range_operand_includable"
4
+ require "qiita/elasticsearch/errors"
5
+ require "qiita/elasticsearch/token"
6
+
7
+ module Qiita
8
+ module Elasticsearch
9
+ class DateToken < Token
10
+ include Concerns::RangeOperandIncludable
11
+
12
+ # @note Matches to "YYYY", "YYYY-MM" and "YYYY-MM-DD"
13
+ DATE_PATTERN = /\A
14
+ (?<year>\d{4})
15
+ (?:
16
+ -
17
+ (?<month>\d{1,2})
18
+ (?:
19
+ -
20
+ (?<day>\d{1,2})
21
+ )?
22
+ )?
23
+ \z/x
24
+
25
+ attr_writer :time_zone
26
+
27
+ # @return [Hash]
28
+ # @raise [InvalidQuery]
29
+ def to_hash
30
+ if date_match
31
+ if range_parameter
32
+ {
33
+ "range" => {
34
+ @field_name => {
35
+ range_parameter => range_query,
36
+ "time_zone" => @time_zone,
37
+ }.reject do |key, value|
38
+ key == "time_zone" && value.nil?
39
+ end,
40
+ },
41
+ }
42
+ else
43
+ {
44
+ "range" => {
45
+ @field_name => {
46
+ "gte" => beginning_of_range.to_s,
47
+ "lt" => end_of_range.to_s,
48
+ "time_zone" => @time_zone,
49
+ }.reject do |key, value|
50
+ key == "time_zone" && value.nil?
51
+ end,
52
+ },
53
+ }
54
+ end
55
+ else
56
+ fail InvalidQuery
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def date_match
63
+ @date_match ||= DATE_PATTERN.match(range_query || @term)
64
+ end
65
+
66
+ # @return [Date]
67
+ def beginning_of_range
68
+ @beginning_of_range ||=
69
+ case
70
+ when date_match[:day]
71
+ Date.new(date_match[:year].to_i, date_match[:month].to_i, date_match[:day].to_i)
72
+ when date_match[:month]
73
+ Date.new(date_match[:year].to_i, date_match[:month].to_i)
74
+ else
75
+ Date.new(date_match[:year].to_i)
76
+ end
77
+ end
78
+
79
+ # @return [Date]
80
+ def end_of_range
81
+ @end_of_range ||=
82
+ case
83
+ when date_match[:day]
84
+ beginning_of_range + 1.day
85
+ when date_match[:month]
86
+ beginning_of_range + 1.month
87
+ else
88
+ beginning_of_range + 1.year
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,10 @@
1
+ module Qiita
2
+ module Elasticsearch
3
+ # @note Custom error class for rescuing from all Qiita::Elasticsearch errors.
4
+ class Error < StandardError
5
+ end
6
+
7
+ class InvalidQuery < Error
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,35 @@
1
+ require "qiita/elasticsearch/concerns/range_operand_includable"
2
+ require "qiita/elasticsearch/errors"
3
+ require "qiita/elasticsearch/token"
4
+
5
+ module Qiita
6
+ module Elasticsearch
7
+ class IntToken < Token
8
+ include Concerns::RangeOperandIncludable
9
+
10
+ INT_PATTERN = /\A\d+\z/
11
+
12
+ # @return [Hash]
13
+ # @raise [InvalidQuery]
14
+ def to_hash
15
+ if range_parameter && INT_PATTERN =~ range_query
16
+ {
17
+ "range" => {
18
+ @field_name => {
19
+ range_parameter => range_query.to_i,
20
+ },
21
+ },
22
+ }
23
+ elsif INT_PATTERN =~ @term
24
+ {
25
+ "term" => {
26
+ @field_name => @term.to_i,
27
+ },
28
+ }
29
+ else
30
+ fail InvalidQuery
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,3 +1,4 @@
1
+ require "qiita/elasticsearch/errors"
1
2
  require "qiita/elasticsearch/nodes/null_node"
2
3
  require "qiita/elasticsearch/nodes/or_separatable_node"
3
4
  require "qiita/elasticsearch/tokenizer"
@@ -5,17 +6,21 @@ require "qiita/elasticsearch/tokenizer"
5
6
  module Qiita
6
7
  module Elasticsearch
7
8
  class QueryBuilder
9
+ # @param [Array<String>, nil] date_fields
8
10
  # @param [Array<String>, nil] downcased_fields
9
11
  # @param [Array<String>, nil] filterable_fields
10
12
  # @param [Array<String>, nil] hierarchal_fields
13
+ # @param [Array<String>, nil] int_fields
11
14
  # @param [Array<String>, nil] matchable_fields
12
- # @param [Array<String>, nil] range_fields
13
- def initialize(downcased_fields: nil, hierarchal_fields: nil, filterable_fields: nil, matchable_fields: nil, range_fields: nil)
15
+ # @param [String, nil] time_zone
16
+ def initialize(date_fields: nil, downcased_fields: nil, hierarchal_fields: nil, filterable_fields: nil, int_fields: nil, matchable_fields: nil, time_zone: nil)
17
+ @date_fields = date_fields
14
18
  @downcased_fields = downcased_fields
15
- @hierarchal_fields = hierarchal_fields
16
19
  @filterable_fields = filterable_fields
20
+ @hierarchal_fields = hierarchal_fields
21
+ @int_fields = int_fields
17
22
  @matchable_fields = matchable_fields
18
- @range_fields = range_fields
23
+ @time_zone = time_zone
19
24
  end
20
25
 
21
26
  # @param [String] query_string Raw query string
@@ -27,17 +32,21 @@ module Qiita
27
32
  else
28
33
  Nodes::OrSeparatableNode.new(tokens).to_hash
29
34
  end
35
+ rescue Error
36
+ Nodes::NullNode.new.to_hash
30
37
  end
31
38
 
32
39
  private
33
40
 
34
41
  def tokenizer
35
42
  @tokenizer ||= Tokenizer.new(
43
+ date_fields: @date_fields,
36
44
  downcased_fields: @downcased_fields,
37
- hierarchal_fields: @hierarchal_fields,
38
45
  filterable_fields: @filterable_fields,
46
+ hierarchal_fields: @hierarchal_fields,
47
+ int_fields: @int_fields,
39
48
  matchable_fields: @matchable_fields,
40
- range_fields: @range_fields,
49
+ time_zone: @time_zone,
41
50
  )
42
51
  end
43
52
  end
@@ -3,7 +3,7 @@ require "qiita/elasticsearch/token"
3
3
  module Qiita
4
4
  module Elasticsearch
5
5
  class RangeToken < Token
6
- RANGE_TERM_REGEXP = /\A(?<operand>\<|\<=|\>|\>=)(?<query>.*)\z/
6
+ RANGE_TERM_REGEXP = /\A(?<operand>\<=|\<|\>=|\>)(?<query>.*)\z/
7
7
 
8
8
  # @return [Hash]
9
9
  def to_hash
@@ -11,14 +11,14 @@ module Qiita
11
11
  {
12
12
  "range" => {
13
13
  @field_name => {
14
- range_parameter => range_query,
14
+ range_parameter => range_query.to_i,
15
15
  },
16
16
  },
17
17
  }
18
18
  else
19
19
  {
20
20
  "term" => {
21
- @field_name => proper_cased_term,
21
+ @field_name => proper_cased_term.to_i,
22
22
  },
23
23
  }
24
24
  end
@@ -1,15 +1,17 @@
1
+ require "qiita/elasticsearch/date_token"
1
2
  require "qiita/elasticsearch/filterable_token"
2
3
  require "qiita/elasticsearch/hierarchal_token"
3
4
  require "qiita/elasticsearch/matchable_token"
4
- require "qiita/elasticsearch/range_token"
5
+ require "qiita/elasticsearch/int_token"
5
6
 
6
7
  module Qiita
7
8
  module Elasticsearch
8
9
  class Tokenizer
10
+ DEFAULT_DATE_FIELDS = []
9
11
  DEFAULT_DOWNCASED_FIELDS = []
10
12
  DEFAULT_FILTERABLE_FIELDS = []
11
13
  DEFAULT_HIERARCHAL_FIELDS = []
12
- DEFAULT_RANGE_FIELDS = []
14
+ DEFAULT_INT_FIELDS = []
13
15
 
14
16
  TOKEN_PATTERN = /
15
17
  (?<token_string>
@@ -23,17 +25,21 @@ module Qiita
23
25
  )
24
26
  /x
25
27
 
28
+ # @param [Array<String>, nil] date_fields
26
29
  # @param [Array<String>, nil] downcased_fields
27
30
  # @param [Array<String>, nil] filterable_fields
28
31
  # @param [Array<String>, nil] hierarchal_fields
32
+ # @param [Array<String>, nil] int_fields
29
33
  # @param [Array<String>, nil] matchable_fields
30
- # @param [Array<String>, nil] range_fields
31
- def initialize(downcased_fields: nil, filterable_fields: nil, hierarchal_fields: nil, matchable_fields: nil, range_fields: nil)
34
+ # @param [String, nil] time_zone
35
+ def initialize(date_fields: nil, downcased_fields: nil, filterable_fields: nil, hierarchal_fields: nil, int_fields: nil, matchable_fields: nil, time_zone: nil)
36
+ @date_fields = date_fields
32
37
  @downcased_fields = downcased_fields
33
38
  @filterable_fields = filterable_fields
34
39
  @hierarchal_fields = hierarchal_fields
40
+ @int_fields = int_fields
35
41
  @matchable_fields = matchable_fields
36
- @range_fields = range_fields
42
+ @time_zone = time_zone
37
43
  end
38
44
 
39
45
  # @param [String] query_string Raw query string
@@ -54,12 +60,17 @@ module Qiita
54
60
  token_string: token_string,
55
61
  )
56
62
  token.matchable_fields = @matchable_fields if token.is_a?(MatchableToken)
63
+ token.time_zone = @time_zone if token.is_a?(DateToken)
57
64
  token
58
65
  end
59
66
  end
60
67
 
61
68
  private
62
69
 
70
+ def date_fields
71
+ @date_fields || DEFAULT_DATE_FIELDS
72
+ end
73
+
63
74
  def downcased_fields
64
75
  @downcased_fields || DEFAULT_DOWNCASED_FIELDS
65
76
  end
@@ -72,14 +83,16 @@ module Qiita
72
83
  @hierarchal_fields || DEFAULT_HIERARCHAL_FIELDS
73
84
  end
74
85
 
75
- def range_fields
76
- @range_fields || DEFAULT_RANGE_FIELDS
86
+ def int_fields
87
+ @int_fields || DEFAULT_INT_FIELDS
77
88
  end
78
89
 
79
90
  def token_class(field_name)
80
91
  case
81
- when range_fields.include?(field_name)
82
- RangeToken
92
+ when date_fields.include?(field_name)
93
+ DateToken
94
+ when int_fields.include?(field_name)
95
+ IntToken
83
96
  when hierarchal_fields.include?(field_name)
84
97
  HierarchalToken
85
98
  when filterable_fields.include?(field_name)
@@ -1,5 +1,5 @@
1
1
  module Qiita
2
2
  module Elasticsearch
3
- VERSION = "0.3.0"
3
+ VERSION = "0.4.0"
4
4
  end
5
5
  end
@@ -14,6 +14,7 @@ Gem::Specification.new do |spec|
14
14
  spec.bindir = "bin"
15
15
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
16
  spec.require_paths = ["lib"]
17
+ spec.add_dependency "activesupport"
17
18
  spec.add_development_dependency "bundler", ">= 1.7"
18
19
  spec.add_development_dependency "codeclimate-test-reporter"
19
20
  spec.add_development_dependency "rake", "~> 10.0"
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qiita-elasticsearch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryo Nakamura
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-22 00:00:00.000000000 Z
11
+ date: 2015-04-23 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -97,8 +111,12 @@ files:
97
111
  - README.md
98
112
  - Rakefile
99
113
  - lib/qiita/elasticsearch.rb
114
+ - lib/qiita/elasticsearch/concerns/range_operand_includable.rb
115
+ - lib/qiita/elasticsearch/date_token.rb
116
+ - lib/qiita/elasticsearch/errors.rb
100
117
  - lib/qiita/elasticsearch/filterable_token.rb
101
118
  - lib/qiita/elasticsearch/hierarchal_token.rb
119
+ - lib/qiita/elasticsearch/int_token.rb
102
120
  - lib/qiita/elasticsearch/matchable_token.rb
103
121
  - lib/qiita/elasticsearch/nodes/filter_node.rb
104
122
  - lib/qiita/elasticsearch/nodes/filterable_node.rb
@@ -134,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
152
  version: '0'
135
153
  requirements: []
136
154
  rubyforge_project:
137
- rubygems_version: 2.4.5
155
+ rubygems_version: 2.2.2
138
156
  signing_key:
139
157
  specification_version: 4
140
158
  summary: Elasticsearch client helper for Qiita.