chewy 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/.travis.yml +3 -2
- data/CHANGELOG.md +28 -2
- data/README.md +156 -7
- data/filters +6 -4
- data/lib/chewy/index.rb +114 -18
- data/lib/chewy/index/actions.rb +117 -14
- data/lib/chewy/index/aliases.rb +21 -0
- data/lib/chewy/query.rb +5 -5
- data/lib/chewy/query/compose.rb +59 -0
- data/lib/chewy/query/criteria.rb +8 -55
- data/lib/chewy/query/{context.rb → filters.rb} +60 -7
- data/lib/chewy/query/loading.rb +1 -1
- data/lib/chewy/query/nodes/has_child.rb +14 -0
- data/lib/chewy/query/nodes/has_parent.rb +14 -0
- data/lib/chewy/query/nodes/has_relation.rb +61 -0
- data/lib/chewy/query/nodes/match_all.rb +11 -0
- data/lib/chewy/railtie.rb +2 -2
- data/lib/chewy/rspec/update_index.rb +1 -0
- data/lib/chewy/type.rb +1 -1
- data/lib/chewy/type/adapter/active_record.rb +28 -12
- data/lib/chewy/type/adapter/object.rb +12 -2
- data/lib/chewy/type/import.rb +60 -20
- data/lib/chewy/type/wrapper.rb +1 -1
- data/lib/chewy/version.rb +1 -1
- data/lib/tasks/chewy.rake +41 -8
- data/spec/chewy/index/actions_spec.rb +282 -2
- data/spec/chewy/index/aliases_spec.rb +50 -0
- data/spec/chewy/index_spec.rb +64 -0
- data/spec/chewy/query/criteria_spec.rb +11 -11
- data/spec/chewy/query/{context_spec.rb → filters_spec.rb} +2 -2
- data/spec/chewy/query/loading_spec.rb +5 -3
- data/spec/chewy/query/nodes/and_spec.rb +1 -1
- data/spec/chewy/query/nodes/bool_spec.rb +1 -1
- data/spec/chewy/query/nodes/equal_spec.rb +1 -1
- data/spec/chewy/query/nodes/exists_spec.rb +1 -1
- data/spec/chewy/query/nodes/has_child_spec.rb +40 -0
- data/spec/chewy/query/nodes/has_parent_spec.rb +40 -0
- data/spec/chewy/query/nodes/match_all_spec.rb +11 -0
- data/spec/chewy/query/nodes/missing_spec.rb +1 -1
- data/spec/chewy/query/nodes/not_spec.rb +1 -1
- data/spec/chewy/query/nodes/or_spec.rb +1 -1
- data/spec/chewy/query/nodes/prefix_spec.rb +1 -1
- data/spec/chewy/query/nodes/query_spec.rb +1 -1
- data/spec/chewy/query/nodes/range_spec.rb +1 -1
- data/spec/chewy/query/nodes/raw_spec.rb +1 -1
- data/spec/chewy/query/nodes/regexp_spec.rb +1 -1
- data/spec/chewy/query/nodes/script_spec.rb +1 -1
- data/spec/chewy/query/pagination_spec.rb +7 -7
- data/spec/chewy/query_spec.rb +60 -0
- data/spec/chewy/type/adapter/active_record_spec.rb +68 -9
- data/spec/chewy/type/adapter/object_spec.rb +18 -0
- data/spec/chewy/type/import_spec.rb +87 -2
- data/spec/spec_helper.rb +1 -0
- metadata +47 -33
data/lib/chewy/index/actions.rb
CHANGED
@@ -1,41 +1,144 @@
|
|
1
1
|
module Chewy
|
2
2
|
class Index
|
3
|
+
# Module provides per-index actions, such as deletion,
|
4
|
+
# creation and existance check.
|
5
|
+
#
|
3
6
|
module Actions
|
4
7
|
extend ActiveSupport::Concern
|
5
8
|
|
6
9
|
module ClassMethods
|
10
|
+
# Checks index existance. Returns true or false
|
11
|
+
#
|
12
|
+
# UsersIndex.exist? #=> true
|
13
|
+
#
|
7
14
|
def exists?
|
8
15
|
client.indices.exists(index: index_name)
|
9
16
|
end
|
10
17
|
|
11
|
-
|
12
|
-
|
18
|
+
# Creates index and applies mappings and settings.
|
19
|
+
# Returns false in case of unsuccessful creation.
|
20
|
+
#
|
21
|
+
# UsersIndex.create # creates index named `users`
|
22
|
+
#
|
23
|
+
# Index name suffix might be passed optionally. In this case,
|
24
|
+
# method creates index with suffix and makes unsuffixed alias
|
25
|
+
# for it.
|
26
|
+
#
|
27
|
+
# UsersIndex.create '01-2013' # creates index `uses_01-2013` and alias `users` for it
|
28
|
+
# UsersIndex.create '01-2013', alias: false # creates index `uses_01-2013` only and no alias
|
29
|
+
#
|
30
|
+
# Suffixed index names might be used for zero-downtime mapping change, for example.
|
31
|
+
# Description: (http://www.elasticsearch.org/blog/changing-mapping-with-zero-downtime/).
|
32
|
+
#
|
33
|
+
def create *args
|
34
|
+
create! *args
|
13
35
|
rescue Elasticsearch::Transport::Transport::Errors::BadRequest
|
14
36
|
false
|
15
37
|
end
|
16
38
|
|
17
|
-
|
18
|
-
|
39
|
+
# Creates index and applies mappings and settings.
|
40
|
+
# Raises elasticsearch-ruby transport error in case of
|
41
|
+
# unsuccessfull creation.
|
42
|
+
#
|
43
|
+
# UsersIndex.create! # creates index named `users`
|
44
|
+
#
|
45
|
+
# Index name suffix might be passed optionally. In this case,
|
46
|
+
# method creates index with suffix and makes unsuffixed alias
|
47
|
+
# for it.
|
48
|
+
#
|
49
|
+
# UsersIndex.create! '01-2014' # creates index `users_01-2014` and alias `users` for it
|
50
|
+
# UsersIndex.create! '01-2014', alias: false # creates index `users_01-2014` only and no alias
|
51
|
+
#
|
52
|
+
# Suffixed index names might be used for zero-downtime mapping change, for example.
|
53
|
+
# Description: (http://www.elasticsearch.org/blog/changing-mapping-with-zero-downtime/).
|
54
|
+
#
|
55
|
+
def create! *args
|
56
|
+
options = args.extract_options!.reverse_merge!(alias: true)
|
57
|
+
name = build_index_name(suffix: args.first)
|
58
|
+
result = client.indices.create(index: name, body: index_params)
|
59
|
+
result &&= client.indices.put_alias(index: name, name: index_name) if options[:alias] && name != index_name
|
60
|
+
result
|
19
61
|
end
|
20
62
|
|
21
|
-
|
22
|
-
|
63
|
+
# Deletes ES index. Returns false in case of error.
|
64
|
+
#
|
65
|
+
# UsersIndex.delete # deletes `users` index
|
66
|
+
#
|
67
|
+
# Supports index suffix passed as the first argument
|
68
|
+
#
|
69
|
+
# UsersIndex.delete '01-2014' # deletes `users_01-2014` index
|
70
|
+
#
|
71
|
+
def delete suffix = nil
|
72
|
+
delete! suffix
|
23
73
|
rescue Elasticsearch::Transport::Transport::Errors::NotFound
|
24
74
|
false
|
25
75
|
end
|
26
76
|
|
27
|
-
|
28
|
-
|
77
|
+
# Deletes ES index. Raises elasticsearch-ruby transport error
|
78
|
+
# in case of error.
|
79
|
+
#
|
80
|
+
# UsersIndex.delete # deletes `users` index
|
81
|
+
#
|
82
|
+
# Supports index suffix passed as the first argument
|
83
|
+
#
|
84
|
+
# UsersIndex.delete '01-2014' # deletes `users_01-2014` index
|
85
|
+
#
|
86
|
+
def delete! suffix = nil
|
87
|
+
client.indices.delete index: build_index_name(suffix: suffix)
|
29
88
|
end
|
30
89
|
|
31
|
-
|
32
|
-
|
33
|
-
|
90
|
+
# Deletes and recreates index. Supports suffixes.
|
91
|
+
# Returns result of index creation.
|
92
|
+
#
|
93
|
+
# UsersIndex.purge # deletes and creates `users` index
|
94
|
+
# UsersIndex.purge '01-2014' # deletes `users` and `users_01-2014` indexes, creates `users_01-2014`
|
95
|
+
#
|
96
|
+
def purge suffix = nil
|
97
|
+
delete if suffix.present?
|
98
|
+
delete suffix
|
99
|
+
create suffix
|
34
100
|
end
|
35
101
|
|
36
|
-
|
37
|
-
|
38
|
-
|
102
|
+
# Deletes and recreates index. Supports suffixes.
|
103
|
+
# Returns result of index creation. Raises error in case
|
104
|
+
# of unsuccessfull creation
|
105
|
+
#
|
106
|
+
# UsersIndex.purge! # deletes and creates `users` index
|
107
|
+
# UsersIndex.purge! '01-2014' # deletes `users` and `users_01-2014` indexes, creates `users_01-2014`
|
108
|
+
#
|
109
|
+
def purge! suffix = nil
|
110
|
+
delete if suffix.present?
|
111
|
+
delete suffix
|
112
|
+
create! suffix
|
113
|
+
end
|
114
|
+
|
115
|
+
# Deletes, creates and imports data to the index.
|
116
|
+
# Returns import result
|
117
|
+
#
|
118
|
+
# UsersIndex.reset!
|
119
|
+
#
|
120
|
+
# If index name suffix passed as the first argument - performs
|
121
|
+
# zero-downtime index resetting (described here:
|
122
|
+
# http://www.elasticsearch.org/blog/changing-mapping-with-zero-downtime/).
|
123
|
+
#
|
124
|
+
# UsersIndex.reset! Time.now.to_i
|
125
|
+
#
|
126
|
+
def reset! suffix = nil
|
127
|
+
if suffix.present? && (indexes = self.indexes).any?
|
128
|
+
create! suffix, alias: false
|
129
|
+
result = import suffix: suffix
|
130
|
+
client.indices.update_aliases body: {actions: [
|
131
|
+
*indexes.map do |index|
|
132
|
+
{remove: {index: index, alias: index_name}}
|
133
|
+
end,
|
134
|
+
{add: {index: build_index_name(suffix: suffix), alias: index_name}}
|
135
|
+
]}
|
136
|
+
client.indices.delete index: indexes if indexes.any?
|
137
|
+
result
|
138
|
+
else
|
139
|
+
purge! suffix
|
140
|
+
import
|
141
|
+
end
|
39
142
|
end
|
40
143
|
end
|
41
144
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Chewy
|
2
|
+
class Index
|
3
|
+
module Aliases
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def indexes
|
8
|
+
client.indices.get_alias(name: index_name).keys
|
9
|
+
rescue Elasticsearch::Transport::Transport::Errors::NotFound
|
10
|
+
[]
|
11
|
+
end
|
12
|
+
|
13
|
+
def aliases
|
14
|
+
client.indices.get_alias(index: index_name, name: '*')[index_name].try(:[], 'aliases').try(:keys) || []
|
15
|
+
rescue Elasticsearch::Transport::Transport::Errors::NotFound
|
16
|
+
[]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/chewy/query.rb
CHANGED
@@ -4,7 +4,7 @@ rescue LoadError
|
|
4
4
|
end
|
5
5
|
|
6
6
|
require 'chewy/query/criteria'
|
7
|
-
require 'chewy/query/
|
7
|
+
require 'chewy/query/filters'
|
8
8
|
require 'chewy/query/loading'
|
9
9
|
require 'chewy/query/pagination'
|
10
10
|
|
@@ -281,7 +281,7 @@ module Chewy
|
|
281
281
|
# See <tt>#filter_mode</tt> chainable method for more info.
|
282
282
|
#
|
283
283
|
# Also this method supports block DSL.
|
284
|
-
# See <tt>Chewy::Query::
|
284
|
+
# See <tt>Chewy::Query::Filters</tt> for more info.
|
285
285
|
#
|
286
286
|
# UsersIndex.filter(term: {name: 'Johny'}).filter(range: {age: {lte: 42}})
|
287
287
|
# UsersIndex::User.filter(term: {name: 'Johny'}).filter(range: {age: {lte: 42}})
|
@@ -301,7 +301,7 @@ module Chewy
|
|
301
301
|
# }}}}
|
302
302
|
#
|
303
303
|
def filter params = nil, &block
|
304
|
-
params =
|
304
|
+
params = Filters.new(&block).__render__ if block
|
305
305
|
chain { criteria.update_filters params }
|
306
306
|
end
|
307
307
|
|
@@ -450,8 +450,8 @@ module Chewy
|
|
450
450
|
def _results
|
451
451
|
@_results ||= _response['hits']['hits'].map do |hit|
|
452
452
|
attributes = hit['_source'] || hit['fields'] || {}
|
453
|
-
attributes.reverse_merge!(id: hit['_id'])
|
454
|
-
|
453
|
+
attributes.reverse_merge!(id: hit['_id'])
|
454
|
+
.merge!(_score: hit['_score'], _explanation: hit['_explanation'])
|
455
455
|
index.type_hash[hit['_type']].new attributes
|
456
456
|
end
|
457
457
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Chewy
|
2
|
+
class Query
|
3
|
+
module Compose
|
4
|
+
|
5
|
+
protected
|
6
|
+
|
7
|
+
def _composed_query queries, filters
|
8
|
+
if filters
|
9
|
+
{query: {
|
10
|
+
filtered: {
|
11
|
+
query: queries ? queries : {match_all: {}},
|
12
|
+
filter: filters
|
13
|
+
}
|
14
|
+
}}
|
15
|
+
elsif queries
|
16
|
+
{query: queries}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def _queries_join queries, logic
|
21
|
+
queries = queries.compact
|
22
|
+
|
23
|
+
if queries.many?
|
24
|
+
case logic
|
25
|
+
when :dis_max
|
26
|
+
{dis_max: {queries: queries}}
|
27
|
+
when :must, :should
|
28
|
+
{bool: {logic => queries}}
|
29
|
+
else
|
30
|
+
if logic.is_a?(Float)
|
31
|
+
{dis_max: {queries: queries, tie_breaker: logic}}
|
32
|
+
else
|
33
|
+
{bool: {should: queries, minimum_should_match: logic}}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
else
|
37
|
+
queries.first
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def _filters_join filters, logic
|
42
|
+
filters = filters.compact
|
43
|
+
|
44
|
+
if filters.many?
|
45
|
+
case logic
|
46
|
+
when :and, :or
|
47
|
+
{logic => filters}
|
48
|
+
when :must, :should
|
49
|
+
{bool: {logic => filters}}
|
50
|
+
else
|
51
|
+
{bool: {should: filters, minimum_should_match: logic}}
|
52
|
+
end
|
53
|
+
else
|
54
|
+
filters.first
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/chewy/query/criteria.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
require 'chewy/query/compose'
|
2
|
+
|
1
3
|
module Chewy
|
2
4
|
class Query
|
3
5
|
class Criteria
|
6
|
+
include Compose
|
4
7
|
STORAGES = [:options, :queries, :facets, :filters, :sort, :fields, :types]
|
5
8
|
|
6
9
|
def initialize options = {}
|
@@ -36,11 +39,11 @@ module Chewy
|
|
36
39
|
end
|
37
40
|
|
38
41
|
def update_queries(modifer)
|
39
|
-
@queries = queries + Array.wrap(modifer).
|
42
|
+
@queries = queries + Array.wrap(modifer).reject(&:blank?)
|
40
43
|
end
|
41
44
|
|
42
45
|
def update_filters(modifer)
|
43
|
-
@filters = filters + Array.wrap(modifer).
|
46
|
+
@filters = filters + Array.wrap(modifer).reject(&:blank?)
|
44
47
|
end
|
45
48
|
|
46
49
|
def update_sort(modifer, options = {})
|
@@ -55,7 +58,7 @@ module Chewy
|
|
55
58
|
define_method "update_#{storage}" do |modifer, options = {}|
|
56
59
|
variable = "@#{storage}"
|
57
60
|
instance_variable_set(variable, nil) if options[:purge]
|
58
|
-
modifer = send(storage) | Array.wrap(modifer).flatten.map(&:to_s).
|
61
|
+
modifer = send(storage) | Array.wrap(modifer).flatten.map(&:to_s).reject(&:blank?)
|
59
62
|
instance_variable_set(variable, modifer)
|
60
63
|
end
|
61
64
|
end
|
@@ -72,7 +75,7 @@ module Chewy
|
|
72
75
|
end
|
73
76
|
|
74
77
|
def request_body
|
75
|
-
body = (_request_query || {}).tap do |body|
|
78
|
+
body = (_composed_query(_request_query, _request_filter) || {}).tap do |body|
|
76
79
|
body.merge!(facets: facets) if facets?
|
77
80
|
body.merge!(sort: sort) if sort?
|
78
81
|
body.merge!(fields: fields) if fields?
|
@@ -102,19 +105,7 @@ module Chewy
|
|
102
105
|
end
|
103
106
|
|
104
107
|
def _request_query
|
105
|
-
|
106
|
-
request_query = _queries_join(queries, options[:query_mode])
|
107
|
-
|
108
|
-
if request_filter
|
109
|
-
{query: {
|
110
|
-
filtered: {
|
111
|
-
query: request_query ? request_query : {match_all: {}},
|
112
|
-
filter: request_filter
|
113
|
-
}
|
114
|
-
}}
|
115
|
-
elsif request_query
|
116
|
-
{query: request_query}
|
117
|
-
end
|
108
|
+
_queries_join(queries, options[:query_mode])
|
118
109
|
end
|
119
110
|
|
120
111
|
def _request_filter
|
@@ -131,44 +122,6 @@ module Chewy
|
|
131
122
|
def _request_types
|
132
123
|
_filters_join(types.map { |type| {type: {value: type}} }, :or)
|
133
124
|
end
|
134
|
-
|
135
|
-
def _queries_join queries, logic
|
136
|
-
queries = queries.compact
|
137
|
-
|
138
|
-
if queries.many?
|
139
|
-
case logic
|
140
|
-
when :dis_max
|
141
|
-
{dis_max: {queries: queries}}
|
142
|
-
when :must, :should
|
143
|
-
{bool: {logic => queries}}
|
144
|
-
else
|
145
|
-
if logic.is_a?(Float)
|
146
|
-
{dis_max: {queries: queries, tie_breaker: logic}}
|
147
|
-
else
|
148
|
-
{bool: {should: queries, minimum_should_match: logic}}
|
149
|
-
end
|
150
|
-
end
|
151
|
-
else
|
152
|
-
queries.first
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
def _filters_join filters, logic
|
157
|
-
filters = filters.compact
|
158
|
-
|
159
|
-
if filters.many?
|
160
|
-
case logic
|
161
|
-
when :and, :or
|
162
|
-
{logic => filters}
|
163
|
-
when :must, :should
|
164
|
-
{bool: {logic => filters}}
|
165
|
-
else
|
166
|
-
{bool: {should: filters, minimum_should_match: logic}}
|
167
|
-
end
|
168
|
-
else
|
169
|
-
filters.first
|
170
|
-
end
|
171
|
-
end
|
172
125
|
end
|
173
126
|
end
|
174
127
|
end
|
@@ -14,6 +14,9 @@ require 'chewy/query/nodes/regexp'
|
|
14
14
|
require 'chewy/query/nodes/equal'
|
15
15
|
require 'chewy/query/nodes/query'
|
16
16
|
require 'chewy/query/nodes/script'
|
17
|
+
require 'chewy/query/nodes/has_child'
|
18
|
+
require 'chewy/query/nodes/has_parent'
|
19
|
+
require 'chewy/query/nodes/match_all'
|
17
20
|
|
18
21
|
module Chewy
|
19
22
|
class Query
|
@@ -24,10 +27,10 @@ module Chewy
|
|
24
27
|
# UsersIndex.filter{ (article.title =~ /Honey/) & (age < 42) & !rate }
|
25
28
|
#
|
26
29
|
#
|
27
|
-
class
|
28
|
-
def initialize &block
|
30
|
+
class Filters
|
31
|
+
def initialize outer = nil, &block
|
29
32
|
@block = block
|
30
|
-
@outer = eval('self', block.binding)
|
33
|
+
@outer = outer || eval('self', block.binding)
|
31
34
|
end
|
32
35
|
|
33
36
|
# Outer scope call
|
@@ -123,10 +126,10 @@ module Chewy
|
|
123
126
|
# Bool filter chainable methods
|
124
127
|
# Used to create bool query. Nodes are passed as arguments.
|
125
128
|
#
|
126
|
-
#
|
127
|
-
#
|
128
|
-
#
|
129
|
-
#
|
129
|
+
# UsersIndex.filter{ must(age < 42, name == 'Name') }
|
130
|
+
# UsersIndex.filter{ should(age < 42, name == 'Name') }
|
131
|
+
# UsersIndex.filter{ must(age < 42).should(name == 'Name1', name == 'Name2') }
|
132
|
+
# UsersIndex.filter{ should_not(age >= 42).must(name == 'Name1') }
|
130
133
|
#
|
131
134
|
%w(must must_not should).each do |method|
|
132
135
|
define_method method do |*exprs|
|
@@ -134,6 +137,56 @@ module Chewy
|
|
134
137
|
end
|
135
138
|
end
|
136
139
|
|
140
|
+
# Initializes has_child filter.
|
141
|
+
# Chainable interface acts the same as main query interface. You can pass plain
|
142
|
+
# filters or plain queries or filter with DSL block.
|
143
|
+
#
|
144
|
+
# UsersIndex.filter{ has_child('user').filter(term: {role: 'Admin'}) }
|
145
|
+
# UsersIndex.filter{ has_child('user').filter{ role == 'Admin' } }
|
146
|
+
# UsersIndex.filter{ has_child('user').query(match: {name: 'borogoves'}) }
|
147
|
+
#
|
148
|
+
# Filters and queries might be combined and filter_mode and query_mode are configurable:
|
149
|
+
#
|
150
|
+
# UsersIndex.filter do
|
151
|
+
# has_child('user')
|
152
|
+
# .filter{ name: 'Peter' }
|
153
|
+
# .query(match: {name: 'Peter'})
|
154
|
+
# .filter{ age > 42 }
|
155
|
+
# .filter_mode(:or)
|
156
|
+
# end
|
157
|
+
#
|
158
|
+
def has_child type
|
159
|
+
Nodes::HasChild.new(type, @outer)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Initializes has_parent filter.
|
163
|
+
# Chainable interface acts the same as main query interface. You can pass plain
|
164
|
+
# filters or plain queries or filter with DSL block.
|
165
|
+
#
|
166
|
+
# UsersIndex.filter{ has_parent('user').filter(term: {role: 'Admin'}) }
|
167
|
+
# UsersIndex.filter{ has_parent('user').filter{ role == 'Admin' } }
|
168
|
+
# UsersIndex.filter{ has_parent('user').query(match: {name: 'borogoves'}) }
|
169
|
+
#
|
170
|
+
# Filters and queries might be combined and filter_mode and query_mode are configurable:
|
171
|
+
#
|
172
|
+
# UsersIndex.filter do
|
173
|
+
# has_parent('user')
|
174
|
+
# .filter{ name: 'Peter' }
|
175
|
+
# .query(match: {name: 'Peter'})
|
176
|
+
# .filter{ age > 42 }
|
177
|
+
# .filter_mode(:or)
|
178
|
+
# end
|
179
|
+
#
|
180
|
+
def has_parent type
|
181
|
+
Nodes::HasParent.new(type, @outer)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Just simple match_all filter.
|
185
|
+
#
|
186
|
+
def match_all
|
187
|
+
Nodes::MatchAll.new
|
188
|
+
end
|
189
|
+
|
137
190
|
# Creates field or exists node
|
138
191
|
# Additional options for further expression might be passed as hash
|
139
192
|
#
|