UNPKG

@fondation-io/fast-db-batch-search-client

Version:

TypeScript client for Fast-DB batch search API with support for fuzzy search

261 lines 9.46 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BatchSearchClient = void 0; const axios_1 = __importDefault(require("axios")); /** * Client for Fast-DB Batch Search API */ class BatchSearchClient { constructor(options = {}) { const { baseUrl = 'http://localhost:8080', timeout = 30000, includeMetrics = true } = options; this.axios = axios_1.default.create({ baseURL: baseUrl, timeout: timeout, headers: { 'Content-Type': 'application/json', }, }); this.includeMetrics = includeMetrics; } /** * Execute a batch search query * @param table The table/collection to search in * @param nodeField The field containing node information * @param nodeQuery The node to search for * @param targetField The field containing target information * @param targetQueries Array of target queries to search for * @param projection Fields to return in results * @param fuzzy Whether to use fuzzy search (default: true) * @param resultsPerQuery Maximum results per target query (default: 10) */ async batchSearch(table, nodeField, nodeQuery, targetField, targetQueries, projection = ['*'], fuzzy = true, resultsPerQuery = 10) { const query = { query: { $select: projection, $from: table, $where: { $batch: { $node_field: nodeField, $node_query: nodeQuery, $target_field: targetField, $target_queries: targetQueries, $fuzzy: fuzzy, $results_per_query: resultsPerQuery, }, }, }, include_metrics: this.includeMetrics, }; try { const startTime = Date.now(); const response = await this.axios.post('/query', query); const elapsedTime = Date.now() - startTime; if (!response.data.success) { throw new Error(response.data.error || 'Query failed'); } const data = response.data.data; if (!data) { return { results: [], grouped: {}, metrics: response.data.metrics, totalResults: 0, }; } // Convert DataFrame response to array of objects const results = this.dataFrameToObjects(data); // Group results by search_group_hash const grouped = this.groupResultsByHash(results); return { results, grouped, metrics: { ...(response.data.metrics || {}), client_elapsed_ms: elapsedTime, }, totalResults: results.length, }; } catch (error) { const axiosError = error; if (axiosError.response) { throw new Error(`API Error: ${axiosError.response.data?.error || axiosError.response.statusText}`); } else if (axiosError.request) { throw new Error('Network error: No response from server'); } else { throw error; } } } /** * Convert DataFrame response to array of objects */ dataFrameToObjects(data) { const { columns, rows } = data; return rows.map((row) => { const obj = { search_group_hash: '' }; columns.forEach((col, index) => { obj[col] = row[index]; }); return obj; }); } /** * Group results by search_group_hash */ groupResultsByHash(results) { const groups = {}; for (const result of results) { const hash = result.search_group_hash || 'unknown'; if (!groups[hash]) { groups[hash] = []; } groups[hash].push(result); } return groups; } /** * Execute a batch search query with joins across multiple tables * @param params Join search parameters */ async batchSearchWithJoins(params) { const query = { query: { $select: params.projection || ['*'], $from: params.tables, $join: params.joins, $where: { $batch: { $node_field: params.nodeField, $node_query: params.nodeQuery, $target_field: params.targetField, $target_queries: params.targetQueries, $fuzzy: params.fuzzy !== false, $results_per_query: params.resultsPerQuery || 10, }, }, $orderBy: params.orderBy, $limit: params.limit, }, include_metrics: this.includeMetrics, }; try { const startTime = Date.now(); const response = await this.axios.post('/query', query); const elapsedTime = Date.now() - startTime; if (!response.data.success) { throw new Error(response.data.error || 'Query failed'); } const data = response.data.data; if (!data) { return { results: [], grouped: {}, metrics: response.data.metrics, totalResults: 0, }; } // Convert DataFrame response to array of objects const results = this.dataFrameToObjects(data); // Group results by search_group_hash const grouped = this.groupResultsByHash(results); return { results, grouped, metrics: { ...(response.data.metrics || {}), client_elapsed_ms: elapsedTime, }, totalResults: results.length, }; } catch (error) { const axiosError = error; if (axiosError.response) { throw new Error(`API Error: ${axiosError.response.data?.error || axiosError.response.statusText}`); } else if (axiosError.request) { throw new Error('Network error: No response from server'); } else { throw error; } } } /** * Convenience method for searching book series by author * @deprecated Use searchRelatedItems instead */ async searchBookSeries(author, seriesTitles, maxPerTitle = 3) { return this.batchSearch('books', 'auteurs', author, 'titre', seriesTitles, ['titre', 'auteurs'], true, maxPerTitle); } /** * Generic method for searching related items by a common node */ async searchRelatedItems(table, nodeField, nodeValue, targetField, targetValues, projection, maxPerTarget = 3) { return this.batchSearch(table, nodeField, nodeValue, targetField, targetValues, projection || ['*'], true, maxPerTarget); } /** * Convenience method for searching albums by artist using joins */ async searchAlbumsByArtist(artist, albumTitles, maxPerAlbum = 3) { return this.batchSearchWithJoins({ tables: ['id_artists', 'album_artist', 'albums'], joins: [ { $type: 'inner', $left: 'id_artists', $right: 'album_artist', $on: ['id_artists.id', 'album_artist.artist_id'], }, { $type: 'inner', $left: 'album_artist', $right: 'albums', $on: ['album_artist.cb', 'albums.cb'], }, ], nodeField: 'id_artists.artiste', nodeQuery: artist, targetField: 'albums.album', targetQueries: albumTitles, projection: { artist_name: 'artiste', album_title: 'album', release_year: 'street_date', }, fuzzy: true, resultsPerQuery: maxPerAlbum, }); } /** * Get search statistics from grouped results */ getSearchStats(grouped) { const groupSizes = {}; let totalItems = 0; let emptyGroups = 0; for (const [hash, items] of Object.entries(grouped)) { groupSizes[hash] = items.length; totalItems += items.length; if (items.length === 0) { emptyGroups++; } } const totalGroups = Object.keys(grouped).length; const averageGroupSize = totalGroups > 0 ? totalItems / totalGroups : 0; return { totalGroups, groupSizes, averageGroupSize, emptyGroups, }; } } exports.BatchSearchClient = BatchSearchClient; //# sourceMappingURL=client.js.map