1 of 17

COVEO SETUP WALKTHROUGH

Building a Pokédex

on Coveo

How the organization is set up, how each piece works, and what to consider at every step.

Mariam Al-Ansary

2 of 17

OVERVIEW

The setup, in five parts

Follow them in order. Each part depends on the one before it.

01

Foundations

Organization, search hub, API key, and how the pieces connect.

02

The source

Scope, exclusions, scraping, fields, and data hidden in tabs.

03

Query pipeline

One condition, no extra rules, and query correction.

04

Machine learning

An encoder, generated answers, passages, and suggestions.

05

Front end

An Atomic page that sends the hub and logs clicks.

Coveo Pokédex · Setup walkthrough

2

3 of 17

PART 1 · FOUNDATIONS

How the pieces fit together

pokemondb.net

Species pages

Web source

pokedex-V2 · 1,025 items

Scraping + extension

Type, generation, image, forms

Pipeline + ML

Pokemon Search · SE, RGA, PR, QS

Atomic page

Search and species pages

Coveo Pokédex · Setup walkthrough

3

4 of 17

PART 2 · THE SOURCE

Building the source

01

Scope

Which pages belong in the index?

02

Exclusions

What must stay out, even when it is linked?

03

Structure

Which values become fields and facets?

04

Hidden data

What is on the page.

5 of 17

PART 2 · STEP 1 · SCOPE

Define which pages belong

A Web source that starts at the species directory and only keeps species pages.

SOURCE TYPE

Web

START URL

pokemondb.net/pokedex/all

PAGE LEVELS

1

INCLUDE

^https://pokemondb\.net/pokedex/[a-z0-9-]+$

EXCLUDE

/pokedex/all · /pokedex/national

EXPAND BEFORE FILTERING

true, in the source JSON

1,025

species pages indexed

One crawled URL becomes one item in the index.

Coveo Pokédex · Setup walkthrough

5

6 of 17

PART 2 · STEP 2 · EXCLUSIONS

Keep out what does not belong

Species pages link to the whole site. One regex and two named exclusions keep the index to species pages.

WHAT’S INDEXED

/pokedex/bulbasaur

/pokedex/charizard

/pokedex/raichu

/pokedex/mr-mime

One path segment after /pokedex

KEPT OUT, AND WHY

/move/tackle

outside /pokedex

/type/fire

linked from the type column

/pokedex/bulbasaur/moves/9

two path segments

/pokedex/all

read for links, then dropped

/pokedex/national

excluded by name

sitemap.xml

not used as the source type

Coveo Pokédex · Setup walkthrough

6

7 of 17

PART 2 · STEP 3 · SCRAPING

Pull structure out of the HTML

A species configuration on top of Coveo’s default one. It cleans the body and extracts what the fields need.

REMOVED FROM THE BODY

header, nav, footer

Coveo default configuration

#dex-moves

move tables

#dex-sprites

sprite galleries

Consider: this keeps move lists out of the text the models read. URL rules, not body exclusions, keep pages out of the index.

METADATA EXTRACTED

pokemon_type

XPath on the first basics panel only, ending in /text()

pokemon_generation

XPath on the first <abbr> in “introduced in”, not “Generations 2–7”

pokemon_name

CSS h1::text

og:image

XPath on the Open Graph image, the artwork URL

Coveo Pokédex · Setup walkthrough

7

8 of 17

PART 2 · STEP 4 · FIELDS

Map metadata into fields

A facet filters a field. A field is filled by a mapping. A mapping reads the metadata the scraper extracted.

1 · SCRAPE

XPath on the vitals table

…th['Type']/…/td[1]//a/text()

2 · MAP

Mapping rule

%[pokemon_type:crawler]

3 · INDEX

Facet field

pokemon_type · multi-value

FIELDS IN THE INDEX

pokemon_type

pokemon_generation

pokemon_name

pokemon_img

pokemon_forms

Consider: turn on multi-value for any field that holds a list. Without it, Grass and Poison collapse into one “Grass;Poison” value. A field’s type cannot be changed later.

Coveo Pokédex · Setup walkthrough

8

9 of 17

PART 2 · STEP 5 · HIDDEN DATA

Capture data that is not at its own URL

Alolan Raichu, Mega Raichu X and Y, Partner Pikachu: all of them live as tabs on a single species page.

HOW THE PAGE IS BUILT

Form tabs are in-page anchors, not URLs. One crawled URL is one item, so one set of fields cannot describe every form.

HOW TO CAPTURE IT

A Python indexing extension runs pre-conversion on the original HTML and writes every form into pokemon_forms as JSON.

pokemon_forms (one form of Raichu)

{ "name": "Alolan Raichu", "types": ["Electric", "Psychic"], "abilities": "Surge Surfer",

"stats": { "HP": "60", "Attack": "85", "Speed": "110" }, "total": "485" }

Consider: Body HTML is already converted, so the tabs are gone. Read the original file (documentdata) and apply the extension pre-conversion.

Coveo Pokédex · Setup walkthrough

9

10 of 17

PART 2 · STEP 6 · ITERATE

