@@ -89,6 +89,56 @@ namespace JSMdict {
8989 });
9090 }
9191
92+ std::string toLower(std::string_view str) {
93+ std::string result = std::string(str);
94+ std::transform(result.begin(), result.end(), result.begin(), ::tolower);
95+ return result;
96+ }
97+
98+ int levenshteinDistance(const std::string &s1, const std::string &s2) {
99+ size_t len1 = s1.size(), len2 = s2.size();
100+ std::vector<std::vector<int>> dp(len1 + 1, std::vector<int>(len2 + 1));
101+
102+ for (size_t i = 0; i <= len1; ++i) dp[i][0] = i;
103+ for (size_t j = 0; j <= len2; ++j) dp[0][j] = j;
104+
105+ for (size_t i = 1; i <= len1; ++i) {
106+ for (size_t j = 1; j <= len2; ++j) {
107+ dp[i][j] = std::min({dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + (s1[i - 1] != s2[j - 1])});
108+ }
109+ }
110+
111+ return dp[len1][len2];
112+ }
113+
114+ int computeScore(const std::string &candidateOriginal, const std::string &queryOriginal) {
115+ std::string candidate = toLower(candidateOriginal);
116+ std::string query = toLower(queryOriginal);
117+
118+ if (candidate == query) return 100;
119+ if (candidate.starts_with(query)) return 80;
120+ if (candidate.find(query) != std::string::npos) return 50;
121+ return std::max(0, 100 - levenshteinDistance(candidate, query) * 10);
122+ }
123+
124+ std::vector<JSVariant> sortKeyListByKeywordRelevance(std::vector<mdict::key_list_item *> &items, const std::string &query) {
125+ std::partial_sort(
126+ items.begin(),
127+ items.begin() + 10,
128+ items.end(),
129+ [&query](const mdict::key_list_item *a, const mdict::key_list_item *b) {
130+ int scoreA = computeScore(a->key_word, query);
131+ int scoreB = computeScore(b->key_word, query);
132+ return scoreA > scoreB || (scoreA == scoreB && a->record_start < b->record_start);
133+ }
134+ );
135+ items.resize(10);
136+ auto list = std::vector<JSVariant>();
137+ for (auto item: items) list.push_back(JSVariant(item->key_word));
138+ return list;
139+ }
140+
141+
92142 jsi::Value JSMdict::keyList(jsi::Runtime &runtime, std::string query) {
93143 checkInitialized(runtime);
94144 return Promise::createPromise(
@@ -97,17 +147,16 @@ namespace JSMdict {
97147 [this, query](const std::shared_ptr<Promise> &promise) {
98148 threadPool.cancel(currentSearchTaskId);
99149 currentSearchTaskId = threadPool.enqueue([this, promise, query]() {
100- auto list = std::vector<JSVariant >();
150+ auto tmp = std::vector<mdict::key_list_item * >();
101151 auto keys = mdict->keyList();
102- for (auto &key: keys ) {
103- if ( key->key_word.find(query) != std::string::npos) {
104- list.insert(list.end(), JSVariant(key->key_word) );
105- }
106- }
107- promise->resolve(JSVariant(list) );
152+ std::copy_if(keys.begin(), keys.end(), std::back_inserter(tmp), [&query](auto key ) {
153+ return key->key_word.find(query) != std::string::npos;
154+ } );
155+ keys.clear();
156+ promise->resolve(JSVariant(sortKeyListByKeywordRelevance(tmp, query)));
157+ tmp.clear( );
108158 });
109159 });
110160 }
111-
112161}
113162
0 commit comments