Skip to content
This repository was archived by the owner on Jan 9, 2026. It is now read-only.

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

A Search Component

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.

workshops/02/searcher.go

Lines 15 to 68 in 4eca79e

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.

workshops/02/main.go

Lines 30 to 44 in 4eca79e

// 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 ➡️