2024-09-11 17:31:38 +02:00
|
|
|
import Locale from "../../UI/i18n/Locale"
|
|
|
|
import { Utils } from "../../Utils"
|
|
|
|
import ThemeSearch from "./ThemeSearch"
|
2025-08-01 04:02:09 +02:00
|
|
|
import { Lists } from "../../Utils/Lists"
|
2025-08-27 00:05:14 +02:00
|
|
|
import { Strings } from "../../Utils/Strings"
|
2024-09-11 17:31:38 +02:00
|
|
|
|
|
|
|
export default class SearchUtils {
|
|
|
|
/** Applies special search terms, such as 'studio', 'osmcha', ...
|
|
|
|
* Returns 'false' if nothing is matched.
|
|
|
|
* Doesn't return control flow if a match is found (navigates to another page in this case)
|
|
|
|
*/
|
2024-10-19 14:44:55 +02:00
|
|
|
public static applySpecialSearch(searchTerm: string) {
|
2024-09-11 17:31:38 +02:00
|
|
|
searchTerm = searchTerm.toLowerCase()
|
|
|
|
if (!searchTerm) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if (searchTerm === "personal") {
|
|
|
|
window.location.href = ThemeSearch.createUrlFor({ id: "personal" }, undefined)
|
2024-10-18 00:27:39 +02:00
|
|
|
return true
|
2024-09-11 17:31:38 +02:00
|
|
|
}
|
|
|
|
if (searchTerm === "bugs" || searchTerm === "issues") {
|
2025-04-07 19:10:11 +02:00
|
|
|
window.location.href = "https://source.mapcomplete.org/MapComplete/MapComplete/issues"
|
2024-10-18 00:27:39 +02:00
|
|
|
return true
|
2024-09-11 17:31:38 +02:00
|
|
|
}
|
|
|
|
if (searchTerm === "source") {
|
2025-04-07 19:10:11 +02:00
|
|
|
window.location.href = "https://source.mapcomplete.org/MapComplete/MapComplete"
|
2024-10-18 00:27:39 +02:00
|
|
|
return true
|
2024-09-11 17:31:38 +02:00
|
|
|
}
|
|
|
|
if (searchTerm === "docs") {
|
2025-04-07 19:10:11 +02:00
|
|
|
window.location.href =
|
|
|
|
"https://source.mapcomplete.org/MapComplete/MapComplete/src/branch/develop/Docs"
|
2024-10-18 00:27:39 +02:00
|
|
|
return true
|
2024-09-11 17:31:38 +02:00
|
|
|
}
|
|
|
|
if (searchTerm === "osmcha" || searchTerm === "stats") {
|
|
|
|
window.location.href = Utils.OsmChaLinkFor(7)
|
2024-10-18 00:27:39 +02:00
|
|
|
return true
|
2024-09-11 17:31:38 +02:00
|
|
|
}
|
|
|
|
if (searchTerm === "studio") {
|
|
|
|
window.location.href = "./studio.html"
|
2024-10-18 00:27:39 +02:00
|
|
|
return true
|
2024-09-11 17:31:38 +02:00
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Searches for the smallest distance in words; will split both the query and the terms
|
|
|
|
*
|
|
|
|
* SearchUtils.scoreKeywords("drinking water", {"en": ["A layer with drinking water points"]}, "en") // => 0
|
|
|
|
* SearchUtils.scoreKeywords("waste", {"en": ["A layer with drinking water points"]}, "en") // => 2
|
|
|
|
*
|
|
|
|
*/
|
2024-10-19 14:44:55 +02:00
|
|
|
public static scoreKeywords(
|
|
|
|
query: string,
|
|
|
|
keywords: Record<string, string[]> | string[],
|
|
|
|
language?: string
|
|
|
|
): number {
|
|
|
|
if (!keywords) {
|
2024-09-11 17:31:38 +02:00
|
|
|
return Infinity
|
|
|
|
}
|
|
|
|
language ??= Locale.language.data
|
2024-10-19 14:44:55 +02:00
|
|
|
const queryParts = query
|
|
|
|
.trim()
|
|
|
|
.split(" ")
|
2025-08-27 00:05:14 +02:00
|
|
|
.map((q) => Strings.simplifyStringForSearch(q))
|
2024-09-11 17:31:38 +02:00
|
|
|
let terms: string[]
|
|
|
|
if (Array.isArray(keywords)) {
|
|
|
|
terms = keywords
|
|
|
|
} else {
|
|
|
|
terms = (keywords[language] ?? []).concat(keywords["*"])
|
|
|
|
}
|
2025-08-01 04:02:09 +02:00
|
|
|
const termsAll = Lists.noNullInplace(terms).flatMap((t) => t.split(" "))
|
2024-09-11 17:31:38 +02:00
|
|
|
|
|
|
|
let distanceSummed = 0
|
|
|
|
for (let i = 0; i < queryParts.length; i++) {
|
|
|
|
const q = queryParts[i]
|
|
|
|
let minDistance: number = 99
|
|
|
|
for (const term of termsAll) {
|
2025-08-27 00:05:14 +02:00
|
|
|
const d = Utils.levenshteinDistance(q, Strings.simplifyStringForSearch(term))
|
2024-09-11 17:31:38 +02:00
|
|
|
if (d < minDistance) {
|
|
|
|
minDistance = d
|
|
|
|
}
|
|
|
|
}
|
|
|
|
distanceSummed += minDistance
|
|
|
|
}
|
|
|
|
return distanceSummed
|
|
|
|
}
|
|
|
|
}
|