diff --git a/docs/features/search/concepts.md b/docs/features/search/concepts.md new file mode 100644 index 0000000000..5da7c9c58f --- /dev/null +++ b/docs/features/search/concepts.md @@ -0,0 +1,112 @@ +--- +id: concepts +title: Search Concepts +description: Documentation on Backstage Search Concepts +--- + +# Search Concepts + +Backstage Search lets you find the right information you are looking for in the +Backstage ecosystem. + +To get started, you should get familiar with these core concepts: + +- [Search Engines](#search-engines) +- [Query Translators](#query-translators) +- [Documents and Indices](#documents-and-indices) +- [Collators](#collators) +- [Decorators](#decorators) +- [The Scheduler](#the-scheduler) +- [The Search Page](#the-search-page) +- [Search Context and Components](#search-context-and-components) + +### Search Engines + +Backstage Search isn't a search engine itself, rather, it provides an interface +between your Backstage instance and a Search Engine of your choice. More +concretely, a `SearchEngine` is an interface whose concrete implementations +facilitate communication with different search engines (like ElasticSearch, +Lunr, Solr, etc). This abstraction exists in order to support your +organization's needs. + +Out of the box, Backstage Search comes pre-packaged with an in-memory search +engine implementation built on top of Lunr. + +### Query Translators + +Because you can bring your own search engine, and because search engines have +very unique and robust query languages themselves, there needs to be a +translation layer between an abstract search query (containing search terms, +filters, and document types) into a concrete search query that is specific to a +search engine. + +Search Engines come pre-packaged with simple translators that do rudimentary +transformations of search terms and filters, but you may want to provide your +own to help tune search results in the context of your organization. + +### Documents and Indices + +"Document" is an abstract concept representing something that can be found by +searching for it. A document can represent a software entity, a TechDocs page, +etc. Documents are made up of metadata fields, at a minimum including a title, +text, and location (as in a URL). + +An index is a collection of such documents of a given type. + +### Collators + +You need to be able to search something! Collators are the way to define what +can be searched. Specifically, they're classes which return documents conforming +to a minimum set of fields (including a document title, location, and text), but +which can contain any other fields as defined by the collator itself. One +collator is responsible for defining and collecting documents of a type. + +Some plugins, like the Catalog Backend, provide so-called "default" collators +which you can use out-of-the-box to start searching across Backstage quickly. + +### Decorators + +Sometimes you want to add extra information to a set of documents in your search +index that the collator may not be aware of. For example, the Software Catalog +knows about software entities, but it may not know about their usage or quality. + +Decorators are classes which can add extra fields to pre-collated documents. +This extra metadata could then be used to bias search results or otherwise +improve the search experience in your Backstage instance. + +### The Scheduler + +There are many ways a search index could be built and maintained, but Backstage +Search chooses to completely rebuild indices on a schedule. Different collators +can be configured to refresh at different intervals, depending on how often the +source information is updated. + +### The Search Page + +Search pages are very custom things. Not every Backstage instance will want the +same interface! In order to allow you to customize your search experience to +your heart's content, the Search Plugin takes care of state management and other +search logic for you, but most of the layout of a search page lives in a search +page component defined in your Backstage App. + +For an example of a simple search page, check +[getting started](./getting-started.md#adding-search-to-the-frontend) + +### Search Context and Components + +A search experience, like a page, is composed of any number of search +components, which are all wired up using a search context. + +Each search experience's context consists of details like a search term, +filters, types, results, and a page cursor for handling pagination. Different +components use this context in different ways. For example, the `` +can set the search term, `` components can set filters, and +search results can be displayed using the `` component. + +The `` and `` components are special, in that +they themselves are extensible. For an example of how to extend these +components, check +[getting started](./getting-started.md#adding-search-to-the-frontend). + +If you need even more customization, you can use the search context like any +other React context to create custom search components of your own. diff --git a/docs/features/search/getting-started.md b/docs/features/search/getting-started.md new file mode 100644 index 0000000000..7408d69193 --- /dev/null +++ b/docs/features/search/getting-started.md @@ -0,0 +1,297 @@ +--- +id: getting-started +title: Getting Started with Search +description: How to set up and install Backstage Search +--- + +# Getting Started + +Search functions as a plugin to Backstage, so you will need to use Backstage to +use Search. + +If you haven't setup Backstage already, start +[here](../../getting-started/index.md). + +> If you used `npx @backstage/create-app`, and you have a search page defined in +> `packages/app/src/components/search`, skip to +> [`Customizing Search`](#customizing-search) below. + +## Adding Search to the Frontend + +```bash +# From your Backstage root directory +cd packages/app +yarn add @backstage/plugin-search +``` + +Create a new `packages/app/src/components/search/SearchPage.tsx` file in your +Backstage app with the following contents: + +```tsx +import React from 'react'; +import { Content, Header, Page } from '@backstage/core'; +import { Grid, List, Card, CardContent } from '@material-ui/core'; +import { + SearchBar, + SearchResult, + DefaultResultListItem, + SearchFilter, +} from '@backstage/plugin-search'; +import { CatalogResultListItem } from '@backstage/plugin-catalog'; + +export const searchPage = ( + +
+ + + + + + + + + + + + + + + + + + {({ results }) => ( + + {results.map(result => { + switch (result.type) { + case 'software-catalog': + return ( + + ); + default: + return ( + + ); + } + })} + + )} + + + + + +); +``` + +Bind the above Search Page to the `/search` route in your +`packages/app/src/App.tsx` file, like this: + +```tsx +import { SearchPage } from '@backstage/plugin-search'; +import { searchPage } from './components/search/SearchPage'; + +const routes = ( + + }> + {searchPage} + + +); +``` + +## Adding Search to the Backend + +Add the following plugins into your backend app: + +```bash +# From your Backstage root directory +cd packages/backend +yarn add @backstage/plugin-search-backend @backstage/plugin-search-backend-node +``` + +Create a `packages/backend/src/plugins/search.ts` file containing the following +code: + +```typescript +import { useHotCleanup } from '@backstage/backend-common'; +import { createRouter } from '@backstage/plugin-search-backend'; +import { + IndexBuilder, + LunrSearchEngine, +} from '@backstage/plugin-search-backend-node'; +import { PluginEnvironment } from '../types'; +import { DefaultCatalogCollator } from '@backstage/plugin-catalog-backend'; + +export default async function createPlugin({ + logger, + discovery, +}: PluginEnvironment) { + const searchEngine = new LunrSearchEngine({ logger }); + const indexBuilder = new IndexBuilder({ logger, searchEngine }); + + indexBuilder.addCollator({ + type: 'software-catalog', + defaultRefreshIntervalSeconds: 600, + collator: new DefaultCatalogCollator({ discovery }), + }); + + const { scheduler } = await indexBuilder.build(); + + scheduler.start(); + useHotCleanup(module, () => scheduler.stop()); + + return await createRouter({ + engine: indexBuilder.getSearchEngine(), + logger, + }); +} +``` + +Make the following modifications to your `packages/backend/src/index.ts` file: + +Import the `plugins/search` file you created above: + +```typescript +import search from './plugins/search'; +``` + +Set up an environment for search: + +```typescript +const searchEnv = useHotMemoize(module, () => createEnv('search')); +``` + +Register the search service with the router: + +```typescript +apiRouter.use('/search', await search(searchEnv)); +``` + +## Customizing Search + +### Frontend + +The Search Plugin exposes several default filter types as static properties, +including `` and ``. These allow +you to provide values relevant to your Backstage instance that, when selected, +get passed to the backend. + +```tsx {2-5,8-11} + + + + + + +``` + +If you have advanced filter needs, you can specify your own filter component +like this (although new core filter contributions are welcome): + +```tsx +import { useSearch, SearchFilter } from '@backstage/plugin-search'; + +const MyCustomFilter = () => { + // Note: filters contain filter data from other filter components. Be sure + // not to clobber other filters' data! + const { filters, setFilters } = useSearch(); + + return (/* ... */); +}; + +// Which could be rendered like this: + +``` + +It's good practice for search results to highlight information that was used to +return it in the first place! The code below highlights how you might specify a +custom result item component, using the `` component as +an example: + +```tsx {7-13} + + {({ results }) => ( + + {results.map(result => { + // result.type is the index type defined by the collator. + switch (result.type) { + case 'software-catalog': + return ( + + ); + // ... + } + })} + + )} + +``` + +### Backend + +Backstage Search isn't a search engine itself, rather, it provides an interface +between your Backstage instance and a +[Search Engine](./concepts.md#search-engines) of your choice. Currently, we only +support one, an in-memory search Engine called Lunr. It can be instantiated like +this: + +```typescript +const searchEngine = new LunrSearchEngine({ logger }); +const indexBuilder = new IndexBuilder({ logger, searchEngine }); +``` + +Backstage Search can be used to power search of anything! Plugins like the +Catalog offer default [collators](./concepts.md#collators) which are responsible +for providing documents [to be indexed](./concepts.md#documents-and-indices). +You can register any number of collators with the `IndexBuilder` like this: + +```typescript +const indexBuilder = new IndexBuilder({ logger, searchEngine }); + +indexBuilder.addCollator({ + type: 'software-catalog', + defaultRefreshIntervalSeconds: 600, + collator: new DefaultCatalogCollator({ discovery }), +}); + +indexBuilder.addCollator({ + type: 'my-custom-stuff', + defaultRefreshIntervalSeconds: 3600, + collator: new MyCustomCollator(), +}); +``` + +Backstage Search builds and maintains its index +[on a schedule](./concepts.md#the-scheduler). You can change how often the +indexes are rebuilt for a given type of document. You may want to do this if +your documents are updated more or less frequently. You can do so by modifying +its `defaultRefreshIntervalSeconds` value, like this: + +```typescript {3} +indexBuilder.addCollator({ + type: 'software-catalog', + defaultRefreshIntervalSeconds: 600, + collator: new DefaultCatalogCollator({ discovery }), +}); +``` diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 56be7464a5..f2ce425307 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -76,6 +76,8 @@ "label": "Backstage Search", "ids": [ "features/search/search-overview", + "features/search/getting-started", + "features/search/concepts", "features/search/architecture" ] }, diff --git a/mkdocs.yml b/mkdocs.yml index e4f4f01e8a..67b97b6ffb 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -59,6 +59,8 @@ nav: - Backstage Search: - Overview: 'features/search/README.md' + - Getting Started: 'features/search/getting-started.md' + - Concepts: 'features/search/concepts.md' - Search Architecture: 'features/search/architecture.md' - TechDocs: - Overview: 'features/techdocs/README.md'