Packages

Fuzzy string matching, distance, similarity, ranking, and entity resolution in Gleam

Current section

Files

Jump to
starfuzz src starfuzz@resolver.erl
Raw

src/starfuzz@resolver.erl

-module(starfuzz@resolver).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/starfuzz/resolver.gleam").
-export([new/1, with_threshold/2, with_confidence_thresholds/3, component/3, resolve/3]).
-export_type([confidence/0, resolution/1, resolver/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" A module providing declarative entity resolution matching capabilities.\n"
" Structured records can be compared using weighted components, outputting matched status\n"
" and confidence levels (High, Medium, Low) for each candidate.\n"
).
-type confidence() :: high | medium | low.
-type resolution(DYO) :: {resolution,
DYO,
float(),
list(starfuzz@rank:component_score()),
confidence(),
boolean()}.
-type resolver(DYP, DYQ) :: {resolver,
fun((DYP, DYQ) -> list(starfuzz@rank:component_score())),
float(),
{float(), float()}}.
-file("src/starfuzz/resolver.gleam", 38).
?DOC(
" Creates a new Resolver with a composite scoring function.\n"
" Defaults to a threshold of 0.7, medium confidence at 0.75, and high confidence at 0.9.\n"
).
-spec new(fun((DYR, DYS) -> list(starfuzz@rank:component_score()))) -> resolver(DYR, DYS).
new(Scorer) ->
{resolver, Scorer, 0.7, {0.75, 0.9}}.
-file("src/starfuzz/resolver.gleam", 47).
?DOC(" Configures the minimum score threshold to consider a record \"matched\".\n").
-spec with_threshold(resolver(DYW, DYX), float()) -> resolver(DYW, DYX).
with_threshold(Resolver, Threshold) ->
{resolver,
erlang:element(2, Resolver),
Threshold,
erlang:element(4, Resolver)}.
-file("src/starfuzz/resolver.gleam", 52).
?DOC(" Configures the thresholds for Medium and High confidence tiers.\n").
-spec with_confidence_thresholds(resolver(DZC, DZD), float(), float()) -> resolver(DZC, DZD).
with_confidence_thresholds(Resolver, Medium, High) ->
{resolver,
erlang:element(2, Resolver),
erlang:element(3, Resolver),
{Medium, High}}.
-file("src/starfuzz/resolver.gleam", 57).
?DOC(" Helper function to construct a ComponentScore inside the resolver scorer.\n").
-spec component(binary(), float(), float()) -> starfuzz@rank:component_score().
component(Name, Weight, Score) ->
{component_score, Name, Score, Weight}.
-file("src/starfuzz/resolver.gleam", 63).
?DOC(
" Resolves a query entity against a list of candidates.\n"
" Returns resolution records sorted descending by composite score, with stable ordering.\n"
).
-spec resolve(resolver(DZI, DZJ), DZI, list(DZJ)) -> {ok, list(resolution(DZJ))} |
{error, starfuzz@rank:rank_error()}.
resolve(Resolver, Query, Candidates) ->
Candidates_indexed = gleam@list:index_map(
Candidates,
fun(C, I) -> {C, I} end
),
Result = gleam@list:fold(
Candidates_indexed,
{ok, []},
fun(Acc_state, Item) -> case Acc_state of
{error, E} ->
{error, E};
{ok, Acc} ->
{Candidate, _} = Item,
Components = (erlang:element(2, Resolver))(Query, Candidate),
case starfuzz@rank:combine(Components) of
{error, E@1} ->
{error, E@1};
{ok, Score} ->
Matched = Score >= erlang:element(3, Resolver),
{Med_th, High_th} = erlang:element(4, Resolver),
Confidence = case Score >= High_th of
true ->
high;
false ->
case Score >= Med_th of
true ->
medium;
false ->
low
end
end,
{ok,
[{resolution,
Candidate,
Score,
Components,
Confidence,
Matched} |
Acc]}
end
end end
),
case Result of
{error, E@2} ->
{error, E@2};
{ok, Resolutions} ->
Indexed = gleam@list:index_map(
lists:reverse(Resolutions),
fun(R, Idx) -> {R, Idx} end
),
Sorted = gleam@list:sort(
Indexed,
fun(A, B) ->
case erlang:element(3, erlang:element(1, A)) > erlang:element(
3,
erlang:element(1, B)
) of
true ->
lt;
false ->
case erlang:element(3, erlang:element(1, A)) < erlang:element(
3,
erlang:element(1, B)
) of
true ->
gt;
false ->
case erlang:element(2, A) < erlang:element(
2,
B
) of
true ->
lt;
false ->
gt
end
end
end
end
),
{ok, gleam@list:map(Sorted, fun(X) -> erlang:element(1, X) end)}
end.