feat: add member fuzzy search for linking (#168)
This commit is contained in:
parent
88bed75ac9
commit
afd3cc88dc
1 changed files with 76 additions and 1 deletions
|
|
@ -152,7 +152,8 @@ defmodule Mv.Membership.Member do
|
|||
prepare fn query, _ctx ->
|
||||
q = Ash.Query.get_argument(query, :query) || ""
|
||||
|
||||
# 0.2 as similarity threshold (recommended) - lower value can lead to more results but also to more unspecific results
|
||||
# 0.2 as similarity threshold (recommended)
|
||||
# Lower value can lead to more results but also to more unspecific results
|
||||
threshold = Ash.Query.get_argument(query, :similarity_threshold) || 0.2
|
||||
|
||||
if is_binary(q) and String.trim(q) != "" do
|
||||
|
|
@ -187,8 +188,82 @@ defmodule Mv.Membership.Member do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Action to find members available for linking to a user account
|
||||
# Returns only unlinked members (user_id == nil), limited to 10 results
|
||||
#
|
||||
# Special behavior for email matching:
|
||||
# - When user_email AND search_query are both provided: filter by email (email takes precedence)
|
||||
# - When only user_email provided: return all unlinked members (caller should use filter_by_email_match helper)
|
||||
# - When only search_query provided: filter by search terms
|
||||
read :available_for_linking do
|
||||
argument :user_email, :string, allow_nil?: true
|
||||
argument :search_query, :string, allow_nil?: true
|
||||
|
||||
prepare fn query, _ctx ->
|
||||
user_email = Ash.Query.get_argument(query, :user_email)
|
||||
search_query = Ash.Query.get_argument(query, :search_query)
|
||||
|
||||
# Start with base filter: only unlinked members
|
||||
base_query = Ash.Query.filter(query, is_nil(user))
|
||||
|
||||
# Determine filtering strategy
|
||||
# Priority: search_query (if present) > no filters
|
||||
# user_email is used for POST-filtering via filter_by_email_match helper
|
||||
if not is_nil(search_query) and String.trim(search_query) != "" do
|
||||
# Search query present: Use fuzzy search (regardless of user_email)
|
||||
trimmed = String.trim(search_query)
|
||||
|
||||
# Use same fuzzy search as :search action (PostgreSQL Trigram + FTS)
|
||||
base_query
|
||||
|> Ash.Query.filter(
|
||||
expr(
|
||||
# Full-text search
|
||||
# Trigram similarity for names
|
||||
# Exact substring match for email
|
||||
fragment("search_vector @@ websearch_to_tsquery('simple', ?)", ^trimmed) or
|
||||
fragment("search_vector @@ plainto_tsquery('simple', ?)", ^trimmed) or
|
||||
fragment("? % first_name", ^trimmed) or
|
||||
fragment("? % last_name", ^trimmed) or
|
||||
fragment("word_similarity(?, first_name) > 0.2", ^trimmed) or
|
||||
fragment("word_similarity(?, last_name) > 0.2", ^trimmed) or
|
||||
fragment("similarity(first_name, ?) > 0.2", ^trimmed) or
|
||||
fragment("similarity(last_name, ?) > 0.2", ^trimmed) or
|
||||
contains(email, ^trimmed)
|
||||
)
|
||||
)
|
||||
|> Ash.Query.limit(10)
|
||||
else
|
||||
# No search query: return all unlinked members
|
||||
# Caller should use filter_by_email_match helper for email match logic
|
||||
base_query
|
||||
|> Ash.Query.limit(10)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Public helper function to apply email match logic after query execution
|
||||
# This should be called after using :available_for_linking with user_email argument
|
||||
#
|
||||
# If a member with matching email exists, returns only that member
|
||||
# Otherwise returns all members (no filtering)
|
||||
def filter_by_email_match(members, user_email)
|
||||
when is_list(members) and is_binary(user_email) do
|
||||
# Check if any member matches the email
|
||||
email_match = Enum.find(members, &(&1.email == user_email))
|
||||
|
||||
if email_match do
|
||||
# Return only the email-matched member
|
||||
[email_match]
|
||||
else
|
||||
# No email match, return all members
|
||||
members
|
||||
end
|
||||
end
|
||||
|
||||
def filter_by_email_match(members, _user_email), do: members
|
||||
|
||||
validations do
|
||||
# Required fields are covered by allow_nil? false
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue