Skip to content

Commit 6ba6abd

Browse files
Merge pull request #3 from andreibondarev/fix-query-methods
Fix `query#get()` method & add `query#aggs()` method
2 parents a09751f + cc69e13 commit 6ba6abd

File tree

6 files changed

+180
-92
lines changed

6 files changed

+180
-92
lines changed

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
weaviate-ruby (0.2.0)
4+
weaviate-ruby (0.3.0)
55
faraday (~> 2.7)
66
graphlient (~> 0.7.0)
77

README.md

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -137,17 +137,19 @@ response.data
137137
```
138138

139139
### Querying
140+
141+
#### Get
140142
```ruby
141-
near_text = { "concepts": ["biology"] }
142-
sort_obj = { path: ["category"] } # Not supporting the `order:` param currently
143-
where_obj = { path: ["id"], operator: :equal, valueString: "..." }
143+
near_text = '{ concepts: ["biology"] }'
144+
sort_obj = '{ path: ["category"], order: desc }'
145+
where_obj = '{ path: ["id"], operator: Equal, valueString: "..." }'
144146

145147
client.query.get(
146148
class_name: 'Question',
147-
fields: ['question', 'answer', 'category'],
148-
limit: 1,
149-
offset: 1,
150-
after: 'id',
149+
fields: 'question answer category',
150+
limit: "1",
151+
offset: "1",
152+
after: "id",
151153
sort: sort_obj,
152154
where_obj: where_obj,
153155

@@ -164,6 +166,25 @@ client.query.get(
164166

165167
# near_object: ...,
166168
)
169+
170+
# Example queries:
171+
client.query.get class_name: 'Question', where: '{ operator: Like, valueText: "SCIENCE", path: ["category"] }', fields: 'answer question category', limit: "2"
172+
173+
client.query.get class_name: 'Question', fields: 'answer question category _additional { id }', after: "3c5f7039-37f3-4244-b3e2-8f4a083e448d", limit: "1"
174+
175+
176+
177+
```
178+
179+
#### Aggs
180+
```ruby
181+
client.query.aggs(
182+
class_name: "Question",
183+
fields: 'meta { count }'
184+
group_by: ["category"],
185+
object_limit: "10",
186+
near_text: "{ concepts: [\"knowledge\"] }"
187+
)
167188
```
168189

169190
## Development

lib/weaviate/error.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# frozen_string_literal: true
22

33
module Weaviate
4-
class Error
4+
class Error < StandardError
55
end
66
end

lib/weaviate/query.rb

Lines changed: 103 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -2,85 +2,122 @@
22

33
module Weaviate
44
class Query < Base
5-
# Meta-programming hack to dynamically create appropriate Weaviate GraphQL query. Expected query:
6-
#
7-
# Article(where: {
8-
# ...
9-
# operator: GreaterThan, # operator
10-
#
11-
# Since `operator:` parameter expects a literal value, the only way to pass it in Ruby is by defining a class and returning it
12-
# inside of a lambda function when it's called. This is why we have the following code:
13-
# standard:disable all
14-
WHERE_OPERANDS = {
15-
and: -> { class ::And; end; And },
16-
or: -> { class ::Or; end; Or },
17-
equal: -> { class ::Equal; end; Equal },
18-
not_equal: -> { class ::NotEqual; end; NotEqual },
19-
greater_than: -> { class ::GreaterThan; end; GreaterThan },
20-
greater_than_equal: -> { class ::GreaterThanEqual; end; GreaterThanEqual },
21-
less_than: -> { class ::LessThan; end; LessThan },
22-
less_than_equal: -> { class ::LessThanEqual; end; LessThanEqual },
23-
like: -> { class ::Like; end; Like },
24-
within_geo_range: -> { class ::WithinGeoRange; end; WithinGeoRange },
25-
is_null: -> { class ::IsNull; end; IsNull }
26-
}.freeze
27-
# standard:enable all
28-
295
def get(
306
class_name:,
317
fields:,
8+
after: nil,
329
limit: nil,
3310
offset: nil,
34-
after: nil,
3511
sort: nil,
3612
where: nil,
37-
near_text: nil
13+
near_text: nil,
14+
near_vector: nil,
15+
near_object: nil
3816
)
39-
params = {}
40-
params["nearText"] = near_text unless near_text.nil?
41-
params["limit"] = limit unless limit.nil?
42-
params["offset"] = offset unless offset.nil?
43-
params["after"] = after unless after.nil?
44-
45-
if sort.present?
46-
# TODO: Implement the `order:` param
47-
# Unable to currently support the `order:` param. Weaviate GraphQL API expects a literal value, but we can't pass it in Ruby
48-
# Article(sort: [{
49-
# order: asc # <--- this is the problem.
50-
# https://weaviate.io/developers/weaviate/api/graphql/filters#sorting-api
51-
#
52-
sort.delete("order")
53-
params["sort"] = sort
54-
end
55-
56-
if where.present?
57-
params["where"] = where
58-
if where[:operator].present?
59-
params["where"][:operator] = WHERE_OPERANDS[where[:operator].to_sym].call
60-
end
61-
# TODO: Transform the multiple operands: [{}] case
62-
end
63-
64-
# TODO implement the rest of the API params
65-
66-
response = client.graphql.execute(get_query(class_name, params, fields), near_text: near_text)
17+
response = client.graphql.execute(
18+
get_query(
19+
class_name: class_name,
20+
fields: fields,
21+
sort: sort,
22+
where: where,
23+
near_text: near_text,
24+
near_vector: near_vector,
25+
near_object: near_object
26+
),
27+
after: after,
28+
limit: limit,
29+
offset: offset
30+
)
6731
response.data.get.send(class_name.downcase)
32+
rescue Graphlient::Errors::ExecutionError => error
33+
raise Weaviate::Error.new(error.response.data.get.errors.messages.to_h)
34+
end
35+
36+
def aggs(
37+
class_name:,
38+
fields: nil,
39+
object_limit: nil,
40+
near_text: nil,
41+
near_vector: nil,
42+
near_object: nil,
43+
group_by: nil
44+
)
45+
response = client.graphql.execute(
46+
aggs_query(
47+
class_name: class_name,
48+
fields: fields,
49+
near_text: near_text,
50+
near_vector: near_vector,
51+
near_object: near_object
52+
),
53+
group_by: group_by,
54+
object_limit: object_limit
55+
)
56+
response.data.aggregate.send(class_name.downcase)
57+
rescue Graphlient::Errors::ExecutionError => error
58+
raise Weaviate::Error.new(error.response.data.aggregate.errors.messages.to_h)
6859
end
6960

7061
private
7162

72-
def get_query(class_name, params, fields)
73-
client.graphql.parse do
74-
query do
75-
Get do
76-
public_send(class_name, params) do
77-
fields.map do |field|
78-
public_send(field)
79-
end
80-
end
81-
end
82-
end
83-
end
63+
def get_query(
64+
class_name:,
65+
fields:,
66+
where: nil,
67+
near_text: nil,
68+
near_vector: nil,
69+
near_object: nil,
70+
sort: nil
71+
)
72+
client.graphql.parse <<~GRAPHQL
73+
query(
74+
$after: String,
75+
$limit: Int,
76+
$offset: Int,
77+
) {
78+
Get {
79+
#{class_name}(
80+
after: $after,
81+
limit: $limit,
82+
offset: $offset,
83+
#{near_text.present? ? "nearText: #{near_text}" : ""},
84+
#{near_vector.present? ? "nearVector: #{near_vector}" : ""},
85+
#{near_object.present? ? "nearObject: #{near_object}" : ""},
86+
#{where.present? ? "where: #{where}" : ""},
87+
#{sort.present? ? "sort: #{sort}" : ""}
88+
) {
89+
#{fields}
90+
}
91+
}
92+
}
93+
GRAPHQL
94+
end
95+
96+
def aggs_query(
97+
class_name:,
98+
fields:,
99+
near_text: nil,
100+
near_vector: nil,
101+
near_object: nil
102+
)
103+
client.graphql.parse <<~GRAPHQL
104+
query(
105+
$group_by: [String],
106+
$object_limit: Int,
107+
) {
108+
Aggregate {
109+
#{class_name}(
110+
objectLimit: $object_limit,
111+
groupBy: $group_by,
112+
#{near_text.present? ? "nearText: #{near_text}" : ""},
113+
#{near_vector.present? ? "nearVector: #{near_vector}" : ""},
114+
#{near_object.present? ? "nearObject: #{near_object}" : ""}
115+
) {
116+
#{fields}
117+
}
118+
}
119+
}
120+
GRAPHQL
84121
end
85122
end
86123
end

lib/weaviate/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# frozen_string_literal: true
22

33
module Weaviate
4-
VERSION = "0.2.0"
4+
VERSION = "0.3.0"
55
end

spec/weaviate/query_spec.rb

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@
4949
it "returns the query" do
5050
data = query.get(
5151
class_name: "Question",
52-
fields: ["question", "category"],
53-
near_text: {concepts: ["biology"]},
54-
limit: 1
52+
fields: "question, category",
53+
near_text: "{ concepts: [\"biology\"] }",
54+
limit: "1"
5555
)
5656

5757
expect(data.count).to eq(1)
@@ -60,19 +60,49 @@
6060
end
6161
end
6262

63-
describe "WHERE_OPERANDS" do
64-
it "returns the correct class name" do
65-
expect(Weaviate::Query::WHERE_OPERANDS[:and].call).to eq(And)
66-
expect(Weaviate::Query::WHERE_OPERANDS[:or].call).to eq(Or)
67-
expect(Weaviate::Query::WHERE_OPERANDS[:equal].call).to eq(Equal)
68-
expect(Weaviate::Query::WHERE_OPERANDS[:not_equal].call).to eq(NotEqual)
69-
expect(Weaviate::Query::WHERE_OPERANDS[:greater_than].call).to eq(GreaterThan)
70-
expect(Weaviate::Query::WHERE_OPERANDS[:greater_than_equal].call).to eq(GreaterThanEqual)
71-
expect(Weaviate::Query::WHERE_OPERANDS[:less_than].call).to eq(LessThan)
72-
expect(Weaviate::Query::WHERE_OPERANDS[:less_than_equal].call).to eq(LessThanEqual)
73-
expect(Weaviate::Query::WHERE_OPERANDS[:like].call).to eq(Like)
74-
expect(Weaviate::Query::WHERE_OPERANDS[:within_geo_range].call).to eq(WithinGeoRange)
75-
expect(Weaviate::Query::WHERE_OPERANDS[:is_null].call).to eq(IsNull)
63+
describe "#aggs" do
64+
let(:response) {
65+
double(
66+
data: double(
67+
aggregate: double(
68+
question: [double(category: "SCIENCE", question: "In 1953 Watson & Crick built a model of the molecular structure of this, the gene-carrying substance")]
69+
)
70+
)
71+
)
72+
}
73+
74+
let(:graphql_query) {
75+
<<-GRAPHQL
76+
query {
77+
Aggregate {
78+
Question(nearText: { concepts: ["biology"] }) {
79+
meta {
80+
count
81+
}
82+
}
83+
}
84+
}
85+
GRAPHQL
86+
}
87+
88+
before do
89+
allow_any_instance_of(Graphlient::Client).to receive(:parse)
90+
.and_return(graphql_query)
91+
92+
allow_any_instance_of(Graphlient::Client).to receive(:execute)
93+
.and_return(response)
94+
end
95+
96+
it "returns the query" do
97+
data = query.aggs(
98+
class_name: "Question",
99+
fields: "question, category",
100+
near_text: "{ concepts: [\"biology\"] }"
101+
)
102+
103+
expect(data.count).to eq(1)
104+
expect(data.first.category).to eq("SCIENCE")
105+
expect(data.first.question).to eq("In 1953 Watson & Crick built a model of the molecular structure of this, the gene-carrying substance")
76106
end
77107
end
78108
end

0 commit comments

Comments
 (0)