[Developer] #570 show vector VPD effective SQL trace
This commit is contained in:
@@ -142,7 +142,9 @@ public class OrdsProbeService {
|
||||
prettyHeaders(response.getHeaders()),
|
||||
prettyJson(response.getBody())
|
||||
);
|
||||
if (!result.hasSqlTrace() && !isVectorSearchObject(object)) {
|
||||
if (isVectorSearchObject(object)) {
|
||||
result = addVectorSqlTrace(result, command.bearerToken(), object);
|
||||
} else if (!result.hasSqlTrace()) {
|
||||
result = addLocalSqlTrace(result, command.bearerToken(), object);
|
||||
}
|
||||
return auditAndReturn(command, result);
|
||||
@@ -257,6 +259,24 @@ public class OrdsProbeService {
|
||||
return result.withSqlTrace(predicate, effectiveSql);
|
||||
}
|
||||
|
||||
private ProbeResult addVectorSqlTrace(ProbeResult result, String bearerToken, ProtectedObject object) {
|
||||
String predicate = findVpdPredicate(bearerToken, object);
|
||||
if (predicate == null || predicate.isBlank()) {
|
||||
return result;
|
||||
}
|
||||
return result.withSqlTrace(predicate, vectorEffectiveSql(object, predicate));
|
||||
}
|
||||
|
||||
static String vectorEffectiveSql(ProtectedObject object, String predicate) {
|
||||
return "SELECT chunk_id, document_id, chunk_no, title, chunk_text, source_uri, tech_tag, score"
|
||||
+ " FROM (SELECT d.chunk_id, d.document_id, d.chunk_no, d.title, d.chunk_text,"
|
||||
+ " d.source_uri, d.tech_tag, VECTOR_DISTANCE(d.embedding, TO_VECTOR(:embedding), COSINE) AS score"
|
||||
+ " FROM " + object.owner() + "." + object.objectName() + " d"
|
||||
+ " WHERE d.embedding IS NOT NULL AND (" + predicate + ")"
|
||||
+ " ORDER BY score) ranked_chunks"
|
||||
+ " WHERE ROWNUM <= LEAST(GREATEST(NVL(:row_limit, 10), 1), 100)";
|
||||
}
|
||||
|
||||
private String findVpdPredicate(String bearerToken, ProtectedObject object) {
|
||||
try {
|
||||
return jdbcTemplate.execute((ConnectionCallback<String>) connection -> {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.cloudhandson.vpdbackoffice.service;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import com.cloudhandson.vpdbackoffice.domain.protectedobject.ProtectedObject;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class OrdsProbeServiceTest {
|
||||
|
||||
@Test
|
||||
void buildsVectorEffectiveSqlWithVpdPredicateAndDistancePlaceholder() {
|
||||
ProtectedObject object = new ProtectedObject(
|
||||
5L, "ADMIN", "CB_VECTOR_SEARCH_DOCUMENTS", "cb-agent-vector/search", "Y");
|
||||
|
||||
String sql = OrdsProbeService.vectorEffectiveSql(
|
||||
object, "INSTR(',' || UPPER(d.TECH_TAG) || ',', ',ORACLE_VPD,') > 0");
|
||||
|
||||
assertThat(sql)
|
||||
.contains("FROM ADMIN.CB_VECTOR_SEARCH_DOCUMENTS d")
|
||||
.contains("VECTOR_DISTANCE(d.embedding, TO_VECTOR(:embedding), COSINE)")
|
||||
.contains("WHERE d.embedding IS NOT NULL AND (INSTR")
|
||||
.contains("ORDER BY score")
|
||||
.contains("ROWNUM <= LEAST(GREATEST(NVL(:row_limit, 10), 1), 100)")
|
||||
.doesNotContain("Bearer", "0.10", "0.20");
|
||||
}
|
||||
}
|
||||
@@ -134,6 +134,20 @@ class GuidedFlowTemplateTest {
|
||||
.contains("검색 기술 상세 보기");
|
||||
}
|
||||
|
||||
@Test
|
||||
void probeResultExplainsVectorEffectiveSqlWhenTheHandlerReturnsTrace() throws IOException {
|
||||
String result = template("fragments/probe-result.html");
|
||||
String vectorSql = Files.readString(Path.of("sql/adb/29_agent_ords_vector_search_ords.sql"));
|
||||
|
||||
assertThat(result)
|
||||
.contains("토큰 적용 후 SQL")
|
||||
.contains("권한 적용 SQL")
|
||||
.contains("VPD가 추가한 WHERE 조건");
|
||||
assertThat(vectorSql)
|
||||
.contains("VECTOR_DISTANCE(d.embedding, TO_VECTOR(:embedding), COSINE)")
|
||||
.contains("WHERE d.embedding IS NOT NULL AND (' || v_vpd_predicate || ')");
|
||||
}
|
||||
|
||||
@Test
|
||||
void vectorOrdsHandlerReadsBodyStreamOnlyOnce() throws IOException {
|
||||
String sql = Files.readString(Path.of("sql/adb/29_agent_ords_vector_search_ords.sql"));
|
||||
|
||||
Reference in New Issue
Block a user