Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: return matchedName in non-OFAC results #521

Merged
merged 1 commit into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 54 additions & 2 deletions api/client.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1141,6 +1141,10 @@ components:
type: number
description: Match percentage of search query
example: 0.91
matchedName:
type: string
desc: The highest scoring term from the search query
example: Jane Doe
MilitaryEndUser:
properties:
entityID:
Expand All @@ -1165,6 +1169,10 @@ components:
type: number
description: Match percentage of search query
example: 0.92
matchedName:
type: string
desc: The highest scoring term from the search query
example: Jane Doe
Unverified:
properties:
entityID:
Expand All @@ -1189,6 +1197,10 @@ components:
type: number
description: Match percentage of search query
example: 0.92
matchedName:
type: string
desc: The highest scoring term from the search query
example: Jane Doe
NonProliferationSanction:
properties:
entityID:
Expand Down Expand Up @@ -1233,6 +1245,10 @@ components:
type: number
description: Match percentage of search query
example: 0.92
matchedName:
type: string
desc: The highest scoring term from the search query
example: Jane Doe
ForeignSanctionsEvader:
properties:
entityID:
Expand Down Expand Up @@ -1281,6 +1297,10 @@ components:
type: number
description: Match percentage of search query
example: 0.92
matchedName:
type: string
desc: The highest scoring term from the search query
example: Jane Doe
PalestinianLegislativeCouncil:
properties:
entityID:
Expand Down Expand Up @@ -1333,6 +1353,10 @@ components:
type: number
description: Match percentage of search query
example: 0.92
matchedName:
type: string
desc: The highest scoring term from the search query
example: Jane Doe
CAPTAList:
properties:
entityID:
Expand Down Expand Up @@ -1391,6 +1415,10 @@ components:
type: number
description: Match percentage of search query
example: 0.92
matchedName:
type: string
desc: The highest scoring term from the search query
example: Jane Doe
ITARDebarred:
properties:
entityID:
Expand Down Expand Up @@ -1419,6 +1447,10 @@ components:
type: number
description: Match percentage of search query
example: 0.92
matchedName:
type: string
desc: The highest scoring term from the search query
example: Jane Doe
NonSDNChineseMilitaryIndustrialComplex:
properties:
entityID:
Expand Down Expand Up @@ -1475,6 +1507,10 @@ components:
type: number
description: Match percentage of search query
example: 0.92
matchedName:
type: string
desc: The highest scoring term from the search query
example: Jane Doe
NonSDNMenuBasedSanctionsList:
properties:
EntityID:
Expand Down Expand Up @@ -1531,6 +1567,10 @@ components:
type: number
description: Match percentage of search query
example: 0.92
matchedName:
type: string
desc: The highest scoring term from the search query
example: Jane Doe
EUConsolidatedSanctionsList:
properties:
fileGenerationDate:
Expand Down Expand Up @@ -1593,11 +1633,15 @@ components:
description: Match percentage of search query
example: 0.92
type: number
matchedName:
type: string
desc: The highest scoring term from the search query
example: Jane Doe
UKConsolidatedSanctionsList:
properties:
names:
type: array
items:
items:
type: string
addresses:
type: array
Expand All @@ -1613,11 +1657,15 @@ components:
description: Match percentage of search query
example: 0.92
type: number
matchedName:
type: string
desc: The highest scoring term from the search query
example: Jane Doe
UKSanctionsList:
properties:
names:
type: array
items:
items:
type: string
nonLatinNames:
type: array
Expand All @@ -1641,6 +1689,10 @@ components:
description: Match percentage of search query
example: 0.92
type: number
matchedName:
type: string
desc: The highest scoring term from the search query
example: Jane Doe
UpdateOfacCompanyStatus:
description: Request body to update a company status.
properties:
Expand Down
5 changes: 3 additions & 2 deletions cmd/server/largest.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import "sync"

// item represents an arbitrary value with an associated weight
type item struct {
value interface{}
weight float64
matched string
value interface{}
weight float64
}

