weaviate_record 0.0.3
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 +7 -0
- data/lib/weaviate_record/base.rb +241 -0
- data/lib/weaviate_record/concerns/attribute_concern.rb +76 -0
- data/lib/weaviate_record/concerns/record_concern.rb +80 -0
- data/lib/weaviate_record/connection.rb +53 -0
- data/lib/weaviate_record/constants.rb +25 -0
- data/lib/weaviate_record/errors.rb +69 -0
- data/lib/weaviate_record/inspect.rb +36 -0
- data/lib/weaviate_record/method_missing.rb +24 -0
- data/lib/weaviate_record/queries/ask.rb +30 -0
- data/lib/weaviate_record/queries/bm25.rb +29 -0
- data/lib/weaviate_record/queries/count.rb +21 -0
- data/lib/weaviate_record/queries/limit.rb +21 -0
- data/lib/weaviate_record/queries/near_object.rb +35 -0
- data/lib/weaviate_record/queries/near_text.rb +36 -0
- data/lib/weaviate_record/queries/near_vector.rb +32 -0
- data/lib/weaviate_record/queries/offset.rb +22 -0
- data/lib/weaviate_record/queries/order.rb +74 -0
- data/lib/weaviate_record/queries/select.rb +85 -0
- data/lib/weaviate_record/queries/where.rb +126 -0
- data/lib/weaviate_record/relation/query_builder.rb +59 -0
- data/lib/weaviate_record/relation.rb +86 -0
- data/lib/weaviate_record/schema.rb +101 -0
- data/lib/weaviate_record.rb +38 -0
- metadata +111 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: af84dff0fba26b232fd8b537749ff5742f5bcc0197656cc647e8b6791a55bd85
|
4
|
+
data.tar.gz: 9534adbc21943b257d0e8f0a517721dd17bb93a1d1e3c14f284e11f30499f1f9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e7d0fda549172607928d6e943426f5091d481d4cbcd4c44bb767a32ef667769091238125f79926c1070358dc1095a191e52af70d20ea915b17acbf4eeac51beb
|
7
|
+
data.tar.gz: dde3def3b42f332182c8d7f8cc115a1516f177be5abedbd516713e00474f9c1b7b9935da51ef134d9010b773df8acb95d2f867a4be043765edef53308e97b201
|
@@ -0,0 +1,241 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module WeaviateRecord
|
6
|
+
# Base class for the models to inherit from and to interact with the respective collection in weaviate.
|
7
|
+
class Base
|
8
|
+
extend ActiveModel::Naming
|
9
|
+
include ActiveModel::Conversion
|
10
|
+
include ActiveModel::Validations
|
11
|
+
include Inspect
|
12
|
+
include MethodMissing
|
13
|
+
include Concerns::RecordConcern
|
14
|
+
|
15
|
+
class << self
|
16
|
+
extend Forwardable
|
17
|
+
def_delegators(:relation, :all, :bm25, :limit, :near_text, :near_vector, :near_object,
|
18
|
+
:offset, :order, :select, :where, :ask, :destroy_all, :first, :last)
|
19
|
+
|
20
|
+
# Creates a new weaviate record and saves it and returns the record.
|
21
|
+
# Takes a key value pair of attributes that the record should be initialized with.
|
22
|
+
# If the record is not saved, it returns the unsaved record object with errors.
|
23
|
+
#
|
24
|
+
# ==== Example:
|
25
|
+
# Article.create(title: 'Hello World', content: 'This is the content of the article')
|
26
|
+
#
|
27
|
+
# # => #<Article:0x0000000105468ab0 id: "8280210b-9372-4e70-a045-beb7c12a9a24" ...>
|
28
|
+
def create(**attributes_hash)
|
29
|
+
record = new(**attributes_hash)
|
30
|
+
record.save
|
31
|
+
record
|
32
|
+
end
|
33
|
+
|
34
|
+
# Takes an _uuid_ and returns the record with that _uuid_.
|
35
|
+
# If the record is not found, it raises a +RecordNotFoundError+.
|
36
|
+
#
|
37
|
+
# ==== Example:
|
38
|
+
# Article.find('f3b1b3b1-0b3b-4b3b-8b3b-0b3b3b3b3b3b')
|
39
|
+
#
|
40
|
+
# #<Article:0x0000000105468ab0 id: "8280210b-9372-4e70-a045-beb7c12a9a24" ...>
|
41
|
+
def find(id)
|
42
|
+
result = connection.find_call(id)
|
43
|
+
if result.is_a?(Hash) && result['id']
|
44
|
+
new(_additional: meta_attributes(result), **result['properties'])
|
45
|
+
elsif result == ''
|
46
|
+
raise WeaviateRecord::Errors::RecordNotFoundError, "Couldn't find record with id=#{id}"
|
47
|
+
else
|
48
|
+
raise WeaviateRecord::Errors::ServerError, result['message']
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the count of all records in the collection.
|
53
|
+
#
|
54
|
+
# ==== Example:
|
55
|
+
# class Article < WeaviateRecord::Base
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# Article.count # => 0
|
59
|
+
#
|
60
|
+
# Article.create(title: 'Hello World', content: 'This is the content of the article')
|
61
|
+
#
|
62
|
+
# Article.count # => 1
|
63
|
+
def count
|
64
|
+
result = connection.client.query.aggs(class_name: to_s, fields: 'meta { count }')
|
65
|
+
result.dig(0, 'meta', 'count')
|
66
|
+
rescue StandardError
|
67
|
+
raise WeaviateRecord::Errors::ServerError, "unable to get the count for #{self} collection."
|
68
|
+
end
|
69
|
+
|
70
|
+
# Takes an _uuid_ and checks whether a record with that id exists or not.
|
71
|
+
#
|
72
|
+
# ==== Example:
|
73
|
+
# Article.exists?('f3b1b3b1-0b3b-4b3b-8b3b-0b3b3b3b3b3b') # => true
|
74
|
+
# Article.exists?('random_uuid') # => false
|
75
|
+
def exists?(id)
|
76
|
+
connection.check_existence(id)
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def connection
|
82
|
+
@connection ||= Connection.new(self)
|
83
|
+
end
|
84
|
+
|
85
|
+
def relation
|
86
|
+
WeaviateRecord::Relation.new(self)
|
87
|
+
end
|
88
|
+
|
89
|
+
def inherited(klass)
|
90
|
+
WeaviateRecord::Schema.find_collection(klass)
|
91
|
+
super
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Creates a new record with the given attributes.
|
96
|
+
# The attributes can be passed as a hash or as key value pairs.
|
97
|
+
# It does not save the record in the weaviate database.
|
98
|
+
#
|
99
|
+
#
|
100
|
+
# ==== Example:
|
101
|
+
# Article.new(title: 'Hello World', content: 'This is the content of the article')
|
102
|
+
#
|
103
|
+
# # => #<Article:0x0000000105468ab0 ... "Hello World", content: "This is the content of the article">
|
104
|
+
#
|
105
|
+
# Article.title # => "Hello World"
|
106
|
+
# Article.content # => "This is the content of the article"
|
107
|
+
#
|
108
|
+
# Article.title = 'Not Hello World'
|
109
|
+
# Article.title # => "Not Hello World"
|
110
|
+
#
|
111
|
+
# Article.persisted? # => false
|
112
|
+
#
|
113
|
+
#
|
114
|
+
# The +custom_selected+ parameter is used to indicate whether the attributes are custom picked.
|
115
|
+
# If the attributes are custom picked, the attribute readers will be defined only for the selected attributes.
|
116
|
+
# The record with custom_selected as true cannot be saved, updated, or destroyed.
|
117
|
+
def initialize(hash = {}, custom_selected: false, **attributes)
|
118
|
+
attributes_hash = (hash.present? ? hash : attributes).deep_transform_keys(&:to_s)
|
119
|
+
@connection = WeaviateRecord::Connection.new(collection_name)
|
120
|
+
@custom_selected = custom_selected
|
121
|
+
@attributes = {}
|
122
|
+
@meta_attributes = attributes_hash['_additional'] || { 'id' => nil, 'creationTimeUnix' => nil,
|
123
|
+
'lastUpdateTimeUnix' => nil }
|
124
|
+
run_attribute_handlers(attributes_hash)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Saves the record in the weaviate database.
|
128
|
+
# Returns true if the record is saved successfully. Otherwise, it returns false.
|
129
|
+
# If the record is not saved successfully, it adds the errors to the record.
|
130
|
+
# If the record is already saved, it updates the record.
|
131
|
+
#
|
132
|
+
# ==== Example:
|
133
|
+
# class Article < WeaviateRecord::Base
|
134
|
+
# validates :title, presence: true
|
135
|
+
# end
|
136
|
+
#
|
137
|
+
# article = Article.new(title: 'Hello World', content: 'This is the content of the article')
|
138
|
+
# article.save # => true
|
139
|
+
#
|
140
|
+
# article.title = 'New title'
|
141
|
+
# article.save # => true
|
142
|
+
#
|
143
|
+
# article.title # => "New title"
|
144
|
+
#
|
145
|
+
# article = Article.new(title: '')
|
146
|
+
# article.save # => false
|
147
|
+
# article.errors.full_messages # => ["Title can't be blank"]
|
148
|
+
#
|
149
|
+
def save
|
150
|
+
result = validate_and_save
|
151
|
+
return false unless result
|
152
|
+
|
153
|
+
if result['error'].present?
|
154
|
+
errors.add(:base, message: result['error'])
|
155
|
+
false
|
156
|
+
else
|
157
|
+
@meta_attributes.merge!(self.class.meta_attributes(result))
|
158
|
+
true
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Updates the record in the weaviate database.
|
163
|
+
# Returns true if the record is updated successfully. Otherwise, it returns false.
|
164
|
+
# If the record is not updated successfully, it adds the errors to the record.
|
165
|
+
#
|
166
|
+
# ==== Example:
|
167
|
+
# class Article < WeaviateRecord::Base
|
168
|
+
# validates :title, presence: true
|
169
|
+
# end
|
170
|
+
#
|
171
|
+
# article = Article.new(title: 'Hello World', content: 'This is the content of the article')
|
172
|
+
# article.save
|
173
|
+
#
|
174
|
+
# article.update(title: 'Not Hello World') # => true
|
175
|
+
# article.title # => "Not Hello World"
|
176
|
+
#
|
177
|
+
# article.update(title: '') # => false
|
178
|
+
# article.errors.full_messages # => ["Title can't be blank"]
|
179
|
+
#
|
180
|
+
# If you try to update the meta attribute, it will raise an error
|
181
|
+
#
|
182
|
+
# ==== Example:
|
183
|
+
# article.update(id: 'new_id')
|
184
|
+
# # => WeaviateRecord::Errors::MetaAttributeError: 'cannot update meta attributes'
|
185
|
+
def update(hash = {}, **attributes)
|
186
|
+
attributes_hash = (hash.present? ? hash : attributes).deep_transform_keys(&:to_s)
|
187
|
+
validate_record_for_update(attributes_hash)
|
188
|
+
merge_attributes(attributes_hash)
|
189
|
+
return false unless valid?
|
190
|
+
|
191
|
+
result = @connection.update_call(@meta_attributes['id'], @attributes)
|
192
|
+
raise WeaviateRecord::Errors::ServerError, 'unable to update the weaviate record' unless result.is_a?(Hash)
|
193
|
+
|
194
|
+
errors.add(:base, message: result['error']) if result['error'].present?
|
195
|
+
result['id'].present?
|
196
|
+
end
|
197
|
+
|
198
|
+
# Destroys the record in the weaviate database.
|
199
|
+
# Returns true if the record is destroyed successfully. Otherwise, it returns false.
|
200
|
+
# If the record is the original record with valid id, it freezes the existing record object.
|
201
|
+
# If the record does not have a valid id, it converts all the attributes to nil.
|
202
|
+
#
|
203
|
+
# ==== Example:
|
204
|
+
# article = Article.new(title: 'Hello World', content: 'This is the content of the article')
|
205
|
+
#
|
206
|
+
# article.destroy # => false
|
207
|
+
# article.title # => nil
|
208
|
+
#
|
209
|
+
# article.title = 'New title'
|
210
|
+
# article.save
|
211
|
+
#
|
212
|
+
# article.destroy # => true
|
213
|
+
#
|
214
|
+
# article.frozen? # => true
|
215
|
+
def destroy
|
216
|
+
return self unless validate_record_for_destroy
|
217
|
+
|
218
|
+
result = @connection.delete_call(@meta_attributes['id'])
|
219
|
+
return freeze if result == true
|
220
|
+
|
221
|
+
errors.add(:base, message: result['error']) if result['error'].present?
|
222
|
+
false
|
223
|
+
end
|
224
|
+
|
225
|
+
# Checks whether the record is saved in the weaviate database or not.
|
226
|
+
# If the record is saved, it returns true. Otherwise, it returns false.
|
227
|
+
#
|
228
|
+
# ==== Example:
|
229
|
+
# article = Article.new(title: 'Hello World', content: 'This is the content of the article')
|
230
|
+
# article.save
|
231
|
+
# article.persisted? # => true
|
232
|
+
def persisted?
|
233
|
+
if @custom_selected || !respond_to?(:id)
|
234
|
+
raise WeaviateRecord::Errors::CustomQueriedRecordError,
|
235
|
+
'cannot perform persisted? action on custom queried record'
|
236
|
+
end
|
237
|
+
|
238
|
+
id.present?
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WeaviateRecord # :nodoc: all
|
4
|
+
module Concerns
|
5
|
+
module AttributeConcern
|
6
|
+
private
|
7
|
+
|
8
|
+
def list_of_valid_attributes
|
9
|
+
@list_of_valid_attributes ||= WeaviateRecord::Schema.find_collection(collection_name)
|
10
|
+
.attributes_list
|
11
|
+
end
|
12
|
+
|
13
|
+
def check_attributes(attributes_hash)
|
14
|
+
invalid_attributes = attributes_hash.keys - ['_additional', *list_of_valid_attributes]
|
15
|
+
return unless invalid_attributes.present?
|
16
|
+
|
17
|
+
raise WeaviateRecord::Errors::InvalidAttributeError,
|
18
|
+
"Invalid attributes #{invalid_attributes} for #{collection_name} record!"
|
19
|
+
end
|
20
|
+
|
21
|
+
def merge_attributes(attributes_hash)
|
22
|
+
check_attributes(attributes_hash)
|
23
|
+
attributes_hash.each do |key, value|
|
24
|
+
@attributes[key] = value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def load_attributes(attributes_hash)
|
29
|
+
if attributes_hash.empty? || !@custom_selected
|
30
|
+
list_of_valid_attributes.each do |attribute|
|
31
|
+
@attributes[attribute] = nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
attributes_hash.each do |key, value|
|
36
|
+
next if key == '_additional'
|
37
|
+
|
38
|
+
@attributes[key] = value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_attribute_writers
|
43
|
+
list_of_valid_attributes.each do |name|
|
44
|
+
define_singleton_method("#{name}=") do |value|
|
45
|
+
@attributes[name] = value
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def create_attribute_readers
|
51
|
+
attributes_list = @custom_selected ? @attributes.each_key : list_of_valid_attributes
|
52
|
+
attributes_list.each do |name|
|
53
|
+
define_singleton_method(name) { @attributes[name] }
|
54
|
+
end
|
55
|
+
|
56
|
+
handle_timestamp_attributes
|
57
|
+
@meta_attributes.each_key do |name|
|
58
|
+
define_singleton_method(name.underscore) { @meta_attributes[name] }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def handle_timestamp_attributes
|
63
|
+
replace_timestamp_attribute('creationTimeUnix') if @meta_attributes.key? 'creationTimeUnix'
|
64
|
+
replace_timestamp_attribute('lastUpdateTimeUnix') if @meta_attributes.key? 'lastUpdateTimeUnix'
|
65
|
+
end
|
66
|
+
|
67
|
+
def replace_timestamp_attribute(attribute)
|
68
|
+
mapped_attribute = WeaviateRecord::Constants::SPECIAL_ATTRIBUTE_MAPPINGS.key(attribute)
|
69
|
+
@meta_attributes[mapped_attribute] = if @meta_attributes[attribute]
|
70
|
+
DateTime.strptime(@meta_attributes[attribute], '%Q')
|
71
|
+
end
|
72
|
+
@meta_attributes.delete(attribute)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WeaviateRecord # :nodoc: all
|
4
|
+
module Concerns
|
5
|
+
module RecordConcern
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
include AttributeConcern
|
8
|
+
|
9
|
+
def collection_name
|
10
|
+
self.class.to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def run_attribute_handlers(attributes_hash)
|
16
|
+
check_attributes(attributes_hash)
|
17
|
+
load_attributes(attributes_hash)
|
18
|
+
create_attribute_writers
|
19
|
+
create_attribute_readers
|
20
|
+
end
|
21
|
+
|
22
|
+
def validate_record_for_update(attributes_hash)
|
23
|
+
raise ArgumentError, 'update action requires minimum one attribute' if attributes_hash.empty?
|
24
|
+
raise WeaviateRecord::Errors::MissingIdError, 'the record doesn\'t have an id' unless @meta_attributes['id']
|
25
|
+
|
26
|
+
if @custom_selected
|
27
|
+
raise WeaviateRecord::Errors::CustomQueriedRecordError,
|
28
|
+
'cannot perform update action on custom selected record'
|
29
|
+
end
|
30
|
+
|
31
|
+
check_attributes(attributes_hash)
|
32
|
+
return unless attributes_hash['_additional'] || attributes_hash[:_additional]
|
33
|
+
|
34
|
+
raise WeaviateRecord::Errors::MetaAttributeError, 'cannot update meta attributes'
|
35
|
+
end
|
36
|
+
|
37
|
+
def validate_and_save
|
38
|
+
if @custom_selected
|
39
|
+
raise WeaviateRecord::Errors::CustomQueriedRecordError, 'cannot modify custom selected record'
|
40
|
+
end
|
41
|
+
return false unless valid?
|
42
|
+
|
43
|
+
result = create_or_update_record
|
44
|
+
raise WeaviateRecord::Errors::InternalError, 'unable to save the record on Weaviate' unless result.is_a?(Hash)
|
45
|
+
|
46
|
+
result
|
47
|
+
end
|
48
|
+
|
49
|
+
def create_or_update_record
|
50
|
+
if @meta_attributes['id']
|
51
|
+
@connection.update_call(@meta_attributes['id'], @attributes)
|
52
|
+
else
|
53
|
+
@connection.create_call(@attributes)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def validate_record_for_destroy
|
58
|
+
if @custom_selected
|
59
|
+
raise WeaviateRecord::Errors::CustomQueriedRecordError,
|
60
|
+
'cannot perform destroy action on custom selected record'
|
61
|
+
end
|
62
|
+
|
63
|
+
return true if @meta_attributes['id']
|
64
|
+
|
65
|
+
@attributes.each_key { |key| @attributes[key] = nil }
|
66
|
+
false
|
67
|
+
end
|
68
|
+
|
69
|
+
class_methods do
|
70
|
+
def meta_attributes(record)
|
71
|
+
meta = {}
|
72
|
+
meta['id'] = record['id']
|
73
|
+
meta['created_at'] = DateTime.strptime(record['creationTimeUnix'].to_s, '%Q')
|
74
|
+
meta['updated_at'] = DateTime.strptime(record['lastUpdateTimeUnix'].to_s, '%Q')
|
75
|
+
meta
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'weaviate'
|
4
|
+
|
5
|
+
module WeaviateRecord
|
6
|
+
# This module will act as a connection to Weaviate database
|
7
|
+
class Connection
|
8
|
+
# Returns a _Weaviate::Client_ object, which is used to interact with the Weaviate database.
|
9
|
+
attr_reader :client
|
10
|
+
|
11
|
+
# Creates a new Connection to the Weaviate database.
|
12
|
+
def initialize(collection_name = nil)
|
13
|
+
@collection_name = collection_name&.to_s
|
14
|
+
@client = Weaviate::Client.new(
|
15
|
+
url: ENV.fetch('WEAVIATE_DATABASE_URL'),
|
16
|
+
api_key: ENV.fetch('WEAVIATE_API_KEY', nil),
|
17
|
+
model_service: ENV['WEAVIATE_VECTORIZER_MODULE']&.to_sym,
|
18
|
+
model_service_api_key: ENV.fetch('WEAVIATE_VECTORIZER_API_KEY', nil)
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the present schema of the Weaviate database.
|
23
|
+
def schema_list
|
24
|
+
client.schema.list.deep_symbolize_keys!
|
25
|
+
end
|
26
|
+
|
27
|
+
# :enddoc:
|
28
|
+
|
29
|
+
def find_call(id)
|
30
|
+
client.objects.get(class_name: @collection_name, id: id)
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_call(properties)
|
34
|
+
client.objects.create(class_name: @collection_name, properties: properties)
|
35
|
+
end
|
36
|
+
|
37
|
+
def update_call(id, properties)
|
38
|
+
client.objects.update(class_name: @collection_name, id: id, properties: properties)
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete_call(id)
|
42
|
+
client.objects.delete(class_name: @collection_name, id: id)
|
43
|
+
end
|
44
|
+
|
45
|
+
def check_existence(id)
|
46
|
+
client.objects.exists?(class_name: @collection_name, id: id)
|
47
|
+
end
|
48
|
+
|
49
|
+
def delete_where(condition)
|
50
|
+
client.objects.batch_delete(class_name: @collection_name, where: condition)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WeaviateRecord
|
4
|
+
module Constants # :nodoc:
|
5
|
+
SPECIAL_ATTRIBUTE_MAPPINGS = { 'feature_projection' => 'featureProjection',
|
6
|
+
'created_at' => 'creationTimeUnix',
|
7
|
+
'updated_at' => 'lastUpdateTimeUnix',
|
8
|
+
'explain_score' => 'explainScore' }.freeze
|
9
|
+
|
10
|
+
OPERATOR_MAPPING_HASH = { '=' => 'Equal', '==' => 'Equal', '!=' => 'NotEqual',
|
11
|
+
'>' => 'GreaterThan', '<' => 'LessThan', '>=' => 'GreaterThanEqual',
|
12
|
+
'<=' => 'LessThanEqual', 'LIKE' => 'Like', 'CONTAINS_ANY' => 'ContainsAny',
|
13
|
+
'CONTAINS_ALL' => 'ContainsAll' }.freeze
|
14
|
+
|
15
|
+
TYPE_MAPPING_HASH = { Integer => 'valueInt', String => 'valueText', Array => 'valueText',
|
16
|
+
Float => 'valueNumber', TrueClass => 'valueBoolean', FalseClass => 'valueBoolean' }.freeze
|
17
|
+
|
18
|
+
META_ATTRIBUTES = %w[vector certainty distance feature_projection classification
|
19
|
+
creation_at updated_at score explain_score summary].freeze
|
20
|
+
|
21
|
+
UUID_REGEX = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/i.freeze
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
WeaviateRecord::Constants.freeze
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WeaviateRecord # :nodoc: all
|
4
|
+
module Errors
|
5
|
+
# Used when an un-queried attribute is accessed
|
6
|
+
class MissingAttributeError < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
# Used when an undefined attribute is accessed
|
10
|
+
class InvalidAttributeError < StandardError
|
11
|
+
end
|
12
|
+
|
13
|
+
# Used when a collection is not found in the schema
|
14
|
+
class CollectionNotFound < StandardError
|
15
|
+
end
|
16
|
+
|
17
|
+
# Used when a record without id is updated
|
18
|
+
class MissingIdError < StandardError
|
19
|
+
end
|
20
|
+
|
21
|
+
# Used when a custom queried record is updated
|
22
|
+
class CustomQueriedRecordError < StandardError
|
23
|
+
end
|
24
|
+
|
25
|
+
# Used when a meta attribute is updated
|
26
|
+
class MetaAttributeError < StandardError
|
27
|
+
end
|
28
|
+
|
29
|
+
# Used when there is a problem with Weaviate
|
30
|
+
class InternalError < StandardError
|
31
|
+
end
|
32
|
+
|
33
|
+
# Used when the Sorting order is invalid
|
34
|
+
class SortingOptionError < StandardError
|
35
|
+
end
|
36
|
+
|
37
|
+
# Used when the where query is invalid
|
38
|
+
class InvalidWhereQueryError < StandardError
|
39
|
+
end
|
40
|
+
|
41
|
+
# Used when the operator in where query is invalid
|
42
|
+
class InvalidOperatorError < StandardError
|
43
|
+
end
|
44
|
+
|
45
|
+
# Used when the value type in where query is invalid
|
46
|
+
class InvalidValueTypeError < StandardError
|
47
|
+
end
|
48
|
+
|
49
|
+
# Used when the record not found
|
50
|
+
class RecordNotFoundError < StandardError
|
51
|
+
end
|
52
|
+
|
53
|
+
# Used for the errors thrown by weaviate server
|
54
|
+
class ServerError < StandardError
|
55
|
+
end
|
56
|
+
|
57
|
+
# Used when destroy_all called without where conditions
|
58
|
+
class MissingWhereCondition < StandardError
|
59
|
+
end
|
60
|
+
|
61
|
+
# Raised when where condition is not getting converted to Ruby hash
|
62
|
+
class WhereQueryConversionError < StandardError
|
63
|
+
end
|
64
|
+
|
65
|
+
# Raised when required argument is empty
|
66
|
+
class EmptyPrompt < StandardError
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WeaviateRecord
|
4
|
+
module Inspect # :nodoc:
|
5
|
+
def inspect
|
6
|
+
special_attributes, inspection = format_attributes
|
7
|
+
string = to_s
|
8
|
+
string[-1] = ' '
|
9
|
+
string << "id: #{special_attributes['id'].inspect} " << inspection
|
10
|
+
string << " created_at: #{special_attributes['created_at'].inspect}" if special_attributes.key? 'created_at'
|
11
|
+
string << " updated_at: #{special_attributes['updated_at'].inspect}" if special_attributes.key? 'updated_at'
|
12
|
+
string << '>'
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def format_attributes
|
18
|
+
attributes_list = { **@meta_attributes, **@attributes }
|
19
|
+
special_attributes = attributes_list.slice('id', 'created_at', 'updated_at')
|
20
|
+
attributes_list.except!(*special_attributes.keys)
|
21
|
+
inspection = attributes_list.sort.map do |key, value|
|
22
|
+
format_for_inspect(key, value)
|
23
|
+
end.join(', ')
|
24
|
+
[special_attributes, inspection]
|
25
|
+
end
|
26
|
+
|
27
|
+
def format_for_inspect(key, value)
|
28
|
+
formatted_value = case value
|
29
|
+
when String then value.truncate(25).inspect
|
30
|
+
when Array then key == 'vector' ? "[#{value.first(4).join(', ')}...]" : value.inspect
|
31
|
+
else value.inspect
|
32
|
+
end
|
33
|
+
"#{key.underscore}: #{formatted_value}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WeaviateRecord
|
4
|
+
module MethodMissing # :nodoc:
|
5
|
+
private
|
6
|
+
|
7
|
+
def list_of_all_attributes
|
8
|
+
[*WeaviateRecord::Schema.find_collection(collection_name).attributes_list,
|
9
|
+
*WeaviateRecord::Constants::META_ATTRIBUTES]
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(method, *args, &block)
|
13
|
+
if list_of_all_attributes.include? method.to_s
|
14
|
+
raise WeaviateRecord::Errors::MissingAttributeError, "missing attribute: #{method}"
|
15
|
+
end
|
16
|
+
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def respond_to_missing?(method, include_all = false)
|
21
|
+
list_of_all_attributes.include?(method.to_s) ? true : super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WeaviateRecord
|
4
|
+
module Queries # :nodoc:
|
5
|
+
# This module provides method for ask query
|
6
|
+
module Ask
|
7
|
+
# Answers the question using the QnA module with the help of the records in the database.
|
8
|
+
#
|
9
|
+
# Requires QNA_INFERENCE_API to be set in the weaviate instance.
|
10
|
+
# The answer will be available in the _result_ key of the _answer_ meta attribute.
|
11
|
+
#
|
12
|
+
# You can also optionally pass an array of attributes to searched for the answer.
|
13
|
+
#
|
14
|
+
# ==== Example:
|
15
|
+
# Article.create(content: 'the name is Sparrow')
|
16
|
+
# Article.select(_additional: { answer: :result }).ask('whats your name?')
|
17
|
+
# # => [#<Article:0x0000000109a85de0 id: nil answer: {"result"=>"Sparrow"}>]
|
18
|
+
def ask(question, on_attributes: [])
|
19
|
+
question.to_str
|
20
|
+
raise WeaviateRecord::Errors::EmptyPrompt, 'text cannot be empty' if question.empty?
|
21
|
+
|
22
|
+
attributes = on_attributes.map(&:to_s)
|
23
|
+
@ask = "{ question: #{question.gsub('"', "'").inspect}" \
|
24
|
+
"#{", properties: #{attributes}" if attributes.present?} }"
|
25
|
+
@loaded = false
|
26
|
+
self
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WeaviateRecord
|
4
|
+
module Queries
|
5
|
+
# Bm25 is an algorithm to perform keyword based search on collection provided by Weaviate
|
6
|
+
# This class contains functions to perform that query
|
7
|
+
module Bm25
|
8
|
+
# Perform a keyword based search on the collection.
|
9
|
+
# You can also optionally pass an array of attributes to search in the collection.
|
10
|
+
#
|
11
|
+
# ==== Example:
|
12
|
+
# Article.create(content: 'This is a movie about friendship, action and adventure')
|
13
|
+
# # => #<Article:0x00000001052091e8 id: "983c0970-2c65-4c38-a93f-2ca9272d784b"... >
|
14
|
+
#
|
15
|
+
# Article.bm25('friendship movie')
|
16
|
+
# # => [#<Article:0x00000001052091e8 id: "983c0970-2c65-4c38-a93f-2ca9272d784b"... >]
|
17
|
+
def bm25(text, on_attributes: [])
|
18
|
+
text = text.to_str
|
19
|
+
return self if text.empty?
|
20
|
+
|
21
|
+
attributes = on_attributes.map(&:to_s)
|
22
|
+
@keyword_search = "{ query: #{text.gsub('"', "'").inspect}" \
|
23
|
+
"#{", properties: #{attributes}" if attributes.present?} }"
|
24
|
+
@loaded = false
|
25
|
+
self
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|