couchbase-orm 3.1.0 → 3.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9ece6e35ae742239bedb003f5865293f86164ba2899801d8f9b18efe93266e09
4
- data.tar.gz: 85f84fa26a635d3fa319e2c5726eb429b52e576689690e61c50ace5b79333551
3
+ metadata.gz: 253e0ee05a3522f641294d615724214c6b03ebfd189f008544e9a41f0e671916
4
+ data.tar.gz: 7628e1f89db8f7649dd6795145b238a06af87cb1901e30337670c3f15b87378c
5
5
  SHA512:
6
- metadata.gz: beea3033581402385e821e1d4512d0a3ae9f76df5ebd571c8907c67a216bf0680a065c1276387160208ca2a49e99310f88b7fd3b2e2147b1b5b1c92e1b4b6fba
7
- data.tar.gz: 9dffa58a606270a0a0c7775b91f6af34cceb0c1bca77ac37f5eec94784e217c9e8322beed82b42e33366cae50e32b65175bb4dc1c5ea9290a305bdaa777578fc
6
+ metadata.gz: af199f51fb0fd808ac4920ca8eb16d50b8d6afa89a23cd311d5e39e92df87f93914368267afd373e7d8d282e50e517b892039008f3a2d234ef5bf1ea2b63bb83
7
+ data.tar.gz: b0703729ad90b504026467377aecdeaed17434010f044ede50d0645ef1e51d9dadef595c31ad1f1990d7366ba7f4101c934c716f4f8cb2152a70d0f846ad7e88
@@ -20,23 +20,15 @@ jobs:
20
20
  - ruby: '3.3'
21
21
  active-model: '7.2'
22
22
  couchbase: '7.2.3'
23
- - ruby: '3.2'
24
- active-model: '7.2.0'
25
- couchbase: '7.1.1'
26
- - ruby: '3.2'
27
- active-model: '7.1.0'
28
- couchbase: '6.6.5'
29
- - ruby: '3.1'
30
- active-model: '7.1.0'
31
- couchbase: '7.1.0'
32
23
  fail-fast: false
33
24
  runs-on: ubuntu-24.04
34
25
  name: ${{ matrix.ruby }} rails-${{ matrix.active-model }} couchbase-${{ matrix.couchbase }}
35
26
  steps:
36
27
  - uses: actions/checkout@v3
37
- - run: sudo apt-get update && sudo apt-get install libevent-dev libev-dev python3-httplib2
38
- - run: wget http://security.ubuntu.com/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb
39
- - run: sudo apt install ./libtinfo5_6.3-2ubuntu0.1_amd64.deb
28
+ - run: |
29
+ sudo apt-get update
30
+ sudo apt-get install -y libevent-dev libev-dev python3-httplib2
31
+ sudo apt-get install -y libtinfo5 || sudo apt-get install -y libtinfo6
40
32
  - uses: ruby/setup-ruby@v1
41
33
  with:
42
34
  ruby-version: ${{ matrix.ruby }}
@@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
15
15
  gem.summary = 'Couchbase ORM for Rails'
16
16
  gem.description = 'A Couchbase ORM for Rails'
17
17
 
18
- gem.required_ruby_version = '>= 3.1.0'
18
+ gem.required_ruby_version = '>= 3.3.0'
19
19
  gem.require_paths = ['lib']
20
20
 
21
21
  gem.add_runtime_dependency 'activemodel', ENV['ACTIVE_MODEL_VERSION'] || '>= 7.1'
@@ -147,6 +147,47 @@ docs = N1QLTest.by_custom_rating_values(key: [[1, 2]]).collect { |ob| ob.name }
147
147
 
148
148
  In the above examples, the `collect` method is used to extract the `name` attribute from each document in the result set.
149
149
 
