search method
Future<KnowledgeSearchResponse>
search(
- String query, {
- KnowledgeSearchPolicy? policy,
- int limit = 5,
- int? contextLimit,
Implementation
Future<KnowledgeSearchResponse> search(
String query, {
KnowledgeSearchPolicy? policy,
int limit = 5,
int? contextLimit,
}) async {
final snapshot = _snapshot;
if (snapshot == null || _synchronizing) {
throw StateError('Synchronize the index before searching.');
}
final budget = contextLimit ?? limit;
if (query.trim().isEmpty || limit <= 0 || budget <= 0) {
return KnowledgeSearchResponse(matches: [], context: [], notices: []);
}
final effective = policy ?? KnowledgeSearchPolicy();
final eligible = _eligibleChunks(snapshot, effective);
_searches++;
try {
final dense = <SearchResult>[];
if (eligible.isNotEmpty) {
final stored = await store.getEmbeddingsForChunks(
eligible.keys.toSet(),
source: embedder.sourceName,
modelName: embeddingModelName,
);
if (stored.length != eligible.length) {
throw StateError(
'Dense search requires compatible embeddings for every eligible '
'passage. Synchronize the index again.',
);
}
final key = query.trim();
final vector =
_queryCache.remove(key) ?? await embedder.generateQueryVector(key);
_queryCache[key] = vector;
if (_queryCache.length > 100) {
_queryCache.remove(_queryCache.keys.first);
}
for (final embedding in stored) {
dense.add(
SearchResult(
chunk: eligible[embedding.chunkId]!,
embedding: embedding,
similarity: cosineSimilarity(vector, embedding.vector),
),
);
}
dense.sort((a, b) {
final score = b.similarity.compareTo(a.similarity);
if (score != 0) return score;
final path = a.chunk.sourcePath.compareTo(b.chunk.sourcePath);
return path == 0 ? a.chunk.id.compareTo(b.chunk.id) : path;
});
}
return _contextForEligible(
snapshot,
eligible,
dense,
policy: effective,
limit: limit,
budget: budget,
);
} finally {
_searches--;
}
}