Guides / Building Search UI / UI & UX patterns

Multi-Index Search with Angular InstantSearch

If you want to search multiple indices, there are a couple of approaches you can take:

The source code for both examples is on GitHub.

Synchronize two InstantSearch indices

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.

You can find the complete example on GitHub.

Use 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 do the search in, in this case the actors index.

First make sure you have the correct setup to use Angular Material’s 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 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.

Did you find this page helpful?