From 44bf52668729ce14b27dd8e94e2cf634b6d523fd Mon Sep 17 00:00:00 2001 From: Nick Saukin Date: Thu, 28 May 2026 16:03:03 +0530 Subject: [PATCH 1/2] Add ids method as in AR --- .../node/query/query_proxy_methods.rb | 5 ++++ lib/active_graph/node/query_methods.rb | 7 +++++ spec/e2e/query_proxy_methods_spec.rb | 28 +++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/lib/active_graph/node/query/query_proxy_methods.rb b/lib/active_graph/node/query/query_proxy_methods.rb index cc0b1f659..67bc24626 100644 --- a/lib/active_graph/node/query/query_proxy_methods.rb +++ b/lib/active_graph/node/query/query_proxy_methods.rb @@ -270,6 +270,11 @@ def first_and_last(func, target) end.first end + # @return [Array] An array of primary key values + def ids + pluck(association_id_key) + end + # @return [String] The primary key of a the current QueryProxy's model or target class def association_id_key self.association.nil? ? model.primary_key : self.association.target_class.primary_key diff --git a/lib/active_graph/node/query_methods.rb b/lib/active_graph/node/query_methods.rb index 508a304c4..38f1c7ef7 100644 --- a/lib/active_graph/node/query_methods.rb +++ b/lib/active_graph/node/query_methods.rb @@ -49,6 +49,13 @@ def find_each(options = {}) end end + # Returns an array of all the IDs (primary key values) of the model's nodes. + # ActiveRecord-compatible shortcut for `pluck(primary_key)`. + # @return [Array] An array of primary key values + def ids + self.all.ids + end + private def exists_query_start(condition) diff --git a/spec/e2e/query_proxy_methods_spec.rb b/spec/e2e/query_proxy_methods_spec.rb index acd34d2ad..b416d44dc 100644 --- a/spec/e2e/query_proxy_methods_spec.rb +++ b/spec/e2e/query_proxy_methods_spec.rb @@ -444,6 +444,34 @@ def destroy_called end end + describe 'ids' do + before(:each) do + [Student, Lesson].each(&:delete_all) + + @john = Student.create(name: 'John') + @history = Lesson.create(name: 'history') + @math2 = Lesson.create(name: 'math2') + @john.lessons << @history + @john.lessons << @math2 + end + + it 'returns the ids of nodes on the class' do + expect(Lesson.ids).to match_array([@history.id, @math2.id]) + end + + it 'returns the ids of nodes on a query proxy association' do + expect(@john.lessons.ids).to match_array([@history.id, @math2.id]) + end + + it 'returns the ids of nodes on a where chain' do + expect(Lesson.where(name: 'history').ids).to eq([@history.id]) + end + + it 'returns an empty array when there are no matches' do + expect(Lesson.where(name: 'nope').ids).to eq([]) + end + end + describe 'distinct' do let(:frank) { Student.create(name: 'Frank') } let(:bill) { Teacher.create(name: 'Bill') } From fc530cadcc0f25167eb0d95d0a4f63af4973ed2a Mon Sep 17 00:00:00 2001 From: Nick Saukin Date: Thu, 28 May 2026 16:16:27 +0530 Subject: [PATCH 2/2] Cover non-default primary key --- .../node/query/query_proxy_methods.rb | 2 +- spec/e2e/query_proxy_methods_spec.rb | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/active_graph/node/query/query_proxy_methods.rb b/lib/active_graph/node/query/query_proxy_methods.rb index 67bc24626..3893e119a 100644 --- a/lib/active_graph/node/query/query_proxy_methods.rb +++ b/lib/active_graph/node/query/query_proxy_methods.rb @@ -272,7 +272,7 @@ def first_and_last(func, target) # @return [Array] An array of primary key values def ids - pluck(association_id_key) + query.pluck(identity => association_id_key) end # @return [String] The primary key of a the current QueryProxy's model or target class diff --git a/spec/e2e/query_proxy_methods_spec.rb b/spec/e2e/query_proxy_methods_spec.rb index b416d44dc..8493b0693 100644 --- a/spec/e2e/query_proxy_methods_spec.rb +++ b/spec/e2e/query_proxy_methods_spec.rb @@ -470,6 +470,34 @@ def destroy_called it 'returns an empty array when there are no matches' do expect(Lesson.where(name: 'nope').ids).to eq([]) end + + context 'when the association target uses a custom id_property' do + before(:each) do + stub_node_class('Book') do + id_property :book_id, on: :generate_book_id + property :title + has_many :in, :owners, model_class: 'Reader', origin: :books + def generate_book_id + "book-#{SecureRandom.hex(4)}" + end + end + + stub_node_class('Reader') do + property :name + has_many :out, :books, model_class: 'Book', type: 'OWNS' + end + + @reader = Reader.create(name: 'Ada') + @b1 = Book.create(title: 'A') + @b2 = Book.create(title: 'B') + @reader.books << @b1 + @reader.books << @b2 + end + + it 'uses the target class primary key when traversing an association' do + expect(@reader.books.ids).to match_array([@b1.book_id, @b2.book_id]) + end + end end describe 'distinct' do