@sinequa/atomic

Datasets and bulk queries

Named, parameterised queries for dashboards — and running several searches in one round trip.

Two ways to get more than one answer at a time, for two different needs.

Use it for
DatasetsNamed queries the administrator wrote, with parameters. Dashboards, analytics, counters.
Bulk queriesSeveral ordinary searches your code composed, in one round trip.

Concept — a dataset

A dataset is a query stored on the server, in a dataset web service, and invoked by name. The SQL and the tuning live server-side; your code passes parameters and renders the result. It is how a dashboard tile gets its numbers without your application knowing how they are computed.

Datasets

Three calls, and the first thing to do is discover what exists:

Prop

Type

A dashboard from several datasets in one request
src/dashboard.ts
import { createAtomicClient } from "@sinequa/atomic";
import type { DatasetError, Result } from "@sinequa/atomic";

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

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

// The dataset web service is named in the configuration, like every other service.
const app = await client.api.app.get();
const service = Object.entries(app.webServices).find(
  ([, ws]) => ws.webServiceType === "dataset",
)?.[0];

if (!service) throw new Error("This application has no dataset web service.");

// What can we ask for?
for (const { name, description } of await client.api.dataset.list(service)) {
  console.log(name, description ?? "");
}

// One request, several datasets, shared parameters.
const tiles = await client.api.dataset.getMany(service, {
  datasets: ["documentsPerSource", "searchesPerDay"],
  parameters: { period: "last30days" },
});

for (const [name, value] of Object.entries(tiles)) {
  // getMany does not throw: a failed dataset arrives as an error entry.
  if (isError(value)) {
    console.error(`${name} failed: ${value.errorMessage}`);
    continue;
  }
  console.log(`${name}: ${value.rowCount} row(s)`);
}

function isError(value: Result | DatasetError): value is DatasetError {
  return "errorCode" in value;
}

Failures behave differently between the two

This asymmetry is worth remembering:

  • dataset.get throws when the server answers with an error envelope for that dataset — an Error carrying the server's errorMessage.
  • dataset.getMany does not. Each entry of the returned record is either a Result or a DatasetError ({ errorCode, errorMessage }), so one broken tile does not take the dashboard down.

Narrow with "errorCode" in value, as the examples do.

The dataset name is lowercased on its first letter

dataset.get lowercases the first character of the dataset name before building the URL. A dataset the administrator named DocumentsPerSource is requested as documentsPerSource. Harmless most of the time, but it explains a 404 that looks impossible.

Bulk queries

client.api.query.bulk(queries, mode?) sends several ordinary queries in one request and returns one result per query, in order. Use it when a view needs two or three searches that would otherwise be sequential round trips — a result list plus a sidebar counter, say.

two searches, one round trip
const [documents, people] = await client.api.query.bulk([
  { name: "<your-query>", text: "onboarding", pageSize: 10 },
  { name: "people", text: "onboarding", pageSize: 5 },
]);

Passing "parallel" as the mode asks the server to run them concurrently rather than in sequence.

Each result gets the same post-processing as a single search — pagination fields, the aggregations' $hasMore, tree paths — so results are interchangeable with those of query.search.

Pitfalls

What's next

On this page