// newLargest returns a `largest` instance which can be used to track items with the highest weights
Expand Down
1 change: 1 addition & 0 deletions cmd/server/search_eu_csl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func TestSearch__EU_CSL(t *testing.T) {

require.Equal(t, http.StatusOK, w.Code)
require.Contains(t, w.Body.String(), `"match":0.65555`)
require.Contains(t, w.Body.String(), `"matchedName":"saddam hussein al tikriti"`)

var wrapper struct {
EUConsolidatedSanctionsList []csl.EUCSLRecord `json:"euConsolidatedSanctionsList"`
Expand Down
19 changes: 14 additions & 5 deletions cmd/server/search_generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ package main

import (
"encoding/json"
"math"
"reflect"
"sync"
)

type Result[T any] struct {
Data T

match float64
match float64
matchedName string

precomputedName string
precomputedAlts []string
}
Expand All @@ -40,6 +41,7 @@ func (e Result[T]) MarshalJSON() ([]byte, error) {
}

result["match"] = e.match
result["matchedName"] = e.matchedName

return json.Marshal(result)
}
Expand All @@ -60,15 +62,21 @@ func topResults[T any](limit int, minMatch float64, name string, data []*Result[
defer wg.Done()

it := &item{
value: data[i],
weight: jaroWinkler(data[i].precomputedName, name),
matched: data[i].precomputedName,
value: data[i],
weight: jaroWinkler(data[i].precomputedName, name),
}

for _, alt := range data[i].precomputedAlts {
if alt == "" {
continue
}
it.weight = math.Max(it.weight, jaroWinkler(alt, name))

score := jaroWinkler(alt, name)
if score > it.weight {
it.matched = alt
it.weight = score
}
}

xs.add(it)
Expand All @@ -86,6 +94,7 @@ func topResults[T any](limit int, minMatch float64, name string, data []*Result[
res := &Result[T]{
Data: vv.Data,
match: v.weight,
matchedName: v.matched,
precomputedName: vv.precomputedName,
precomputedAlts: vv.precomputedAlts,
}
Expand Down
40 changes: 10 additions & 30 deletions cmd/server/search_handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,9 @@ func TestSearch__AddressCountry(t *testing.T) {
router.ServeHTTP(w, req)
w.Flush()

if w.Code != http.StatusOK {
t.Errorf("bogus status code: %d", w.Code)
}
require.Equal(t, http.StatusOK, w.Code)
require.Contains(t, w.Body.String(), `"match":1`)

if v := w.Body.String(); !strings.Contains(v, `"match":1`) {
t.Errorf("%#v", v)
}
}

func TestSearch__AddressMulti(t *testing.T) {
Expand All @@ -84,13 +80,9 @@ func TestSearch__AddressMulti(t *testing.T) {
router.ServeHTTP(w, req)
w.Flush()

if w.Code != http.StatusOK {
t.Errorf("bogus status code: %d", w.Code)
}
require.Equal(t, http.StatusOK, w.Code)
require.Contains(t, w.Body.String(), `"match":0.8847`)

if v := w.Body.String(); !strings.Contains(v, `"match":0.8847`) {
t.Errorf("%#v", v)
}
}

func TestSearch__AddressProvidence(t *testing.T) {
Expand All @@ -102,13 +94,9 @@ func TestSearch__AddressProvidence(t *testing.T) {
router.ServeHTTP(w, req)
w.Flush()

if w.Code != http.StatusOK {
t.Errorf("bogus status code: %d", w.Code)
}
require.Equal(t, http.StatusOK, w.Code)
require.Contains(t, w.Body.String(), `"match":0.923`)

if v := w.Body.String(); !strings.Contains(v, `"match":0.923`) {
t.Errorf("%#v", v)
}
}

func TestSearch__AddressCity(t *testing.T) {
Expand All @@ -120,13 +108,9 @@ func TestSearch__AddressCity(t *testing.T) {
router.ServeHTTP(w, req)
w.Flush()

if w.Code != http.StatusOK {
t.Errorf("bogus status code: %d", w.Code)
}
require.Equal(t, http.StatusOK, w.Code)
require.Contains(t, w.Body.String(), `"match":0.923`)

if v := w.Body.String(); !strings.Contains(v, `"match":0.923`) {
t.Errorf("%#v", v)
}
}

func TestSearch__AddressState(t *testing.T) {
Expand All @@ -138,13 +122,9 @@ func TestSearch__AddressState(t *testing.T) {
router.ServeHTTP(w, req)
w.Flush()

if w.Code != http.StatusOK {
t.Errorf("bogus status code: %d", w.Code)
}
require.Equal(t, http.StatusOK, w.Code)
require.Contains(t, w.Body.String(), `"match":0.923`)

if v := w.Body.String(); !strings.Contains(v, `"match":0.923`) {
t.Errorf("%#v", v)
}
}

func TestSearch__NameAndAddress(t *testing.T) {
Expand Down
2 changes: 2 additions & 0 deletions cmd/server/search_uk_csl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func TestSearch_UK_CSL(t *testing.T) {

require.Equal(t, http.StatusOK, w.Code)
require.Contains(t, w.Body.String(), `"match":1`)
require.Contains(t, w.Body.String(), `"matchedName":"'abd al nasir"`)

var wrapper struct {
UKCSL []csl.UKCSLRecord `json:"ukConsolidatedSanctionsList"`
Expand All @@ -51,6 +52,7 @@ func TestSearch_UK_SanctionsList(t *testing.T) {

require.Equal(t, http.StatusOK, w.Code)
require.Contains(t, w.Body.String(), `"match":1`)
require.Contains(t, w.Body.String(), `"matchedName":"haji khairullah haji sattar money exchange"`)

var wrapper struct {
UKSanctionsList []csl.UKSanctionsListRecord `json:"ukSanctionsList"`
Expand Down
1 change: 1 addition & 0 deletions cmd/server/search_us_csl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func TestSearch_US_CSL(t *testing.T) {

require.Equal(t, http.StatusOK, w.Code)
require.Contains(t, w.Body.String(), `"match":0.6333`)
require.Contains(t, w.Body.String(), `"matchedName":"zaman"`)

var wrapper struct {
NonProliferationSanctions []csl.ISN `json:"nonProliferationSanctions"`
Expand Down