Skip to content

Commit

Permalink
Merge pull request #521 from adamdecaf/add-matchedName
Browse files Browse the repository at this point in the history
feat: return matchedName in non-OFAC results
  • Loading branch information
adamdecaf authored Nov 16, 2023
2 parents 00affd7 + 17e8360 commit 6445031
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 39 deletions.
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

0 comments on commit 6445031

Please sign in to comment.