| package semantic |
|
|
| import "strings" |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| var uiSynonyms = map[string][]string{ |
| |
| "login": {"signin", "log in", "sign in", "authenticate", "logon", "log on"}, |
| "logout": {"signout", "log out", "sign out", "logoff"}, |
| "register": {"signup", "sign up", "create account", "join", "enroll"}, |
| "password": {"passcode", "passphrase", "pwd"}, |
| "username": {"userid", "user name", "user id", "login name"}, |
| "email": {"e-mail", "mail", "email address"}, |
| "forgot": {"reset", "recover", "lost"}, |
|
|
| |
| "search": {"find", "lookup", "look up", "query", "filter"}, |
| "menu": {"navigation", "nav", "sidebar", "hamburger"}, |
| "home": {"homepage", "main page", "start", "landing"}, |
| "back": {"return", "go back", "previous"}, |
| "next": {"continue", "proceed", "forward", "advance"}, |
| "previous": {"prev", "back", "prior"}, |
| "close": {"dismiss", "exit", "x", "cancel"}, |
| "open": {"expand", "show", "reveal"}, |
| "settings": {"preferences", "options", "configuration", "config"}, |
|
|
| |
| "submit": {"send", "confirm", "apply", "save", "done", "go"}, |
| "cancel": {"abort", "discard", "nevermind"}, |
| "edit": {"modify", "change", "update"}, |
| "delete": {"remove", "erase", "trash", "discard"}, |
| "add": {"create", "new", "insert", "plus"}, |
| "upload": {"attach", "choose file", "browse"}, |
| "download": {"export", "save as", "get"}, |
|
|
| |
| "button": {"btn", "cta"}, |
| "input": {"field", "textbox", "text box", "text field"}, |
| "dropdown": {"select", "combobox", "combo box", "picker", "listbox"}, |
| "checkbox": {"check box", "tick", "toggle"}, |
| "link": {"anchor", "hyperlink", "href"}, |
| "tab": {"panel", "pane"}, |
| "modal": {"dialog", "dialogue", "popup", "pop up", "overlay"}, |
| "notification": {"alert", "toast", "banner", "message"}, |
| "tooltip": {"hint", "info", "help text"}, |
| "avatar": {"profile picture", "profile pic", "user image", "photo"}, |
|
|
| |
| "cart": {"basket", "bag", "shopping cart"}, |
| "checkout": {"pay", "payment", "purchase", "buy", "place order", "order", "proceed to payment"}, |
| "price": {"cost", "amount", "total"}, |
| "quantity": {"qty", "count", "amount"}, |
|
|
| |
| "image": {"img", "picture", "photo", "icon"}, |
| "video": {"clip", "media", "player"}, |
| "title": {"heading", "header", "headline"}, |
| "description": {"desc", "summary", "subtitle", "caption"}, |
| "list": {"items", "collection", "grid"}, |
|
|
| |
| "click": {"press", "tap", "hit", "select"}, |
| "scroll": {"swipe", "slide"}, |
| "drag": {"move", "reorder"}, |
| "copy": {"duplicate", "clone"}, |
| "paste": {"insert"}, |
| "undo": {"revert", "rollback"}, |
| "redo": {"repeat"}, |
| "refresh": {"reload", "update"}, |
| "share": {"send", "forward"}, |
| "like": {"favorite", "favourite", "heart", "star", "upvote"}, |
| "accept": {"agree", "allow", "ok", "okay", "yes", "confirm"}, |
| "reject": {"deny", "decline", "refuse", "no"}, |
| } |
|
|
| |
| |
| |
| var synonymIndex map[string]map[string]bool |
|
|
| func init() { |
| synonymIndex = buildSynonymIndex(uiSynonyms) |
| } |
|
|
| |
| |
| func buildSynonymIndex(table map[string][]string) map[string]map[string]bool { |
| idx := make(map[string]map[string]bool) |
|
|
| ensure := func(key string) { |
| if idx[key] == nil { |
| idx[key] = make(map[string]bool) |
| } |
| } |
|
|
| for canonical, synonyms := range table { |
| ensure(canonical) |
| for _, syn := range synonyms { |
| idx[canonical][syn] = true |
| } |
|
|
| for _, syn := range synonyms { |
| ensure(syn) |
| idx[syn][canonical] = true |
| for _, other := range synonyms { |
| if other != syn { |
| idx[syn][other] = true |
| } |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
|
|
| return idx |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| func expandWithSynonyms(queryTokens []string, descTokens []string) []string { |
| descSet := make(map[string]bool, len(descTokens)) |
| for _, dt := range descTokens { |
| descSet[dt] = true |
| } |
|
|
| |
| |
| queryPhrases := buildPhrases(queryTokens, 3) |
|
|
| expanded := make([]string, 0, len(queryTokens)*2) |
| usedIndices := make(map[int]bool) |
|
|
| |
| for _, phrase := range queryPhrases { |
| if syns, ok := synonymIndex[phrase.text]; ok { |
| for syn := range syns { |
| synTokens := strings.Fields(syn) |
| for _, st := range synTokens { |
| if descSet[st] { |
| |
| expanded = append(expanded, synTokens...) |
| for idx := phrase.startIdx; idx <= phrase.endIdx; idx++ { |
| usedIndices[idx] = true |
| } |
| break |
| } |
| } |
| } |
| } |
| } |
|
|
| |
| for i, qt := range queryTokens { |
| if usedIndices[i] { |
| continue |
| } |
| expanded = append(expanded, qt) |
| if syns, ok := synonymIndex[qt]; ok { |
| for syn := range syns { |
| synTokens := strings.Fields(syn) |
| for _, st := range synTokens { |
| if descSet[st] { |
| expanded = append(expanded, synTokens...) |
| break |
| } |
| } |
| } |
| } |
| } |
|
|
| return expanded |
| } |
|
|
| |
| type phrase struct { |
| text string |
| startIdx int |
| endIdx int |
| } |
|
|
| |
| |
| func buildPhrases(tokens []string, maxN int) []phrase { |
| var phrases []phrase |
| for n := 2; n <= maxN && n <= len(tokens); n++ { |
| for i := 0; i <= len(tokens)-n; i++ { |
| phrases = append(phrases, phrase{ |
| text: strings.Join(tokens[i:i+n], " "), |
| startIdx: i, |
| endIdx: i + n - 1, |
| }) |
| } |
| } |
| return phrases |
| } |
|
|
| |
| |
| |
| |
| func synonymScore(queryTokens, descTokens []string) float64 { |
| if len(queryTokens) == 0 || len(descTokens) == 0 { |
| return 0 |
| } |
|
|
| descSet := make(map[string]bool, len(descTokens)) |
| for _, dt := range descTokens { |
| descSet[dt] = true |
| } |
|
|
| matched := 0 |
| consumedIdx := make(map[int]bool) |
|
|
| |
| queryPhrases := buildPhrases(queryTokens, 3) |
| for _, p := range queryPhrases { |
| if syns, ok := synonymIndex[p.text]; ok { |
| for syn := range syns { |
| synTokens := strings.Fields(syn) |
| allPresent := true |
| for _, st := range synTokens { |
| if !descSet[st] { |
| allPresent = false |
| break |
| } |
| } |
| if allPresent { |
| matched++ |
| for idx := p.startIdx; idx <= p.endIdx; idx++ { |
| consumedIdx[idx] = true |
| } |
| break |
| } |
| } |
| } |
| } |
|
|
| |
| for i, qt := range queryTokens { |
| if consumedIdx[i] { |
| continue |
| } |
| if descSet[qt] { |
| continue |
| } |
| if syns, ok := synonymIndex[qt]; ok { |
| for syn := range syns { |
| synTokens := strings.Fields(syn) |
| allPresent := true |
| for _, st := range synTokens { |
| if !descSet[st] { |
| allPresent = false |
| break |
| } |
| } |
| if allPresent { |
| matched++ |
| break |
| } |
| } |
| } |
| } |
|
|
| return float64(matched) / float64(len(queryTokens)) |
| } |
|
|