抢先一步
VMware 提供培训和认证,以加速您的进步。
了解更多自 Spring Data Repositories 诞生以来,它就被设计为可扩展的,无论您是想自定义单个查询方法,还是提供一个全新的基础实现。
2024.1 版本增强了您扩展 repository 的能力,通过 自定义功能,让任何人都可以轻松创建可在不同项目中共享的扩展。
让我们通过一个例子来了解它在实践中是如何运作的。
假设您使用 MongoDB 作为文档存储来管理电影数据库。 您希望通过您的 repository 接口利用 MongoDB Atlas 的 向量搜索 功能来实现 AI 驱动的搜索操作。 通常,您会创建像这样的自定义 repository 片段
package io.movie.db;
interface AtlasMovieRepository {
List<Movie> vectorSearch(String index, String path, List<Double> vector, Limit limit);
}
在这里,由于您正在使用 Movie
类型,因此您已经知道集合。 index 参数指定要使用的向量索引,path 定义保存用于比较的 向量嵌入 的字段。 相似度函数(例如,欧几里得、余弦或点积)在您设置索引时确定。 让我们假设已经有一个余弦向量索引。
在您的片段实现中,您需要创建 $vectorSearch
聚合阶段,这是 MongoDB 运行向量搜索的方法,并使用 MongoOperations
将其集成到 Aggregation API 中
package io.movie.db;
class AtlasMovieRepositoryFragment implements AtlasMovieRepository {
private final MongoOperations mongoOperations;
public AtlasMovieRepositoryFragment(MongoOperations mongoOperations) {
this.mongoOperations = mongoOperations;
}
@Override
public List<Movie> vectorSearch(String index, String path, List<Double> vector, Limit limit) {
Document $vectorSearch = createSearchDocument(index, path, vector, limit);
Aggregation aggregation = Aggregation.newAggregation(ctx -> $vectorSearch);
return mongoOperations.aggregate(aggregation, "movies", Movie.class).getMappedResults();
}
private static Document createSearchDocument(String index, String path, List<Double> vector, Limit limit) {
Document $vectorSearch = new Document();
$vectorSearch.append("index", index);
$vectorSearch.append("path", path);
$vectorSearch.append("queryVector", vector);
$vectorSearch.append("limit", limit.max());
return new Document("$vectorSearch", $vectorSearch);
}
}
现在,只需将片段集成到您的 MovieRepository
中
package io.movie.db;
interface MovieRepository extends CrudRepository<Movie, String>, AtlasMovieRepository { }
虽然这种方法有效,但您可能会注意到它与具有特定域类型 (Movie
) 的单个 repository 紧密耦合。 这使得它难以在其他项目中重用,因为片段实现与 repository 的包相关,并且是特定于域的。
但是向量搜索并不局限于我们的电影数据库。 如果我们想在其他项目中重用此功能,而无需复制和修改解决方案该怎么办? 让我们探索一种使其更通用的方法。
为了实现重用,我们将 AtlasMovieRepository
及其实现移动到一个单独的项目中,以便可以共享它。 然后,我们在 META-INF/spring.factories
文件中注册该片段,以便 Spring Data 了解该扩展
api.mongodb.atlas.AtlasMovieRepository=api.mongodb.atlas.AtlasMovieRepositoryFragment
但是,当前的实现仍然与 Movie
类型相关联,从而限制了其可重用性。 为了解决这个问题,我们需要使片段更加通用。 将 AtlasMovieRepository
重命名为 AtlasRepository
并引入泛型类型参数。 不要忘记更新 spring.factories
文件。
package api.mongodb.atlas;
interface AtlasRepository<T> {
List<T> vectorSearch(String index, String path, List<Double> vector, Limit limit);
}
接下来,我们更新实现以反映新的泛型方法,因为我们不能再假设我们正在定位 Movie
集合。 使用新引入的 RepositoryMethodContext
,我们可以访问 repository 元数据并动态确定适当的集合名称
package api.mongodb.atlas;
class AtlasRepositoryFragment<T> implements AtlasRepository<T>, RepositoryMetadataAccess {
private MongoOperations mongoOperations;
public AtlasRepositoryFragment(MongoOperations mongoOperations) {
this.mongoOperations = mongoOperations;
}
@Override
public List<T> vectorSearch(String index, String path, List<Double> vector, Limit limit) {
RepositoryMethodContext methodContext = RepositoryMethodContext.getContext();
Class<?> domainType = methodContext.getMetadata().getDomainType();
Document $vectorSearch = createSearchDocument(index, path, vector, limit);
Aggregation aggregation = Aggregation.newAggregation(ctx -> $vectorSearch);
return (List<T>) mongoOperations.aggregate(aggregation, mongoOperations.getCollectionName(domainType), domainType).getMappedResults();
}
private static Document createSearchDocument(String indexName, String path, List<Double> vector, Limit limit) {
Document $vectorSearch = new Document();
//…
}
}
提供的方法上下文不仅允许您访问有关 repository 的一般信息,还允许您访问 repository 的泛型、方法等。在上面的代码片段中,我们假设 repository 域类型与我们的自定义片段对齐,这可能不是这种情况。 因此,相反,我们还可以通过 ResolvableType.forClass(getRepositoryInterface()).as(AtlasRepository.class).getGeneric(0)
读取接口的组件类型,甚至检查当前方法的返回类型以应用其他操作,例如投影等。 为了简单起见,让我们坚持这个示例中的域类型。
为避免不必要的开销,我们仅为需要它的 repository 启用上下文访问。 仔细查看上面的代码,您会发现 AtlasRepositoryFragment
类上有一个额外的 RepositoryMetadataAccess
接口。 此标记接口建议基础结构在方法调用时提供所需的元数据。
通过该设置,您现在可以通过简单地扩展您的 repository 在任何项目中使用自定义扩展
package io.movie.db;
interface MovieRepository extends CrudRepository<Movie, String>, AtlasRepository<Movie> { }
要试用它,请访问 Spring Data Examples 项目,您将在其中找到准备运行的代码。