Попытки в мультипарсинг
This commit is contained in:
45
CodeBase/Repositories/DatabaseInitializer.cs
Normal file
45
CodeBase/Repositories/DatabaseInitializer.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using Neo4j.Driver;
|
||||
|
||||
namespace CodeBase.Repositories
|
||||
{
|
||||
public class DatabaseInitializer
|
||||
{
|
||||
private readonly IDriver _neo4jDriver;
|
||||
|
||||
public DatabaseInitializer(IDriver neo4jDriver)
|
||||
{
|
||||
_neo4jDriver = neo4jDriver;
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var session = _neo4jDriver.AsyncSession();
|
||||
|
||||
// IF NOT EXISTS гарантирует, что запрос не упадет с ошибкой,
|
||||
// если индекс уже был создан при предыдущем запуске
|
||||
var query = @"
|
||||
CREATE VECTOR INDEX code_embeddings IF NOT EXISTS
|
||||
FOR (m:CodeEntity) ON (m.embedding)
|
||||
OPTIONS {
|
||||
indexConfig: {
|
||||
`vector.dimensions`: 768, // <-- УКАЖИ ТУТ РАЗМЕРНОСТЬ ТВОЕЙ МОДЕЛИ
|
||||
`vector.similarity_function`: 'cosine'
|
||||
}
|
||||
}";
|
||||
|
||||
await session.ExecuteWriteAsync(async tx =>
|
||||
{
|
||||
await tx.RunAsync(query);
|
||||
});
|
||||
|
||||
Console.WriteLine("[БД] Векторный индекс успешно инициализирован.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[БД ОШИБКА] Ошибка при создании индекса: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,14 +44,14 @@ public class GraphRepository
|
||||
await using var session = _driver.AsyncSession();
|
||||
|
||||
// 1. Делаем данные "безопасными" для базы
|
||||
var parameters = chunks.Where(c => c.Vector != null).Select(chunk => new
|
||||
var parameters = chunks.Where(c => c.Embedding != null).Select(chunk => new
|
||||
{
|
||||
id = Guid.NewGuid().ToString(),
|
||||
projectName = projectName,
|
||||
filePath = chunk.FilePath,
|
||||
methodName = chunk.MethodName,
|
||||
methodName = chunk.EntityName,
|
||||
content = chunk.Content,
|
||||
vector = chunk.Vector,
|
||||
vector = chunk.Embedding,
|
||||
// Защита от null и пустых строк (база их не переварит в цикле FOREACH)
|
||||
outgoingCalls = chunk.OutgoingCalls != null
|
||||
? chunk.OutgoingCalls.Where(x => !string.IsNullOrWhiteSpace(x)).ToList()
|
||||
|
||||
96
CodeBase/Repositories/ScipRepository.cs
Normal file
96
CodeBase/Repositories/ScipRepository.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using CodeBase.Models;
|
||||
using Neo4j.Driver;
|
||||
|
||||
namespace CodeBase.Repositories
|
||||
{
|
||||
public class ScipRepository
|
||||
{
|
||||
private readonly IDriver _neo4jDriver;
|
||||
|
||||
public ScipRepository(IDriver neo4jDriver)
|
||||
{
|
||||
_neo4jDriver = neo4jDriver;
|
||||
}
|
||||
public async Task SaveChunksAsync(List<CodeChunk> chunks)
|
||||
{
|
||||
if (chunks == null || chunks.Count == 0) return;
|
||||
|
||||
await using var session = _neo4jDriver.AsyncSession();
|
||||
|
||||
await session.ExecuteWriteAsync(async tx =>
|
||||
{
|
||||
foreach (var chunk in chunks)
|
||||
{
|
||||
var uniqueId = $"{chunk.ProjectName}::{chunk.Id}";
|
||||
|
||||
var query = @"
|
||||
MERGE (p:Project {name: $projectName})
|
||||
|
||||
MERGE (m:CodeEntity {id: $id})
|
||||
SET m.projectName = $projectName,
|
||||
m.name = $name,
|
||||
m.filePath = $filePath,
|
||||
m.code = $code,
|
||||
m.language = $language,
|
||||
m.embedding = $embedding
|
||||
|
||||
MERGE (p)-[:CONTAINS]->(m)";
|
||||
|
||||
await tx.RunAsync(query, new
|
||||
{
|
||||
id = uniqueId,
|
||||
projectName = chunk.ProjectName,
|
||||
name = chunk.EntityName,
|
||||
filePath = chunk.FilePath,
|
||||
code = chunk.Content,
|
||||
language = chunk.Language,
|
||||
embedding = chunk.Embedding
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ищет в графе узлы, наиболее близкие к переданному вектору.
|
||||
/// </summary>
|
||||
/// <param name="queryVector">Вектор вопроса пользователя</param>
|
||||
/// <param name="projectName">Имя проекта для фильтрации (опционально)</param>
|
||||
/// <param name="topK">Сколько кусков кода вернуть</param>
|
||||
public async Task<List<RetrievedContext>> FindSimilarNodesAsync(float[] queryVector, string projectName = null, int topK = 5)
|
||||
{
|
||||
await using var session = _neo4jDriver.AsyncSession();
|
||||
|
||||
var result = await session.ExecuteReadAsync(async tx =>
|
||||
{
|
||||
// Базовый запрос к векторному индексу
|
||||
string cypherQuery = @"
|
||||
CALL db.index.vector.queryNodes('code_embeddings', $topK, $queryVector)
|
||||
YIELD node AS method, score
|
||||
WHERE $projectName IS NULL OR method.projectName = $projectName
|
||||
RETURN method.projectName AS Project, method.name AS Name, method.filePath AS Path, method.code AS Code, score
|
||||
ORDER BY score DESC";
|
||||
|
||||
var cursor = await tx.RunAsync(cypherQuery, new { topK, queryVector, projectName });
|
||||
var contexts = new List<RetrievedContext>();
|
||||
|
||||
while (await cursor.FetchAsync())
|
||||
{
|
||||
contexts.Add(new RetrievedContext
|
||||
{
|
||||
ProjectName = cursor.Current["Project"].As<string>(), // Читаем имя проекта
|
||||
EntityName = cursor.Current["Name"].As<string>(),
|
||||
FilePath = cursor.Current["Path"].As<string>(),
|
||||
Content = cursor.Current["Code"].As<string>(),
|
||||
SimilarityScore = cursor.Current["score"].As<double>()
|
||||
});
|
||||
}
|
||||
|
||||
return contexts;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user