104 lines
2.4 KiB
TypeScript
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)));
|
|
}
|
|
}
|