qiita-elasticsearch 0.2.2 → 0.3.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -1
- data/CHANGELOG.md +3 -0
- data/README.md +13 -7
- data/lib/qiita/elasticsearch/filterable_token.rb +16 -0
- data/lib/qiita/elasticsearch/hierarchal_token.rb +27 -0
- data/lib/qiita/elasticsearch/matchable_token.rb +29 -0
- data/lib/qiita/elasticsearch/nodes/filter_node.rb +6 -27
- data/lib/qiita/elasticsearch/nodes/filterable_node.rb +4 -21
- data/lib/qiita/elasticsearch/nodes/match_node.rb +3 -19
- data/lib/qiita/elasticsearch/nodes/multi_should_node.rb +2 -7
- data/lib/qiita/elasticsearch/nodes/or_separatable_node.rb +3 -19
- data/lib/qiita/elasticsearch/nodes/query_node.rb +3 -12
- data/lib/qiita/elasticsearch/nodes/term_node.rb +2 -59
- data/lib/qiita/elasticsearch/query_builder.rb +11 -8
- data/lib/qiita/elasticsearch/range_token.rb +57 -0
- data/lib/qiita/elasticsearch/token.rb +21 -32
- data/lib/qiita/elasticsearch/tokenizer.rb +45 -3
- data/lib/qiita/elasticsearch/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc0094615455695aa7148b67052af7b154c5e31e
|
4
|
+
data.tar.gz: 64562e5f4139f14eb727062d7caf39cf706f92eb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 183fabc234d8480455a5804eb80a8ccca40cc0b0e39fc5f6ee2f75800958875170203063f76303c88ea68e45388f347aa000f14c13d204eb6f2f8317c213b5d7
|
7
|
+
data.tar.gz: 857a39fb54f660ed9a3edf6c74b45212589ba558586bd0966f2b388454099ce5aad322a7b77949d1a4129b10ae6e755eacf377b8c2eecf3e6a0f43a042637ba6
|
data/.rubocop.yml
CHANGED
@@ -11,11 +11,14 @@ Metrics/CyclomaticComplexity:
|
|
11
11
|
Enabled: false
|
12
12
|
|
13
13
|
Metrics/LineLength:
|
14
|
-
|
14
|
+
Enabled: false
|
15
15
|
|
16
16
|
Metrics/MethodLength:
|
17
17
|
Enabled: false
|
18
18
|
|
19
|
+
Metrics/ParameterLists:
|
20
|
+
Enabled: false
|
21
|
+
|
19
22
|
Metrics/PerceivedComplexity:
|
20
23
|
Enabled: false
|
21
24
|
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -6,7 +6,6 @@ Elasticsearch client helper for Qiita.
|
|
6
6
|
|
7
7
|
```rb
|
8
8
|
query_builder = Qiita::Elasticsearch::QueryBuilder.new
|
9
|
-
#=> #<Qiita::Elasticsearch::QueryBuilder:0x007ff81e67c4d8 @filterable_fields=nil, @hierarchal_fields=nil, @matchable_fields=nil>
|
10
9
|
|
11
10
|
query_builder.build("a")
|
12
11
|
#=> {"match"=>{"_all"=>"a"}}
|
@@ -29,7 +28,6 @@ Pass `:matchable_fields` option to tell matchable field names (default: `_all`).
|
|
29
28
|
|
30
29
|
```rb
|
31
30
|
query_builder = Qiita::Elasticsearch::QueryBuilder.new(matchable_fields: ["body", "title"])
|
32
|
-
#=> #<Qiita::Elasticsearch::QueryBuilder:0x007ff81fa59168 @filterable_fields=nil, @hierarchal_fields=nil, @matchable_fields=["body", "title"], @range_fields=nil>
|
33
31
|
|
34
32
|
query_builder.build("a")
|
35
33
|
#=> {"multi_match"=>{"fields"=>["body", "title"], "query"=>"a"}}
|
@@ -40,7 +38,6 @@ Pass `:filterable_fields` option to enable filtered queries like `tag:Ruby`.
|
|
40
38
|
|
41
39
|
```rb
|
42
40
|
query_builder = Qiita::Elasticsearch::QueryBuilder.new(filterable_fields: ["tag", "title"])
|
43
|
-
#=> #<Qiita::Elasticsearch::QueryBuilder:0x007ff81e2de1f0 @filterable_fields=["tag", "title"], @hierarchal_fields=nil, @matchable_fields=nil, @range_fields=nil>
|
44
41
|
|
45
42
|
query_builder.build("tag:a")
|
46
43
|
#=> {"filtered"=>{"filter"=>{"term"=>{"tag"=>"a"}}}}
|
@@ -53,25 +50,34 @@ query_builder.build("user:a b")
|
|
53
50
|
```
|
54
51
|
|
55
52
|
### hierarchal_fields
|
56
|
-
Pass `:hierarchal_fields`
|
53
|
+
Pass `:hierarchal_fields` option with `:filterable_fields` to enable prefixed filtered queries.
|
57
54
|
With this option, `tag:foo` will hit documents tagged with `foo`, or `foo/...`.
|
58
55
|
|
59
56
|
```rb
|
60
57
|
query_builder = Qiita::Elasticsearch::QueryBuilder.new(filterable_fields: ["tag"], hierarchal_fields: ["tag"])
|
61
|
-
#=> #<Qiita::Elasticsearch::QueryBuilder:0x007fe96d6d5ed0 @filterable_fields=["tag"], @hierarchal_fields=["tag"], @matchable_fields=nil, @range_fields=nil>
|
62
58
|
|
63
59
|
query_builder.build("tag:ruby")
|
64
60
|
#=> {"filtered"=>{"filter"=>{"bool"=>{"should"=>[{"prefix"=>{"tag"=>"ruby/"}}, {"term"=>{"tag"=>"ruby"}}]}}}}
|
65
61
|
```
|
66
62
|
|
67
63
|
### range_fields
|
68
|
-
Pass `:range_fields`
|
64
|
+
Pass `:range_fields` option with `:filterable_fields` to enable range filtered queries.
|
69
65
|
With this option, `created_at:<2015-04-16` will hit documents created before 2015-04-16.
|
70
66
|
|
71
67
|
```rb
|
72
68
|
query_builder = Qiita::Elasticsearch::QueryBuilder.new(filterable_fields: ["created_at"], range_fields: ["created_at"])
|
73
|
-
#=> #<Qiita::Elasticsearch::QueryBuilder:0x007fe96d6d5ed0 @filterable_fields=["created_at"], @hierarchal_fields=nil, @matchable_fields=nil, @range_fields=["created_at"]>
|
74
69
|
|
75
70
|
query_builder.build("created_at:<2015-04-16")
|
76
71
|
#=> {"filtered"=>{"filter"=>{"range"=>{"created_at"=>{"lt"=>"2015-04-16"}}}}}
|
77
72
|
```
|
73
|
+
|
74
|
+
### downcased_fields
|
75
|
+
Pass `:downcased_fields` option with `:filterable_fields` to downcase any terms in the fields.
|
76
|
+
This option is useful when some fields are stored within downcased format on Elasticsearch.
|
77
|
+
|
78
|
+
```rb
|
79
|
+
query_builder = Qiita::Elasticsearch::QueryBuilder.new(downcased_fields: ["tag"], filterable_fields: ["tag"])
|
80
|
+
|
81
|
+
query_builder.build("tag:Ruby")
|
82
|
+
#=> {"filtered"=>{"filter"=>{"term"=>{"tag"=>"ruby"}}}}
|
83
|
+
```
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "qiita/elasticsearch/token"
|
2
|
+
|
3
|
+
module Qiita
|
4
|
+
module Elasticsearch
|
5
|
+
class HierarchalToken < Token
|
6
|
+
# @return [Hash]
|
7
|
+
def to_hash
|
8
|
+
{
|
9
|
+
"bool" => {
|
10
|
+
"should" => [
|
11
|
+
{
|
12
|
+
"prefix" => {
|
13
|
+
@field_name => proper_cased_term + "/",
|
14
|
+
},
|
15
|
+
},
|
16
|
+
{
|
17
|
+
"term" => {
|
18
|
+
@field_name => proper_cased_term,
|
19
|
+
},
|
20
|
+
},
|
21
|
+
],
|
22
|
+
},
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "qiita/elasticsearch/token"
|
2
|
+
|
3
|
+
module Qiita
|
4
|
+
module Elasticsearch
|
5
|
+
class MatchableToken < Token
|
6
|
+
attr_writer :matchable_fields
|
7
|
+
|
8
|
+
# @return [Hash]
|
9
|
+
def to_hash
|
10
|
+
if @matchable_fields.nil?
|
11
|
+
{
|
12
|
+
quoted? ? "match_phrase" : "match" => {
|
13
|
+
"_all" => @term,
|
14
|
+
}
|
15
|
+
}
|
16
|
+
else
|
17
|
+
hash = {
|
18
|
+
"multi_match" => {
|
19
|
+
"fields" => @matchable_fields,
|
20
|
+
"query" => @term,
|
21
|
+
},
|
22
|
+
}
|
23
|
+
hash["multi_match"]["type"] = "phrase" if quoted?
|
24
|
+
hash
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -6,23 +6,13 @@ module Qiita
|
|
6
6
|
module Nodes
|
7
7
|
class FilterNode
|
8
8
|
# @param [Array<Qiita::Elasticsearch::Tokens>] tokens
|
9
|
-
|
10
|
-
# @param [Array<String>, nil] matchable_fields
|
11
|
-
# @param [Array<String>, nil] range_fields
|
12
|
-
def initialize(tokens, hierarchal_fields: nil, matchable_fields: nil, range_fields: nil)
|
13
|
-
@hierarchal_fields = hierarchal_fields
|
14
|
-
@matchable_fields = matchable_fields
|
15
|
-
@range_fields = range_fields
|
9
|
+
def initialize(tokens)
|
16
10
|
@tokens = tokens
|
17
11
|
end
|
18
12
|
|
19
13
|
def to_hash
|
20
14
|
if must_not_tokens.empty? && must_tokens.length == 1
|
21
|
-
TermNode.new(
|
22
|
-
must_tokens.first,
|
23
|
-
hierarchal_fields: @hierarchal_fields,
|
24
|
-
range_fields: @range_fields,
|
25
|
-
).to_hash
|
15
|
+
TermNode.new(must_tokens.first).to_hash
|
26
16
|
else
|
27
17
|
{
|
28
18
|
"bool" => {
|
@@ -41,17 +31,10 @@ module Qiita
|
|
41
31
|
def must_not_queries
|
42
32
|
must_not_tokens.map do |token|
|
43
33
|
if token.field_name.nil?
|
44
|
-
MatchNode.new(
|
45
|
-
token,
|
46
|
-
matchable_fields: @matchable_fields,
|
47
|
-
)
|
34
|
+
MatchNode.new(token).to_hash
|
48
35
|
else
|
49
|
-
TermNode.new(
|
50
|
-
|
51
|
-
hierarchal_fields: @hierarchal_fields,
|
52
|
-
range_fields: @range_fields,
|
53
|
-
)
|
54
|
-
end.to_hash
|
36
|
+
TermNode.new(token).to_hash
|
37
|
+
end
|
55
38
|
end
|
56
39
|
end
|
57
40
|
|
@@ -61,11 +44,7 @@ module Qiita
|
|
61
44
|
|
62
45
|
def must_queries
|
63
46
|
must_tokens.map do |token|
|
64
|
-
TermNode.new(
|
65
|
-
token,
|
66
|
-
hierarchal_fields: @hierarchal_fields,
|
67
|
-
range_fields: @range_fields,
|
68
|
-
).to_hash
|
47
|
+
TermNode.new(token).to_hash
|
69
48
|
end
|
70
49
|
end
|
71
50
|
|
@@ -6,35 +6,18 @@ module Qiita
|
|
6
6
|
module Nodes
|
7
7
|
class FilterableNode
|
8
8
|
# @param [Array<Qiita::Elasticsearch::Tokens>] tokens
|
9
|
-
|
10
|
-
# @param [Array<String>, nil] matchable_fields
|
11
|
-
# @param [Array<String>, nil] range_fields
|
12
|
-
def initialize(tokens, hierarchal_fields: nil, matchable_fields: nil, range_fields: nil)
|
13
|
-
@hierarchal_fields = hierarchal_fields
|
14
|
-
@matchable_fields = matchable_fields
|
15
|
-
@range_fields = range_fields
|
9
|
+
def initialize(tokens)
|
16
10
|
@tokens = tokens
|
17
11
|
end
|
18
12
|
|
19
13
|
def to_hash
|
20
14
|
if filter_tokens.empty?
|
21
|
-
QueryNode.new(
|
22
|
-
not_filter_tokens,
|
23
|
-
matchable_fields: @matchable_fields,
|
24
|
-
).to_hash
|
15
|
+
QueryNode.new(not_filter_tokens).to_hash
|
25
16
|
else
|
26
17
|
{
|
27
18
|
"filtered" => {
|
28
|
-
"filter" => FilterNode.new(
|
29
|
-
|
30
|
-
hierarchal_fields: @hierarchal_fields,
|
31
|
-
matchable_fields: @matchable_fields,
|
32
|
-
range_fields: @range_fields,
|
33
|
-
).to_hash,
|
34
|
-
"query" => QueryNode.new(
|
35
|
-
not_filter_tokens,
|
36
|
-
matchable_fields: @matchable_fields,
|
37
|
-
).to_hash,
|
19
|
+
"filter" => FilterNode.new(filter_tokens).to_hash,
|
20
|
+
"query" => QueryNode.new(not_filter_tokens).to_hash,
|
38
21
|
}.reject do |key, value|
|
39
22
|
value.empty?
|
40
23
|
end,
|
@@ -3,29 +3,13 @@ module Qiita
|
|
3
3
|
module Nodes
|
4
4
|
class MatchNode
|
5
5
|
# @param [Qiita::Elasticsearch::Token] token
|
6
|
-
|
7
|
-
def initialize(token, matchable_fields: nil)
|
8
|
-
@matchable_fields = matchable_fields
|
6
|
+
def initialize(token)
|
9
7
|
@token = token
|
10
8
|
end
|
11
9
|
|
10
|
+
# @return [Hash]
|
12
11
|
def to_hash
|
13
|
-
|
14
|
-
{
|
15
|
-
@token.quoted? ? "match_phrase" : "match" => {
|
16
|
-
"_all" => @token.term,
|
17
|
-
}
|
18
|
-
}
|
19
|
-
else
|
20
|
-
hash = {
|
21
|
-
"multi_match" => {
|
22
|
-
"fields" => @matchable_fields,
|
23
|
-
"query" => @token.term,
|
24
|
-
},
|
25
|
-
}
|
26
|
-
hash["multi_match"]["type"] = "phrase" if @token.quoted?
|
27
|
-
hash
|
28
|
-
end
|
12
|
+
@token.to_hash
|
29
13
|
end
|
30
14
|
end
|
31
15
|
end
|
@@ -5,9 +5,7 @@ module Qiita
|
|
5
5
|
module Nodes
|
6
6
|
class MultiShouldNode
|
7
7
|
# @param [Array<Qiita::Elasticsearch::Tokens>] tokens
|
8
|
-
|
9
|
-
def initialize(tokens, matchable_fields: nil)
|
10
|
-
@matchable_fields = matchable_fields
|
8
|
+
def initialize(tokens)
|
11
9
|
@tokens = tokens
|
12
10
|
end
|
13
11
|
|
@@ -24,10 +22,7 @@ module Qiita
|
|
24
22
|
# @return [Array<Hash>] Queries to be used as a value of `should` property of bool query.
|
25
23
|
def should_queries
|
26
24
|
@tokens.map do |token|
|
27
|
-
MatchNode.new(
|
28
|
-
token,
|
29
|
-
matchable_fields: @matchable_fields,
|
30
|
-
).to_hash
|
25
|
+
MatchNode.new(token).to_hash
|
31
26
|
end
|
32
27
|
end
|
33
28
|
end
|
@@ -6,13 +6,7 @@ module Qiita
|
|
6
6
|
module Nodes
|
7
7
|
class OrSeparatableNode
|
8
8
|
# @param [Array<Qiita::Elasticsearch::Tokens>] tokens
|
9
|
-
|
10
|
-
# @param [Array<String>, nil] matchable_fields
|
11
|
-
# @param [Array<String>, nil] range_fields
|
12
|
-
def initialize(tokens, hierarchal_fields: nil, matchable_fields: nil, range_fields: nil)
|
13
|
-
@hierarchal_fields = hierarchal_fields
|
14
|
-
@matchable_fields = matchable_fields
|
15
|
-
@range_fields = range_fields
|
9
|
+
def initialize(tokens)
|
16
10
|
@tokens = tokens
|
17
11
|
end
|
18
12
|
|
@@ -21,22 +15,12 @@ module Qiita
|
|
21
15
|
when 0
|
22
16
|
Nodes::NullNode.new.to_hash
|
23
17
|
when 1
|
24
|
-
Nodes::FilterableNode.new(
|
25
|
-
tokens_grouped_by_or_token.first,
|
26
|
-
hierarchal_fields: @hierarchal_fields,
|
27
|
-
matchable_fields: @matchable_fields,
|
28
|
-
range_fields: @range_fields,
|
29
|
-
).to_hash
|
18
|
+
Nodes::FilterableNode.new(tokens_grouped_by_or_token.first).to_hash
|
30
19
|
else
|
31
20
|
{
|
32
21
|
"bool" => {
|
33
22
|
"should" => tokens_grouped_by_or_token.map do |tokens|
|
34
|
-
Nodes::FilterableNode.new(
|
35
|
-
tokens,
|
36
|
-
hierarchal_fields: @hierarchal_fields,
|
37
|
-
matchable_fields: @matchable_fields,
|
38
|
-
range_fields: @range_fields,
|
39
|
-
).to_hash
|
23
|
+
Nodes::FilterableNode.new(tokens).to_hash
|
40
24
|
end,
|
41
25
|
},
|
42
26
|
}
|
@@ -6,10 +6,7 @@ module Qiita
|
|
6
6
|
module Nodes
|
7
7
|
class QueryNode
|
8
8
|
# @param [Array<Qiita::Elasticsearch::Tokens>] tokens
|
9
|
-
|
10
|
-
def initialize(tokens, hierarchal_fields: nil, matchable_fields: nil)
|
11
|
-
@hierarchal_fields = hierarchal_fields
|
12
|
-
@matchable_fields = matchable_fields
|
9
|
+
def initialize(tokens)
|
13
10
|
@tokens = tokens
|
14
11
|
end
|
15
12
|
|
@@ -18,15 +15,9 @@ module Qiita
|
|
18
15
|
when 0
|
19
16
|
{}
|
20
17
|
when 1
|
21
|
-
MatchNode.new(
|
22
|
-
@tokens.first,
|
23
|
-
matchable_fields: @matchable_fields,
|
24
|
-
).to_hash
|
18
|
+
MatchNode.new(@tokens.first).to_hash
|
25
19
|
else
|
26
|
-
MultiShouldNode.new(
|
27
|
-
@tokens,
|
28
|
-
matchable_fields: @matchable_fields,
|
29
|
-
).to_hash
|
20
|
+
MultiShouldNode.new(@tokens).to_hash
|
30
21
|
end
|
31
22
|
end
|
32
23
|
end
|
@@ -2,71 +2,14 @@ module Qiita
|
|
2
2
|
module Elasticsearch
|
3
3
|
module Nodes
|
4
4
|
class TermNode
|
5
|
-
DEFAULT_HIERARCHAL_FIELDS = []
|
6
|
-
DEFAULT_RANGE_FIELDS = []
|
7
|
-
|
8
5
|
# @param [Qiita::Elasticsearch::Token] token
|
9
|
-
|
10
|
-
# @param [Array<String>, nil] range_fields
|
11
|
-
def initialize(token, hierarchal_fields: nil, range_fields: nil)
|
12
|
-
@hierarchal_fields = hierarchal_fields
|
13
|
-
@range_fields = range_fields
|
6
|
+
def initialize(token)
|
14
7
|
@token = token
|
15
8
|
end
|
16
9
|
|
17
10
|
# @return [Hash]
|
18
11
|
def to_hash
|
19
|
-
|
20
|
-
when has_hierarchal_token?
|
21
|
-
{
|
22
|
-
"bool" => {
|
23
|
-
"should" => [
|
24
|
-
{
|
25
|
-
"prefix" => {
|
26
|
-
@token.field_name => @token.downcased_term + "/",
|
27
|
-
},
|
28
|
-
},
|
29
|
-
{
|
30
|
-
"term" => {
|
31
|
-
@token.field_name => @token.downcased_term,
|
32
|
-
},
|
33
|
-
},
|
34
|
-
],
|
35
|
-
},
|
36
|
-
}
|
37
|
-
when has_range_token?
|
38
|
-
{
|
39
|
-
"range" => {
|
40
|
-
@token.field_name => {
|
41
|
-
@token.range_parameter => @token.range_query,
|
42
|
-
},
|
43
|
-
},
|
44
|
-
}
|
45
|
-
else
|
46
|
-
{
|
47
|
-
"term" => {
|
48
|
-
@token.field_name => @token.downcased_term,
|
49
|
-
},
|
50
|
-
}
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
def has_hierarchal_token?
|
57
|
-
hierarchal_fields.include?(@token.field_name)
|
58
|
-
end
|
59
|
-
|
60
|
-
def hierarchal_fields
|
61
|
-
@hierarchal_fields || DEFAULT_HIERARCHAL_FIELDS
|
62
|
-
end
|
63
|
-
|
64
|
-
def has_range_token?
|
65
|
-
range_fields.include?(@token.field_name) && @token.range_parameter
|
66
|
-
end
|
67
|
-
|
68
|
-
def range_fields
|
69
|
-
@range_fields || DEFAULT_RANGE_FIELDS
|
12
|
+
@token.to_hash
|
70
13
|
end
|
71
14
|
end
|
72
15
|
end
|
@@ -5,11 +5,13 @@ require "qiita/elasticsearch/tokenizer"
|
|
5
5
|
module Qiita
|
6
6
|
module Elasticsearch
|
7
7
|
class QueryBuilder
|
8
|
+
# @param [Array<String>, nil] downcased_fields
|
8
9
|
# @param [Array<String>, nil] filterable_fields
|
9
10
|
# @param [Array<String>, nil] hierarchal_fields
|
10
11
|
# @param [Array<String>, nil] matchable_fields
|
11
12
|
# @param [Array<String>, nil] range_fields
|
12
|
-
def initialize(hierarchal_fields: nil, filterable_fields: nil, matchable_fields: nil, range_fields: nil)
|
13
|
+
def initialize(downcased_fields: nil, hierarchal_fields: nil, filterable_fields: nil, matchable_fields: nil, range_fields: nil)
|
14
|
+
@downcased_fields = downcased_fields
|
13
15
|
@hierarchal_fields = hierarchal_fields
|
14
16
|
@filterable_fields = filterable_fields
|
15
17
|
@matchable_fields = matchable_fields
|
@@ -23,19 +25,20 @@ module Qiita
|
|
23
25
|
if tokens.size.zero?
|
24
26
|
Nodes::NullNode.new.to_hash
|
25
27
|
else
|
26
|
-
Nodes::OrSeparatableNode.new(
|
27
|
-
tokens,
|
28
|
-
hierarchal_fields: @hierarchal_fields,
|
29
|
-
matchable_fields: @matchable_fields,
|
30
|
-
range_fields: @range_fields,
|
31
|
-
).to_hash
|
28
|
+
Nodes::OrSeparatableNode.new(tokens).to_hash
|
32
29
|
end
|
33
30
|
end
|
34
31
|
|
35
32
|
private
|
36
33
|
|
37
34
|
def tokenizer
|
38
|
-
@tokenizer ||= Tokenizer.new(
|
35
|
+
@tokenizer ||= Tokenizer.new(
|
36
|
+
downcased_fields: @downcased_fields,
|
37
|
+
hierarchal_fields: @hierarchal_fields,
|
38
|
+
filterable_fields: @filterable_fields,
|
39
|
+
matchable_fields: @matchable_fields,
|
40
|
+
range_fields: @range_fields,
|
41
|
+
)
|
39
42
|
end
|
40
43
|
end
|
41
44
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "qiita/elasticsearch/token"
|
2
|
+
|
3
|
+
module Qiita
|
4
|
+
module Elasticsearch
|
5
|
+
class RangeToken < Token
|
6
|
+
RANGE_TERM_REGEXP = /\A(?<operand>\<|\<=|\>|\>=)(?<query>.*)\z/
|
7
|
+
|
8
|
+
# @return [Hash]
|
9
|
+
def to_hash
|
10
|
+
if range_parameter
|
11
|
+
{
|
12
|
+
"range" => {
|
13
|
+
@field_name => {
|
14
|
+
range_parameter => range_query,
|
15
|
+
},
|
16
|
+
},
|
17
|
+
}
|
18
|
+
else
|
19
|
+
{
|
20
|
+
"term" => {
|
21
|
+
@field_name => proper_cased_term,
|
22
|
+
},
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# @return [String, nil]
|
30
|
+
# @example Suppose @term is "created_at:>=2015-04-16"
|
31
|
+
# range_parameter #=> "gte"
|
32
|
+
def range_parameter
|
33
|
+
range_match[:operand] ? operand_map[range_match[:operand]] : nil
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [String, nil]
|
37
|
+
# @example Suppose @term is "created_at:>=2015-04-16"
|
38
|
+
# range_query #=> "2015-04-16"
|
39
|
+
def range_query
|
40
|
+
range_match[:query]
|
41
|
+
end
|
42
|
+
|
43
|
+
def range_match
|
44
|
+
@range_match ||= RANGE_TERM_REGEXP.match(@term) || {}
|
45
|
+
end
|
46
|
+
|
47
|
+
def operand_map
|
48
|
+
{
|
49
|
+
">" => "gt",
|
50
|
+
">=" => "gte",
|
51
|
+
"<" => "lt",
|
52
|
+
"<=" => "lte",
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
module Qiita
|
2
2
|
module Elasticsearch
|
3
3
|
class Token
|
4
|
-
RANGE_TERM_REGEXP = /\A(?<operand>\<|\<=|\>|\>=)(?<query>.*)\z/
|
5
|
-
|
6
4
|
attr_reader :field_name, :term
|
7
5
|
|
8
|
-
|
6
|
+
# @param [true, false] downcased
|
7
|
+
def initialize(downcased: nil, field_name: nil, minus: nil, quoted: nil, term: nil, token_string: nil)
|
8
|
+
@downcased = downcased
|
9
9
|
@field_name = field_name
|
10
10
|
@minus = minus
|
11
11
|
@quoted = quoted
|
@@ -13,10 +13,19 @@ module Qiita
|
|
13
13
|
@token_string = token_string
|
14
14
|
end
|
15
15
|
|
16
|
+
# @return [true, false] True if its term must be treated with downcased
|
17
|
+
def downcased?
|
18
|
+
!!@downcased
|
19
|
+
end
|
20
|
+
|
16
21
|
def downcased_term
|
17
22
|
@downcased_term ||= term.downcase
|
18
23
|
end
|
19
24
|
|
25
|
+
def to_hash
|
26
|
+
fail NotImplementedError
|
27
|
+
end
|
28
|
+
|
20
29
|
def filter?
|
21
30
|
!field_name.nil? || negative?
|
22
31
|
end
|
@@ -50,6 +59,15 @@ module Qiita
|
|
50
59
|
@minus.nil?
|
51
60
|
end
|
52
61
|
|
62
|
+
# @return [String] Downcased or not-downcased term
|
63
|
+
def proper_cased_term
|
64
|
+
if downcased?
|
65
|
+
downcased_term
|
66
|
+
else
|
67
|
+
term
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
53
71
|
# @return [true, false] True if this token is for phrase matching
|
54
72
|
# @note `Express OR "Ruby on Rails"`
|
55
73
|
# ^^^^^^^^^^^^^^^
|
@@ -57,35 +75,6 @@ module Qiita
|
|
57
75
|
def quoted?
|
58
76
|
!!@quoted
|
59
77
|
end
|
60
|
-
|
61
|
-
# @return [String, nil]
|
62
|
-
# @example Suppose @term is "created_at:>=2015-04-16"
|
63
|
-
# range_parameter #=> "gte"
|
64
|
-
def range_parameter
|
65
|
-
range_match[:operand] ? operand_map[range_match[:operand]] : nil
|
66
|
-
end
|
67
|
-
|
68
|
-
# @return [String, nil]
|
69
|
-
# @example Suppose @term is "created_at:>=2015-04-16"
|
70
|
-
# range_query #=> "2015-04-16"
|
71
|
-
def range_query
|
72
|
-
range_match[:query]
|
73
|
-
end
|
74
|
-
|
75
|
-
private
|
76
|
-
|
77
|
-
def range_match
|
78
|
-
@range_match ||= RANGE_TERM_REGEXP.match(@term) || {}
|
79
|
-
end
|
80
|
-
|
81
|
-
def operand_map
|
82
|
-
{
|
83
|
-
">" => "gt",
|
84
|
-
">=" => "gte",
|
85
|
-
"<" => "lt",
|
86
|
-
"<=" => "lte",
|
87
|
-
}
|
88
|
-
end
|
89
78
|
end
|
90
79
|
end
|
91
80
|
end
|
@@ -1,9 +1,15 @@
|
|
1
|
-
require "qiita/elasticsearch/
|
1
|
+
require "qiita/elasticsearch/filterable_token"
|
2
|
+
require "qiita/elasticsearch/hierarchal_token"
|
3
|
+
require "qiita/elasticsearch/matchable_token"
|
4
|
+
require "qiita/elasticsearch/range_token"
|
2
5
|
|
3
6
|
module Qiita
|
4
7
|
module Elasticsearch
|
5
8
|
class Tokenizer
|
9
|
+
DEFAULT_DOWNCASED_FIELDS = []
|
6
10
|
DEFAULT_FILTERABLE_FIELDS = []
|
11
|
+
DEFAULT_HIERARCHAL_FIELDS = []
|
12
|
+
DEFAULT_RANGE_FIELDS = []
|
7
13
|
|
8
14
|
TOKEN_PATTERN = /
|
9
15
|
(?<token_string>
|
@@ -17,9 +23,17 @@ module Qiita
|
|
17
23
|
)
|
18
24
|
/x
|
19
25
|
|
26
|
+
# @param [Array<String>, nil] downcased_fields
|
20
27
|
# @param [Array<String>, nil] filterable_fields
|
21
|
-
|
28
|
+
# @param [Array<String>, nil] hierarchal_fields
|
29
|
+
# @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)
|
32
|
+
@downcased_fields = downcased_fields
|
22
33
|
@filterable_fields = filterable_fields
|
34
|
+
@hierarchal_fields = hierarchal_fields
|
35
|
+
@matchable_fields = matchable_fields
|
36
|
+
@range_fields = range_fields
|
23
37
|
end
|
24
38
|
|
25
39
|
# @param [String] query_string Raw query string
|
@@ -31,21 +45,49 @@ module Qiita
|
|
31
45
|
term = "#{field_name}:#{term}"
|
32
46
|
field_name = nil
|
33
47
|
end
|
34
|
-
|
48
|
+
token = token_class(field_name).new(
|
49
|
+
downcased: downcased_fields.include?(field_name),
|
35
50
|
field_name: field_name,
|
36
51
|
minus: minus,
|
37
52
|
quoted: !quoted_term.nil?,
|
38
53
|
term: term,
|
39
54
|
token_string: token_string,
|
40
55
|
)
|
56
|
+
token.matchable_fields = @matchable_fields if token.is_a?(MatchableToken)
|
57
|
+
token
|
41
58
|
end
|
42
59
|
end
|
43
60
|
|
44
61
|
private
|
45
62
|
|
63
|
+
def downcased_fields
|
64
|
+
@downcased_fields || DEFAULT_DOWNCASED_FIELDS
|
65
|
+
end
|
66
|
+
|
46
67
|
def filterable_fields
|
47
68
|
@filterable_fields || DEFAULT_FILTERABLE_FIELDS
|
48
69
|
end
|
70
|
+
|
71
|
+
def hierarchal_fields
|
72
|
+
@hierarchal_fields || DEFAULT_HIERARCHAL_FIELDS
|
73
|
+
end
|
74
|
+
|
75
|
+
def range_fields
|
76
|
+
@range_fields || DEFAULT_RANGE_FIELDS
|
77
|
+
end
|
78
|
+
|
79
|
+
def token_class(field_name)
|
80
|
+
case
|
81
|
+
when range_fields.include?(field_name)
|
82
|
+
RangeToken
|
83
|
+
when hierarchal_fields.include?(field_name)
|
84
|
+
HierarchalToken
|
85
|
+
when filterable_fields.include?(field_name)
|
86
|
+
FilterableToken
|
87
|
+
else
|
88
|
+
MatchableToken
|
89
|
+
end
|
90
|
+
end
|
49
91
|
end
|
50
92
|
end
|
51
93
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: qiita-elasticsearch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.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-
|
11
|
+
date: 2015-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -97,6 +97,9 @@ files:
|
|
97
97
|
- README.md
|
98
98
|
- Rakefile
|
99
99
|
- lib/qiita/elasticsearch.rb
|
100
|
+
- lib/qiita/elasticsearch/filterable_token.rb
|
101
|
+
- lib/qiita/elasticsearch/hierarchal_token.rb
|
102
|
+
- lib/qiita/elasticsearch/matchable_token.rb
|
100
103
|
- lib/qiita/elasticsearch/nodes/filter_node.rb
|
101
104
|
- lib/qiita/elasticsearch/nodes/filterable_node.rb
|
102
105
|
- lib/qiita/elasticsearch/nodes/match_node.rb
|
@@ -106,6 +109,7 @@ files:
|
|
106
109
|
- lib/qiita/elasticsearch/nodes/query_node.rb
|
107
110
|
- lib/qiita/elasticsearch/nodes/term_node.rb
|
108
111
|
- lib/qiita/elasticsearch/query_builder.rb
|
112
|
+
- lib/qiita/elasticsearch/range_token.rb
|
109
113
|
- lib/qiita/elasticsearch/token.rb
|
110
114
|
- lib/qiita/elasticsearch/tokenizer.rb
|
111
115
|
- lib/qiita/elasticsearch/version.rb
|
@@ -130,7 +134,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
130
134
|
version: '0'
|
131
135
|
requirements: []
|
132
136
|
rubyforge_project:
|
133
|
-
rubygems_version: 2.
|
137
|
+
rubygems_version: 2.4.5
|
134
138
|
signing_key:
|
135
139
|
specification_version: 4
|
136
140
|
summary: Elasticsearch client helper for Qiita.
|