SearchFacade.java
package com.taxonomy.search.service;
import com.taxonomy.catalog.service.SearchService;
import com.taxonomy.catalog.service.TaxonomyService;
import com.taxonomy.dto.GraphSearchResult;
import com.taxonomy.dto.TaxonomyNodeDto;
import com.taxonomy.relations.service.GraphSearchService;
import com.taxonomy.relations.service.HybridSearchService;
import com.taxonomy.shared.service.LocalEmbeddingService;
import com.taxonomy.workspace.service.WorkspaceContext;
import com.taxonomy.workspace.service.WorkspaceContextResolver;
import org.springframework.stereotype.Service;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* High-level facade that aggregates the search domain services.
*
* <p>Provides coarse-grained operations for full-text, semantic,
* hybrid, and graph search, as well as embedding status, so that
* the {@code SearchApiController} only needs a single dependency.
*
* <p>Node searches (full-text, semantic, hybrid, similar) operate on the
* global {@link com.taxonomy.catalog.model.TaxonomyNode} index and are
* <b>not</b> workspace-scoped — taxonomy nodes are shared across all
* workspaces. Graph search, which queries {@link com.taxonomy.catalog.model.TaxonomyRelation},
* is workspace-scoped via {@link WorkspaceContextResolver}.
*/
@Service
public class SearchFacade {
private final TaxonomyService taxonomyService;
private final SearchService searchService;
private final HybridSearchService hybridSearchService;
private final LocalEmbeddingService embeddingService;
private final GraphSearchService graphSearchService;
private final WorkspaceContextResolver contextResolver;
public SearchFacade(TaxonomyService taxonomyService,
SearchService searchService,
HybridSearchService hybridSearchService,
LocalEmbeddingService embeddingService,
GraphSearchService graphSearchService,
WorkspaceContextResolver contextResolver) {
this.taxonomyService = taxonomyService;
this.searchService = searchService;
this.hybridSearchService = hybridSearchService;
this.embeddingService = embeddingService;
this.graphSearchService = graphSearchService;
this.contextResolver = contextResolver;
}
/**
* Check whether the taxonomy has been fully initialized.
*/
public boolean isInitialized() {
return taxonomyService.isInitialized();
}
/**
* Returns the current taxonomy initialization status message.
*/
public String getInitStatus() {
return taxonomyService.getInitStatus();
}
/**
* Full-text search across taxonomy nodes using Lucene.
* Nodes are global — no workspace filtering needed.
*/
public List<TaxonomyNodeDto> fullTextSearch(String query, int maxResults) {
return searchService.search(query, maxResults);
}
/**
* Semantic search using embedding similarity (KNN).
* Nodes are global — no workspace filtering needed.
*/
public List<TaxonomyNodeDto> semanticSearch(String query, int maxResults) {
return embeddingService.semanticSearch(query, maxResults);
}
/**
* Hybrid search combining full-text and semantic results via Reciprocal Rank Fusion.
* Nodes are global — no workspace filtering needed.
*/
public List<TaxonomyNodeDto> hybridSearch(String query, int maxResults) {
return hybridSearchService.hybridSearch(query, maxResults);
}
/**
* Find taxonomy nodes semantically similar to the given node code.
* Nodes are global — no workspace filtering needed.
*/
public List<TaxonomyNodeDto> findSimilarNodes(String code, int topK) {
return embeddingService.findSimilarNodes(code, topK);
}
/**
* Graph-semantic search combining node and relation KNN queries.
*
* <p>The relation KNN query is workspace-scoped: only relations
* belonging to the current user's workspace (or shared/legacy relations
* with {@code workspace_id = NULL}) are included.
*
* <p>The workspace context is resolved internally by the
* {@link GraphSearchService}.
*/
public GraphSearchResult graphSearch(String query, int maxResults) {
return graphSearchService.graphSearch(query, maxResults);
}
/**
* Returns the current status of the local embedding model.
*/
public Map<String, Object> getEmbeddingStatus() {
Map<String, Object> status = new LinkedHashMap<>();
status.put("enabled", embeddingService.isEnabled());
status.put("available", embeddingService.isAvailable());
status.put("modelUrl", embeddingService.effectiveModelUrl());
status.put("indexedNodes", embeddingService.indexedNodeCount());
return status;
}
}