Atomic Angular — Doc interne

Query

Run a search, tell a failed request apart from a genuine zero results, and page through a result — the service every result-rendering component in this library sits on top of.

QueryService is the one place a search actually happens: it turns a Query into a request, exposes the last Result and its failure state as signals, and moves between pages. Almost every other component in this section — sorting, spelling correction, missing terms — reads the Result this service produced rather than issuing a request of its own.

Concept — Result vs. failure

A search that fails still resolves to a Result — the request never throws — with the failure captured on $error instead. An empty records array is ambiguous by itself: it means "the corpus matched nothing" on a successful request, and "something went wrong" on a failed one. Check $error first, always.

Minimal example

Search, and tell a failure apart from zero results
results.component.ts
import { Component, inject } from "@angular/core";
import { QueryService } from "@sinequa/atomic-angular";

@Component({
  selector: "results-component",
  template: `
    @if (queryService.failure(); as error) {
      <p>Search failed: {{ error.message }}</p>
    } @else if (queryService.result().rowCount === 0) {
      <p>No results</p>
    } @else {
      <ul>
        @for (record of queryService.result().records; track record.id) {
          <li>{{ record.title }}</li>
        }
      </ul>
    }
  `,
})
export class ResultsComponent {
  protected readonly queryService = inject(QueryService);

  constructor() {
    this.queryService.search({ text: "quarterly report" }).subscribe();
  }
}

How it works

no yes search(partialQuery) merge with QueryParamsStore, unless includeQueryParams=false HTTP request Request succeeded? Result with $error set, empty records Result with records, rowCount, $error undefined result signal / failure computed

search() still returns an Observable<Result> for callers that want to compose it, but the same Result also lands on the result signal (and failure, result().$error) — which is what a template reads instead of subscribing manually. The observable never errors: a failed request still emits, because throwing would cancel the navigation of any route resolver built on this service, which historically rendered an empty page instead.

A global toast is not a substitute

provideAtomicNotifications() (see Notify failed requests as toasts) already surfaces every failed request to the user as a toast — but that leaves the component still rendering "0 results" underneath unless it also checks $error itself. The two are complementary, not either/or.

Recipes

Paging through a result

Next/previous page
pager.component.ts
import { Component, inject } from "@angular/core";
import { QueryService } from "@sinequa/atomic-angular";

@Component({
  selector: "pager-component",
  template: `
    <button type="button" (click)="queryService.gotoPage(currentPage() - 1)" [disabled]="currentPage() <= 1">
      Previous
    </button>
    <button type="button" (click)="queryService.gotoPage(currentPage() + 1)">Next</button>
  `,
})
export class PagerComponent {
  protected readonly queryService = inject(QueryService);
  protected readonly currentPage = () => this.queryService.result().page ?? 1;
}

Running several queries in parallel

Compare two searches at once
compare.component.ts
import { Component, inject } from "@angular/core";
import { QueryService } from "@sinequa/atomic-angular";
import type { Query } from "@sinequa/atomic";

@Component({
  selector: "compare-component",
  template: ``,
})
export class CompareComponent {
  private readonly queryService = inject(QueryService);

  protected compare(a: Query, b: Query) {
    this.queryService.bulkSearch([a, b]).subscribe(([resultA, resultB]) => {
      console.log(resultA.rowCount, resultB.rowCount);
    });
  }
}

Results come back in the same order as the input queries — index them by position, not by matching them back to a query afterward.

Options

Prop

Type

Pitfalls

What's next

On this page