Pilot first, then scale

Check selectors on a handful of pages before crawling the full site.

PILOT

pokemon-pilot-3

Five pages: Bulbasaur, Pikachu, Charmander, Pidgey, Squirtle. Depth 0. Confirms each selector returns the right value.

FIRST FULL SOURCE

pokedex

All 1,025 species. Shows how real pages behave, such as tables repeated once per form.

LIVE SOURCE

pokedex-V2

Same URL rules, the first-panel type selector, and the forms extension.

Coveo Pokédex · Setup walkthrough

10

11 of 17

PART 3 · QUERY PIPELINE

Keep the pipeline thin

Condition: Search Hub is Pokemon Search. No extra ranking rules, so the ML models drive relevance.

Facets filter through the advanced query, so they need no pipeline rules.

Query correction is on. It applies automatically when the corrected query is the one with results.

The legacy Did You Mean switches have no effect once Query correction is on.

⌕ pikacu

Still has hits, so the page offers Did you mean pikachu?

⌕ manchop

No hits, so it is corrected in the same response: We couldn’t find anything for manchop, did you mean machop

Coveo Pokédex · Setup walkthrough

11

12 of 17

PART 4 · MACHINE LEARNING

Four models on one pipeline

Scope the encoder and RGA to the source. They read the cleaned page body from step 3.

SEMANTIC ENCODER

Pokedex SE

Embeds every page body. RGA and Passage Retrieval both depend on it.

GENERATIVE ANSWERING

Pokedex RGA

Writes the answer from the retrieved chunks, and only from them.

PASSAGE RETRIEVAL

Pokemon PR

Serves the Passage Retrieval API, called on every search.

QUERY SUGGESTIONS

Pokedex QS

Suggests queries from what people searched and then clicked.

All four are associated with the Pokemon Search pipeline.

Coveo Pokédex · Setup walkthrough

12

13 of 17

PART 4 · MACHINE LEARNING

Answers and passages are different calls

Both stay inside the index, and both send the same search hub.

When the pages say it

RGA answers from the chunks it retrieved. Ask for a type, an ability, or a stat and it cites the Pokédex page.

When they don’t

A fight winner or an unstated comparison gets no answer. The page says so:

“The Pokédex pages don’t state an answer to that. The closest entries are below.”

POST /rest/search/v3/passages/retrieve

​

{

"query": "what type is charizard",

"searchHub": "Pokemon Search",

"filter": "@source==(\"pokedex-V2\")",

"maxPassages": 10

}

Same hub, filtered to pokedex-V2. One passage per species, top three shown. Scores are cosine similarity.

Coveo Pokédex · Setup walkthrough

13

14 of 17

PART 5 · FRONT END

Connect an Atomic page to the hub

The page only reads what the organization provides. Everything it sends carries the same search hub.

1

Scaffold npm create @coveo/ui@latest with the atomic-search template.

2

Initialize Organization ID, anonymous search key, and search hub Pokemon Search.

3

Scope Constant query @source==("pokedex-V2").

4

Request fields registerFieldsToInclude for type, generation, name, and image.

index.html

<atomic-search-interface search-hub="Pokemon Search">

<atomic-search-box></atomic-search-box>

<atomic-facet field="pokemon_type"></atomic-facet>

<atomic-facet field="pokemon_generation"

sort-criteria="alphanumericNatural"></atomic-facet>

<atomic-generated-answer></atomic-generated-answer>

<atomic-did-you-mean></atomic-did-you-mean>

<atomic-result-list display="grid">…</atomic-result-list>

</atomic-search-interface>

Consider: fields are not returned in results unless you register them; facets still work because their values come from the index. Keep atomic-result-link, which logs the clicks the models learn from.

Coveo Pokédex · Setup walkthrough

14

15 of 17

PART 5 · FRONT END

Beyond the search page

A species page, result shaping, and passages, all on the same hub and token.

SPECIES PAGE

A second interface on the same hub. A constant query on @clickableuri returns one item, and form tabs read pokemon_forms.

RESPONSE MIDDLEWARE

A Headless hook placed under configuration.search. It sets the title, image, and slug on each result before it renders.

PASSAGES

Passage Retrieval has no Headless controller. Call the REST API with fetch, the same token, and the same hub.

Consider: result templates render inside shadow DOM, so page CSS does not reach them. Style cards inline or through shadow parts.

Run it locally: npm run dev, then open localhost:5173.

Coveo Pokédex · Setup walkthrough

15

16 of 17

CHECKLIST

Things to watch for

Symptoms you may see while setting this up, and the fix for each.

SYMPTOM

FIX

A type facet lists Fire, Flying, Fire, Dragon…

Read type from the first basics panel only

Facet values show raw <a> markup

End the XPath in /text()

Grass and Poison appear as one value

Turn on multi-value before relying on the field

An extension cannot see the tabs

Read the original file, pre-conversion

ExpandBeforeFiltering has no effect

Set it in the source JSON, not in scraping

Coveo Pokédex · Setup walkthrough

16

17 of 17

Questions?

Mariam · Coveo Pokédex