typesensual 0.1.0

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.
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'typesensual/search/hit'
4
+ require 'typesensual/search/results'
5
+
6
+ class Typesensual
7
+ class Search
8
+ # Initialize a new search object for a collection
9
+ #
10
+ # @param collection [Typesensual::Collection] the Typesensual collection object
11
+ # @param query [String] the query string to search for
12
+ # @param query_by [String, Symbol, Array<String, Symbol>, Hash<String, Symbol>] the fields to
13
+ # search in. If a hash is provided, the keys are the fields and the values are the weights.
14
+ # If a string is provided, it is used directly as the query_by parameter. If an array is
15
+ # provided, the values are the fields.
16
+ def initialize(collection:, query:, query_by:)
17
+ @filter_by = []
18
+ @sort_by = []
19
+ @facet_by = []
20
+ @facet_query = []
21
+ @include_fields = []
22
+ @exclude_fields = []
23
+ @params = {}
24
+
25
+ @collection = collection
26
+ @query = query
27
+
28
+ if query_by.is_a?(Hash)
29
+ @query_by = query_by.keys
30
+ @query_by_weights = query_by.values
31
+ elsif query_by.is_a?(String) || query_by.is_a?(Symbol)
32
+ @query_by = [query_by]
33
+ else
34
+ @query_by = query_by
35
+ end
36
+ end
37
+
38
+ # Set the number of results to return per page
39
+ # @param count [Integer] the number of results to return per page
40
+ def per(count)
41
+ set(per_page: count)
42
+ end
43
+
44
+ # Set the page number to return
45
+ # @param number [Integer] the page number to return
46
+ def page(number)
47
+ set(page: number)
48
+ end
49
+
50
+ # Add a filter to the search
51
+ # @param filter [String, Symbol, Hash<String, Symbol>] the filter to add. If a hash is
52
+ # provided, the keys are the fields and the values are the values to filter by. If a
53
+ # string is provided, it is added directly as a filter. All filters are ANDed together.
54
+ def filter(filter)
55
+ if filter.is_a?(Hash)
56
+ @filter_by += filter.map { |key, value| "#{key}:#{value}" }
57
+ else
58
+ @filter_by << filter.to_s
59
+ end
60
+ self
61
+ end
62
+
63
+ # Add a sort to the search
64
+ # @param value [String, Symbol, Hash<String, Symbol>] the sort to add to the search. If
65
+ # a hash is provided, the keys are the fields and the values are the directions to sort.
66
+ # If a string is provided, it is added directly as a sort.
67
+ def sort(value)
68
+ if value.is_a?(Hash)
69
+ @sort_by += value.map { |key, direction| "#{key}:#{direction}" }
70
+ else
71
+ @sort_by << value.to_s
72
+ end
73
+ self
74
+ end
75
+
76
+ # Add a field to facet to the seach
77
+ # @param facets [String, Symbol, Array<String, Symbol>, Hash<String, Symbol>] the fields to
78
+ # facet by. If a hash is provided, the keys are the fields and the values are strings to
79
+ # query each facet. If an Array is provided, the values are fields to facet by. If a string
80
+ # is provided, it is added directly as a facet.
81
+ def facet(facets)
82
+ if facets.is_a?(Hash)
83
+ facets.each do |key, value|
84
+ @facet_by << key.to_s
85
+ @facet_query << "#{key}:#{value}" if value
86
+ end
87
+ elsif facets.is_a?(Array)
88
+ @facet_by += facets.map(&:to_s)
89
+ else
90
+ @facet_by << facets.to_s
91
+ end
92
+ self
93
+ end
94
+
95
+ # Add fields to include in the search result documents
96
+ # @param fields [String, Symbol, Array<String, Symbol>] the fields to include
97
+ def include_fields(*fields)
98
+ @include_fields += fields.map(&:to_s)
99
+ self
100
+ end
101
+
102
+ # Add fields to exclude from the search result documents
103
+ # @param fields [String, Symbol, Array<String, Symbol>] the fields to exclude
104
+ def exclude_fields(*fields)
105
+ @exclude_fields += fields.map(&:to_s)
106
+ self
107
+ end
108
+
109
+ # Set additional parameters to pass to the search
110
+ # @param values [Hash] the parameters to set
111
+ def set(values)
112
+ @params.merge!(values)
113
+ self
114
+ end
115
+
116
+ # Generate the query document
117
+ def query
118
+ {
119
+ collection: @collection.name,
120
+ filter_by: @filter_by.join(' && '),
121
+ q: @query,
122
+ query_by: @query_by&.join(','),
123
+ query_by_weights: @query_by_weights&.join(','),
124
+ sort_by: @sort_by&.join(','),
125
+ facet_by: @facet_by&.join(','),
126
+ facet_query: @facet_query&.join(','),
127
+ include_fields: @include_fields&.join(','),
128
+ exclude_fields: @exclude_fields&.join(',')
129
+ }.merge(@params).reject { |_, v| v.blank? }
130
+ end
131
+
132
+ # Load the results from the search query
133
+ def load
134
+ Results.new(@collection.typesense_collection.documents.search(query))
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Typesensual
4
+ module StateHelpers
5
+ extend ActiveSupport::Concern
6
+
7
+ delegate :config, :env, :client, to: :class
8
+
9
+ class_methods do
10
+ delegate :config, to: :Typesensual
11
+ delegate :env, :client, to: :config, allow_nil: true
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Typesensual
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ require 'active_support/concern'
6
+ require 'active_support/core_ext/string/inflections'
7
+ require 'active_support/core_ext/class/subclasses'
8
+ require 'active_support/core_ext/module/delegation'
9
+ require 'active_support/core_ext/hash/keys'
10
+
11
+ require 'typesense'
12
+
13
+ require 'typesensual/version'
14
+ require 'typesensual/config'
15
+ require 'typesensual/index'
16
+ require 'typesensual/collection'
17
+ require 'typesensual/search'
18
+ require 'typesensual/railtie' if defined?(Rails)
19
+
20
+ class Typesensual
21
+ class << self
22
+ attr_accessor :config
23
+
24
+ def client
25
+ config&.client
26
+ end
27
+
28
+ def configure(&block)
29
+ self.config = Typesensual::Config.new(&block)
30
+ end
31
+
32
+ # Get the collections that match the alias name
33
+ #
34
+ # @return [Array<Collection>] the collections that match the alias name
35
+ def collections
36
+ Typesensual.client.collections.retrieve.map do |collection|
37
+ Collection.new(collection)
38
+ end
39
+ end
40
+ end
41
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: typesensual
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Emma Lejeck
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-07-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 6.1.5
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 6.1.5
27
+ - !ruby/object:Gem::Dependency
28
+ name: paint
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 2.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: typesense
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 0.13.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 0.13.0
55
+ description:
56
+ email:
57
+ - nuck@kitsu.io
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".rspec"
63
+ - ".rubocop.yml"
64
+ - ".ruby-version"
65
+ - ".yardopts"
66
+ - CHANGELOG.md
67
+ - Gemfile
68
+ - Gemfile.lock
69
+ - LICENSE.txt
70
+ - README.md
71
+ - Rakefile
72
+ - lib/tasks/typesensual.rake
73
+ - lib/typesensual.rb
74
+ - lib/typesensual/callbacks.rb
75
+ - lib/typesensual/collection.rb
76
+ - lib/typesensual/config.rb
77
+ - lib/typesensual/field.rb
78
+ - lib/typesensual/index.rb
79
+ - lib/typesensual/railtie.rb
80
+ - lib/typesensual/rake_helper.rb
81
+ - lib/typesensual/schema.rb
82
+ - lib/typesensual/search.rb
83
+ - lib/typesensual/search/hit.rb
84
+ - lib/typesensual/search/results.rb
85
+ - lib/typesensual/state_helpers.rb
86
+ - lib/typesensual/version.rb
87
+ homepage: https://github.com/hummingbird-me/typesensual
88
+ licenses:
89
+ - MIT
90
+ metadata:
91
+ homepage_uri: https://github.com/hummingbird-me/typesensual
92
+ source_code_uri: https://github.com/hummingbird-me/typesensual
93
+ changelog_uri: https://github.com/hummingbird-me/typesensual/blob/main/CHANGELOG.md
94
+ rubygems_mfa_required: 'true'
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 2.7.0
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubygems_version: 3.3.26
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: A simple, sensual wrapper around Typesense for Ruby
114
+ test_files: []