150
+ ## 7.8 Prepared Statement Plan Caching
151
+
152
+ Couchbase Server can cache the query execution plan for a SQL++ query so that subsequent executions skip the planning step. This is controlled by the `adhoc` query option: `adhoc: false` tells the server to prepare and cache the plan on first execution and reuse it on subsequent ones.
153
+
154
+ ### Default behaviour
155
+
156
+ By default CouchbaseOrm runs queries with `adhoc: true` (the Couchbase SDK default), meaning no plan caching. This preserves the existing behaviour — you opt into plan caching explicitly.
157
+
158
+ ### Enabling caching for a specific call
159
+
160
+ Pass `adhoc: false` directly to the query method to prepare and cache the plan (useful for frequently repeated queries):
161
+
162
+ ```ruby
163
+ # Cache the plan for this query
164
+ N1QLTest.by_rating(key: 1, adhoc: false)
165
+
166
+ # Relation query with plan caching
167
+ User.where(country: 'FR').with(adhoc: false).to_a
168
+ ```
169
+
170
+ ### Enabling caching for a specific `n1ql` definition
171
+
172
+ Set `adhoc: false` in the macro options to always cache the plan for that particular query:
173
+
174
+ ```ruby
175
+ n1ql :by_stable_filter, emit_key: [:name], adhoc: false
176
+ ```
177
+
178
+ ### Changing the global default
179
+
180
+ Override the thread-local config to change the default for all queries in the current thread:
181
+
182
+ ```ruby
183
+ # Enable plan caching for all queries in this thread
184
+ CouchbaseOrm::N1ql.config(adhoc: false)
185
+ ```
186
+
187
+ ### Override priority
188
+
189
+ From highest to lowest: **per-call kwarg** > **per-`n1ql`-definition option** > **`N1ql.config`** > **default (`true`)**.
190
+
150
191
  ## 7.7 Indexing for SQL++
151
192
 
152
193
  To optimize the performance of SQL++ queries, it's important to create appropriate indexes on the fields used in the query conditions. Couchbase Server provides a way to create indexes using the Index service.
@@ -9,6 +9,7 @@ module CouchbaseOrm
9
9
  extend ActiveSupport::Concern
10
10
  NO_VALUE = :no_value_specified
11
11
  DEFAULT_SCAN_CONSISTENCY = :request_plus
12
+ DEFAULT_ADHOC = true
12
13
  # sanitize for injection query
13
14
  def self.sanitize(value)
14
15
  if value.is_a?(String)
@@ -22,9 +23,10 @@ module CouchbaseOrm
22
23
 
23
24
  def self.config(new_config = nil)
24
25
  Thread.current['__couchbaseorm_n1ql_config__'] = new_config if new_config
25
- Thread.current['__couchbaseorm_n1ql_config__'] || {
26
- scan_consistency: DEFAULT_SCAN_CONSISTENCY
27
- }
26
+ {
27
+ scan_consistency: DEFAULT_SCAN_CONSISTENCY,
28
+ adhoc: DEFAULT_ADHOC
29
+ }.merge(Thread.current['__couchbaseorm_n1ql_config__'] || {})
28
30
  end
29
31
 
30
32
  module ClassMethods
@@ -57,7 +59,10 @@ module CouchbaseOrm
57
59
  @indexes[name] = method_opts
58
60
 
59
61
  singleton_class.__send__(:define_method, name) do |key: NO_VALUE, **opts, &result_modifier|
60
- opts = options.merge(opts).reverse_merge(scan_consistency: CouchbaseOrm::N1ql.config[:scan_consistency])
62
+ opts = options.merge(opts).reverse_merge(
63
+ scan_consistency: CouchbaseOrm::N1ql.config[:scan_consistency],
64
+ adhoc: CouchbaseOrm::N1ql.config[:adhoc]
65
+ )
61
66
  values = key == NO_VALUE ? NO_VALUE : convert_values(method_opts[:emit_key], key)
62
67
  current_query = run_query(method_opts[:emit_key], values, query_fn, custom_order: custom_order, **opts.except(:include_docs, :key))
63
68
  if result_modifier
