Guides / Building Search UI / UI & UX patterns

Multi-index search with Angular InstantSearch

Angular InstantSearch is deprecated. Please use InstantSearch.js instead. For more information, see Migrating from Angular InstantSearch.

Multi-index search (federated search) is a method for searching multiple data sources simultaneously. This means that when users enter a search term, Algolia will look for and display results from all these data sources.

This doesn’t necessarily mean that the results from Algolia indices are combined since their contents could be quite different. Your approach may be to display the results from each index separately. You could display the top-rated items from a movie index alongside the list of results from a book index. Or you could display category matches alongside the list of results from a product index

Search multiple indices with InstantSearch

This example uses a single input to search multiple indices. It uses the ais-index to query two indices at the same time: players and actors.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// app.component.ts

import { Component } from '@angular/core';
import algoliasearch from 'algoliasearch/lite';

const searchClient = algoliasearch(
  'latency',
  '6be0576ff61c053d5f9a3225e2a90f76'
);

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  config = {
    indexName: 'instant_search',
    searchClient,
  };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- app.component.html -->
<ais-instantsearch [config]="config">
  <ais-search-box placeholder=""></ais-search-box>

  <ais-index indexName="actors">
    <h2>Actors</h2>
    <ais-hits></ais-hits>
  </ais-index>

  <ais-index indexName="players">
    <h2>Players</h2>
    <ais-hits></ais-hits>
  </ais-index>

</ais-instantsearch>

The example uses a custom search box and injects the query into two InstantSearch instances with the ais-configure component.

Search multiple indices with Autocomplete

This example builds an Autocomplete to search multiple indices. It’s built with Angular Material’s Autocomplete and the connectAutocomplete component. The only difference to the previous guide is how hits are appended to Autocomplete.

The ais-autocomplete component takes indices as a prop. This is an array of additional indices to search, in this case, the actors index.

First, ensure you have the correct setup to use Angular Material UI components, then import MatInputModule and MatAutocompleteModule inside your project.

$
ng add @angular/material
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { NgAisModule } from 'angular-instantsearch';
import { MatInputModule, MatAutocompleteModule } from '@angular/material';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    NgAisModule.forRoot(),
    BrowserModule,
    BrowserAnimationsModule,

    MatInputModule,
    MatAutocompleteModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Now create a new component autocomplete.component.ts inheriting from BaseWidget and connect it to your instant-search instance to connectAutocomplete.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// autocomplete.component.ts

import {
  Component,
  Inject,
  forwardRef,
  Output,
  EventEmitter, Optional
} from "@angular/core";
import {NgAisIndex, NgAisInstantSearch, TypedBaseWidget} from "angular-instantsearch";
import {connectAutocomplete} from "instantsearch.js/es/connectors";
import {
  AutocompleteWidgetDescription,
  AutocompleteConnectorParams
} from "instantsearch.js/es/connectors/autocomplete/connectAutocomplete";

@Component({
  selector: 'app-autocomplete',
  template: ` ... `,
})
export class AutocompleteComponent extends TypedBaseWidget<
  AutocompleteWidgetDescription,
  AutocompleteConnectorParams
> {
  state: AutocompleteWidgetDescription['renderState'] = {
    currentRefinement: '',
    refine: () => null,
    indices: [],
  };

  @Output() onQuerySuggestionClick = new EventEmitter<{ query: string }>();

  constructor(
    @Inject(forwardRef(() => NgAisIndex))
    @Optional()
    public parentIndex: NgAisIndex,
    @Inject(forwardRef(() => NgAisInstantSearch))
    public instantSearchInstance: NgAisInstantSearch
  ) {
    super('Autocomplete');
    this!.createWidget(connectAutocomplete, {});
  }

  public handleChange($event: KeyboardEvent) {
    this.state.refine(($event.target as HTMLInputElement).value);
  }

  public ngOnInit() {
    super.ngOnInit();
  }
}

Now you just need to use the Angular Material Autocomplete component and feed it with the data from the indices.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// autocomplete.component.ts

@Component({
  selector: 'app-autocomplete',
  template: `
    <div>
      <input
        matInput
        [matAutocomplete]="auto"
        (keyup)="handleChange($event)"
        style="width: 100%; padding: 10px"
      />
      <mat-autocomplete #auto="matAutocomplete" style="height: 800px">
        <div *ngFor="let index of state.indices.slice(1) || []">
          <mat-optgroup>{{ index.indexName }}</mat-optgroup>
          <mat-option
            *ngFor="let option of index.hits"
            [value]="option.name"
            (click)="onQuerySuggestionClick.emit({ query: option.name })"
          >
            <ais-highlight [hit]="option" attribute="name"></ais-highlight>
          </mat-option>
        </div>
      </mat-autocomplete>
    </div>
  `
})
export class Autocomplete extends TypedBaseWidget<...> { /* ... */ }

Now use the newly created Autocomplete component in your code.

1
2
3
4
5
6
  <ais-instantsearch [config]="config">
      <ais-configure [searchParameters]="{ hitsPerPage: 3 }"></ais-configure>
      <ais-index indexName="players"></ais-index>
      <ais-index indexName="actors"></ais-index>
      <app-autocomplete></app-autocomplete>
  </ais-instantsearch>

The Autocomplete component is now searching in two indices: players and actors.

The focus of this guide is on searching multiple indices. The Autocomplete implementation isn’t covered in depth because it has a dedicated guide.

Category display

Algolia can help you display both category matches and results if you:

  • Add categories to your Query Suggestions either inline or listed below a result. For example, you might see the following in your Query Suggestions list “game of thrones in Books
  • Use multi-index search to display categories from a separate category index. This is useful if you want to display categories and Query Suggestions at the same time. Clicking such a result typically redirects to a category page. The following is a sample dataset for a product index and a category index.

Example product index

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[
  {
    "name": "Fashion Krisp",
    "description": "A pair of red shoes with a comfortable fit.",
    "image": "/fashion-krisp.jpg",
    "price": 18.98,
    "likes": 284,
    "category": "Fashion > Women > Shoes > Court shoes"
  },
  {
    "name": "Jiver",
    "description": "A blue shirt made of cotton.",
    "image": "/jiver.jpg",
    "price": 17.70,
    "likes": 338,
    "category": "Fashion > Men > Shirts > Dress shirt"
  }
]

Example category index

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[
  {
    "name": "Court shoes",
    "category": "Fashion > Women > Shoes > Court shoes",
    "description": "A dress shoe with a low-cut front and a closed heel.",
    "url": "/women/shoes/court/"
  },
  {
    "name": "Dress shirt",
    "category": "Fashion > Men > Shirts > Dress shirt",
    "description": "A long-sleeved, button-up formal shirt that is typically worn with a suit or tie.",
    "url": "/men/shirts/dress/"
  }
]