Skip to content

Commit 414ab00

Browse files
authored
Enhanced search logic of board listall, core search and lib search (#1222)
* Remove fuzzy logic from lib search command * Remove fuzzy logic from core search command * Remove fuzzy logic from board listall command * Remove fuzzysearch dependency
1 parent b7f3dae commit 414ab00

File tree

9 files changed

+111
-163
lines changed

9 files changed

+111
-163
lines changed

commands/board/listall.go

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ import (
2020
"errors"
2121
"strings"
2222

23+
"github.com/arduino/arduino-cli/arduino/utils"
2324
"github.com/arduino/arduino-cli/commands"
2425
rpc "github.com/arduino/arduino-cli/rpc/commands"
25-
"github.com/lithammer/fuzzysearch/fuzzy"
2626
)
2727

2828
// maximumSearchDistance is the maximum Levenshtein distance accepted when using fuzzy search.
@@ -36,18 +36,26 @@ func ListAll(ctx context.Context, req *rpc.BoardListAllReq) (*rpc.BoardListAllRe
3636
return nil, errors.New("invalid instance")
3737
}
3838

39-
searchArgs := strings.Join(req.SearchArgs, " ")
39+
searchArgs := []string{}
40+
for _, s := range req.SearchArgs {
41+
searchArgs = append(searchArgs, strings.Trim(s, " "))
42+
}
4043

41-
match := func(toTest []string) bool {
44+
match := func(toTest []string) (bool, error) {
4245
if len(searchArgs) == 0 {
43-
return true
46+
return true, nil
4447
}
45-
for _, rank := range fuzzy.RankFindNormalizedFold(searchArgs, toTest) {
46-
if rank.Distance < maximumSearchDistance {
47-
return true
48+
49+
for _, t := range toTest {
50+
matches, err := utils.Match(t, searchArgs)
51+
if err != nil {
52+
return false, err
53+
}
54+
if matches {
55+
return matches, nil
4856
}
4957
}
50-
return false
58+
return false, nil
5159
}
5260

5361
list := &rpc.BoardListAllResp{Boards: []*rpc.BoardListItem{}}
@@ -90,10 +98,11 @@ func ListAll(ctx context.Context, req *rpc.BoardListAllReq) (*rpc.BoardListAllRe
9098
continue
9199
}
92100

93-
toTest := toTest
94-
toTest = append(toTest, strings.Split(board.Name(), " ")...)
101+
toTest := append(toTest, board.Name())
95102
toTest = append(toTest, board.FQBN())
96-
if !match(toTest) {
103+
if ok, err := match(toTest); err != nil {
104+
return nil, err
105+
} else if !ok {
97106
continue
98107
}
99108

commands/core/search.go

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ import (
2121
"strings"
2222

2323
"github.com/arduino/arduino-cli/arduino/cores"
24+
"github.com/arduino/arduino-cli/arduino/utils"
2425
"github.com/arduino/arduino-cli/commands"
2526
rpc "github.com/arduino/arduino-cli/rpc/commands"
26-
"github.com/lithammer/fuzzysearch/fuzzy"
2727
)
2828

2929
// maximumSearchDistance is the maximum Levenshtein distance accepted when using fuzzy search.
@@ -44,6 +44,26 @@ func PlatformSearch(req *rpc.PlatformSearchReq) (*rpc.PlatformSearchResp, error)
4444
vid, pid := searchArgs[:4], searchArgs[5:]
4545
res = pm.FindPlatformReleaseProvidingBoardsWithVidPid(vid, pid)
4646
} else {
47+
48+
searchArgs := strings.Split(searchArgs, " ")
49+
50+
match := func(toTest []string) (bool, error) {
51+
if len(searchArgs) == 0 {
52+
return true, nil
53+
}
54+
55+
for _, t := range toTest {
56+
matches, err := utils.Match(t, searchArgs)
57+
if err != nil {
58+
return false, err
59+
}
60+
if matches {
61+
return matches, nil
62+
}
63+
}
64+
return false, nil
65+
}
66+
4767
for _, targetPackage := range pm.Packages {
4868
for _, platform := range targetPackage.Platforms {
4969
// discard invalid platforms
@@ -60,15 +80,6 @@ func PlatformSearch(req *rpc.PlatformSearchReq) (*rpc.PlatformSearchResp, error)
6080
continue
6181
}
6282

63-
if searchArgs == "" {
64-
if allVersions {
65-
res = append(res, platform.GetAllReleases()...)
66-
} else {
67-
res = append(res, platformRelease)
68-
}
69-
continue
70-
}
71-
7283
// Gather all strings that can be used for searching
7384
toTest := []string{
7485
platform.String(),
@@ -82,32 +93,18 @@ func PlatformSearch(req *rpc.PlatformSearchReq) (*rpc.PlatformSearchResp, error)
8293
toTest = append(toTest, board.Name)
8394
}
8495

85-
// Removes some chars from query strings to enhance results
86-
cleanSearchArgs := strings.Map(func(r rune) rune {
87-
switch r {
88-
case '_':
89-
case '-':
90-
case ' ':
91-
return -1
92-
}
93-
return r
94-
}, searchArgs)
96+
// Search
97+
if ok, err := match(toTest); err != nil {
98+
return nil, err
99+
} else if !ok {
100+
continue
101+
}
95102

96-
// Fuzzy search
97-
for _, arg := range []string{searchArgs, cleanSearchArgs} {
98-
for _, rank := range fuzzy.RankFindNormalizedFold(arg, toTest) {
99-
// Accepts only results that close to the searched terms
100-
if rank.Distance < maximumSearchDistance {
101-
if allVersions {
102-
res = append(res, platform.GetAllReleases()...)
103-
} else {
104-
res = append(res, platformRelease)
105-
}
106-
goto nextPlatform
107-
}
108-
}
103+
if allVersions {
104+
res = append(res, platform.GetAllReleases()...)
105+
} else {
106+
res = append(res, platformRelease)
109107
}
110-
nextPlatform:
111108
}
112109
}
113110
}

commands/lib/search.go

Lines changed: 22 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ import (
2222

2323
"github.com/arduino/arduino-cli/arduino/libraries/librariesindex"
2424
"github.com/arduino/arduino-cli/arduino/libraries/librariesmanager"
25+
"github.com/arduino/arduino-cli/arduino/utils"
2526
"github.com/arduino/arduino-cli/commands"
2627
rpc "github.com/arduino/arduino-cli/rpc/commands"
27-
"github.com/lithammer/fuzzysearch/fuzzy"
2828
semver "go.bug.st/relaxed-semver"
2929
)
3030

@@ -43,44 +43,33 @@ func searchLibrary(req *rpc.LibrarySearchReq, lm *librariesmanager.LibrariesMana
4343
res := []*rpc.SearchedLibrary{}
4444
status := rpc.LibrarySearchStatus_success
4545

46-
// If the query is empty all libraries are returned
47-
if strings.Trim(query, " ") == "" {
48-
for _, lib := range lm.Index.Libraries {
49-
res = append(res, indexLibraryToRPCSearchLibrary(lib))
46+
searchArgs := strings.Split(strings.Trim(query, " "), " ")
47+
48+
match := func(toTest []string) (bool, error) {
49+
if len(searchArgs) == 0 {
50+
return true, nil
5051
}
51-
return &rpc.LibrarySearchResp{Libraries: res, Status: status}, nil
52-
}
5352

54-
// maximumSearchDistance is the maximum Levenshtein distance accepted when using fuzzy search.
55-
// This value is completely arbitrary and picked randomly.
56-
maximumSearchDistance := 150
57-
// Use a lower distance for shorter query or the user might be flooded with unrelated results
58-
if len(query) <= 4 {
59-
maximumSearchDistance = 40
53+
for _, t := range toTest {
54+
matches, err := utils.Match(t, searchArgs)
55+
if err != nil {
56+
return false, err
57+
}
58+
if matches {
59+
return matches, nil
60+
}
61+
}
62+
return false, nil
6063
}
6164

62-
// Removes some chars from query strings to enhance results
63-
cleanQuery := strings.Map(func(r rune) rune {
64-
switch r {
65-
case '_':
66-
case '-':
67-
case ' ':
68-
return -1
69-
}
70-
return r
71-
}, query)
7265
for _, lib := range lm.Index.Libraries {
73-
// Use both uncleaned and cleaned query
74-
for _, q := range []string{query, cleanQuery} {
75-
toTest := []string{lib.Name, lib.Latest.Paragraph, lib.Latest.Sentence}
76-
for _, rank := range fuzzy.RankFindNormalizedFold(q, toTest) {
77-
if rank.Distance < maximumSearchDistance {
78-
res = append(res, indexLibraryToRPCSearchLibrary(lib))
79-
goto nextLib
80-
}
81-
}
66+
toTest := []string{lib.Name, lib.Latest.Paragraph, lib.Latest.Sentence}
67+
if ok, err := match(toTest); err != nil {
68+
return nil, err
69+
} else if !ok {
70+
continue
8271
}
83-
nextLib:
72+
res = append(res, indexLibraryToRPCSearchLibrary(lib))
8473
}
8574

8675
return &rpc.LibrarySearchResp{Libraries: res, Status: status}, nil

commands/lib/search_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func TestSearchLibrarySimilar(t *testing.T) {
3939

4040
req := &rpc.LibrarySearchReq{
4141
Instance: &rpc.Instance{Id: 1},
42-
Query: "ardino",
42+
Query: "arduino",
4343
}
4444

4545
resp, err := searchLibrary(req, lm)

go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ require (
2121
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect
2222
github.com/kr/text v0.2.0 // indirect
2323
github.com/leonelquinteros/gotext v1.4.0
24-
github.com/lithammer/fuzzysearch v1.1.1
2524
github.com/marcinbor85/gohex v0.0.0-20210308104911-55fb1c624d84
2625
github.com/mattn/go-colorable v0.1.2
2726
github.com/mattn/go-isatty v0.0.8

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
150150
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
151151
github.com/leonelquinteros/gotext v1.4.0 h1:2NHPCto5IoMXbrT0bldPrxj0qM5asOCwtb1aUQZ1tys=
152152
github.com/leonelquinteros/gotext v1.4.0/go.mod h1:yZGXREmoGTtBvZHNcc+Yfug49G/2spuF/i/Qlsvz1Us=
153-
github.com/lithammer/fuzzysearch v1.1.1 h1:8F9OAV2xPuYblToVohjanztdnPjbtA0MLgMvDKQ0Z08=
154-
github.com/lithammer/fuzzysearch v1.1.1/go.mod h1:H2bng+w5gsR7NlfIJM8ElGZI0sX6C/9uzGqicVXGU6c=
155153
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
156154
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
157155
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=

test/test_board.py

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -455,34 +455,6 @@ def test_board_listall_with_manually_installed_platform(run_command, data_dir):
455455
assert "Arduino SAMD (32-bits ARM Cortex-M0+) Boards" == platform["Name"]
456456

457457

458-
def test_board_listall_fuzzy_search(run_command, data_dir):
459-
assert run_command("update")
460-
461-
# Install from platform manager
462-
assert run_command("core install arduino:[email protected]")
463-
464-
# Manually installs a core in sketchbooks hardware folder
465-
git_url = "https://github.com/arduino/ArduinoCore-samd.git"
466-
repo_dir = Path(data_dir, "hardware", "arduino-beta-development", "samd")
467-
assert Repo.clone_from(git_url, repo_dir, multi_options=["-b 1.8.11"])
468-
469-
res = run_command("board listall --format json samd")
470-
assert res.ok
471-
data = json.loads(res.stdout)
472-
boards = {b["FQBN"]: b for b in data["boards"]}
473-
assert len(boards) == 17
474-
assert "arduino-beta-development:samd:mkr1000" in boards
475-
assert "arduino:avr:uno" not in boards
476-
477-
res = run_command("board listall --format json avr")
478-
assert res.ok
479-
data = json.loads(res.stdout)
480-
boards = {b["FQBN"]: b for b in data["boards"]}
481-
assert len(boards) == 26
482-
assert "arduino:avr:uno" in boards
483-
assert "arduino-beta-development:samd:mkr1000" not in boards
484-
485-
486458
def test_board_details(run_command):
487459
run_command("core update-index")
488460
# Download samd core pinned to 1.8.6

test/test_core.py

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,33 @@ def get_platforms(stdout):
7676
assert "1.0.5" in platforms["Retrokits-RK002:arm"]
7777
assert "1.0.6" in platforms["Retrokits-RK002:arm"]
7878

79-
# Search using a board name
79+
# Search using board names
8080
result = run_command(f"core search myboard --all --additional-urls={url} --format json")
8181
assert result.ok
8282
platforms = get_platforms(result.stdout)
8383
assert "1.2.3" in platforms["Package:x86"]
8484

85+
def run_search(search_args, expected_ids):
86+
res = run_command(f"core search --format json {search_args}")
87+
assert res.ok
88+
data = json.loads(res.stdout)
89+
platform_ids = [p["ID"] for p in data]
90+
for platform_id in expected_ids:
91+
assert platform_id in platform_ids
92+
93+
run_search("mkr1000", ["arduino:samd"])
94+
run_search("mkr 1000", ["arduino:samd"])
95+
96+
run_search("yún", ["arduino:avr"])
97+
run_search("yùn", ["arduino:avr"])
98+
run_search("yun", ["arduino:avr"])
99+
100+
run_search("nano", ["arduino:avr", "arduino:megaavr", "arduino:samd", "arduino:mbed"])
101+
run_search("nano 33", ["arduino:samd", "arduino:mbed"])
102+
run_search("nano ble", ["arduino:mbed"])
103+
run_search("ble", ["arduino:mbed"])
104+
run_search("ble nano", ["arduino:mbed"])
105+
85106

86107
def test_core_search_no_args(run_command, httpserver):
87108
"""
@@ -146,32 +167,6 @@ def test_core_search_no_args(run_command, httpserver):
146167
assert len(platforms) == num_platforms
147168

148169

149-
def test_core_search_fuzzy(run_command):
150-
assert run_command("update")
151-
152-
def run_fuzzy_search(search_args, expected_ids):
153-
res = run_command(f"core search --format json {search_args}")
154-
assert res.ok
155-
data = json.loads(res.stdout)
156-
platform_ids = [p["ID"] for p in data]
157-
for platform_id in expected_ids:
158-
assert platform_id in platform_ids
159-
160-
run_fuzzy_search("mkr1000", ["arduino:samd"])
161-
run_fuzzy_search("mkr 1000", ["arduino:samd"])
162-
163-
run_fuzzy_search("yún", ["arduino:avr"])
164-
run_fuzzy_search("yùn", ["arduino:avr"])
165-
run_fuzzy_search("yun", ["arduino:avr"])
166-
167-
run_fuzzy_search("nano", ["arduino:avr", "arduino:megaavr", "arduino:samd", "arduino:mbed"])
168-
run_fuzzy_search("nano33", ["arduino:samd", "arduino:mbed"])
169-
run_fuzzy_search("nano 33", ["arduino:samd", "arduino:mbed"])
170-
run_fuzzy_search("nano ble", ["arduino:mbed"])
171-
run_fuzzy_search("ble", ["arduino:mbed"])
172-
run_fuzzy_search("ble nano", [])
173-
174-
175170
def test_core_updateindex_url_not_found(run_command, httpserver):
176171
assert run_command("core update-index")
177172

0 commit comments

Comments
 (0)