@sinequa/atomic

Your first search

From an empty file to rendered results with a working facet, in JavaScript, Angular or React.

A search interface needs four things: a client, a session, a query, and something to render. This page builds them in that order, then shows the finished file in your language.

Before you start

You need a reachable server, an app name, and a query web service — verified in Prerequisites. If step 1 below fails, that page tells you which piece is missing.

Build it up

Create the client

The client owns the configuration, the session, and the typed API. Create one and keep it — do not build a new one per request, or each gets its own session.

step 1
import { createAtomicClient } from "@sinequa/atomic";

const client = createAtomicClient({
  app: "<your-app>",
  backendUrl: "https://<your-sinequa-server>",
});

Nothing has happened on the network yet. Creating a client is inert.

Sign in

Two calls, in this order. The first reads the server's configuration and settles how to authenticate; the second does it.

step 2
await client.initializeConfig();
await client.auth.login();

You do not choose the method: login() branches on what the server announced — a form, Windows SSO, a provider redirect, or a configured token. In a credentials deployment it returns false and you show a form; see Authentication.

A query is a plain object. Only name is required — the query web service to run.

step 3
const result = await client.api.query.search({
  name: "<your-query>",
  text: "quarterly report",
  pageSize: 10,
});

The response carries the documents in records and the facet counts in aggregations. You do not fetch facets separately.

Render, and check for failure first

records is an array of documents. Read $error before concluding anything from an empty one: a failed request and a corpus that matched nothing both arrive with no records.

step 4
if (result.$error) throw result.$error;

for (const record of result.records) {
  console.log(record.title, record.modified);
}

title and modified are always present. Anything the administrator mapped into a custom column is not on the type — read those with getMetadata, which normalises the several shapes such a value can take.

Add a facet

The counts are already in the response. Turning a click into a filter is the filter builder — and re-running the search with it is the whole interaction.

step 5
import { filter } from "@sinequa/atomic";

const formats = result.aggregations.find((a) => a.column === "docformat");

const refined = await client.api.query.search({
  name: "<your-query>",
  text: "quarterly report",
  pageSize: 10,
  filters: filter.eq("docformat", "pdf"),
});

The whole thing

Complete and runnable. Substitute your app name and server URL.

A search with one facet
src/main.ts
import { createAtomicClient, filter, getMetadata } from "@sinequa/atomic";
import type { Result } from "@sinequa/atomic";

const client = createAtomicClient({
  app: "<your-app>",
  backendUrl: "https://<your-sinequa-server>",
});

async function main() {
  await client.initializeConfig();

  if (!(await client.auth.login())) {
    // A credentials deployment: pass { username, password } instead.
    throw new Error("Not signed in — see the Authentication guide.");
  }

  // First search: no filter, so the facets describe the whole result set.
  const all = await search();
  report(all);

  // Then narrow it, reusing the same query with one filter added.
  const pdfOnly = await search(filter.eq("docformat", "pdf"));
  report(pdfOnly);
}

function search(filters?: ReturnType<typeof filter.eq>) {
  return client.api.query.search({
    name: "<your-query>",
    text: "quarterly report",
    pageSize: 10,
    filters,
  });
}

function report(result: Result) {
  if (result.$error) throw result.$error;

  console.log(`\n${result.rowCount} document(s)`);

  for (const record of result.records) {
    // Custom metadata is not on the Article type; getMetadata normalises it.
    const authors = getMetadata(record, "authors");
    console.log(`- ${record.title}${authors.length ? ` — ${authors.join(", ")}` : ""}`);
  }

  const formats = result.aggregations.find((a) => a.column === "docformat");
  for (const item of formats?.items ?? []) {
    console.log(`  ${item.display ?? item.value}: ${item.count}`);
  }
}

main().catch((error) => console.error(error));

Pitfalls

What's next

On this page