ObsiViewer/src/core/search/search-meilisearch.service.ts

104 lines
2.4 KiB
TypeScript

import { inject, Injectable, signal } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { finalize } from 'rxjs/operators';
/**
* Search hit from Meilisearch with highlighting
*/
export interface SearchHit {
id: string;
title: string;
path: string;
file: string;
tags?: string[];
properties?: Record<string, unknown>;
updatedAt?: number;
createdAt?: number;
excerpt?: string;
_formatted?: {
title?: string;
content?: string;
};
}
/**
* Search response from Meilisearch API
*/
export interface SearchResponse {
hits: SearchHit[];
estimatedTotalHits: number;
facetDistribution?: Record<string, Record<string, number>>;
processingTimeMs: number;
query: string;
}
/**
* Search options
*/
export interface SearchOptions {
limit?: number;
offset?: number;
sort?: string;
highlight?: boolean;
}
/**
* Service for server-side search using Meilisearch
*/
@Injectable({ providedIn: 'root' })
export class SearchMeilisearchService {
private http = inject(HttpClient);
/** Loading state signal */
loading = signal(false);
/**
* Execute a search query
*/
search(q: string, opts?: SearchOptions): Observable<SearchResponse> {
this.loading.set(true);
let params = new HttpParams().set('q', q);
if (opts?.limit !== undefined) {
params = params.set('limit', String(opts.limit));
}
if (opts?.offset !== undefined) {
params = params.set('offset', String(opts.offset));
}
if (opts?.sort) {
params = params.set('sort', opts.sort);
}
if (opts?.highlight === false) {
params = params.set('highlight', 'false');
}
const url = `/api/search?${params.toString()}`;
console.log('[SearchMeilisearchService] Sending request:', url);
return this.http
.get<SearchResponse>('/api/search', { params })
.pipe(
finalize(() => {
console.log('[SearchMeilisearchService] Request completed');
this.loading.set(false);
})
);
}
/**
* Trigger a full reindex of the vault
*/
reindex(): Observable<{ ok: boolean; indexed: boolean; count: number; elapsedMs: number }> {
this.loading.set(true);
return this.http
.post<{ ok: boolean; indexed: boolean; count: number; elapsedMs: number }>(
'/api/reindex',
{}
)
.pipe(finalize(() => this.loading.set(false)));
}
}