fix: custom field substring search - pass id as parameter
All checks were successful
continuous-integration/drone/push Build is passing

Fragment 'member_id = id' did not resolve correctly. Now passes id as
Ash expression. Also changed LIKE to ILIKE for case-insensitive search.
This commit is contained in:
Moritz 2025-12-11 14:04:13 +01:00
parent ca5fad0dcc
commit 00fe471bc0
2 changed files with 160 additions and 3 deletions

View file

@ -544,4 +544,159 @@ defmodule Mv.Membership.MemberSearchWithCustomFieldsTest do
# Substring matching for custom fields may need to be implemented separately
end
end
describe "custom field substring search (ILIKE)" do
test "finds member by prefix of custom field value", %{
member1: member1,
string_field: string_field
} do
# Create custom field value with a distinct word
{:ok, _cfv} =
CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member1.id,
custom_field_id: string_field.id,
value: %{"_union_type" => "string", "_union_value" => "Premium"}
})
|> Ash.create()
# Test prefix searches - should all find the member
for prefix <- ["Premium", "Premiu", "Premi", "Prem", "Pre"] do
results =
Member
|> Member.fuzzy_search(%{query: prefix})
|> Ash.read!()
assert Enum.any?(results, fn m -> m.id == member1.id end),
"Prefix '#{prefix}' should find member with custom field 'Premium'"
end
end
test "custom field search is case-insensitive", %{
member1: member1,
string_field: string_field
} do
# Create custom field value
{:ok, _cfv} =
CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member1.id,
custom_field_id: string_field.id,
value: %{"_union_type" => "string", "_union_value" => "GoldMember"}
})
|> Ash.create()
# Test case variations - should all find the member
for variant <- [
"GoldMember",
"goldmember",
"GOLDMEMBER",
"GoLdMeMbEr",
"gold",
"GOLD",
"Gold"
] do
results =
Member
|> Member.fuzzy_search(%{query: variant})
|> Ash.read!()
assert Enum.any?(results, fn m -> m.id == member1.id end),
"Case variant '#{variant}' should find member with custom field 'GoldMember'"
end
end
test "finds member by suffix/middle of custom field value", %{
member1: member1,
string_field: string_field
} do
# Create custom field value
{:ok, _cfv} =
CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member1.id,
custom_field_id: string_field.id,
value: %{"_union_type" => "string", "_union_value" => "ActiveMember"}
})
|> Ash.create()
# Test suffix and middle substring searches
for substring <- ["Member", "ember", "tiveMem", "ctive"] do
results =
Member
|> Member.fuzzy_search(%{query: substring})
|> Ash.read!()
assert Enum.any?(results, fn m -> m.id == member1.id end),
"Substring '#{substring}' should find member with custom field 'ActiveMember'"
end
end
test "finds correct member among multiple with different custom field values", %{
member1: member1,
member2: member2,
member3: member3,
string_field: string_field
} do
# Create different custom field values for each member
{:ok, _cfv1} =
CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member1.id,
custom_field_id: string_field.id,
value: %{"_union_type" => "string", "_union_value" => "Beginner"}
})
|> Ash.create()
{:ok, _cfv2} =
CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member2.id,
custom_field_id: string_field.id,
value: %{"_union_type" => "string", "_union_value" => "Advanced"}
})
|> Ash.create()
{:ok, _cfv3} =
CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member3.id,
custom_field_id: string_field.id,
value: %{"_union_type" => "string", "_union_value" => "Expert"}
})
|> Ash.create()
# Search for "Begin" - should only find member1
results_begin =
Member
|> Member.fuzzy_search(%{query: "Begin"})
|> Ash.read!()
assert length(results_begin) == 1
assert List.first(results_begin).id == member1.id
# Search for "Advan" - should only find member2
results_advan =
Member
|> Member.fuzzy_search(%{query: "Advan"})
|> Ash.read!()
assert length(results_advan) == 1
assert List.first(results_advan).id == member2.id
# Search for "Exper" - should only find member3
results_exper =
Member
|> Member.fuzzy_search(%{query: "Exper"})
|> Ash.read!()
assert length(results_exper) == 1
assert List.first(results_exper).id == member3.id
end
# Note: Legacy format (type/value) is supported via the SQL ILIKE query on value->>'value'
# This is tested implicitly by the migration trigger which handles both formats.
# The Ash union type only accepts the new format (_union_type/_union_value) for creation,
# but the search works on existing legacy data.
end
end