activerecord-turbopuffer-adapter 0.1.8 → 0.1.10

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: f7e4579b0235c712aef39dedae94abdb3e8053ef5b04fff2e46ee38c0fd5710a
4
- data.tar.gz: dadc48af421f105ed95d7964b7929ced1dd613217fdda1b88b00a58c291867cf
3
+ metadata.gz: ae2b8fa993fe940121966a4d3c8c9b95a43afc484348f1598ed76bb24ad0c021
4
+ data.tar.gz: 02b90f77da9ec0f5133fc021fa516b64516a79eea2b6d63d0bacf6b7feb97399
5
5
  SHA512:
6
- metadata.gz: 6037d2898aa914ed3990f238ac00b86c4b710d630fd3aa7c9dd3af9200f768736b9207cf8cedf079c01d60fd8fce614cdafdaa5873241b1c7a47fa1f6bb4b9df
7
- data.tar.gz: 8b6c7caf5e1b82fbed33da7e5e06e1232cf9603aab035b6a9ef8540223047020429927721928911c80512c778fbdef5a52b1628ca0110ac4414d0419f18a9aed
6
+ metadata.gz: fe02be8410aae63cc2479a3cb9ec52e1b54ffae8f34e21b33a63168f3572edeef99d5ccce08a675ce30adc227f967e519f4dd66cedb160a4f1b0dc4299a79c1b
7
+ data.tar.gz: ba93057de23dd67659cdbfd4d0a8e822fddd7ff162129932b995061c8c8e8d19cb10ae832c08e41639b2edf619d5119239b7f01043d42404bd7aaaac1f0ce37e
data/README.md CHANGED
@@ -109,6 +109,8 @@ Document.where(tags: nil)
109
109
  Document.where(scores: 90..)
110
110
 
111
111
  Document.order(:title).limit(20)
112
+ Document.order(:category, views: :desc)
113
+ Document.order(:title).limit(20).offset(20)
112
114
  Document.group(:title).count
113
115
  Document.count
114
116
  Document.find("018f...")
@@ -128,7 +130,7 @@ Document
128
130
  .limit(10)
