mirror of
https://github.com/iflytek/skillhub.git
synced 2026-08-27 11:14:59 +00:00
feat(search): index compliance snapshot mappings
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
This commit is contained in:
parent
a7b25b072b
commit
da5c3cba3b
2 changed files with 100 additions and 3 deletions
|
|
@ -14,6 +14,7 @@ import com.iflytek.skillhub.domain.skill.SkillRepository;
|
|||
import com.iflytek.skillhub.domain.skill.SkillStatus;
|
||||
import com.iflytek.skillhub.domain.skill.SkillVersion;
|
||||
import com.iflytek.skillhub.domain.skill.SkillVersionRepository;
|
||||
import com.iflytek.skillhub.domain.skill.metadata.ComplianceMetadataService;
|
||||
import com.iflytek.skillhub.search.SearchIndexService;
|
||||
import com.iflytek.skillhub.search.SearchRebuildService;
|
||||
import com.iflytek.skillhub.search.SearchTextTokenizer;
|
||||
|
|
@ -127,9 +128,13 @@ public class PostgresSearchRebuildService implements SearchRebuildService {
|
|||
Set<String> keywords = new TreeSet<>();
|
||||
resolveLatestVersion(skill)
|
||||
.map(this::extractParsedMetadata)
|
||||
.map(metadata -> metadata.get("frontmatter"))
|
||||
.map(this::asMap)
|
||||
.ifPresent(frontmatter -> appendFrontmatter(frontmatter, keywords, searchParts));
|
||||
.ifPresent(metadata -> {
|
||||
appendFrontmatter(asMap(metadata.get("frontmatter")), keywords, searchParts);
|
||||
appendComplianceSnapshot(
|
||||
asMap(metadata.get(ComplianceMetadataService.SNAPSHOT_FIELD_NAME)),
|
||||
keywords,
|
||||
searchParts);
|
||||
});
|
||||
appendLabelKeywords(skill.getId(), keywords);
|
||||
|
||||
return new SearchIndexPayload(
|
||||
|
|
@ -197,6 +202,29 @@ public class PostgresSearchRebuildService implements SearchRebuildService {
|
|||
}
|
||||
}
|
||||
|
||||
private void appendComplianceSnapshot(Map<String, Object> snapshot,
|
||||
Set<String> keywords,
|
||||
List<String> searchParts) {
|
||||
Object rawItems = snapshot.get("items");
|
||||
if (!(rawItems instanceof Collection<?> items)) {
|
||||
return;
|
||||
}
|
||||
for (Object rawItem : items) {
|
||||
Map<String, Object> item = asMap(rawItem);
|
||||
appendComplianceSearchValue(item.get("standard"), keywords, searchParts);
|
||||
appendComplianceSearchValue(item.get("version"), keywords, searchParts);
|
||||
appendComplianceSearchValue(item.get("controlId"), keywords, searchParts);
|
||||
appendComplianceSearchValue(item.get("title"), keywords, searchParts);
|
||||
}
|
||||
}
|
||||
|
||||
private void appendComplianceSearchValue(Object value, Set<String> keywords, List<String> searchParts) {
|
||||
flattenToStrings(value).forEach(text -> {
|
||||
addKeyword(keywords, text);
|
||||
addPart(searchParts, text);
|
||||
});
|
||||
}
|
||||
|
||||
private List<String> flattenToStrings(Object value) {
|
||||
if (value == null) {
|
||||
return List.of();
|
||||
|
|
|
|||
|
|
@ -158,6 +158,75 @@ class PostgresSearchRebuildServiceTest {
|
|||
assertThat(document.searchText()).doesNotContain("智能体");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rebuildBySkill_shouldAppendComplianceSnapshotMappingsIntoSearchDocument() {
|
||||
SkillRepository skillRepository = mock(SkillRepository.class);
|
||||
NamespaceRepository namespaceRepository = mock(NamespaceRepository.class);
|
||||
SkillVersionRepository skillVersionRepository = mock(SkillVersionRepository.class);
|
||||
SearchIndexService searchIndexService = mock(SearchIndexService.class);
|
||||
|
||||
Skill skill = new Skill(7L, "terminal-agent", "owner-1", SkillVisibility.PUBLIC);
|
||||
skill.setDisplayName("Terminal Agent");
|
||||
skill.setSummary("Runs shell workflows safely");
|
||||
skill.setLatestVersionId(104L);
|
||||
|
||||
Namespace namespace = new Namespace("team-ai", "Team AI", "owner-1");
|
||||
SkillVersion version = new SkillVersion(1L, "1.7.0", "owner-1");
|
||||
version.setParsedMetadataJson("""
|
||||
{
|
||||
"frontmatter": {
|
||||
"keywords": ["shell"]
|
||||
},
|
||||
"complianceSnapshot": {
|
||||
"schemaVersion": "1",
|
||||
"digest": "sha256:abc123",
|
||||
"items": [
|
||||
{
|
||||
"standard": "mitre-attack",
|
||||
"version": "15",
|
||||
"controlId": "T1059",
|
||||
"title": "Command and Scripting Interpreter",
|
||||
"evidence": [
|
||||
{
|
||||
"type": "packaged-file",
|
||||
"path": "docs/security.md",
|
||||
"sha256": "sha256:def456"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
when(skillRepository.findById(1L)).thenReturn(Optional.of(skill));
|
||||
when(namespaceRepository.findById(7L)).thenReturn(Optional.of(namespace));
|
||||
when(skillVersionRepository.findById(104L)).thenReturn(Optional.of(version));
|
||||
|
||||
PostgresSearchRebuildService service = newService(
|
||||
skillRepository,
|
||||
namespaceRepository,
|
||||
skillVersionRepository,
|
||||
searchIndexService
|
||||
);
|
||||
|
||||
service.rebuildBySkill(1L);
|
||||
|
||||
ArgumentCaptor<SkillSearchDocument> captor = ArgumentCaptor.forClass(SkillSearchDocument.class);
|
||||
verify(searchIndexService).index(captor.capture());
|
||||
|
||||
SkillSearchDocument document = captor.getValue();
|
||||
assertThat(document.keywords()).contains("shell");
|
||||
assertThat(document.keywords()).contains("mitre-attack");
|
||||
assertThat(document.keywords()).contains("T1059");
|
||||
assertThat(document.keywords()).contains("Command and Scripting Interpreter");
|
||||
assertThat(document.searchText()).contains("mitre-attack");
|
||||
assertThat(document.searchText()).contains("T1059");
|
||||
assertThat(document.searchText()).contains("Command and Scripting Interpreter");
|
||||
assertThat(document.searchText()).doesNotContain("docs/security.md");
|
||||
assertThat(document.searchText()).doesNotContain("sha256:def456");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rebuildBySkill_shouldIgnoreNullFrontmatterValues() {
|
||||
SkillRepository skillRepository = mock(SkillRepository.class);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue