找回密码
 立即注册
首页 业界区 安全 Spring AI Alibaba 项目源码学习(四)-Graph中的存储分 ...

Spring AI Alibaba 项目源码学习(四)-Graph中的存储分析

胁冉右 2025-11-13 23:45:00
Store 存储系统分析

请关注微信公众号:阿呆-bot
概述

本文档分析 spring-ai-alibaba-graph-core 模块中的 Store 存储系统,包括接口设计、实现模式、支持的存储类型和关键设计点。
入口类说明

Store - 存储接口

Store 是长期内存存储的接口,提供跨会话的持久化存储能力,与 CheckpointSaver(短期状态持久化)不同,Store 专注于长期数据管理。
核心职责

  • 提供层次化命名空间支持
  • 支持结构化数据存储(Map 格式)
  • 提供搜索和过滤功能
  • 支持分页查询
  • 跨会话数据持久化
关键代码
  1. public interface Store {
  2.         /**
  3.          * Store an item in the specified namespace with the given key. If an item with the
  4.          * same namespace and key already exists, it will be updated.
  5.          * @param item the item to store
  6.          * @throws IllegalArgumentException if item is null or invalid
  7.          */
  8.         void putItem(StoreItem item);
  9.         /**
  10.          * Retrieve an item from the specified namespace with the given key.
  11.          * @param namespace the hierarchical namespace path
  12.          * @param key the item key
  13.          * @return Optional containing the item if found, empty otherwise
  14.          * @throws IllegalArgumentException if namespace or key is null/invalid
  15.          */
  16.         Optional<StoreItem> getItem(List<String> namespace, String key);
  17.         /**
  18.          * Delete an item from the specified namespace with the given key.
  19.          * @param namespace the hierarchical namespace path
  20.          * @param key the item key
  21.          * @return true if the item was deleted, false if it didn't exist
  22.          * @throws IllegalArgumentException if namespace or key is null/invalid
  23.          */
  24.         boolean deleteItem(List<String> namespace, String key);
  25.         /**
  26.          * Search for items based on the provided search criteria.
  27.          * @param searchRequest the search parameters
  28.          * @return search results with matching items
  29.          * @throws IllegalArgumentException if searchRequest is null
  30.          */
  31.         StoreSearchResult searchItems(StoreSearchRequest searchRequest);
  32.         /**
  33.          * List available namespaces based on the provided criteria.
  34.          * @param namespaceRequest the namespace listing parameters
  35.          * @return list of namespace paths
  36.          * @throws IllegalArgumentException if namespaceRequest is null
  37.          */
  38.         List<String> listNamespaces(NamespaceListRequest namespaceRequest);
  39.         /**
  40.          * Clear all items from the store.
  41.          * <p>
  42.          * <strong>WARNING:</strong> This operation is irreversible and will remove all stored
  43.          * data.
  44.          * </p>
  45.          */
  46.         void clear();
  47.         /**
  48.          * Get the total number of items in the store.
  49.          * @return the number of items stored
  50.          */
  51.         long size();
  52.         /**
  53.          * Check if the store is empty.
  54.          * @return true if the store contains no items, false otherwise
  55.          */
  56.         boolean isEmpty();
  57. }
复制代码
BaseStore - 抽象基类

BaseStore 为所有 Store 实现提供通用的验证和工具方法,实现了模板方法模式。
核心职责

  • 提供参数验证方法
  • 提供键编码/解码工具
  • 提供搜索匹配逻辑
  • 提供排序比较器