@@ -3,7 +3,7 @@ module CouchbaseOrm
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  class CouchbaseOrm_Relation
6
- def initialize(model:, where: where = nil, order: order = nil, limit: limit = nil, _not: _not = false, strict_loading: strict_loading = false)
6
+ def initialize(model:, where: where = nil, order: order = nil, limit: limit = nil, _not: _not = false, strict_loading: strict_loading = false, query_options: query_options = {})
7
7
  CouchbaseOrm::logger.debug "CouchbaseOrm_Relation init: #{model} where:#{where.inspect} not:#{_not.inspect} order:#{order.inspect} limit: #{limit} strict_loading: #{strict_loading}"
8
8
  @model = model
9
9
  @limit = limit
@@ -12,6 +12,7 @@ module CouchbaseOrm
12
12
  @order = merge_order(**order) if order
13
13
  @where = merge_where(where, _not) if where
14
14
  @strict_loading = strict_loading
15
+ @query_options = query_options || {}
15
16
  CouchbaseOrm::logger.debug "- #{to_s}"
16
17
  end
17
18
 
@@ -70,6 +71,10 @@ module CouchbaseOrm
70
71
  !!@strict_loading
71
72
  end
72
73
 
74
+ def with(opts = {})
75
+ CouchbaseOrm_Relation.new(**initializer_arguments.merge(query_options: @query_options.merge(opts)))
76
+ end
77
+
73
78
  def first
74
79
  n1ql_query, params = self.limit(1).to_n1ql_with_params
75
80
  result = @model.cluster.query(n1ql_query, build_query_options(positional_parameters: params))
@@ -168,7 +173,7 @@ module CouchbaseOrm
168
173
  end
169
174
 
170
175
  def initializer_arguments
171
- { model: @model, order: @order, where: @where, limit: @limit, strict_loading: @strict_loading }
176
+ { model: @model, order: @order, where: @where, limit: @limit, strict_loading: @strict_loading, query_options: @query_options }
172
177
  end
173
178
 
174
179
  def merge_order(*lorder, **horder)
@@ -238,7 +243,7 @@ module CouchbaseOrm
238
243
  end
239
244
 
240
245
  def build_query_options(positional_parameters: [])
241
- opts = { scan_consistency: CouchbaseOrm::N1ql.config[:scan_consistency] }
246
+ opts = CouchbaseOrm::N1ql.config.merge(@query_options)
242
247
  opts[:positional_parameters] = positional_parameters unless positional_parameters.empty?
243
248
  Couchbase::Options::Query.new(**opts)
244
249
  end
@@ -261,7 +266,7 @@ module CouchbaseOrm
261
266
 
262
267
  delegate :ids, :update_all, :delete_all, :count, :empty?, :filter, :reduce, :find_by, to: :all
263
268
 
264
- delegate :where, :not, :order, :limit, :all, :strict_loading, :strict_loading?, to: :relation
269
+ delegate :where, :not, :order, :limit, :all, :strict_loading, :strict_loading?, :with, to: :relation
265
270
  end
266
271
  end
267
272
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true, encoding: ASCII-8BIT
2
2
 
3
3
  module CouchbaseOrm
4
- VERSION = '3.1.0'
4
+ VERSION = '3.2.0'
5
5
  end
data/spec/n1ql_spec.rb CHANGED
@@ -196,6 +196,25 @@ describe CouchbaseOrm::N1ql do
196
196
  end
197
197
  end
198
198
 
199
+ it "should use adhoc: true by default (no prepared statement plan caching)" do
200
+ expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: true)).and_call_original
201
+ N1QLTest.by_rating_reverse()
202
+ end
203
+
204
+ it "should allow overriding adhoc per call to enable plan caching" do
205
+ expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: false)).and_call_original
206
+ N1QLTest.by_rating_reverse(adhoc: false)
207
+ end
208
+
209
+ it "should respect N1ql.config adhoc setting" do
210
+ default_config = CouchbaseOrm::N1ql.config
211
+ CouchbaseOrm::N1ql.config({ adhoc: false })
212
+ expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: false)).and_call_original
213
+ N1QLTest.by_rating_reverse()
214
+ ensure
215
+ CouchbaseOrm::N1ql.config(default_config)
216
+ end
217
+
199
218
  after(:all) do
