Nest extraData by section for clearer embedding content
Homebox's location fields were flattened into single-purpose keys (locationName/locationDescription), which gave EmbeddingService no way to group related fields when rendering vector-store content. extraData is now Map<String, Map<String, Object>>, letting HomeboxItemClient express "location" as its own section; buildExtraData renders each section as a header with indented fields instead of a flat "Extradata: key: value" line.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
package com.vaessl.app.homebox;
|
package com.vaessl.app.homebox;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
@@ -73,12 +73,18 @@ public class HomeboxItemClient {
|
|||||||
String description = i.description();
|
String description = i.description();
|
||||||
|
|
||||||
HomeboxParent parent = i.parent();
|
HomeboxParent parent = i.parent();
|
||||||
Map<String, Object> extraData = new HashMap<>();
|
Map<String, Map<String, Object>> extraData = new LinkedHashMap<>();
|
||||||
|
Map<String, Object> locationData = new LinkedHashMap<>();
|
||||||
|
|
||||||
if (parent.name() != null && !parent.name().isBlank()) {
|
if (parent.name() != null && !parent.name().isBlank()) {
|
||||||
extraData.put("locationName", parent.name());
|
locationData.put("name", parent.name());
|
||||||
}
|
}
|
||||||
if (parent.description() != null && !parent.description().isBlank()) {
|
if (parent.description() != null && !parent.description().isBlank()) {
|
||||||
extraData.put("locationDescription", parent.description());
|
locationData.put("description", parent.description());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locationData != null && !locationData.isEmpty()) {
|
||||||
|
extraData.put("location", locationData);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ServiceItem(id, title, description, extraData);
|
return new ServiceItem(id, title, description, extraData);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import java.util.Map;
|
|||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
|
||||||
public record ServiceItem(String id, @NotNull String title, String description,
|
public record ServiceItem(String id, @NotNull String title, String description,
|
||||||
Map<String, Object> extraData) {
|
Map<String, Map<String, Object>> extraData) {
|
||||||
|
|
||||||
public String getExtra(String key) {
|
public String getExtra(String key) {
|
||||||
if (extraData == null) {
|
if (extraData == null) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import org.springframework.ai.document.Document;
|
import org.springframework.ai.document.Document;
|
||||||
import org.springframework.ai.vectorstore.VectorStore;
|
import org.springframework.ai.vectorstore.VectorStore;
|
||||||
import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
|
import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
|
||||||
@@ -54,21 +55,30 @@ public class EmbeddingService {
|
|||||||
return new Document(connectionId + ":" + item.id(), content, metadata);
|
return new Document(connectionId + ":" + item.id(), content, metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String buildExtraData(Map<String, Object> extraData) {
|
private String buildExtraData(Map<String, Map<String, Object>> extraData) {
|
||||||
if (extraData == null) {
|
if (extraData == null) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
StringBuilder data = new StringBuilder();
|
|
||||||
for (Map.Entry<String, Object> entry : extraData.entrySet()) {
|
StringBuilder formattedData = new StringBuilder();
|
||||||
|
|
||||||
|
for (Entry<String, Map<String, Object>> entry : extraData.entrySet()) {
|
||||||
|
StringBuilder data = new StringBuilder();
|
||||||
if (entry.getValue() == null || entry.getValue().toString().isEmpty()) {
|
if (entry.getValue() == null || entry.getValue().toString().isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!data.isEmpty()) {
|
|
||||||
data.append(" \n");
|
if (entry.getValue().entrySet() != null
|
||||||
|
&& !entry.getValue().entrySet().toString().isEmpty()) {
|
||||||
|
for (Entry<String, Object> nestedEntry : entry.getValue().entrySet()) {
|
||||||
|
data.append(" ").append(nestedEntry.getKey()).append(": ")
|
||||||
|
.append(nestedEntry.getValue()).append("\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
data.append(entry.getKey()).append(": ").append(entry.getValue());
|
|
||||||
|
formattedData.append(entry.getKey()).append(": ").append("\n").append(data.toString());
|
||||||
}
|
}
|
||||||
return data.toString();
|
return formattedData.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> buildMetadata(ServiceItem item, ServiceType serviceType,
|
private Map<String, Object> buildMetadata(ServiceItem item, ServiceType serviceType,
|
||||||
@@ -81,12 +91,13 @@ public class EmbeddingService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String buildContent(ServiceItem item, String extraData) {
|
private String buildContent(ServiceItem item, String extraData) {
|
||||||
StringBuilder content = new StringBuilder("Title: ").append(item.title());
|
StringBuilder content = new StringBuilder("title: ").append(item.title());
|
||||||
if (item.description() != null && !item.description().isEmpty()) {
|
if (item.description() != null && !item.description().isEmpty()) {
|
||||||
content.append("\nDescription: ").append(item.description());
|
content.append("\ndescription: ").append(item.description());
|
||||||
}
|
}
|
||||||
if (!extraData.isEmpty()) {
|
if (!extraData.isEmpty()) {
|
||||||
content.append("\nExtradata: ").append(extraData);
|
|
||||||
|
content.append("\n").append(extraData);
|
||||||
}
|
}
|
||||||
return content.toString();
|
return content.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class SearchControllerTest {
|
|||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.content[0].title").value("MacBook Pro A1398"))
|
.andExpect(jsonPath("$.content[0].title").value("MacBook Pro A1398"))
|
||||||
.andExpect(jsonPath("$.totalElements").value(1))
|
.andExpect(jsonPath("$.totalElements").value(1))
|
||||||
.andExpect(jsonPath("$.content[0].extraData.locationName")
|
.andExpect(jsonPath("$.content[0].extraData.location.name")
|
||||||
.value("Server Schrank Ikea weiß"));
|
.value("Server Schrank Ikea weiß"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class SearchResponseTest {
|
|||||||
@Test
|
@Test
|
||||||
void shouldReturnNullWhenExtraDataKeyIsMissing() {
|
void shouldReturnNullWhenExtraDataKeyIsMissing() {
|
||||||
|
|
||||||
ServiceItem response = new ServiceItem(MOCK_ID, MOCK_TITLE, MOCK_DESCRIPTION, Map.of("key", "value"));
|
ServiceItem response = new ServiceItem(MOCK_ID, MOCK_TITLE, MOCK_DESCRIPTION, Map.of("key", Map.of("key", "value")));
|
||||||
|
|
||||||
assertThat(response.getExtra("missing")).isNull();
|
assertThat(response.getExtra("missing")).isNull();
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,8 @@ class SearchResponseTest {
|
|||||||
@Test
|
@Test
|
||||||
void shouldReturnExtraDataValue() {
|
void shouldReturnExtraDataValue() {
|
||||||
|
|
||||||
ServiceItem response = new ServiceItem(MOCK_ID, MOCK_TITLE, MOCK_DESCRIPTION, Map.of("key", "value"));
|
ServiceItem response = new ServiceItem(MOCK_ID, MOCK_TITLE, MOCK_DESCRIPTION, Map.of("key",
|
||||||
|
Map.of("key", "value")));
|
||||||
|
|
||||||
assertThat(response.id()).isEqualTo(MOCK_ID);
|
assertThat(response.id()).isEqualTo(MOCK_ID);
|
||||||
assertThat(response.getExtra("key")).contains("value");
|
assertThat(response.getExtra("key")).contains("value");
|
||||||
|
|||||||
Reference in New Issue
Block a user