From 3f692b06bde751e0c9cf15710f0de932b22f295e Mon Sep 17 00:00:00 2001 From: Andrew Kane Date: Tue, 8 Mar 2022 11:11:45 -0500 Subject: [PATCH] Added where and rewhere methods on relation --- lib/searchkick/relation.rb | 34 ++++++++++++++++++++++++++++++++++ test/parameters_test.rb | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ test/where_test.rb | 17 +++++++++++++++++ 3 files changed, 107 insertions(+), 0 deletions(-) diff --git a/lib/searchkick/relation.rb b/lib/searchkick/relation.rb index b07212e..2ae84a8 100644 --- a/lib/searchkick/relation.rb +++ b/lib/searchkick/relation.rb @@ -89,6 +89,34 @@ module Searchkick end # experimental + def where(value) + clone.where!(value) + end + + # experimental + def where!(value) + check_loaded + if @options[:where] + @options[:where] = {_and: [@options[:where], ensure_permitted(value)]} + else + @options[:where] = ensure_permitted(value) + end + self + end + + # experimental + def rewhere(value) + clone.rewhere!(value) + end + + # experimental + def rewhere!(value) + check_loaded + @options[:where] = ensure_permitted(value) + self + end + + # experimental def only(*keys) Relation.new(@model, @term, **@options.slice(*keys)) end @@ -118,5 +146,11 @@ module Searchkick # reset query since options will change @query = nil end + + # provides *very* basic protection from unfiltered parameters + # this is not meant to be comprehensive and may be expanded in the future + def ensure_permitted(obj) + obj.to_h + end end end diff --git a/test/parameters_test.rb b/test/parameters_test.rb index 62d0589..15484a5 100644 --- a/test/parameters_test.rb +++ b/test/parameters_test.rb @@ -20,18 +20,56 @@ class ParametersTest < Minitest::Test end end + def test_where_relation + params = ActionController::Parameters.new({store_id: 1}) + assert_raises(ActionController::UnfilteredParameters) do + Product.search("*").where(params) + end + end + + def test_rewhere_relation + params = ActionController::Parameters.new({store_id: 1}) + assert_raises(ActionController::UnfilteredParameters) do + Product.search("*").where(params) + end + end + def test_where_permitted store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}] params = ActionController::Parameters.new({store_id: 1}) assert_search "product", ["Product A"], where: params.permit(:store_id) end + def test_where_permitted_relation + store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}] + params = ActionController::Parameters.new({store_id: 1}) + assert_equal ["Product A"], Product.search("product").where(params.permit(:store_id)).map(&:name) + end + + def test_rewhere_permitted_relation + store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}] + params = ActionController::Parameters.new({store_id: 1}) + assert_equal ["Product A"], Product.search("product").rewhere(params.permit(:store_id)).map(&:name) + end + def test_where_value store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}] params = ActionController::Parameters.new({store_id: 1}) assert_search "product", ["Product A"], where: {store_id: params[:store_id]} end + def test_where_value_relation + store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}] + params = ActionController::Parameters.new({store_id: 1}) + assert_equal ["Product A"], Product.search("product").where(store_id: params[:store_id]).map(&:name) + end + + def test_rewhere_value_relation + store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}] + params = ActionController::Parameters.new({store_id: 1}) + assert_equal ["Product A"], Product.search("product").where(store_id: params[:store_id]).map(&:name) + end + def test_where_hash params = ActionController::Parameters.new({store_id: {value: 10, boost: 2}}) error = assert_raises(TypeError) do @@ -40,6 +78,24 @@ class ParametersTest < Minitest::Test assert_equal error.message, "can't cast ActionController::Parameters" end + # TODO raise error without to_a + def test_where_hash_relation + params = ActionController::Parameters.new({store_id: {value: 10, boost: 2}}) + error = assert_raises(TypeError) do + Product.search("product").where(store_id: params[:store_id]).to_a + end + assert_equal error.message, "can't cast ActionController::Parameters" + end + + # TODO raise error without to_a + def test_rewhere_hash_relation + params = ActionController::Parameters.new({store_id: {value: 10, boost: 2}}) + error = assert_raises(TypeError) do + Product.search("product").rewhere(store_id: params[:store_id]).to_a + end + assert_equal error.message, "can't cast ActionController::Parameters" + end + def test_aggs_where params = ActionController::Parameters.new({store_id: 1}) assert_raises(ActionController::UnfilteredParameters) do diff --git a/test/where_test.rb b/test/where_test.rb index 8681be4..0e03e08 100644 --- a/test/where_test.rb +++ b/test/where_test.rb @@ -82,6 +82,23 @@ class WhereTest < Minitest::Test assert_search "product", ["Product B"], where: {user_ids: {_not: [3, nil]}} end + def test_where_relation + now = Time.now + store [ + {name: "Product A", store_id: 1, in_stock: true, backordered: true, created_at: now, orders_count: 4, user_ids: [1, 2, 3]}, + {name: "Product B", store_id: 2, in_stock: true, backordered: false, created_at: now - 1, orders_count: 3, user_ids: [1]}, + {name: "Product C", store_id: 3, in_stock: false, backordered: true, created_at: now - 2, orders_count: 2, user_ids: [1, 3]}, + {name: "Product D", store_id: 4, in_stock: false, backordered: false, created_at: now - 3, orders_count: 1} + ] + assert_equal ["Product A", "Product B"], Product.search("product").where(in_stock: true).map(&:name) + + # multiple where + assert_equal ["Product A"], Product.search("product").where(in_stock: true).where(backordered: true).map(&:name) + + # rewhere + assert_equal ["Product A", "Product C"], Product.search("product").where(in_stock: true).rewhere(backordered: true).map(&:name) + end + def test_where_string_operators error = assert_raises(ArgumentError) do assert_search "product", [], where: {store_id: {"lt" => 2}} -- libgit2 0.21.0