weaviate_record 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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