typesense 0.1.1 → 0.5.2
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 +4 -4
- data/.circleci/config.yml +4 -2
- data/.gitignore +2 -0
- data/.rubocop.yml +7 -0
- data/.rubocop_todo.yml +36 -27
- data/LICENSE +198 -10
- data/README.md +10 -2
- data/codecov.yml +10 -0
- data/examples/aliases.rb +58 -0
- data/examples/client_initialization.rb +67 -0
- data/examples/collections_and_documents.rb +45 -68
- data/examples/keys.rb +127 -0
- data/examples/overrides.rb +108 -0
- data/examples/search.rb +26 -53
- data/lib/typesense.rb +8 -0
- data/lib/typesense/alias.rb +24 -0
- data/lib/typesense/aliases.rb +30 -0
- data/lib/typesense/api_call.rb +186 -69
- data/lib/typesense/client.rb +12 -3
- data/lib/typesense/collection.rb +8 -6
- data/lib/typesense/collections.rb +6 -6
- data/lib/typesense/configuration.rb +35 -19
- data/lib/typesense/debug.rb +3 -3
- data/lib/typesense/document.rb +4 -4
- data/lib/typesense/documents.rb +11 -6
- data/lib/typesense/error.rb +6 -0
- data/lib/typesense/health.rb +15 -0
- data/lib/typesense/key.rb +24 -0
- data/lib/typesense/keys.rb +34 -0
- data/lib/typesense/metrics.rb +15 -0
- data/lib/typesense/override.rb +25 -0
- data/lib/typesense/overrides.rb +31 -0
- data/lib/typesense/version.rb +1 -1
- data/typesense.gemspec +14 -11
- metadata +79 -25
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../lib/typesense'
|
4
|
+
require 'awesome_print'
|
5
|
+
|
6
|
+
AwesomePrint.defaults = {
|
7
|
+
indent: -2
|
8
|
+
}
|
9
|
+
|
10
|
+
##
|
11
|
+
## Setup
|
12
|
+
#
|
13
|
+
### Option 1: Start a single-node cluster
|
14
|
+
# $ docker run -i -p 8108:8108 -v/tmp/typesense-server-data-1b/:/data -v`pwd`/typesense-server-peers:/typesense-server-peers typesense/typesense:0.12.1.rc1 --data-dir /data --api-key=xyz --listen-port 8108 --enable-cors
|
15
|
+
#
|
16
|
+
### Option 2: Start a 3-node cluster
|
17
|
+
#
|
18
|
+
# Create file in present working directory called typesense-server-peers (update IP Addresses appropriately to your local network):
|
19
|
+
# $ echo '172.17.0.2:8107:8108,172.17.0.3:7107:7108,172.17.0.4:9107:9108' > `pwd`/typesense-server-peers
|
20
|
+
#
|
21
|
+
# Start node 1:
|
22
|
+
# $ docker run -i -p 8108:8108 -p 8107:8107 -v/tmp/typesense-server-data-1b/:/data -v`pwd`/typesense-server-peers:/typesense-server-peers typesense/typesense:0.12.1.rc1 --data-dir /data --api-key=xyz --listen-port 8108 --peering-port 8107 --enable-cors --nodes=/typesense-server-peers
|
23
|
+
#
|
24
|
+
# Start node 2:
|
25
|
+
# $ docker run -i -p 7108:7108 -p 7107:7107 -v/tmp/.typesense-server-data-2b/:/data -v`pwd`/typesense-server-peers:/typesense-server-peers typesense/typesense:0.12.1.rc1 --data-dir /data --api-key=xyz --listen-port 7108 --peering-port 7107 --enable-cors --nodes=/typesense-server-peers
|
26
|
+
#
|
27
|
+
# Start node 3:
|
28
|
+
# $ docker run -i -p 9108:9108 -p 9107:9107 -v/tmp/.typesense-server-data-3b/:/data -v`pwd`/typesense-server-peers:/typesense-server-peers typesense/typesense:0.12.1.rc1 --data-dir /data --api-key=xyz --listen-port 9108 --peering-port 9107 --enable-cors --nodes=/typesense-server-peers
|
29
|
+
#
|
30
|
+
# Note: Be sure to add `--license-key=<>` at the end when starting a Typesense Premium server
|
31
|
+
|
32
|
+
##
|
33
|
+
# Create a client
|
34
|
+
@typesense = Typesense::Client.new(
|
35
|
+
nodes: [
|
36
|
+
{
|
37
|
+
host: 'localhost',
|
38
|
+
port: 8108,
|
39
|
+
protocol: 'http'
|
40
|
+
},
|
41
|
+
# Uncomment if starting a 3-node cluster, using Option 2 under Setup instructions above
|
42
|
+
{
|
43
|
+
host: 'localhost',
|
44
|
+
port: 7108,
|
45
|
+
protocol: 'http'
|
46
|
+
},
|
47
|
+
{
|
48
|
+
host: 'localhost',
|
49
|
+
port: 9108,
|
50
|
+
protocol: 'http'
|
51
|
+
}
|
52
|
+
],
|
53
|
+
# If this optional key is specified, requests are always sent to this node first if it is healthy
|
54
|
+
# before falling back on the nodes mentioned in the `nodes` key. This is useful when running a distributed set of search clusters.
|
55
|
+
'nearest_node': {
|
56
|
+
'host': 'localhost',
|
57
|
+
'port': '8108',
|
58
|
+
'protocol': 'http'
|
59
|
+
},
|
60
|
+
api_key: 'xyz',
|
61
|
+
num_retries: 10,
|
62
|
+
healthcheck_interval_seconds: 1,
|
63
|
+
retry_interval_seconds: 0.01,
|
64
|
+
connection_timeout_seconds: 10,
|
65
|
+
logger: Logger.new(STDOUT),
|
66
|
+
log_level: Logger::DEBUG
|
67
|
+
)
|
@@ -4,65 +4,37 @@
|
|
4
4
|
# These examples walk you through all the operations you can do on a collection and a document
|
5
5
|
# Search is specifically covered in another file in the examples folder
|
6
6
|
|
7
|
-
require_relative '
|
8
|
-
require 'awesome_print'
|
9
|
-
|
10
|
-
AwesomePrint.defaults = {
|
11
|
-
indent: -2
|
12
|
-
}
|
13
|
-
|
14
|
-
##
|
15
|
-
# Setup
|
16
|
-
#
|
17
|
-
# Start the master
|
18
|
-
# $ docker run -p 8108:8108 -it -v/tmp/typesense-data-master/:/data -it typesense/typesense:0.8.0-rc1 --data-dir /data --api-key=abcd --listen-port 8108
|
19
|
-
#
|
20
|
-
# Start the read replica
|
21
|
-
# $ docker run -p 8109:8109 -it -v/tmp/typesense-data-read-replica-1/:/data -it typesense/typesense:0.8.0-rc1 --data-dir /data --api-key=wxyz --listen-port 8109 --master http://localhost:8108
|
22
|
-
|
23
|
-
##
|
24
|
-
# Create a client
|
25
|
-
typesense = Typesense::Client.new(
|
26
|
-
master_node: {
|
27
|
-
host: 'localhost',
|
28
|
-
port: 8108,
|
29
|
-
protocol: 'http',
|
30
|
-
api_key: 'abcd'
|
31
|
-
},
|
32
|
-
read_replica_nodes: [
|
33
|
-
{
|
34
|
-
host: 'localhost',
|
35
|
-
port: 8109,
|
36
|
-
protocol: 'http',
|
37
|
-
api_key: 'wxyz'
|
38
|
-
}
|
39
|
-
],
|
40
|
-
timeout_seconds: 10
|
41
|
-
)
|
7
|
+
require_relative './client_initialization'
|
42
8
|
|
43
9
|
##
|
44
10
|
# Create a collection
|
45
11
|
schema = {
|
46
|
-
'name'
|
47
|
-
'fields'
|
12
|
+
'name' => 'companies',
|
13
|
+
'fields' => [
|
48
14
|
{
|
49
15
|
'name' => 'company_name',
|
50
16
|
'type' => 'string'
|
51
17
|
},
|
52
18
|
{
|
53
|
-
'name'
|
54
|
-
'type'
|
19
|
+
'name' => 'num_employees',
|
20
|
+
'type' => 'int32'
|
55
21
|
},
|
56
22
|
{
|
57
|
-
'name'
|
58
|
-
'type'
|
23
|
+
'name' => 'country',
|
24
|
+
'type' => 'string',
|
59
25
|
'facet' => true
|
60
26
|
}
|
61
27
|
],
|
62
28
|
'default_sorting_field' => 'num_employees'
|
63
29
|
}
|
64
30
|
|
65
|
-
collection
|
31
|
+
# Delete the collection if it already exists
|
32
|
+
begin
|
33
|
+
@typesense.collections['companies'].delete
|
34
|
+
rescue Typesense::Error::ObjectNotFound
|
35
|
+
end
|
36
|
+
|
37
|
+
collection = @typesense.collections.create(schema)
|
66
38
|
ap collection
|
67
39
|
|
68
40
|
# {
|
@@ -87,7 +59,8 @@ ap collection
|
|
87
59
|
|
88
60
|
##
|
89
61
|
# Retrieve a collection
|
90
|
-
collection
|
62
|
+
sleep 0.5 # Give Typesense cluster a few hundred ms to create the collection on all nodes, before reading it right after (eventually consistent)
|
63
|
+
collection = @typesense.collections['companies'].retrieve
|
91
64
|
ap collection
|
92
65
|
|
93
66
|
# {
|
@@ -115,7 +88,7 @@ ap collection
|
|
115
88
|
|
116
89
|
##
|
117
90
|
# Retrieve all collections
|
118
|
-
collections = typesense.collections.retrieve
|
91
|
+
collections = @typesense.collections.retrieve
|
119
92
|
ap collections
|
120
93
|
|
121
94
|
# [
|
@@ -146,7 +119,7 @@ ap collections
|
|
146
119
|
##
|
147
120
|
# Delete a collection
|
148
121
|
# Deletion returns the schema of the collection after deletion
|
149
|
-
collection = typesense.collections['companies'].delete
|
122
|
+
collection = @typesense.collections['companies'].delete
|
150
123
|
ap collection
|
151
124
|
|
152
125
|
# {
|
@@ -173,18 +146,18 @@ ap collection
|
|
173
146
|
# }
|
174
147
|
|
175
148
|
# Let's create the collection again for use in our remaining examples
|
176
|
-
typesense.collections.create(schema)
|
149
|
+
@typesense.collections.create(schema)
|
177
150
|
|
178
151
|
##
|
179
152
|
# Create (index) a document
|
180
153
|
document = {
|
181
|
-
'id'
|
182
|
-
'company_name'
|
154
|
+
'id' => '124',
|
155
|
+
'company_name' => 'Stark Industries',
|
183
156
|
'num_employees' => 5215,
|
184
|
-
'country'
|
157
|
+
'country' => 'USA'
|
185
158
|
}
|
186
159
|
|
187
|
-
document = typesense.collections['companies'].documents.create(document)
|
160
|
+
document = @typesense.collections['companies'].documents.create(document)
|
188
161
|
ap document
|
189
162
|
|
190
163
|
# {
|
@@ -196,7 +169,8 @@ ap document
|
|
196
169
|
|
197
170
|
##
|
198
171
|
# Retrieve a document
|
199
|
-
document
|
172
|
+
sleep 0.5 # Give Typesense cluster a few hundred ms to create the document on all nodes, before reading it right after (eventually consistent)
|
173
|
+
document = @typesense.collections['companies'].documents['124'].retrieve
|
200
174
|
ap document
|
201
175
|
|
202
176
|
# {
|
@@ -209,7 +183,7 @@ ap document
|
|
209
183
|
##
|
210
184
|
# Delete a document
|
211
185
|
# Deleting a document, returns the document after deletion
|
212
|
-
document = typesense.collections['companies'].documents['124'].delete
|
186
|
+
document = @typesense.collections['companies'].documents['124'].delete
|
213
187
|
ap document
|
214
188
|
|
215
189
|
# {
|
@@ -219,25 +193,28 @@ ap document
|
|
219
193
|
# "num_employees" => 5215
|
220
194
|
# }
|
221
195
|
|
222
|
-
# Let's create two documents again for use in our remaining examples
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
196
|
+
# Let's bulk create two documents again for use in our remaining examples
|
197
|
+
documents = [
|
198
|
+
{
|
199
|
+
'id' => '124',
|
200
|
+
'company_name' => 'Stark Industries',
|
201
|
+
'num_employees' => 5215,
|
202
|
+
'country' => 'USA'
|
203
|
+
},
|
204
|
+
{
|
205
|
+
'id' => '125',
|
206
|
+
'company_name' => 'Acme Corp',
|
207
|
+
'num_employees' => 1002,
|
208
|
+
'country' => 'France'
|
209
|
+
}
|
210
|
+
]
|
211
|
+
ap @typesense.collections['companies'].documents.create_many(documents)
|
236
212
|
|
237
213
|
##
|
238
214
|
# Export all documents in a collection in JSON Lines format
|
239
215
|
# We use JSON Lines format for performance reasons. You can choose to parse selected lines (elements in the array) as needed.
|
240
|
-
|
216
|
+
sleep 0.5 # Give Typesense cluster a few hundred ms to create the document on all nodes, before reading it right after (eventually consistent)
|
217
|
+
array_of_json_strings = @typesense.collections['companies'].documents.export
|
241
218
|
ap array_of_json_strings
|
242
219
|
|
243
220
|
# [
|
@@ -248,4 +225,4 @@ ap array_of_json_strings
|
|
248
225
|
##
|
249
226
|
# Cleanup
|
250
227
|
# Drop the collection
|
251
|
-
typesense.collections['companies'].delete
|
228
|
+
@typesense.collections['companies'].delete
|
data/examples/keys.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# These examples walk you through operations to manage API Keys
|
5
|
+
|
6
|
+
require_relative './client_initialization'
|
7
|
+
|
8
|
+
# Let's setup some test data for this example
|
9
|
+
schema = {
|
10
|
+
'name' => 'users',
|
11
|
+
'fields' => [
|
12
|
+
{
|
13
|
+
'name' => 'company_id',
|
14
|
+
'type' => 'int32',
|
15
|
+
'facet' => false
|
16
|
+
},
|
17
|
+
{
|
18
|
+
'name' => 'user_name',
|
19
|
+
'type' => 'string',
|
20
|
+
'facet' => false
|
21
|
+
},
|
22
|
+
{
|
23
|
+
'name' => 'login_count',
|
24
|
+
'type' => 'int32',
|
25
|
+
'facet' => false
|
26
|
+
},
|
27
|
+
{
|
28
|
+
'name' => 'country',
|
29
|
+
'type' => 'string',
|
30
|
+
'facet' => true
|
31
|
+
}
|
32
|
+
],
|
33
|
+
'default_sorting_field' => 'company_id'
|
34
|
+
}
|
35
|
+
|
36
|
+
# We have four users, belonging to two companies: 124 and 126
|
37
|
+
documents = [
|
38
|
+
{
|
39
|
+
'company_id' => 124,
|
40
|
+
'user_name' => 'Hilary Bradford',
|
41
|
+
'login_count' => 10,
|
42
|
+
'country' => 'USA'
|
43
|
+
},
|
44
|
+
{
|
45
|
+
'company_id' => 124,
|
46
|
+
'user_name' => 'Nile Carty',
|
47
|
+
'login_count' => 100,
|
48
|
+
'country' => 'USA'
|
49
|
+
},
|
50
|
+
{
|
51
|
+
'company_id' => 126,
|
52
|
+
'user_name' => 'Tahlia Maxwell',
|
53
|
+
'login_count' => 1,
|
54
|
+
'country' => 'France'
|
55
|
+
},
|
56
|
+
{
|
57
|
+
'company_id' => 126,
|
58
|
+
'user_name' => 'Karl Roy',
|
59
|
+
'login_count' => 2,
|
60
|
+
'country' => 'Germany'
|
61
|
+
}
|
62
|
+
]
|
63
|
+
|
64
|
+
# Delete if the collection already exists from a previous example run
|
65
|
+
begin
|
66
|
+
@typesense.collections['users'].delete
|
67
|
+
rescue Typesense::Error::ObjectNotFound
|
68
|
+
end
|
69
|
+
|
70
|
+
# create a collection
|
71
|
+
@typesense.collections.create(schema)
|
72
|
+
|
73
|
+
# Index documents
|
74
|
+
documents.each do |document|
|
75
|
+
@typesense.collections['users'].documents.create(document)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Generate an API key and restrict it to only allow searches
|
79
|
+
# You want to use this API Key in the browser instead of the master API Key
|
80
|
+
unscoped_search_only_api_key_response = @typesense.keys.create({
|
81
|
+
'description' => 'Search-only key.',
|
82
|
+
'actions' => ['documents:search'],
|
83
|
+
'collections' => ['*']
|
84
|
+
})
|
85
|
+
ap unscoped_search_only_api_key_response
|
86
|
+
|
87
|
+
# Save the key returned, since this will be the only time the full API Key is returned, for security purposes
|
88
|
+
unscoped_search_only_api_key = unscoped_search_only_api_key_response['value']
|
89
|
+
|
90
|
+
# Side note: you can also retrieve metadata of API keys using the ID returned in the above response
|
91
|
+
unscoped_search_only_api_key_response = @typesense.keys[unscoped_search_only_api_key_response['id']].retrieve
|
92
|
+
ap unscoped_search_only_api_key_response
|
93
|
+
|
94
|
+
# We'll now use this search-only API key to generate a scoped search API key that can only access documents that have company_id:124
|
95
|
+
# This is useful when you store multi-tenant data in a single Typesense server, but you only want
|
96
|
+
# a particular tenant to access their own data. You'd generate one scoped search key per tenant.
|
97
|
+
# IMPORTANT: scoped search keys should only be generated *server-side*, so as to not leak the unscoped main search key to clients
|
98
|
+
scoped_search_only_api_key = @typesense.keys.generate_scoped_search_key(unscoped_search_only_api_key, { 'filter_by': 'company_id:124' })
|
99
|
+
ap "scoped_search_only_api_key: #{scoped_search_only_api_key}"
|
100
|
+
|
101
|
+
# Now let's search the data using the scoped API Key for company_id:124
|
102
|
+
# You can do searches with this scoped_search_only_api_key from the server-side or client-side
|
103
|
+
scoped_typesense_client = Typesense::Client.new({
|
104
|
+
'nodes': [{
|
105
|
+
'host': 'localhost',
|
106
|
+
'port': '8108',
|
107
|
+
'protocol': 'http'
|
108
|
+
}],
|
109
|
+
'api_key': scoped_search_only_api_key
|
110
|
+
})
|
111
|
+
|
112
|
+
search_results = scoped_typesense_client.collections['users'].documents.search({
|
113
|
+
'q' => 'Hilary',
|
114
|
+
'query_by' => 'user_name'
|
115
|
+
})
|
116
|
+
ap search_results
|
117
|
+
|
118
|
+
# Search for a user that exists, but is outside the current key's scope
|
119
|
+
search_results = scoped_typesense_client.collections['users'].documents.search({
|
120
|
+
'q': 'Maxwell',
|
121
|
+
'query_by': 'user_name'
|
122
|
+
})
|
123
|
+
ap search_results # Will return empty result set
|
124
|
+
|
125
|
+
# Now let's delete the unscoped_search_only_api_key. You'd want to do this when you need to rotate keys for example.
|
126
|
+
results = @typesense.keys[unscoped_search_only_api_key_response['id']].delete
|
127
|
+
ap results
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# These examples walk you through operations specifically related to overrides
|
5
|
+
# This is a Typesense Premium feature (see: https://typesense.org/premium)
|
6
|
+
# Be sure to add `--license-key=<>` as a parameter, when starting a Typesense Premium server
|
7
|
+
|
8
|
+
require_relative './client_initialization'
|
9
|
+
|
10
|
+
##
|
11
|
+
# Create a collection
|
12
|
+
schema = {
|
13
|
+
'name' => 'companies',
|
14
|
+
'fields' => [
|
15
|
+
{
|
16
|
+
'name' => 'company_name',
|
17
|
+
'type' => 'string'
|
18
|
+
},
|
19
|
+
{
|
20
|
+
'name' => 'num_employees',
|
21
|
+
'type' => 'int32'
|
22
|
+
},
|
23
|
+
{
|
24
|
+
'name' => 'country',
|
25
|
+
'type' => 'string',
|
26
|
+
'facet' => true
|
27
|
+
}
|
28
|
+
],
|
29
|
+
'default_sorting_field' => 'num_employees'
|
30
|
+
}
|
31
|
+
|
32
|
+
@typesense.collections.create(schema)
|
33
|
+
|
34
|
+
# Let's create a couple documents for us to use in our search examples
|
35
|
+
@typesense.collections['companies'].documents.create(
|
36
|
+
'id' => '124',
|
37
|
+
'company_name' => 'Stark Industries',
|
38
|
+
'num_employees' => 5215,
|
39
|
+
'country' => 'USA'
|
40
|
+
)
|
41
|
+
|
42
|
+
@typesense.collections['companies'].documents.create(
|
43
|
+
'id' => '127',
|
44
|
+
'company_name' => 'Stark Corp',
|
45
|
+
'num_employees' => 1031,
|
46
|
+
'country' => 'USA'
|
47
|
+
)
|
48
|
+
|
49
|
+
@typesense.collections['companies'].documents.create(
|
50
|
+
'id' => '125',
|
51
|
+
'company_name' => 'Acme Corp',
|
52
|
+
'num_employees' => 1002,
|
53
|
+
'country' => 'France'
|
54
|
+
)
|
55
|
+
|
56
|
+
@typesense.collections['companies'].documents.create(
|
57
|
+
'id' => '126',
|
58
|
+
'company_name' => 'Doofenshmirtz Inc',
|
59
|
+
'num_employees' => 2,
|
60
|
+
'country' => 'Tri-State Area'
|
61
|
+
)
|
62
|
+
|
63
|
+
##
|
64
|
+
# Create overrides
|
65
|
+
|
66
|
+
@typesense.collections['companies'].overrides.create(
|
67
|
+
"id": 'promote-doofenshmirtz',
|
68
|
+
"rule": {
|
69
|
+
"query": 'doofen',
|
70
|
+
"match": 'exact'
|
71
|
+
},
|
72
|
+
"includes": [{ 'id' => '126', 'position' => 1 }]
|
73
|
+
)
|
74
|
+
@typesense.collections['companies'].overrides.create(
|
75
|
+
"id": 'promote-acme',
|
76
|
+
"rule": {
|
77
|
+
"query": 'stark',
|
78
|
+
"match": 'exact'
|
79
|
+
},
|
80
|
+
"includes": [{ 'id' => '125', 'position' => 1 }]
|
81
|
+
)
|
82
|
+
|
83
|
+
##
|
84
|
+
# Search for documents
|
85
|
+
results = @typesense.collections['companies'].documents.search(
|
86
|
+
'q' => 'doofen',
|
87
|
+
'query_by' => 'company_name'
|
88
|
+
)
|
89
|
+
ap results
|
90
|
+
|
91
|
+
results = @typesense.collections['companies'].documents.search(
|
92
|
+
'q' => 'stark',
|
93
|
+
'query_by' => 'company_name'
|
94
|
+
)
|
95
|
+
ap results
|
96
|
+
|
97
|
+
results = @typesense.collections['companies'].documents.search(
|
98
|
+
'q' => 'Inc',
|
99
|
+
'query_by' => 'company_name',
|
100
|
+
'filter_by' => 'num_employees:<100',
|
101
|
+
'sort_by' => 'num_employees:desc'
|
102
|
+
)
|
103
|
+
ap results
|
104
|
+
|
105
|
+
##
|
106
|
+
# Cleanup
|
107
|
+
# Drop the collection
|
108
|
+
@typesense.collections['companies'].delete
|