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 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