关键代码
  1. public abstract class BaseStore implements Store {
  2.         /**
  3.          * Validates the putItem parameters.
  4.          * @param item the item to validate
  5.          */
  6.         protected void validatePutItem(StoreItem item) {
  7.                 if (item == null) {
  8.                         throw new IllegalArgumentException("item cannot be null");
  9.                 }
  10.                 if (item.getNamespace() == null) {
  11.                         throw new IllegalArgumentException("namespace cannot be null");
  12.                 }
  13.                 if (item.getKey() == null || item.getKey().trim().isEmpty()) {
  14.                         throw new IllegalArgumentException("key cannot be null or empty");
  15.                 }
  16.         }
  17.         /**
  18.          * Validates the getItem parameters.
  19.          * @param namespace namespace
  20.          * @param key key
  21.          */
  22.         protected void validateGetItem(List<String> namespace, String key) {
  23.                 if (namespace == null) {
  24.                         throw new IllegalArgumentException("namespace cannot be null");
  25.                 }
  26.                 if (key == null) {
  27.                         throw new IllegalArgumentException("key cannot be null");
  28.                 }
  29.                 if (key.trim().isEmpty()) {
  30.                         throw new IllegalArgumentException("key cannot be empty");
  31.                 }
  32.         }
  33.         /**
  34.          * Validates the deleteItem parameters.
  35.          * @param namespace namespace
  36.          * @param key key
  37.          */
  38.         protected void validateDeleteItem(List<String> namespace, String key) {
  39.                 if (namespace == null) {
  40.                         throw new IllegalArgumentException("namespace cannot be null");
  41.                 }
  42.                 if (key == null) {
  43.                         throw new IllegalArgumentException("key cannot be null");
  44.                 }
  45.                 if (key.trim().isEmpty()) {
  46.                         throw new IllegalArgumentException("key cannot be empty");
  47.                 }
  48.         }
  49.         /**
  50.          * Validates the searchItems parameters.
  51.          * @param searchRequest search request
  52.          */
  53.         protected void validateSearchItems(StoreSearchRequest searchRequest) {
  54.                 if (searchRequest == null) {
  55.                         throw new IllegalArgumentException("searchRequest cannot be null");
  56.                 }
  57.         }
  58.         /**
  59.          * Validates the listNamespaces parameters.
  60.          * @param namespaceRequest namespace request
  61.          */
  62.         protected void validateListNamespaces(NamespaceListRequest namespaceRequest) {
  63.                 if (namespaceRequest == null) {
  64.                         throw new IllegalArgumentException("namespaceRequest cannot be null");
  65.                 }
  66.         }
  67.         /**
  68.          * Create store key from namespace and key Uses safe encoding to avoid conflicts from
  69.          * special characters.
  70.          * @param namespace namespace
  71.          * @param key key
  72.          * @return store key
  73.          */
  74.         protected String createStoreKey(List<String> namespace, String key) {
  75.                 try {
  76.                         Map<String, Object> keyData = new HashMap<>();
  77.                         keyData.put("namespace", namespace);
  78.                         keyData.put("key", key);
  79.                         return Base64.getEncoder().encodeToString(new ObjectMapper().writeValueAsBytes(keyData));
  80.                 }
  81.                 catch (Exception e) {
  82.                         throw new RuntimeException("Failed to create store key", e);
  83.                 }
  84.         }
  85.         /**
  86.          * Parse store key to namespace and key.
  87.          * @param storeKey store key
  88.          * @return array containing [namespace, key]
  89.          */
  90.         @SuppressWarnings("unchecked")
  91.         protected Object[] parseStoreKey(String storeKey) {
  92.                 try {
  93.                         byte[] decoded = Base64.getDecoder().decode(storeKey);
  94.                         Map<String, Object> keyData = new ObjectMapper().readValue(decoded, Map.class);
  95.                         List<String> namespace = (List<String>) keyData.get("namespace");
  96.                         String key = (String) keyData.get("key");
  97.                         return new Object[] { namespace, key };
  98.                 }
  99.                 catch (Exception e) {
  100.                         throw new RuntimeException("Failed to parse store key: " + storeKey, e);
  101.                 }
  102.         }
复制代码
StoreItem - 存储项

StoreItem 表示存储中的单个数据项,包含命名空间、键、值和时间戳。
关键代码
  1. public class StoreItem implements Serializable {
  2.         /**
  3.          * The hierarchical namespace path for organizing items. For example: ["users",
  4.          * "user123", "preferences"]
  5.          */
  6.         private List<String> namespace;
  7.         /**
  8.          * The unique key within the namespace.
  9.          */
  10.         private String key;
  11.         /**
  12.          * The item value as a structured Map.
  13.          */
  14.         private Map<String, Object> value;
  15.         /**
  16.          * Timestamp when the item was created (milliseconds since epoch).
  17.          */
  18.         private long createdAt;
  19.         /**
  20.          * Timestamp when the item was last updated (milliseconds since epoch).
  21.          */
  22.         private long updatedAt;
  23.         /**
  24.          * Default constructor for serialization frameworks.
  25.          */
  26.         public StoreItem() {
  27.         }
  28.         /**
  29.          * Constructs a StoreItem with the specified namespace, key, and value. Timestamps are
  30.          * set to the current time.
  31.          * @param namespace the hierarchical namespace path
  32.          * @param key the item key
  33.          * @param value the item value
  34.          */
  35.         public StoreItem(List<String> namespace, String key, Map<String, Object> value) {
  36.                 this.namespace = namespace;
  37.                 this.key = key;
  38.                 this.value = value;
  39.                 long now = System.currentTimeMillis();
  40.                 this.createdAt = now;
  41.                 this.updatedAt = now;
  42.         }
  43.         /**
  44.          * Constructs a StoreItem with full parameters including timestamps.
  45.          * @param namespace the hierarchical namespace path
  46.          * @param key the item key
  47.          * @param value the item value
  48.          * @param createdAt creation timestamp (milliseconds since epoch)
  49.          * @param updatedAt last update timestamp (milliseconds since epoch)
  50.          */
  51.         public StoreItem(List<String> namespace, String key, Map<String, Object> value, long createdAt, long updatedAt) {
  52.                 this.namespace = namespace;
  53.                 this.key = key;
  54.                 this.value = value;
  55.                 this.createdAt = createdAt;
  56.                 this.updatedAt = updatedAt;
  57.         }
  58.         /**
  59.          * Static factory method to create a StoreItem.
  60.          * @param namespace the hierarchical namespace path
  61.          * @param key the item key
  62.          * @param value the item value
  63.          * @return a new StoreItem instance
  64.          */
  65.         public static StoreItem of(List<String> namespace, String key, Map<String, Object> value) {
  66.                 return new StoreItem(namespace, key, value);
  67.         }
复制代码
Store 实现类

MemoryStore - 内存存储

基于 ConcurrentHashMap 的内存实现,适用于测试和开发环境。
关键代码
  1. public class MemoryStore extends BaseStore {
  2.         /**
  3.          * Thread-safe storage for store items. Key format: "namespace1/namespace2/key"
  4.          */
  5.         private final Map<String, StoreItem> storage = new ConcurrentHashMap<>();
  6.         /**
  7.          * Read-write lock for thread safety during search operations.
  8.          */
  9.         private final ReadWriteLock lock = new ReentrantReadWriteLock();
  10.         @Override
  11.         public void putItem(StoreItem item) {
  12.                 validatePutItem(item);
  13.                 lock.writeLock().lock();
  14.                 try {
  15.                         String storeKey = createStoreKey(item.getNamespace(), item.getKey());
  16.                         storage.put(storeKey, item);
  17.                 }
  18.                 finally {
  19.                         lock.writeLock().unlock();
  20.                 }
  21.         }
  22.         @Override
  23.         public Optional<StoreItem> getItem(List<String> namespace, String key) {
  24.                 validateGetItem(namespace, key);
  25.                 lock.readLock().lock();
  26.                 try {
  27.                         String storeKey = createStoreKey(namespace, key);
  28.                         return Optional.ofNullable(storage.get(storeKey));
  29.                 }
  30.                 finally {
  31.                         lock.readLock().unlock();
  32.                 }
  33.         }
复制代码
特点

  • 线程安全(使用 ConcurrentHashMap 和 ReadWriteLock)
  • 高性能(内存访问)
  • 数据不持久化(应用重启后丢失)
FileSystemStore - 文件系统存储

基于文件系统的持久化存储实现。
特点

  • 数据持久化到本地文件系统
  • 支持目录结构组织
  • 适合单机部署
DatabaseStore - 数据库存储

基于关系型数据库(MySQL/PostgreSQL)的存储实现。
特点

  • 支持事务
  • 支持复杂查询
  • 适合生产环境
RedisStore - Redis 存储

基于 Redis 的存储实现。
特点

  • 高性能
  • 支持分布式
  • 支持过期策略
MongoStore - MongoDB 存储

基于 MongoDB 的存储实现。
特点

  • 支持文档存储
  • 支持复杂查询
  • 适合非结构化数据
关键类关系图

以下 PlantUML 类图展示了 Store 系统的类关系:
1.png

这种设计避免了特殊字符冲突,确保键的唯一性。
3. 搜索和过滤

StoreSearchRequest 支持多种搜索方式:

  • 命名空间过滤:在指定命名空间下搜索
  • 文本查询:在键和值中搜索文本
  • 自定义过滤:基于值的键值对过滤
  • 排序:支持多字段排序
  • 分页:支持 offset 和 limit
关键代码
  1. protected boolean matchesSearchCriteria(StoreItem item, StoreSearchRequest searchRequest) {
  2.                 // Namespace filter
  3.                 List<String> namespaceFilter = searchRequest.getNamespace();
  4.                 if (!namespaceFilter.isEmpty() && !startsWithPrefix(item.getNamespace(), namespaceFilter)) {
  5.                         return false;
  6.                 }
  7.                 // Text query filter
  8.                 String query = searchRequest.getQuery();
  9.                 if (query != null && !query.trim().isEmpty()) {
  10.                         String lowerQuery = query.toLowerCase();
  11.                         // Search in key
  12.                         if (!item.getKey().toLowerCase().contains(lowerQuery)) {
  13.                                 // Search in value
  14.                                 String valueStr = item.getValue().toString().toLowerCase();
  15.                                 if (!valueStr.contains(lowerQuery)) {
  16.                                         return false;
  17.                                 }
  18.                         }
  19.                 }
  20.                 // Custom filters
  21.                 Map<String, Object> filters = searchRequest.getFilter();
  22.                 if (!filters.isEmpty()) {
  23.                         Map<String, Object> itemValue = item.getValue();
  24.                         for (Map.Entry<String, Object> filter : filters.entrySet()) {
  25.                                 Object itemVal = itemValue.get(filter.getKey());
  26.                                 if (!Objects.equals(itemVal, filter.getValue())) {
  27.                                         return false;
  28.                                 }
  29.                         }
  30.                 }
  31.                 return true;
  32.         }
复制代码
实现关键点说明

1. 模板方法模式

BaseStore 实现了模板方法模式:

  • 定义通用的验证和工具方法
  • 子类实现具体的存储逻辑
  • 确保所有实现的一致性
2. 策略模式

不同的 Store 实现代表不同的存储策略:

  • MemoryStore:内存策略(快速但不持久)
  • FileSystemStore:文件系统策略(持久但单机)
  • DatabaseStore:数据库策略(事务支持)
  • RedisStore:缓存策略(高性能)
  • MongoStore:文档数据库策略(灵活)
3. 线程安全


  • MemoryStore 使用 ConcurrentHashMap 和 ReadWriteLock 确保线程安全
  • 其他实现依赖底层存储的线程安全机制
4. 键编码设计

使用 Base64 编码避免特殊字符问题:

  • 支持任意命名空间和键名
  • 确保键的唯一性
  • 便于序列化和反序列化
5. 搜索优化

BaseStore 提供通用的搜索匹配逻辑:

  • 命名空间前缀匹配
  • 文本包含匹配
  • 自定义过滤匹配
  • 多字段排序支持
总结说明

核心设计理念


  • 统一接口:Store 接口定义统一的存储操作,屏蔽底层实现差异
  • 层次化组织:通过命名空间支持数据的层次化组织
  • 灵活查询:支持多种搜索和过滤方式
  • 可扩展性:通过接口和抽象类支持新的存储实现
支持的存储模式


  • 内存模式(MemoryStore):适合测试和开发
  • 文件系统模式(FileSystemStore):适合单机部署
  • 数据库模式(DatabaseStore):适合生产环境,支持事务
  • 缓存模式(RedisStore):适合高性能场景
  • 文档数据库模式(MongoStore):适合非结构化数据
关键优势


  • 一致性:所有实现遵循相同的接口规范
  • 可替换性:不同实现可以无缝替换
  • 可扩展性:易于添加新的存储实现
  • 类型安全:使用泛型和接口确保类型安全
使用场景


  • 长期记忆:Agent 的长期记忆存储
  • 知识库:存储结构化知识
  • 用户数据:存储用户偏好和配置
  • 会话数据:跨会话的数据持久化

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册