129
131
  ```
130
132
 
131
- > Note that turbopuffer has a limit on the maximum number of documents returned (https://turbopuffer.com/docs/query#param-limit), so Document.all.to_a etc. will only return at most 10,000 items
133
+ > Note that turbopuffer has a limit on the maximum number of documents returned (https://turbopuffer.com/docs/query#param-limit), so Document.all.to_a etc. will only return at most 10,000 items, and `limit` plus `offset` must stay within that
132
134
 
133
135
  > Eventual reads are cheaper but may not include writes from the last few seconds. Set a default with `turbopuffer_consistency "eventual"` in a model or `consistency: eventual` in `database.yml`. Strong is the default.
134
136
 
@@ -279,10 +279,12 @@ module ActiveRecord
279
279
 
280
280
  tpuf_query_args = {
281
281
  include_attributes: fields,
282
- top_k: query.top_k.present? ? query.top_k : 10_000
282
+ top_k: query.top_k.present? ? query.top_k : 10_000 - query.offset.to_i
283
283
  }
284
284
 
285
- tpuf_query_args[:rank_by] = query.rank_by.first if query.rank_by.present?
285
+ tpuf_query_args[:offset] = query.offset if query.offset
286
+
287
+ tpuf_query_args[:rank_by] = query.rank_by if query.rank_by
286
288
  tpuf_query_args[:filters] = query.filters if query.filters
287
289
  consistency = consistency_for(query)
288
290
  tpuf_query_args[:consistency] = consistency if consistency
@@ -12,7 +12,8 @@ module Arel::Visitors
12
12
  aggregate_by: @aggregate_by,
13
13
  upsert_rows: @upsert_rows,
14
14
  consistency: @consistency,
15
- namespace_override: @namespace_override
15
+ namespace_override: @namespace_override,
16
+ offset: @offset
16
17
  }
17
18
  end
18
19
  def to_s = JSON.generate(to_h)
@@ -22,10 +23,10 @@ module Arel::Visitors
22
23
  alias == eql?
23
24
 
24
25
  attr_reader :op, :namespace, :filters, :top_k, :rank_by, :include_attributes, :upsert_rows, :aggregate_by, :group_by,
25
- :consistency, :namespace_override
26
+ :consistency, :namespace_override, :offset
26
27
 
27
- def initialize(op:, namespace:, filters: nil, top_k: nil, rank_by: nil, include_attributes: nil,
28
- upsert_rows: nil, aggregate_by: nil, group_by: nil, consistency: nil, namespace_override: nil)
28
+ def initialize(op:, namespace:, filters: nil, top_k: nil, rank_by: nil, include_attributes: nil, upsert_rows: nil,
29
+ aggregate_by: nil, group_by: nil, consistency: nil, namespace_override: nil, offset: nil)
29
30
  @op = op
30
31
  @namespace = namespace
31
32
  @filters = filters
@@ -37,6 +38,7 @@ module Arel::Visitors
37
38
  @upsert_rows = upsert_rows
38
39
  @consistency = consistency
39
40
  @namespace_override = namespace_override
41
+ @offset = offset
40
42
  end
41
43
  end
42
44
 
@@ -99,12 +101,18 @@ module Arel::Visitors
99
101
  end
100
102
  end
101
103
 
102
- def visit_Arel_Nodes_SelectStatement(o)
103
- raise NotImplementedError, "offset is not supported, filter on a sortable attribute for cursor pagination" if o.offset
104
- # https://turbopuffer.com/docs/query#ordering-by-attributes: "Ordering by
105
- # multiple attributes isn't yet implemented."
106
- raise NotImplementedError, "one ranking per query, sort in Ruby after loading" if o.orders.size > 1
104
+ def ranking(orders)
105
+ return if orders.empty?
106
+ return visit(orders.first) if orders.size == 1
107
+
108
+ if orders.any? { |order| order.is_a?(Arel::Nodes::RankByNode) }
109
+ raise NotImplementedError, "a ranking cannot be combined with other orderings, sort in Ruby after loading"
110
+ end
111
+
112
+ orders.map { |order| visit(order) }
113
+ end
107
114
 
115
+ def visit_Arel_Nodes_SelectStatement(o)
108
116
  core = o.cores.last
109
117
 
110
118
  raise NotImplementedError, "distinct is not implemented yet" if core.set_quantifier
@@ -113,6 +121,8 @@ module Arel::Visitors
113
121
  aggregates, attributes = core.projections.partition { |p| aggregate?(p) }
114
122
  aggregate = aggregates.first
115
123
 
124
+ raise NotImplementedError, "offset is not supported on aggregate queries" if o.offset && aggregate
125
+
116
126
  filters = core.wheres.map { |w| visit(w) }
117
127
  counted = aggregate && counted_attribute(aggregate)
118
128
  filters << [ visit(counted), "NotEq", nil ] if counted
@@ -122,12 +132,13 @@ module Arel::Visitors
122
132
  namespace: visit(core.source),
123
133
  filters: conjoin(filters),
124
134
  top_k: o.limit && visit(o.limit),
125
- rank_by: o.orders.map { |ord| visit(ord) },
135
+ rank_by: ranking(o.orders),
126
136
  include_attributes: attributes.flat_map { |p| visit(p) },
127
137
  group_by: core.groups.map { |g| visit(g) },
128
138
  aggregate_by: aggregate && visit(aggregate),
129
139
  consistency: consistency_hint(core.optimizer_hints),
130
- namespace_override: namespace_annotation(core.comment)
140
+ namespace_override: namespace_annotation(core.comment),
141
+ offset: o.offset && visit(o.offset)
131
142
  )
132
143
  end
133
144
 
@@ -207,6 +218,10 @@ module Arel::Visitors
207
218
  visit(o.left)
208
219
  end
209
220
 
221
+ def visit_Arel_Nodes_TableAlias(o)
222
+ raise NotImplementedError, "subqueries are not supported, count a limited or offset relation in Ruby after loading"
223
+ end
224
+
210
225
  def visit_Arel_Table(o) = o.name
211
226
 
212
227
  # filters
@@ -293,6 +308,7 @@ module Arel::Visitors
293
308
  def visit_Arel_Nodes_Casted(o) = o.value_for_database
294
309
  def visit_Arel_Nodes_Quoted(o) = o.expr
295
310
  def visit_Arel_Nodes_Limit(o) = visit(o.expr)
311
+ def visit_Arel_Nodes_Offset(o) = visit(o.expr)
296
312
  def visit_Arel_Nodes_Ascending(o) = [ visit(o.expr), "asc" ]
297
313
  def visit_Arel_Nodes_Descending(o) = [ visit(o.expr), "desc" ]
298
314
 
@@ -1,5 +1,5 @@
1
1
  module Turbopuffer
2
2
  module ActiveRecord
3
- VERSION = "0.1.8"
3
+ VERSION = "0.1.10"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-turbopuffer-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Monette
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-10-07 00:00:00.000000000 Z
10
+ date: 2026-10-18 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activerecord
@@ -57,7 +57,7 @@ dependencies:
57
57
  requirements:
58
58
  - - ">="
59
59
  - !ruby/object:Gem::Version
60
- version: '2.4'
60
+ version: '2.5'
61
61
  - - "<"
62
62
  - !ruby/object:Gem::Version
63
63
  version: '3'
@@ -67,7 +67,7 @@ dependencies:
67
67
  requirements:
68
68
  - - ">="
69
69
  - !ruby/object:Gem::Version
70
- version: '2.4'
70
+ version: '2.5'
71
71
  - - "<"
72
72
  - !ruby/object:Gem::Version
73
73
  version: '3'