博主在使用Spring AI的ElasticsearchVectorStore实现ES向量存储的时候,想在metadata中存一个date字段,实体代码如下:
1 2 3 4 5 6 7 8
| public class KnowledgeBase { private String id; private String title; private String content; private String url; private LocalDate date; private Map<String, Object> metadata; }
|
众所周知,Jackson序列化Java 8时间(Local家族)需要导入jackson-datatype-jsr310模块,博主也是导入了,可是还是报错,尝试了各种方法,都无果后,博主跑去仔细研读了Spring AI源码,总算是发现了罪魁祸首:
1 2 3 4 5 6 7 8 9
| protected ElasticsearchVectorStore(Builder builder) { super(builder); Assert.notNull(builder.restClient, "RestClient must not be null"); this.initializeSchema = builder.initializeSchema; this.options = builder.options; this.filterExpressionConverter = builder.filterExpressionConverter; String version = Version.VERSION == null ? "Unknown" : Version.VERSION.toString(); this.elasticsearchClient = (ElasticsearchClient)(new ElasticsearchClient(new RestClientTransport(builder.restClient, new JacksonJsonpMapper((new ObjectMapper()).configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false))))).withTransportOptions((t) -> t.addHeader("user-agent", "spring-ai elastic-java/" + version)); }
|
主要看ElasticsearchClient的创建这一部分,Spring AI直接new JacksonJsonpMapper(),导致博主无论怎么配置Bean都无效,那怎么办,Spring AI设计太不灵活,博主就只能”越狱“了
使用Java的”不讲武德“工具——反射!!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| return new ElasticsearchVectorStore(ElasticsearchVectorStore .builder(elasticsearchRestClient, embeddingModel) .initializeSchema(true) .options(vectorStoreOptions) .batchingStrategy(batchingStrategy)) { { try { Field field = ElasticsearchVectorStore.class.getDeclaredField("elasticsearchClient"); field.setAccessible(true); field.set(this, elasticsearchClient); } catch (Exception e) { throw new RuntimeException("Failed to override elasticsearchClient", e); } } };
|
直接new一个匿名的ElasticsearchVectorStore类,然后在匿名类中声明一个静态代码块强制覆盖ElasticsearchClient,直接美美解决序列化异常问题啦~
就看Spring AI团段后续会不会改成传入ElasticsearchRestClient而不是RestClient,或者其他更为灵活的方法。
目前博主这种方式是最为有效的方式了!