Similar documents
Finding documents close to one you already have — "more like this" — and cancelling the request when the user moves on.
Given a document, the engine can find others like it. Unlike a search, there is no query text: the source document is the query.
const similar = await client.api.query.similarDocuments(record.id, "<your-query>");It returns Article[] — the same shape as result.records, so an existing hit-list component renders them
without changes.
With cancellation
This call belongs in a panel that follows the user's selection, which means a request is often obsolete before
it answers. It is one of the few calls that takes an AbortSignal directly.
A panel that follows the selected document
import { createAtomicClient } from "@sinequa/atomic";
import type { Article } from "@sinequa/atomic";
const client = createAtomicClient({
app: "<your-app>",
backendUrl: "https://<your-sinequa-server>",
});
await client.initializeConfig();
await client.auth.login();
let inFlight: AbortController | undefined;
export async function showSimilar(recordId: string): Promise<Article[]> {
// The user can select another document before this one answers.
inFlight?.abort();
inFlight = new AbortController();
try {
return await client.api.query.similarDocuments(recordId, "<your-query>", inFlight.signal);
} catch (error) {
// An aborted request is not a failure to report.
if (error instanceof DOMException && error.name === "AbortError") return [];
throw error;
}
}import { ChangeDetectionStrategy, Component, inject, input, resource } from "@angular/core";
import { SINEQUA } from "./sinequa";
@Component({
selector: "app-similar",
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
@if (similar.isLoading()) {
<p>Looking for similar documents…</p>
} @else if (similar.value()?.length) {
<ul>
@for (record of similar.value() ?? []; track record.id) {
<li>{{ record.title }}</li>
}
</ul>
} @else {
<p>No similar documents.</p>
}
`,
})
export class SimilarComponent {
private readonly client = inject(SINEQUA);
readonly recordId = input.required<string>();
readonly similar = resource({
params: () => ({ id: this.recordId() }),
// `resource` aborts the previous load when the selection changes, and this
// call takes the signal directly.
loader: ({ params, abortSignal }) =>
this.client.api.query.similarDocuments(params.id, "<your-query>", abortSignal),
});
}import type { Article } from "@sinequa/atomic";
import { useEffect, useState } from "react";
import { ready, sinequa } from "./sinequa";
export function Similar({ recordId }: { recordId: string }) {
const [similar, setSimilar] = useState<Article[]>();
useEffect(() => {
// The user can select another document before this one answers.
const controller = new AbortController();
setSimilar(undefined);
ready
.then(() => sinequa.api.query.similarDocuments(recordId, "<your-query>", controller.signal))
.then(setSimilar)
.catch((error: unknown) => {
// An aborted request is not a failure to report.
if (error instanceof DOMException && error.name === "AbortError") return;
throw error;
});
return () => controller.abort();
}, [recordId]);
if (!similar) return <p>Looking for similar documents…</p>;
if (similar.length === 0) return <p>No similar documents.</p>;
return (
<ul>
{similar.map((record) => (
<li key={record.id}>{record.title}</li>
))}
</ul>
);
}