200
219
  N1QLTest.delete_all
201
220
  end
@@ -501,5 +501,38 @@ describe CouchbaseOrm::Relation do
501
501
  end
502
502
  end
503
503
  end
504
+
505
+ it "should use adhoc: true by default (no prepared statement plan caching)" do
506
+ expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: true)).and_call_original
507
+ RelationModel.where(active: true).ids
508
+ end
509
+
510
+ describe "adhoc option via with" do
511
+ it "should return a relation when calling with(adhoc:)" do
512
+ expect(RelationModel.all.with(adhoc: false)).to be_a(CouchbaseOrm::Relation::CouchbaseOrm_Relation)
513
+ end
514
+
515
+ it "should pass adhoc: false to query options when set on the relation" do
516
+ expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: false)).and_call_original
517
+ RelationModel.where(active: true).with(adhoc: false).ids
518
+ end
519
+
520
+ it "should override N1ql.config adhoc when set on the relation" do
521
+ default_config = CouchbaseOrm::N1ql.config
522
+ CouchbaseOrm::N1ql.config(adhoc: false)
523
+ expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: true)).and_call_original
524
+ RelationModel.where(active: true).with(adhoc: true).ids
525
+ ensure
526
+ CouchbaseOrm::N1ql.config(default_config)
527
+ end
528
+
529
+ it "should be chainable with other relation methods" do
530
+ m1 = RelationModel.create!(active: true, age: 10)
531
+ _m2 = RelationModel.create!(active: false, age: 20)
532
+ expect(Couchbase::Options::Query).to receive(:new).with(hash_including(adhoc: false)).and_call_original
533
+ result = RelationModel.where(active: true).order(:age).with(adhoc: false).to_a
534
+ expect(result).to match_array([m1])
535
+ end
536
+ end
504
537
  end
505
538
 
metadata CHANGED
@@ -1,17 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: couchbase-orm
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen von Takach
8
8
  - Gauthier Monserand
9
9
  - Pierre Merlin
10
10
  - Julien Burnet-Fauche
11
- autorequire:
12
11
  bindir: bin
13
12
  cert_chain: []
14
- date: 2026-06-30 00:00:00.000000000 Z
13
+ date: 1980-01-02 00:00:00.000000000 Z
15
14
  dependencies:
16
15
  - !ruby/object:Gem::Dependency
17
16
  name: activemodel
@@ -210,7 +209,6 @@ dependencies:
210
209
  - !ruby/object:Gem::Version
211
210
  version: '0'
212
211
  description: A Couchbase ORM for Rails
213
- email:
214
212
  executables: []
215
213
  extensions: []
216
214
  extra_rdoc_files: []
@@ -354,7 +352,6 @@ metadata:
354
352
  bug_tracker_uri: https://github.com/Couchbase-Ecosystem/couchbase-ruby-orm/issues
355
353
  documentation_uri: https://www.couchbase-ruby-orm.com/
356
354
  homepage_uri: https://github.com/Couchbase-Ecosystem/couchbase-ruby-orm
357
- post_install_message:
358
355
  rdoc_options: []
359
356
  require_paths:
360
357
  - lib
@@ -362,15 +359,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
362
359
  requirements:
363
360
  - - ">="
364
361
  - !ruby/object:Gem::Version
365
- version: 3.1.0
362
+ version: 3.3.0
366
363
  required_rubygems_version: !ruby/object:Gem::Requirement
367
364
  requirements:
368
365
  - - ">="
369
366
  - !ruby/object:Gem::Version
370
367
  version: '0'
371
368
  requirements: []
372
- rubygems_version: 3.0.3.1
373
- signing_key:
369
+ rubygems_version: 4.0.10
374
370
  specification_version: 4
375
371
  summary: Couchbase ORM for Rails
376
372
  test_files: