diff --git a/api/client.yaml b/api/client.yaml index 9c847669..6186095a 100644 --- a/api/client.yaml +++ b/api/client.yaml @@ -917,6 +917,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. This term is the precomputed indexed value used by the search algorithm. + example: jane doe OfacEntityAddresses: type: array items: @@ -969,6 +973,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. This term is the precomputed indexed value used by the search algorithm. + example: jane doe SdnType: description: 'Used for classifying SDNs — typically represents an individual or company' type: string @@ -1039,6 +1047,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. This term is the precomputed indexed value used by the search algorithm. + example: jane doe SSI: description: Treasury Department Sectoral Sanctions Identifications List (SSI) properties: @@ -1094,6 +1106,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. This term is the precomputed indexed value used by the search algorithm. + example: jane doe BISEntities: description: Bureau of Industry and Security Entity List properties: @@ -1143,8 +1159,8 @@ components: example: 0.91 matchedName: type: string - desc: The highest scoring term from the search query - example: Jane Doe + desc: The highest scoring term from the search query. This term is the precomputed indexed value used by the search algorithm. + example: jane doe MilitaryEndUser: properties: entityID: @@ -1171,8 +1187,8 @@ components: example: 0.92 matchedName: type: string - desc: The highest scoring term from the search query - example: Jane Doe + desc: The highest scoring term from the search query. This term is the precomputed indexed value used by the search algorithm. + example: jane doe Unverified: properties: entityID: @@ -1199,8 +1215,8 @@ components: example: 0.92 matchedName: type: string - desc: The highest scoring term from the search query - example: Jane Doe + desc: The highest scoring term from the search query. This term is the precomputed indexed value used by the search algorithm. + example: jane doe NonProliferationSanction: properties: entityID: @@ -1247,8 +1263,8 @@ components: example: 0.92 matchedName: type: string - desc: The highest scoring term from the search query - example: Jane Doe + desc: The highest scoring term from the search query. This term is the precomputed indexed value used by the search algorithm. + example: jane doe ForeignSanctionsEvader: properties: entityID: @@ -1299,8 +1315,8 @@ components: example: 0.92 matchedName: type: string - desc: The highest scoring term from the search query - example: Jane Doe + desc: The highest scoring term from the search query. This term is the precomputed indexed value used by the search algorithm. + example: jane doe PalestinianLegislativeCouncil: properties: entityID: @@ -1355,8 +1371,8 @@ components: example: 0.92 matchedName: type: string - desc: The highest scoring term from the search query - example: Jane Doe + desc: The highest scoring term from the search query. This term is the precomputed indexed value used by the search algorithm. + example: jane doe CAPTAList: properties: entityID: @@ -1417,8 +1433,8 @@ components: example: 0.92 matchedName: type: string - desc: The highest scoring term from the search query - example: Jane Doe + desc: The highest scoring term from the search query. This term is the precomputed indexed value used by the search algorithm. + example: jane doe ITARDebarred: properties: entityID: @@ -1449,8 +1465,8 @@ components: example: 0.92 matchedName: type: string - desc: The highest scoring term from the search query - example: Jane Doe + desc: The highest scoring term from the search query. This term is the precomputed indexed value used by the search algorithm. + example: jane doe NonSDNChineseMilitaryIndustrialComplex: properties: entityID: @@ -1509,8 +1525,8 @@ components: example: 0.92 matchedName: type: string - desc: The highest scoring term from the search query - example: Jane Doe + desc: The highest scoring term from the search query. This term is the precomputed indexed value used by the search algorithm. + example: jane doe NonSDNMenuBasedSanctionsList: properties: EntityID: @@ -1569,8 +1585,8 @@ components: example: 0.92 matchedName: type: string - desc: The highest scoring term from the search query - example: Jane Doe + desc: The highest scoring term from the search query. This term is the precomputed indexed value used by the search algorithm. + example: jane doe EUConsolidatedSanctionsList: properties: fileGenerationDate: @@ -1635,8 +1651,8 @@ components: type: number matchedName: type: string - desc: The highest scoring term from the search query - example: Jane Doe + desc: The highest scoring term from the search query. This term is the precomputed indexed value used by the search algorithm. + example: jane doe UKConsolidatedSanctionsList: properties: names: @@ -1659,8 +1675,8 @@ components: type: number matchedName: type: string - desc: The highest scoring term from the search query - example: Jane Doe + desc: The highest scoring term from the search query. This term is the precomputed indexed value used by the search algorithm. + example: jane doe UKSanctionsList: properties: names: @@ -1691,8 +1707,8 @@ components: type: number matchedName: type: string - desc: The highest scoring term from the search query - example: Jane Doe + desc: The highest scoring term from the search query. This term is the precomputed indexed value used by the search algorithm. + example: jane doe UpdateOfacCompanyStatus: description: Request body to update a company status. properties: diff --git a/cmd/server/search.go b/cmd/server/search.go index a2cad095..d5b2a73a 100644 --- a/cmd/server/search.go +++ b/cmd/server/search.go @@ -266,8 +266,9 @@ func (s *searcher) TopAltNames(limit int, minMatch float64, alt string) []Alt { defer wg.Done() defer s.Gate.Done() xs.add(&item{ - value: s.Alts[i], - weight: jaroWinkler(s.Alts[i].name, alt), + matched: s.Alts[i].name, + value: s.Alts[i], + weight: jaroWinkler(s.Alts[i].name, alt), }) }(i) } @@ -282,6 +283,7 @@ func (s *searcher) TopAltNames(limit int, minMatch float64, alt string) []Alt { } alt := *aa alt.match = v.weight + alt.matchedName = v.matched out = append(out, alt) } } @@ -385,8 +387,9 @@ func (s *searcher) TopSDNs(limit int, minMatch float64, name string, keepSDN fun defer wg.Done() defer s.Gate.Done() xs.add(&item{ - value: s.SDNs[i], - weight: jaroWinkler(s.SDNs[i].name, name), + matched: s.SDNs[i].name, + value: s.SDNs[i], + weight: jaroWinkler(s.SDNs[i].name, name), }) }(i) } @@ -401,6 +404,7 @@ func (s *searcher) TopSDNs(limit int, minMatch float64, name string, keepSDN fun } sdn := *ss // deref for a copy sdn.match = v.weight + sdn.matchedName = v.matched out = append(out, &sdn) } } @@ -427,8 +431,9 @@ func (s *searcher) TopDPs(limit int, minMatch float64, name string) []DP { defer wg.Done() defer s.Gate.Done() xs.add(&item{ - value: s.DPs[i], - weight: jaroWinkler(s.DPs[i].name, name), + matched: s.DPs[i].name, + value: s.DPs[i], + weight: jaroWinkler(s.DPs[i].name, name), }) }(i) } @@ -443,6 +448,7 @@ func (s *searcher) TopDPs(limit int, minMatch float64, name string) []DP { } dp := *ss dp.match = v.weight + dp.matchedName = v.matched out = append(out, dp) } } @@ -456,6 +462,9 @@ type SDN struct { // match holds the match ratio for an SDN in search results match float64 + // matchedName holds the highest scoring term from the search query + matchedName string + // name is precomputed for speed name string @@ -471,10 +480,12 @@ type SDN struct { func (s SDN) MarshalJSON() ([]byte, error) { return json.Marshal(struct { *ofac.SDN - Match float64 `json:"match"` + Match float64 `json:"match"` + MatchedName string `json:"matchedName"` }{ s.SDN, s.match, + s.matchedName, }) } @@ -545,7 +556,11 @@ func precomputeAddresses(adds []*ofac.Address) []*Address { type Alt struct { AlternateIdentity *ofac.AlternateIdentity - match float64 // match % + // match holds the match ratio for an Alt in search results + match float64 + + // matchedName holds the highest scoring term from the search query + matchedName string // name is precomputed for speed name string @@ -555,10 +570,12 @@ type Alt struct { func (a Alt) MarshalJSON() ([]byte, error) { return json.Marshal(struct { *ofac.AlternateIdentity - Match float64 `json:"match"` + Match float64 `json:"match"` + MatchedName string `json:"matchedName"` }{ a.AlternateIdentity, a.match, + a.matchedName, }) } @@ -584,6 +601,7 @@ func precomputeAlts(alts []*ofac.AlternateIdentity, pipe *pipeliner) []*Alt { type DP struct { DeniedPerson *dpl.DPL match float64 + matchedName string name string } @@ -591,10 +609,12 @@ type DP struct { func (d DP) MarshalJSON() ([]byte, error) { return json.Marshal(struct { *dpl.DPL - Match float64 `json:"match"` + Match float64 `json:"match"` + MatchedName string `json:"matchedName"` }{ d.DeniedPerson, d.match, + d.matchedName, }) } diff --git a/cmd/server/search_handlers_test.go b/cmd/server/search_handlers_test.go index f96a70ff..05973fa7 100644 --- a/cmd/server/search_handlers_test.go +++ b/cmd/server/search_handlers_test.go @@ -270,6 +270,7 @@ func TestSearch__Name(t *testing.T) { require.Equal(t, http.StatusOK, w.Code) require.Contains(t, w.Body.String(), `"match":0.89166`) + require.Contains(t, w.Body.String(), `"matchedName":"dr ayman al zawahiri"`) var wrapper struct { // OFAC @@ -317,9 +318,9 @@ func TestSearch__AltName(t *testing.T) { t.Errorf("bogus status code: %d", w.Code) } - if v := w.Body.String(); !strings.Contains(v, `"match":0.5`) { - t.Error(v) - } + require.Equal(t, http.StatusOK, w.Code) + require.Contains(t, w.Body.String(), `"match":0.5`) + require.Contains(t, w.Body.String(), `"matchedName":"i c sogo kenkyusho"`) var wrapper struct { Alts []*ofac.AlternateIdentity `json:"altNames"`