couchbase 3.9.0-aarch64-linux-musl
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/LICENSE.txt +202 -0
- data/README.md +154 -0
- data/ext/extconf.rb +0 -0
- data/lib/active_support/cache/couchbase_store.rb +407 -0
- data/lib/couchbase/3.2/libcouchbase.so +0 -0
- data/lib/couchbase/3.3/libcouchbase.so +0 -0
- data/lib/couchbase/3.4/libcouchbase.so +0 -0
- data/lib/couchbase/4.0/libcouchbase.so +0 -0
- data/lib/couchbase/analytics_options.rb +109 -0
- data/lib/couchbase/authenticator.rb +80 -0
- data/lib/couchbase/binary_collection.rb +145 -0
- data/lib/couchbase/binary_collection_options.rb +26 -0
- data/lib/couchbase/bucket.rb +173 -0
- data/lib/couchbase/cluster.rb +555 -0
- data/lib/couchbase/cluster_registry.rb +49 -0
- data/lib/couchbase/collection.rb +770 -0
- data/lib/couchbase/collection_options.rb +401 -0
- data/lib/couchbase/config_profiles.rb +57 -0
- data/lib/couchbase/configuration.rb +58 -0
- data/lib/couchbase/datastructures/couchbase_list.rb +192 -0
- data/lib/couchbase/datastructures/couchbase_map.rb +231 -0
- data/lib/couchbase/datastructures/couchbase_queue.rb +161 -0
- data/lib/couchbase/datastructures/couchbase_set.rb +145 -0
- data/lib/couchbase/datastructures.rb +26 -0
- data/lib/couchbase/deprecations.rb +61 -0
- data/lib/couchbase/diagnostics.rb +183 -0
- data/lib/couchbase/errors.rb +438 -0
- data/lib/couchbase/fork_hooks.rb +41 -0
- data/lib/couchbase/get_replica_strategy.rb +89 -0
- data/lib/couchbase/json_transcoder.rb +41 -0
- data/lib/couchbase/key_value_scan.rb +119 -0
- data/lib/couchbase/libcouchbase.rb +6 -0
- data/lib/couchbase/logger.rb +87 -0
- data/lib/couchbase/management/analytics_index_manager.rb +1160 -0
- data/lib/couchbase/management/bucket_manager.rb +479 -0
- data/lib/couchbase/management/collection_manager.rb +515 -0
- data/lib/couchbase/management/collection_query_index_manager.rb +247 -0
- data/lib/couchbase/management/query_index_manager.rb +671 -0
- data/lib/couchbase/management/scope_search_index_manager.rb +267 -0
- data/lib/couchbase/management/search_index_manager.rb +649 -0
- data/lib/couchbase/management/user_manager.rb +639 -0
- data/lib/couchbase/management/view_index_manager.rb +332 -0
- data/lib/couchbase/management.rb +31 -0
- data/lib/couchbase/metrics/logging_meter.rb +108 -0
- data/lib/couchbase/metrics/logging_value_recorder.rb +50 -0
- data/lib/couchbase/metrics/meter.rb +27 -0
- data/lib/couchbase/metrics/noop_meter.rb +30 -0
- data/lib/couchbase/metrics/noop_value_recorder.rb +27 -0
- data/lib/couchbase/metrics/value_recorder.rb +25 -0
- data/lib/couchbase/mutation_state.rb +65 -0
- data/lib/couchbase/options.rb +3097 -0
- data/lib/couchbase/protostellar/binary_collection.rb +55 -0
- data/lib/couchbase/protostellar/bucket.rb +59 -0
- data/lib/couchbase/protostellar/client.rb +97 -0
- data/lib/couchbase/protostellar/cluster.rb +182 -0
- data/lib/couchbase/protostellar/collection.rb +156 -0
- data/lib/couchbase/protostellar/connect_options.rb +63 -0
- data/lib/couchbase/protostellar/error_handling.rb +204 -0
- data/lib/couchbase/protostellar/generated/admin/analytics/v1/analytics_pb.rb +54 -0
- data/lib/couchbase/protostellar/generated/admin/analytics/v1/analytics_services_pb.rb +51 -0
- data/lib/couchbase/protostellar/generated/admin/bucket/v1/bucket_pb.rb +42 -0
- data/lib/couchbase/protostellar/generated/admin/bucket/v1/bucket_services_pb.rb +51 -0
- data/lib/couchbase/protostellar/generated/admin/collection/v1/collection_pb.rb +38 -0
- data/lib/couchbase/protostellar/generated/admin/collection/v1/collection_services_pb.rb +52 -0
- data/lib/couchbase/protostellar/generated/admin/query/v1/query_pb.rb +42 -0
- data/lib/couchbase/protostellar/generated/admin/query/v1/query_services_pb.rb +55 -0
- data/lib/couchbase/protostellar/generated/admin/search/v1/search_pb.rb +51 -0
- data/lib/couchbase/protostellar/generated/admin/search/v1/search_services_pb.rb +67 -0
- data/lib/couchbase/protostellar/generated/analytics/v1/analytics_pb.rb +31 -0
- data/lib/couchbase/protostellar/generated/analytics/v1/analytics_services_pb.rb +40 -0
- data/lib/couchbase/protostellar/generated/internal/hooks/v1/hooks_pb.rb +51 -0
- data/lib/couchbase/protostellar/generated/internal/hooks/v1/hooks_services_pb.rb +54 -0
- data/lib/couchbase/protostellar/generated/internal/xdcr/v1/xdcr_pb.rb +46 -0
- data/lib/couchbase/protostellar/generated/internal/xdcr/v1/xdcr_services_pb.rb +56 -0
- data/lib/couchbase/protostellar/generated/kv/v1/kv_pb.rb +74 -0
- data/lib/couchbase/protostellar/generated/kv/v1/kv_services_pb.rb +93 -0
- data/lib/couchbase/protostellar/generated/query/v1/query_pb.rb +35 -0
- data/lib/couchbase/protostellar/generated/query/v1/query_services_pb.rb +40 -0
- data/lib/couchbase/protostellar/generated/routing/v2/routing_pb.rb +26 -0
- data/lib/couchbase/protostellar/generated/routing/v2/routing_services_pb.rb +43 -0
- data/lib/couchbase/protostellar/generated/search/v1/search_pb.rb +78 -0
- data/lib/couchbase/protostellar/generated/search/v1/search_services_pb.rb +41 -0
- data/lib/couchbase/protostellar/generated/transactions/v1/transactions_pb.rb +36 -0
- data/lib/couchbase/protostellar/generated/transactions/v1/transactions_services_pb.rb +66 -0
- data/lib/couchbase/protostellar/generated/view/v1/view_pb.rb +30 -0
- data/lib/couchbase/protostellar/generated/view/v1/view_services_pb.rb +39 -0
- data/lib/couchbase/protostellar/generated.rb +9 -0
- data/lib/couchbase/protostellar/management/bucket_manager.rb +67 -0
- data/lib/couchbase/protostellar/management/collection_manager.rb +94 -0
- data/lib/couchbase/protostellar/management/collection_query_index_manager.rb +124 -0
- data/lib/couchbase/protostellar/management/query_index_manager.rb +112 -0
- data/lib/couchbase/protostellar/management.rb +24 -0
- data/lib/couchbase/protostellar/request.rb +79 -0
- data/lib/couchbase/protostellar/request_behaviour.rb +42 -0
- data/lib/couchbase/protostellar/request_generator/admin/bucket.rb +124 -0
- data/lib/couchbase/protostellar/request_generator/admin/collection.rb +96 -0
- data/lib/couchbase/protostellar/request_generator/admin/query.rb +132 -0
- data/lib/couchbase/protostellar/request_generator/admin.rb +24 -0
- data/lib/couchbase/protostellar/request_generator/kv.rb +488 -0
- data/lib/couchbase/protostellar/request_generator/query.rb +133 -0
- data/lib/couchbase/protostellar/request_generator/search.rb +403 -0
- data/lib/couchbase/protostellar/request_generator.rb +26 -0
- data/lib/couchbase/protostellar/response_converter/admin/bucket.rb +55 -0
- data/lib/couchbase/protostellar/response_converter/admin/collection.rb +42 -0
- data/lib/couchbase/protostellar/response_converter/admin/query.rb +59 -0
- data/lib/couchbase/protostellar/response_converter/admin.rb +24 -0
- data/lib/couchbase/protostellar/response_converter/kv.rb +151 -0
- data/lib/couchbase/protostellar/response_converter/query.rb +84 -0
- data/lib/couchbase/protostellar/response_converter/search.rb +136 -0
- data/lib/couchbase/protostellar/response_converter.rb +26 -0
- data/lib/couchbase/protostellar/retry/action.rb +38 -0
- data/lib/couchbase/protostellar/retry/orchestrator.rb +61 -0
- data/lib/couchbase/protostellar/retry/reason.rb +67 -0
- data/lib/couchbase/protostellar/retry/strategies/best_effort.rb +49 -0
- data/lib/couchbase/protostellar/retry/strategies/fail_fast.rb +33 -0
- data/lib/couchbase/protostellar/retry/strategies.rb +27 -0
- data/lib/couchbase/protostellar/retry.rb +28 -0
- data/lib/couchbase/protostellar/scope.rb +61 -0
- data/lib/couchbase/protostellar/timeout_defaults.rb +30 -0
- data/lib/couchbase/protostellar/timeouts.rb +83 -0
- data/lib/couchbase/protostellar.rb +29 -0
- data/lib/couchbase/query_options.rb +122 -0
- data/lib/couchbase/railtie.rb +47 -0
- data/lib/couchbase/raw_binary_transcoder.rb +39 -0
- data/lib/couchbase/raw_json_transcoder.rb +40 -0
- data/lib/couchbase/raw_string_transcoder.rb +42 -0
- data/lib/couchbase/scope.rb +272 -0
- data/lib/couchbase/search_options.rb +1744 -0
- data/lib/couchbase/subdoc.rb +293 -0
- data/lib/couchbase/tracing/noop_span.rb +29 -0
- data/lib/couchbase/tracing/noop_tracer.rb +29 -0
- data/lib/couchbase/tracing/request_span.rb +34 -0
- data/lib/couchbase/tracing/request_tracer.rb +28 -0
- data/lib/couchbase/tracing/threshold_logging_span.rb +112 -0
- data/lib/couchbase/tracing/threshold_logging_tracer.rb +231 -0
- data/lib/couchbase/transcoder_flags.rb +64 -0
- data/lib/couchbase/utils/generic_logger_adapter.rb +40 -0
- data/lib/couchbase/utils/hdr_histogram.rb +55 -0
- data/lib/couchbase/utils/observability.rb +263 -0
- data/lib/couchbase/utils/observability_constants.rb +200 -0
- data/lib/couchbase/utils/stdlib_logger_adapter.rb +65 -0
- data/lib/couchbase/utils/time.rb +71 -0
- data/lib/couchbase/utils.rb +23 -0
- data/lib/couchbase/version.rb +25 -0
- data/lib/couchbase/view_options.rb +67 -0
- data/lib/couchbase.rb +32 -0
- data/lib/rails/generators/couchbase/config/config_generator.rb +29 -0
- metadata +228 -0
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright 2020-2021 Couchbase, Inc.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
require "couchbase/configuration"
|
|
18
|
+
require "couchbase/authenticator"
|
|
19
|
+
require "couchbase/bucket"
|
|
20
|
+
require "couchbase/cluster_registry"
|
|
21
|
+
|
|
22
|
+
require "couchbase/management"
|
|
23
|
+
require "couchbase/options"
|
|
24
|
+
|
|
25
|
+
require "couchbase/search_options"
|
|
26
|
+
require "couchbase/query_options"
|
|
27
|
+
require "couchbase/analytics_options"
|
|
28
|
+
require "couchbase/diagnostics"
|
|
29
|
+
|
|
30
|
+
require "couchbase/protostellar"
|
|
31
|
+
require "couchbase/utils/observability"
|
|
32
|
+
|
|
33
|
+
require "couchbase/tracing/threshold_logging_tracer"
|
|
34
|
+
require "couchbase/tracing/noop_tracer"
|
|
35
|
+
require "couchbase/metrics/noop_meter"
|
|
36
|
+
require "couchbase/metrics/logging_meter"
|
|
37
|
+
|
|
38
|
+
module Couchbase
|
|
39
|
+
# The main entry point when connecting to a Couchbase cluster.
|
|
40
|
+
class Cluster
|
|
41
|
+
alias inspect to_s
|
|
42
|
+
|
|
43
|
+
# Connect to the Couchbase cluster
|
|
44
|
+
#
|
|
45
|
+
# @overload connect(connection_string_or_config, options)
|
|
46
|
+
# @param [String, Configuration] connection_string_or_config connection string used to locate the Couchbase Cluster
|
|
47
|
+
# @param [Options::Cluster] options custom options when creating the cluster connection
|
|
48
|
+
#
|
|
49
|
+
# @overload connect(connection_string_or_config, username, password, options)
|
|
50
|
+
# Shortcut for {PasswordAuthenticator}
|
|
51
|
+
# @param [String] connection_string_or_config connection string used to locate the Couchbase Cluster
|
|
52
|
+
# @param [String] username name of the user
|
|
53
|
+
# @param [String] password password of the user
|
|
54
|
+
# @param [Options::Cluster, nil] options custom options when creating the cluster connection
|
|
55
|
+
#
|
|
56
|
+
# @example Explicitly create options object and initialize PasswordAuthenticator internally
|
|
57
|
+
# options = Options::Cluster.new
|
|
58
|
+
# options.authenticate("Administrator", "password")
|
|
59
|
+
# Cluster.connect("couchbase://localhost", options)
|
|
60
|
+
#
|
|
61
|
+
# @example Pass authenticator object to Options
|
|
62
|
+
# Cluster.connect("couchbase://localhost",
|
|
63
|
+
# Options::Cluster(authenticator: PasswordAuthenticator.new("Administrator", "password")))
|
|
64
|
+
#
|
|
65
|
+
# @example Shorter version, more useful for interactive sessions
|
|
66
|
+
# Cluster.connect("couchbase://localhost", "Administrator", "password")
|
|
67
|
+
#
|
|
68
|
+
# @example Authentication with TLS client certificate (note +couchbases://+ schema)
|
|
69
|
+
# Cluster.connect("couchbases://localhost?trust_certificate=/tmp/ca.pem",
|
|
70
|
+
# Options::Cluster(authenticator: CertificateAuthenticator.new("/tmp/certificate.pem", "/tmp/private.key")))
|
|
71
|
+
#
|
|
72
|
+
# @see https://docs.couchbase.com/server/current/manage/manage-security/configure-client-certificates.html
|
|
73
|
+
#
|
|
74
|
+
# @note The +couchbase2://+ scheme is currently at stability _uncommitted_
|
|
75
|
+
#
|
|
76
|
+
# @return [Cluster]
|
|
77
|
+
def self.connect(connection_string_or_config, *options)
|
|
78
|
+
connection_string = if connection_string_or_config.is_a?(Configuration)
|
|
79
|
+
connection_string_or_config.connection_string
|
|
80
|
+
else
|
|
81
|
+
connection_string_or_config
|
|
82
|
+
end
|
|
83
|
+
if connection_string =~ /\Acouchbases?:\/\/.*\z/i || !connection_string.include?("://")
|
|
84
|
+
Cluster.new(connection_string_or_config, *options)
|
|
85
|
+
else
|
|
86
|
+
ClusterRegistry.instance.connect(connection_string_or_config, *options)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Returns an instance of the {Bucket}
|
|
91
|
+
#
|
|
92
|
+
# @param [String] name name of the bucket
|
|
93
|
+
#
|
|
94
|
+
# @return [Bucket]
|
|
95
|
+
def bucket(name)
|
|
96
|
+
Bucket.new(@backend, name, @observability)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Updates the authenticator used for this cluster connection
|
|
100
|
+
#
|
|
101
|
+
# @param [PasswordAuthenticator, CertificateAuthenticator, JWTAuthenticator] authenticator the new authenticator
|
|
102
|
+
def update_authenticator(authenticator)
|
|
103
|
+
credentials = {}
|
|
104
|
+
|
|
105
|
+
case authenticator
|
|
106
|
+
when PasswordAuthenticator
|
|
107
|
+
credentials[:username] = authenticator.username
|
|
108
|
+
raise ArgumentError, "missing username" unless credentials[:username]
|
|
109
|
+
|
|
110
|
+
credentials[:password] = authenticator.password
|
|
111
|
+
raise ArgumentError, "missing password" unless credentials[:password]
|
|
112
|
+
|
|
113
|
+
when CertificateAuthenticator
|
|
114
|
+
credentials[:certificate_path] = authenticator.certificate_path
|
|
115
|
+
raise ArgumentError, "missing certificate path" unless credentials[:certificate_path]
|
|
116
|
+
|
|
117
|
+
credentials[:key_path] = authenticator.key_path
|
|
118
|
+
raise ArgumentError, "missing key path" unless credentials[:key_path]
|
|
119
|
+
|
|
120
|
+
when JWTAuthenticator
|
|
121
|
+
credentials[:jwt] = authenticator.token
|
|
122
|
+
raise ArgumentError, "missing token" unless credentials[:jwt]
|
|
123
|
+
|
|
124
|
+
else
|
|
125
|
+
raise ArgumentError, "argument must be an authenticator"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
@backend.update_credentials(credentials)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Performs a query against the query (N1QL) services
|
|
132
|
+
#
|
|
133
|
+
# @param [String] statement the N1QL query statement
|
|
134
|
+
# @param [Options::Query] options the custom options for this query
|
|
135
|
+
#
|
|
136
|
+
# @example Select first ten hotels from travel sample dataset
|
|
137
|
+
# cluster.query("SELECT * FROM `travel-sample` WHERE type = $type LIMIT 10",
|
|
138
|
+
# Options::Query(named_parameters: {type: "hotel"}, metrics: true))
|
|
139
|
+
#
|
|
140
|
+
# @example Execute query with consistency requirement. Make sure that the index is in sync with selected mutation
|
|
141
|
+
# res = collection.upsert("user:42", {
|
|
142
|
+
# "name" => "Brass Doorknob",
|
|
143
|
+
# "email" => "brass.doorknob@example.com",
|
|
144
|
+
# })
|
|
145
|
+
# cluster.query("SELECT name, email FROM `mybucket`",
|
|
146
|
+
# Options::Query(consistent_with: MutationState.new(res.mutation_token)))
|
|
147
|
+
#
|
|
148
|
+
# @return [QueryResult]
|
|
149
|
+
def query(statement, options = Options::Query::DEFAULT)
|
|
150
|
+
@observability.record_operation(Observability::OP_QUERY, options.parent_span, self, :query) do |obs_handler|
|
|
151
|
+
obs_handler.add_query_statement(statement, options)
|
|
152
|
+
|
|
153
|
+
resp = @backend.document_query(statement, options.to_backend, obs_handler)
|
|
154
|
+
|
|
155
|
+
QueryResult.new do |res|
|
|
156
|
+
res.meta_data = QueryMetaData.new do |meta|
|
|
157
|
+
meta.status = resp[:meta][:status]
|
|
158
|
+
meta.request_id = resp[:meta][:request_id]
|
|
159
|
+
meta.client_context_id = resp[:meta][:client_context_id]
|
|
160
|
+
meta.signature = JSON.parse(resp[:meta][:signature]) if resp[:meta][:signature]
|
|
161
|
+
meta.profile = JSON.parse(resp[:meta][:profile]) if resp[:meta][:profile]
|
|
162
|
+
meta.metrics = QueryMetrics.new do |metrics|
|
|
163
|
+
if resp[:meta][:metrics]
|
|
164
|
+
metrics.elapsed_time = resp[:meta][:metrics][:elapsed_time]
|
|
165
|
+
metrics.execution_time = resp[:meta][:metrics][:execution_time]
|
|
166
|
+
metrics.sort_count = resp[:meta][:metrics][:sort_count]
|
|
167
|
+
metrics.result_count = resp[:meta][:metrics][:result_count]
|
|
168
|
+
metrics.result_size = resp[:meta][:metrics][:result_size]
|
|
169
|
+
metrics.mutation_count = resp[:meta][:metrics][:mutation_count]
|
|
170
|
+
metrics.error_count = resp[:meta][:metrics][:error_count]
|
|
171
|
+
metrics.warning_count = resp[:meta][:metrics][:warning_count]
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
meta.warnings = resp[:warnings].map { |warn| QueryWarning.new(warn[:code], warn[:message]) } if resp[:warnings]
|
|
175
|
+
end
|
|
176
|
+
res.instance_variable_set(:@rows, resp[:rows])
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Performs an analytics query
|
|
182
|
+
#
|
|
183
|
+
# @param [String] statement the N1QL query statement
|
|
184
|
+
# @param [Options::Analytics] options the custom options for this query
|
|
185
|
+
#
|
|
186
|
+
# @example Select name of the given user
|
|
187
|
+
# cluster.analytics_query("SELECT u.name AS uname FROM GleambookUsers u WHERE u.id = $user_id ",
|
|
188
|
+
# Options::Analytics(named_parameters: {user_id: 2}))
|
|
189
|
+
#
|
|
190
|
+
# @return [AnalyticsResult]
|
|
191
|
+
def analytics_query(statement, options = Options::Analytics::DEFAULT)
|
|
192
|
+
@observability.record_operation(Observability::OP_ANALYTICS_QUERY, options.parent_span, self, :analytics) do |obs_handler|
|
|
193
|
+
obs_handler.add_query_statement(statement, options)
|
|
194
|
+
|
|
195
|
+
resp = @backend.document_analytics(statement, options.to_backend, obs_handler)
|
|
196
|
+
|
|
197
|
+
AnalyticsResult.new do |res|
|
|
198
|
+
res.transcoder = options.transcoder
|
|
199
|
+
res.meta_data = AnalyticsMetaData.new do |meta|
|
|
200
|
+
meta.status = resp[:meta][:status]
|
|
201
|
+
meta.request_id = resp[:meta][:request_id]
|
|
202
|
+
meta.client_context_id = resp[:meta][:client_context_id]
|
|
203
|
+
meta.signature = JSON.parse(resp[:meta][:signature]) if resp[:meta][:signature]
|
|
204
|
+
meta.profile = JSON.parse(resp[:meta][:profile]) if resp[:meta][:profile]
|
|
205
|
+
meta.metrics = AnalyticsMetrics.new do |metrics|
|
|
206
|
+
if resp[:meta][:metrics]
|
|
207
|
+
metrics.elapsed_time = resp[:meta][:metrics][:elapsed_time]
|
|
208
|
+
metrics.execution_time = resp[:meta][:metrics][:execution_time]
|
|
209
|
+
metrics.result_count = resp[:meta][:metrics][:result_count]
|
|
210
|
+
metrics.result_size = resp[:meta][:metrics][:result_size]
|
|
211
|
+
metrics.error_count = resp[:meta][:metrics][:error_count]
|
|
212
|
+
metrics.warning_count = resp[:meta][:metrics][:warning_count]
|
|
213
|
+
metrics.processed_objects = resp[:meta][:metrics][:processed_objects]
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
res[:warnings] = resp[:warnings].map { |warn| AnalyticsWarning.new(warn[:code], warn[:message]) } if resp[:warnings]
|
|
217
|
+
end
|
|
218
|
+
res.instance_variable_set(:@rows, resp[:rows])
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Performs a Full Text Search (FTS) query
|
|
224
|
+
#
|
|
225
|
+
# @param [String] index_name the name of the search index
|
|
226
|
+
# @param [SearchQuery] query the query tree
|
|
227
|
+
# @param [Options::Search] options the custom options for this search query
|
|
228
|
+
#
|
|
229
|
+
# @example Return first 10 results of "hop beer" query and request highlighting
|
|
230
|
+
# cluster.search_query("beer_index", Cluster::SearchQuery.match_phrase("hop beer"),
|
|
231
|
+
# Options::Search(
|
|
232
|
+
# limit: 10,
|
|
233
|
+
# fields: %w[name],
|
|
234
|
+
# highlight_style: :html,
|
|
235
|
+
# highlight_fields: %w[name description]
|
|
236
|
+
# ))
|
|
237
|
+
#
|
|
238
|
+
# @return [SearchResult]
|
|
239
|
+
def search_query(index_name, query, options = Options::Search::DEFAULT)
|
|
240
|
+
@observability.record_operation(Observability::OP_SEARCH_QUERY, options.parent_span, self, :search) do |obs_handler|
|
|
241
|
+
resp = @backend.document_search(nil, nil, index_name, JSON.generate(query), {}, options.to_backend, obs_handler)
|
|
242
|
+
convert_search_result(resp, options)
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Performs a request against the Full Text Search (FTS) service.
|
|
247
|
+
#
|
|
248
|
+
# @param [String] index_name the name of the search index
|
|
249
|
+
# @param [SearchRequest] search_request the request
|
|
250
|
+
# @param [Options::Search] options the custom options for this search request
|
|
251
|
+
#
|
|
252
|
+
# @return [SearchResult]
|
|
253
|
+
def search(index_name, search_request, options = Options::Search::DEFAULT)
|
|
254
|
+
@observability.record_operation(Observability::OP_SEARCH_QUERY, options.parent_span, self, :search) do |obs_handler|
|
|
255
|
+
encoded_query, encoded_req = search_request.to_backend
|
|
256
|
+
resp = @backend.document_search(nil, nil, index_name, encoded_query, encoded_req, options.to_backend(show_request: false),
|
|
257
|
+
obs_handler)
|
|
258
|
+
convert_search_result(resp, options)
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# @return [Management::UserManager]
|
|
263
|
+
def users
|
|
264
|
+
Management::UserManager.new(@backend, @observability)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# @return [Management::BucketManager]
|
|
268
|
+
def buckets
|
|
269
|
+
Management::BucketManager.new(@backend, @observability)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# @return [Management::QueryIndexManager]
|
|
273
|
+
def query_indexes
|
|
274
|
+
Management::QueryIndexManager.new(@backend, @observability)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# @return [Management::AnalyticsIndexManager]
|
|
278
|
+
def analytics_indexes
|
|
279
|
+
Management::AnalyticsIndexManager.new(@backend, @observability)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# @return [Management::SearchIndexManager]
|
|
283
|
+
def search_indexes
|
|
284
|
+
Management::SearchIndexManager.new(@backend, @observability)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Closes all connections to services and free allocated resources
|
|
288
|
+
#
|
|
289
|
+
# @return [void]
|
|
290
|
+
def disconnect
|
|
291
|
+
@backend.close
|
|
292
|
+
@observability.close
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# Creates diagnostic report that can be used to determine the health of the network connections.
|
|
296
|
+
#
|
|
297
|
+
# It does not proactively perform any I/O against the network
|
|
298
|
+
#
|
|
299
|
+
# @param [Options::Diagnostics] options
|
|
300
|
+
#
|
|
301
|
+
# @return [DiagnosticsResult]
|
|
302
|
+
def diagnostics(options = Options::Diagnostics::DEFAULT)
|
|
303
|
+
resp = @backend.diagnostics(options.report_id)
|
|
304
|
+
DiagnosticsResult.new do |res|
|
|
305
|
+
res.version = resp[:version]
|
|
306
|
+
res.id = resp[:id]
|
|
307
|
+
res.sdk = resp[:sdk]
|
|
308
|
+
resp[:services].each do |type, svcs|
|
|
309
|
+
res.services[type] = svcs.map do |svc|
|
|
310
|
+
DiagnosticsResult::ServiceInfo.new do |info|
|
|
311
|
+
info.id = svc[:id]
|
|
312
|
+
info.state = svc[:state]
|
|
313
|
+
info.last_activity_us = svc[:last_activity_us]
|
|
314
|
+
info.remote = svc[:remote]
|
|
315
|
+
info.local = svc[:local]
|
|
316
|
+
info.details = svc[:details]
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Performs application-level ping requests against services in the couchbase cluster
|
|
324
|
+
#
|
|
325
|
+
# @param [Options::Ping] options
|
|
326
|
+
#
|
|
327
|
+
# @return [PingResult]
|
|
328
|
+
def ping(options = Options::Ping::DEFAULT)
|
|
329
|
+
@observability.record_operation(Observability::OP_PING, options.parent_span, self) do |_obs_handler|
|
|
330
|
+
resp = @backend.ping(nil, options.to_backend)
|
|
331
|
+
PingResult.new do |res|
|
|
332
|
+
res.version = resp[:version]
|
|
333
|
+
res.id = resp[:id]
|
|
334
|
+
res.sdk = resp[:sdk]
|
|
335
|
+
resp[:services].each do |type, svcs|
|
|
336
|
+
res.services[type] = svcs.map do |svc|
|
|
337
|
+
PingResult::ServiceInfo.new do |info|
|
|
338
|
+
info.id = svc[:id]
|
|
339
|
+
info.state = svc[:state]
|
|
340
|
+
info.latency = svc[:latency]
|
|
341
|
+
info.remote = svc[:remote]
|
|
342
|
+
info.local = svc[:local]
|
|
343
|
+
info.error = svc[:error]
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
# Waits until the cluster reaches the desired state, actively pinging its services
|
|
352
|
+
#
|
|
353
|
+
# +Thread#raise+ does not interrupt the wait. It ends on success, on +timeout+, or when the cluster is disconnected.
|
|
354
|
+
#
|
|
355
|
+
# @param [Integer, #in_milliseconds] timeout the maximum time to wait
|
|
356
|
+
# @param [Options::WaitUntilReady] options
|
|
357
|
+
#
|
|
358
|
+
# @return [void]
|
|
359
|
+
#
|
|
360
|
+
# @raise [Error::UnambiguousTimeout] if the desired state is not reached within +timeout+
|
|
361
|
+
# @raise [Error::ClusterClosed] if the cluster has been disconnected
|
|
362
|
+
# @raise [Error::InvalidArgument] if +timeout+ is negative or longer than 365 days, or +options+ name an
|
|
363
|
+
# unsupported desired state or service type
|
|
364
|
+
def wait_until_ready(timeout, options = Options::WaitUntilReady::DEFAULT)
|
|
365
|
+
@backend.wait_until_ready(nil, Utils::Time.extract_duration(timeout), options.to_backend)
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
private
|
|
369
|
+
|
|
370
|
+
# Initialize {Cluster} object
|
|
371
|
+
#
|
|
372
|
+
# @overload new(connection_string, options)
|
|
373
|
+
# @param [String] connection_string connection string used to locate the Couchbase Cluster
|
|
374
|
+
# @param [Options::Cluster] options custom options when creating the cluster connection
|
|
375
|
+
#
|
|
376
|
+
# @overload new(connection_string, username, password, options)
|
|
377
|
+
# Shortcut for {PasswordAuthenticator}
|
|
378
|
+
# @param [String] connection_string connection string used to locate the Couchbase Cluster
|
|
379
|
+
# @param [String] username name of the user
|
|
380
|
+
# @param [String] password password of the user
|
|
381
|
+
# @param [Options::Cluster, nil] options custom options when creating the cluster connection
|
|
382
|
+
#
|
|
383
|
+
# @overload new(configuration)
|
|
384
|
+
# @param [Configuration] configuration configuration object
|
|
385
|
+
def initialize(connection_string, *args)
|
|
386
|
+
credentials = {}
|
|
387
|
+
open_options = {}
|
|
388
|
+
|
|
389
|
+
if connection_string.is_a?(Configuration)
|
|
390
|
+
options = connection_string
|
|
391
|
+
connection_string = options.connection_string
|
|
392
|
+
credentials[:username] = options.username
|
|
393
|
+
credentials[:password] = options.password
|
|
394
|
+
raise ArgumentError, "missing connection_string" unless connection_string
|
|
395
|
+
raise ArgumentError, "missing username" unless credentials[:username]
|
|
396
|
+
raise ArgumentError, "missing password" unless credentials[:password]
|
|
397
|
+
else
|
|
398
|
+
options = args.shift
|
|
399
|
+
case options
|
|
400
|
+
when String
|
|
401
|
+
credentials[:username] = options
|
|
402
|
+
credentials[:password] = args.shift
|
|
403
|
+
raise ArgumentError, "missing username" unless credentials[:username]
|
|
404
|
+
raise ArgumentError, "missing password" unless credentials[:password]
|
|
405
|
+
when Options::Cluster
|
|
406
|
+
tracer = options.tracer
|
|
407
|
+
meter = options.meter
|
|
408
|
+
open_options = options.to_backend || {}
|
|
409
|
+
authenticator = options.authenticator
|
|
410
|
+
case authenticator
|
|
411
|
+
when PasswordAuthenticator
|
|
412
|
+
credentials[:username] = authenticator.username
|
|
413
|
+
raise ArgumentError, "missing username" unless credentials[:username]
|
|
414
|
+
|
|
415
|
+
credentials[:password] = authenticator.password
|
|
416
|
+
raise ArgumentError, "missing password" unless credentials[:password]
|
|
417
|
+
|
|
418
|
+
open_options[:allowed_sasl_mechanisms] = authenticator.allowed_sasl_mechanisms
|
|
419
|
+
when CertificateAuthenticator
|
|
420
|
+
credentials[:certificate_path] = authenticator.certificate_path
|
|
421
|
+
raise ArgumentError, "missing certificate path" unless credentials[:certificate_path]
|
|
422
|
+
|
|
423
|
+
credentials[:key_path] = authenticator.key_path
|
|
424
|
+
raise ArgumentError, "missing key path" unless credentials[:key_path]
|
|
425
|
+
when JWTAuthenticator
|
|
426
|
+
credentials[:jwt] = authenticator.token
|
|
427
|
+
raise ArgumentError, "missing token" unless credentials[:jwt]
|
|
428
|
+
else
|
|
429
|
+
raise ArgumentError, "options must have authenticator configured"
|
|
430
|
+
end
|
|
431
|
+
else
|
|
432
|
+
raise ArgumentError, "unexpected second argument, have to be String or Options::Cluster"
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
@backend = Backend.new
|
|
437
|
+
|
|
438
|
+
@observability = Observability::Wrapper.new(backend: @backend) do |w|
|
|
439
|
+
w.tracer = if !open_options[:enable_tracing].nil? && !open_options[:enable_tracing]
|
|
440
|
+
Tracing::NoopTracer.new
|
|
441
|
+
elsif tracer.nil?
|
|
442
|
+
Tracing::ThresholdLoggingTracer.new(
|
|
443
|
+
emit_interval: open_options[:threshold_emit_interval],
|
|
444
|
+
kv_threshold: open_options[:key_value_threshold],
|
|
445
|
+
query_threshold: open_options[:query_threshold],
|
|
446
|
+
views_threshold: open_options[:view_threshold],
|
|
447
|
+
search_threshold: open_options[:search_threshold],
|
|
448
|
+
analytics_threshold: open_options[:analytics_threshold],
|
|
449
|
+
management_threshold: open_options[:management_threshold],
|
|
450
|
+
sample_size: open_options[:threshold_sample_size],
|
|
451
|
+
)
|
|
452
|
+
else
|
|
453
|
+
tracer
|
|
454
|
+
end
|
|
455
|
+
w.meter = if !open_options[:enable_metrics].nil? && !open_options[:enable_metrics]
|
|
456
|
+
Metrics::NoopMeter.new
|
|
457
|
+
elsif meter.nil?
|
|
458
|
+
Metrics::LoggingMeter.new(
|
|
459
|
+
emit_interval: open_options[:metrics_emit_interval],
|
|
460
|
+
)
|
|
461
|
+
else
|
|
462
|
+
meter
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
@backend.open(connection_string, credentials, open_options)
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
# @api private
|
|
470
|
+
def convert_search_result(resp, options)
|
|
471
|
+
SearchResult.new do |res|
|
|
472
|
+
res.meta_data = SearchMetaData.new do |meta|
|
|
473
|
+
meta.metrics.max_score = resp[:meta_data][:metrics][:max_score]
|
|
474
|
+
meta.metrics.error_partition_count = resp[:meta_data][:metrics][:error_partition_count]
|
|
475
|
+
meta.metrics.success_partition_count = resp[:meta_data][:metrics][:success_partition_count]
|
|
476
|
+
meta.metrics.took = resp[:meta_data][:metrics][:took]
|
|
477
|
+
meta.metrics.total_rows = resp[:meta_data][:metrics][:total_rows]
|
|
478
|
+
meta.errors = resp[:meta_data][:errors]
|
|
479
|
+
end
|
|
480
|
+
res.rows = resp[:rows].map do |r|
|
|
481
|
+
SearchRow.new do |row|
|
|
482
|
+
row.transcoder = options.transcoder
|
|
483
|
+
row.index = r[:index]
|
|
484
|
+
row.id = r[:id]
|
|
485
|
+
row.score = r[:score]
|
|
486
|
+
row.fragments = r[:fragments]
|
|
487
|
+
unless r[:locations].empty?
|
|
488
|
+
row.locations = SearchRowLocations.new(
|
|
489
|
+
r[:locations].map do |loc|
|
|
490
|
+
SearchRowLocation.new do |location|
|
|
491
|
+
location.field = loc[:field]
|
|
492
|
+
location.term = loc[:term]
|
|
493
|
+
location.position = loc[:position]
|
|
494
|
+
location.start_offset = loc[:start_offset]
|
|
495
|
+
location.end_offset = loc[:end_offset]
|
|
496
|
+
location.array_positions = loc[:array_positions]
|
|
497
|
+
end
|
|
498
|
+
end,
|
|
499
|
+
)
|
|
500
|
+
end
|
|
501
|
+
row.instance_variable_set(:@fields, r[:fields])
|
|
502
|
+
row.explanation = JSON.parse(r[:explanation]) if r[:explanation]
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
if resp[:facets]
|
|
506
|
+
res.facets = resp[:facets].each_with_object({}) do |(k, v), o|
|
|
507
|
+
facet = case options.facets[k]
|
|
508
|
+
when SearchFacet::SearchFacetTerm
|
|
509
|
+
SearchFacetResult::TermFacetResult.new do |f|
|
|
510
|
+
f.terms =
|
|
511
|
+
if v[:terms]
|
|
512
|
+
v[:terms].map do |t|
|
|
513
|
+
SearchFacetResult::TermFacetResult::TermFacet.new(t[:term], t[:count])
|
|
514
|
+
end
|
|
515
|
+
else
|
|
516
|
+
[]
|
|
517
|
+
end
|
|
518
|
+
end
|
|
519
|
+
when SearchFacet::SearchFacetDateRange
|
|
520
|
+
SearchFacetResult::DateRangeFacetResult.new do |f|
|
|
521
|
+
f.date_ranges =
|
|
522
|
+
if v[:date_ranges]
|
|
523
|
+
v[:date_ranges].map do |r|
|
|
524
|
+
SearchFacetResult::DateRangeFacetResult::DateRangeFacet.new(r[:name], r[:count], r[:start_time], r[:end_time])
|
|
525
|
+
end
|
|
526
|
+
else
|
|
527
|
+
[]
|
|
528
|
+
end
|
|
529
|
+
end
|
|
530
|
+
when SearchFacet::SearchFacetNumericRange
|
|
531
|
+
SearchFacetResult::NumericRangeFacetResult.new do |f|
|
|
532
|
+
f.numeric_ranges =
|
|
533
|
+
if v[:numeric_ranges]
|
|
534
|
+
v[:numeric_ranges].map do |r|
|
|
535
|
+
SearchFacetResult::NumericRangeFacetResult::NumericRangeFacet.new(r[:name], r[:count], r[:min], r[:max])
|
|
536
|
+
end
|
|
537
|
+
else
|
|
538
|
+
[]
|
|
539
|
+
end
|
|
540
|
+
end
|
|
541
|
+
else
|
|
542
|
+
next # ignore unknown facet result
|
|
543
|
+
end
|
|
544
|
+
facet.name = v[:name]
|
|
545
|
+
facet.field = v[:field]
|
|
546
|
+
facet.total = v[:total]
|
|
547
|
+
facet.missing = v[:missing]
|
|
548
|
+
facet.other = v[:other]
|
|
549
|
+
o[k] = facet
|
|
550
|
+
end
|
|
551
|
+
end
|
|
552
|
+
end
|
|
553
|
+
end
|
|
554
|
+
end
|
|
555
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright 2023 Couchbase, Inc.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
require "singleton"
|
|
18
|
+
|
|
19
|
+
require "couchbase/errors"
|
|
20
|
+
|
|
21
|
+
module Couchbase
|
|
22
|
+
class ClusterRegistry
|
|
23
|
+
include Singleton
|
|
24
|
+
|
|
25
|
+
def initialize
|
|
26
|
+
@handlers = {}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def connect(connection_string_or_config, *options)
|
|
30
|
+
connection_string = if connection_string_or_config.is_a?(Configuration)
|
|
31
|
+
connection_string_or_config.connection_string
|
|
32
|
+
else
|
|
33
|
+
connection_string_or_config
|
|
34
|
+
end
|
|
35
|
+
@handlers.each do |regexp, cluster_class|
|
|
36
|
+
return cluster_class.connect(connection_string_or_config, *options) if regexp.match?(connection_string)
|
|
37
|
+
end
|
|
38
|
+
raise(Error::FeatureNotAvailable, "Connection string '#{connection_string}' not supported.")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def register_connection_handler(regexp, cluster_class)
|
|
42
|
+
@handlers[regexp] = cluster_class
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def deregister_connection_handler(regexp)
|
|
46
|
+
@handlers.delete(regexp)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|