In this part, you will implement a simple component that searches for emojis
that match a query. First, download or copy-and-paste emojis.go
into your emojis/ directory. This file contains a map[string][]string called
emojis that maps every emoji to a list of labels. For example, the black cat
emoji 🐈⬛ has labels "animal", "animals", "black", "cat",
"mammal", and "nature".
Next, review the documentation on writing components.
Then, in a file called searcher.go, write a component called Searcher with
the following method:
Search(ctx context.Context, query string) ([]string, error)
The Search method receives a query like "black cat" and returns the emojis
that match the query. To implement the Search method, first lowercase the
query (strings.ToLower) and then split
the query into words (strings.Fields).
Then iterate over the emojis map in emojis.go to find all matching emojis.
We say an emoji matches a query if every word in the query is one of the
emoji's labels. Return the matching emojis is sorted order
(sort.Strings).
Solution.
|
package main |
|
|
|
import ( |
|
"context" |
|
"sort" |
|
"strings" |
|
|
|
"github.com/ServiceWeaver/weaver" |
|
"golang.org/x/exp/slices" |
|
) |
|
|
|
// Searcher is an emoji search engine component. |
|
type Searcher interface { |
|
// Search returns the set of emojis that match the provided query. |
|
Search(context.Context, string) ([]string, error) |
|
} |
|
|
|
// searcher is the implementation of the Searcher component. |
|
type searcher struct { |
|
weaver.Implements[Searcher] |
|
} |
|
|
|
func (s *searcher) Search(ctx context.Context, query string) ([]string, error) { |
|
// Perform the search. First, we lowercase the query and split it into |
|
// words. For example, the query "Black cat" is tokenized to the words |
|
// "black" and "cat". Then, we say an emoji matches a query if every word |
|
// in the query is one of the emoji's labels. |
|
// |
|
// For example, the cat emoji has labels ["animal", "cat"]. It does NOT |
|
// match the "Black cat" query because "black" is not a label. The black |
|
// cat emoji, on the other hand, has labels ["animal", "black", "cat"] and |
|
// therefore does match the query "Black cat". |
|
// |
|
// We iterate over all emojis and return the ones that match the query. |
|
words := strings.Fields(strings.ToLower(query)) |
|
results := []string{} |
|
for emoji, labels := range emojis { |
|
if matches(labels, words) { |
|
results = append(results, emoji) |
|
} |
|
} |
|
sort.Strings(results) |
|
return results, nil |
|
} |
|
|
|
// matches returns whether words is a subset of labels. |
|
func matches(labels, words []string) bool { |
|
for _, word := range words { |
|
if !slices.Contains(labels, word) { |
|
return false |
|
} |
|
} |
|
return true |
|
} |
Next, update your application to print out the emojis that match the query
"pig":
Solution.
|
// app is the main component of our application. |
|
type app struct { |
|
weaver.Implements[weaver.Main] |
|
searcher weaver.Ref[Searcher] |
|
} |
|
|
|
// run implements the application main. |
|
func run(ctx context.Context, a *app) error { |
|
emojis, err := a.searcher.Get().Search(ctx, "pig") |
|
if err != nil { |
|
return err |
|
} |
|
fmt.Println(emojis) |
|
return nil |
|
} |
Finally, run your application.
$ weaver generate .
$ go run .
[🐖 🐗 🐷 🐽]
The "pig" query matches the pig emoji 🐖, the boar emoji 🐗, the pig face
emoji 🐷, and the pig nose emoji 🐽.
Note that you'll have to run weaver generate whenever you add a component,
remove a component, or change the interface of a component. If your application
ever fails to build with an error coming from a weaver_gen.go file, try
re-running weaver generate.
⬅️ Previous Part
⚫
Next Part ➡️