空间猫共享数据访问
@adobe/spacecat-shared-data-access 是Spacecat服务使用的共享数据访问层.
- 主数据存储器 : 通过 PostgREST 发送邮件 (单位:千美元)
@supabase/postgrest-js(中文(简体) ) - 可选二级数据库: 第3条 (计:
Configuration(中文(简体) )
安装
npm install @adobe/spacecat-shared-data-access
你得到的东西
该套件提供:
createDataAccess(config, log?, client?)——返回实体收藏+services.postgrestClientdataAccessWrapper(fn)(默认导出) Helix/ Lambda 样式处理器- 外部API形状稳定的服务实体收藏/模型
services.postgrestClient对非实体表格的PostgREST查询
快速启动
import { createDataAccess } from '@adobe/spacecat-shared-data-access';
const dataAccess = createDataAccess({
postgrestUrl: process.env.POSTGREST_URL,
postgrestSchema: process.env.POSTGREST_SCHEMA || 'public',
postgrestApiKey: process.env.POSTGREST_API_KEY,
// Only needed if you use Configuration entity:
s3Bucket: process.env.S3_CONFIG_BUCKET,
region: process.env.AWS_REGION,
}, console);
const site = await dataAccess.Site.findById('0983c6da-0dee-45cc-b897-3f1fed6b460b');
console.log(site?.getBaseURL());
配置
createDataAccess 配置
postgrestUrl(需要): PostgREST 服务器的基础 URLpostgrestSchema(可选) Postgres schema 由 PostgREST 曝光, 默认publicpostgrestApiKey(可选) : 添加为apikey财务报告和审定财务报表Authorization: Bearer ...postgrestHeaders(可选): PostgREST 客户端的额外信头s3Bucket(可选) : 仅限Configuration实体region(可选):S3客户端的 AWS 区域
自定义 PostgREST 客户端
您可以输入已经构建的 PostgREST 客户端作为第三个参数 :
import { PostgrestClient } from '@supabase/postgrest-js';
import { createDataAccess } from '@adobe/spacecat-shared-data-access';
const client = new PostgrestClient(process.env.POSTGREST_URL, { schema: 'public' });
const dataAccess = createDataAccess({ postgrestUrl: process.env.POSTGREST_URL }, console, client);
包装使用
默认导出是附加的包件 context.dataAccess。 。 。 .
import wrap from '@adobe/helix-shared-wrap';
import dataAccessWrapper from '@adobe/spacecat-shared-data-access';
async function run(request, context) {
const { dataAccess } = context;
const site = await dataAccess.Site.findById(request.params.siteId);
return {
statusCode: site ? 200 : 404,
body: site ? site.toJSON() : { error: 'not found' },
};
}
export const main = wrap(run)
.with(dataAccessWrapper);
包装从 context.env数字 :
POSTGREST_URL(默认倒计时 :http://localhost:3000(中文(简体) )POSTGREST_SCHEMAPOSTGREST_API_KEYS3_CONFIG_BUCKETAWS_REGION
直接 PostREST 查询
用于查询不作为实体建模的 PostgREST 表格(例如分析视图、报告表格) postgrestClient 可在 dataAccess.services数字 :
const { Site } = dataAccess; // entity collections
const { postgrestClient } = dataAccess.services; // raw PostgREST client
// Use entity collections as usual
const site = await Site.findById(siteId);
// Direct queries against non-entity tables
const { data, error } = await postgrestClient
.from('brand_presence_executions')
.select('execution_date, visibility_score, sentiment')
.eq('site_id', siteId)
.gte('execution_date', '2025-01-01')
.order('execution_date', { ascending: false })
.limit(100);
这是相同的 @supabase/postgrest-js PostgrestClient 用于实体收藏的内部实例。 查询构建器链可获取完整的 IDE 自动完成 .
URL 索引
源-URL指数表opportunity_urls, (中文(简体) ), suggestion_urls),不直接查询客户端——使用共享 syncUrlIndex 编号 : lookupEntityIdsByUrl 帮助器( 从软件包根导出) 因此, 写入和读取共享一个卡通化和一个存储路径 :
import {
syncUrlIndex, syncUrlIndexMany, lookupEntityIdsByUrl,
} from '@adobe/spacecat-shared-data-access';
const { postgrestClient } = dataAccess.services;
// writer (needs the postgrest_writer role): full-replace an entity's source URLs
await syncUrlIndex(postgrestClient, {
table: 'opportunity_urls', siteId, entityId, entityType, urls,
});
// writer, batched: sync many entities in one table without the per-entity fan-out
await syncUrlIndexMany(postgrestClient, {
table: 'suggestion_urls', siteId, entityType, entries: [{ entityId, urls }],
});
// reader: which opportunities/suggestions are backed by these URLs?
const matches = await lookupEntityIdsByUrl(postgrestClient, {
table: 'opportunity_urls', siteId, urls,
});
见包裹 CLAUDE.md ("URL Index"),用于舟曲化,单笔写作,和站点拓扑合同.
语义索引
矢量指数(opportunity_semantic_embedding, (中文(简体) ), suggestion_semantic_embedding)和全局查询-嵌入缓存(semantic_query_embedding),使用共享的帮助器,所以编剧和读者共享一个文本正常化,一个嵌入的一代和一个存储路径. 帮助者使用嵌入客户端 createEmbeddings(texts) (例如:. AzureEmbeddingClient 从 @adobe/spacecat-shared-gpt-client并进行嵌入本身.
import {
indexSemanticTopics, indexSemanticClaims, embedQueries,
lookupOpportunitiesByTopic, lookupSuggestionsByTopic, lookupSuggestionsByClaim,
SEMANTIC_TARGETS,
} from '@adobe/spacecat-shared-data-access';
const { postgrestClient } = dataAccess.services;
// writer (needs the postgrest_writer role): full-replace each (entity, matchFieldType) topic group
// in one call. Unchanged texts are kept; a new text reuses a vector already stored in the same
// table for the site, then the query cache, and is embedded otherwise (in batches); stale texts
// are deleted; fields not listed are left alone. texts: [] clears a field.
const result = await indexSemanticTopics(postgrestClient, embeddingClient, {
target: SEMANTIC_TARGETS.OPPORTUNITY, // or SEMANTIC_TARGETS.SUGGESTION
siteId,
entities: [{
entityId, entityType: 'cited-analysis',
fields: [{ matchFieldType: 'topic', texts: ['running shoes'] }],
}],
});
// result: { entities: [{ entityId, fields: [{ matchFieldType, rejected, inserted, deleted, unchanged, ... }] }],
// embedded, indexHits, cacheHits, indexError?, cacheError? }
// claim writer: same parameters and result, stored under the claim match type.
await indexSemanticClaims(postgrestClient, embeddingClient, {
target: SEMANTIC_TARGETS.SUGGESTION,
siteId,
entities: [{
entityId: suggestionId, entityType: 'cited-analysis',
fields: [
{ matchFieldType: 'title', texts: ['Publish a pricing comparison page'] },
{ matchFieldType: 'topic', texts: ['running shoes'] },
],
}],
});
// query side: validate (at most MAX_QUERY_TEXTS texts, each up to 2048 chars once normalized), read the
// cache (best-effort), embed the distinct misses in one call within a time budget.
// cacheWrites (touch hits + store misses) is best-effort; await it or hand it to waitUntil.
const { vectors, cacheWrites, cacheError } = await embedQueries(postgrestClient, embeddingClient, texts, { log });
// reader, batched: one best-first list of { entityId, entityType, score } per query vector.
// matchFieldTypes / entityTypes / statuses are optional filters (omitted or [] means no filter).
// statuses is applied before k, so filtered-out rows never take result slots.
// Searches run in the SEMANTIC_MATCHING_CONFIG.embedding generation, like the writer.
const opportunities = await lookupOpportunitiesByTopic(postgrestClient, {
siteId, matchFieldTypes: ['topic'], entityTypes: ['cited-analysis'],
statuses: ['NEW', 'IN_PROGRESS'], vectors, k: 10, minScore: 0.5,
});
// suggestions: entityTypes are parent opportunity types, statuses are Suggestion.STATUSES,
// opportunityStatuses filter on the parent opportunity.
const suggestions = await lookupSuggestionsByTopic(postgrestClient, {
siteId, opportunityStatuses: ['NEW'], vectors,
});
// claim reader: same parameters, over the claim rows.
const claimMatches = await lookupSuggestionsByClaim(postgrestClient, {
siteId, matchFieldTypes: ['title'], vectors,
});
匹配类型( N)topic, (中文(简体) ), claim)永远不是一个参数:每个维度都有自己的作家和读者(indexSemanticTopics 编号 : ...ByTopic, (中文(简体) ), indexSemanticClaims 编号 : ...ByClaim),设置 match_type 内部,所以一个话题搜索从不看到权利行,反之亦然. 尽管今天只有建议有权利主张读者. matchFieldType (文字来自哪个字段)和 entityType 是free-form,长度限制字符串:没有注册,所以一个生产者可以在没有共享发布的情况下索引一个新的字段或实体类型.
SEMANTIC_MATCHING_CONFIG.embedding (单位:千美元)model, (中文(简体) ), dims)是编剧和读者都使用的嵌入式一代;编剧, embedQueries 观察者从那里读出来 而不是把它当作参数 它是一个代码常数,而不是env配置,所以编剧和读者都同意,只要两者运行相同的数据访问版本;改变它意味着重新嵌入加升级两个消费者. 被再嵌入是自动的:编剧将另一代的行视为 Stale 并覆盖在实体的下一个刷新上.
编译器重新使用它已经存储在同一个表格中用于站点的向量(任何实体,匹配类型或字段,仅是当前世代),因此在其主题后索引一个建议的要求不会两次嵌入;它不会读取另一个目标表. 它读取了查询缓存, 但从未写; 只有 embedQueries 已经 低级缓存帮助器是内部的,因此任何外部的数据访问都无法写出作者和读者所信任的向量.
电话是内部分组的(分批256个,写作和搜索为20个组,id/hash读取和删除为50个组),因此呼叫者通过整个列表,而不是每个项目循环.
实地绘图行为
公共模型 API 仍为骆驼Case,而 Postgres/ PostgREST 表格则为蛇 case.
实例:
site.siteId< - > (中文(简体) )sites.idsite.baseURL< - > (中文(简体) )sites.base_url
绘图工作由基础PostgREST公用事业处理,并应用于读写路径.
实体
目前出口的实体包括:
ApiKeyAsyncJobAuditAuditUrlConfigurationEntitlementExperimentFixEntityFixEntitySuggestionImportJobImportUrlKeyEventLatestAuditOpportunityOrganizationPageCitabilityPageIntentProjectReportScrapeJobScrapeUrlSentimentGuidelineSentimentTopicSiteSiteCandidateSiteEnrollmentSiteTopFormSiteTopPageSuggestionTrialUserTrialUserActivity
现场投递
这个 deliveryConfig object上的站点存储发送基础设施细节。 它是一个灵活的 JSON 对象(type: 'any'——未知财产被保留(未剥取).
| 财产 | 类型 | 说明 |
|---|---|---|
programId | 字符串 | 云管理程序 ID |
environmentId | 字符串 | 云管理器环境 ID( 仅 AEM CS) |
authorURL | 字符串 | AEM 作者实例 URL( 例如, ), https://author-p12345-e67890.adobeaemcloud.com(中文(简体) ) |
siteId | 字符串 | 空间猫站点编号 |
imsOrgId | 字符串 | Adobe IMS 组织 客户身份识别 |
tenantId | 字符串 | 云管理器租户标识符(来自 /program/{programId} 答复) |
ipAllowlistExists | 布尔 | CM 程序是否配置了真正的 IP 允许列表( 不包括默认) 0.0.0.0/32 条目) |
preferContentApi | 布尔 | 是否为内容检索选择内容 API |
contentSourcePath | 字符串 | 网站的 AEM 内容根路径 。 曾分拆多个共享同名"云管理器"程序及环境的网站. 相应 /content/<site-name> 在 AEM 仓库中. |
站点朗姆酒
这个 rumConfig 对象在 Site 跟踪 Real User Monitor (RUM) 域键可用性。 它被自动设定在网站创建上,并由每周更新 rum-config-refresh 审计主管.
| 财产 | 类型 | 说明 |
|---|---|---|
hasDomainKey | 布尔 | 该网站的域是否注册了活动 RUM 域名密钥 |
lastCheckedAt | 字符串( ISO 8601) | 最近的 RUM 域名密钥检查的时间戳 |
配置模型方法
| 方法 | 回返 | 说明 |
|---|---|---|
getRumConfig() | { hasDomainKey, lastCheckedAt } | undefined | 返回当前朗姆花序,或 undefined 尚未设定的 |
hasRumDomainKey() | boolean | 回返 true 如果一个 RUM 域名键激活, false 否则 |
updateRumConfig(hasDomainKey) | void | 设定 hasDomainKey 和最新情况 lastCheckedAt 到当前时间 |
创建于 rum-config-refresh 处理器运行将没有 rumConfig 密钥 - 呼叫者必须治疗 undefined 作为"未知"而不是"没有设置".
建筑
Lambda / ECS service
-> @adobe/spacecat-shared-data-access (this package)
-> @supabase/postgrest-js
-> mysticat-data-service (PostgREST + Aurora PostgreSQL)
https://github.com/adobe/mysticat-data-service
v2(已退休): ElectroDB - > DynamoDB (直接, schema- in-code) 电子数据库 - > DynamoDB v3(当前): PostgREST 客户端 - > 神秘数据服务 (数据库中)
数据库计划(表、索引、图表、赠款) 神秘数据服务 作为dbmate迁移. 这个软件包提供了JavaScript模型/收集层,将骆驼Case实体映射到蛇 case PostgREST API.
V3 行为说明
Configuration在v3中仍为S3支持.KeyEvent在v3中被贬值,并故意放弃访问/模拟方法.LatestAuditv3 中为虚拟,并衍生自Audit查询(不需要专用表格).
正在变化的实体
添加或修改一个实体现在需要更改 两个储存库数字 :
1. 数据库计划 - 神秘数据服务
创建用于计划变化的 dmate 迁移( 表格、 列、 索引、 赠款、 图表) :
# In mysticat-data-service
make migrate-new name=add_foo_column_to_sites
# Edit db/migrations/YYYYMMDDHHMMSS_add_foo_column_to_sites.sql
make migrate
docker compose -f docker/docker-compose.yml restart postgrest
make test
见 神秘数据服务 移民公约(所需赠款、指数、评论等).
2. 模型/收集层——本套件
更新实体计划、模型和/或收藏 src/models/<entity>/数字 :
| 文件 | 改变什么 |
|---|---|
<entity>.schema.js | 添加/修改属性、参考文献、索引 |
<entity>.model.js | 添加业务逻辑方法 |
<entity>.collection.js | 添加自定义查询方法 |
添加新的属性示例 :
// In <entity>.schema.js, add to the SchemaBuilder chain:
.addAttribute('myNewField', {
type: 'string',
required: false,
// Optional: custom DB column name (default: camelToSnake)
// postgrestField: 'custom_column_name',
})
此自动生成 getMyNewField() 财务报告和审定财务报表 setMyNewField() 在模型上.
添加新实体 : 在任何现有的实体文件夹中按照模式创建 4 文件 :
<entity>.schema.js- SchemaBuilder定义<entity>.model.js- 延伸BaseModel<entity>.collection.js- 延伸BaseCollectionindex.js——再出口型号,收集型,计划型
然后在 src/models/index.js。 。 。 .
3. 整合测试全叠
# In this package — runs PostgREST in Docker
npm run test:it
综合测试 mysticat-data-service 来自ECR的 Docker 图像,所以新的 schema 更改必须首先作为新的图像标记(或针对本地 PostgREST 的测试)发布.
从 V2 移动
如果您正在从基于 DynamoDB/ ElectroDB 的 v2 升级 :
什么还是一样
- 你还在用
createDataAccess(...)。 。 。 . - 您仍然通过
dataAccess.<Entity>(例如dataAccess.Site) (中文(简体) ). - 模式/收集API旨在为服务呼叫者保持稳定.
什么变化
- 后置商店现在通过PostgREST,而不是DynamoDB/ElectroDB.
- 您必须提供
postgrestUrl(或POSTGREST_URL(通过包装箱). - 变换时速 神秘数据服务 迁移,而不是代码.
Configuration由S3支持(要求)s3Bucket编号 :S3_CONFIG_BUCKET(在使用时).KeyEvent在v3中贬值,现在丢出.LatestAudit不再是专用的表格,而是从Audit查询.
需要的环境/配置更新
- 将旧的迪纳摩组合替换为:
POSTGREST_URL- 选项
POSTGREST_SCHEMA - 选项
POSTGREST_API_KEY
- 仅在使用时保留 S3 配置
Configuration数字 :S3_CONFIG_BUCKETAWS_REGION
发展
地方发展
第一次设置
从单列波根:
npm install
可选: 从此工作空间校验软件包工具 :
cd packages/spacecat-shared-data-access
node -v
npm -v
日常工作流程
- 创建/ 切换到特性分支 .
- 在
src/试验test/unit财务报告和审定财务报表test/it。 。 。 . - 运行单元测试在延时.
- 在打开/合并公关前进行整合测试.
- 处理好问题.
常见命令(来自 packages/spacecat-shared-data-access(中文(简体) )
运行单元测试
npm test
用调试器运行单元测试
npm run test:debug
运行集成测试
npm run test:it
运行线
npm run lint
自动补丁问题
npm run lint:fix
清洁本地安装文物
npm run clean
整合套房 test/it 以 PostgREST 为主,并通过 Docker 运行.
整合测试
集成测试通过 Docker 编译并执行本地 Postgres + PostgREST 堆栈
摩卡套房在下面 test/it。 。 。 .
先决条件
- Docker 桌面( 或者等同的 Docker 守护进程)
- AWS CLI 配置了可以访问 Spacecat 开发 AWS 账户的证书 ECR寄存器( 仅在拉出默认的 ECR 图像时需要)
IT 带使用的默认图像
- 仓库 :
682033462621.dkr.ecr.us-east-1.amazonaws.com/mysticat-data-service - 标记 :
v1.11.0(通过env var 管理)
认证 Docker 到 ECR
默认图像在 ECR repo 中 :
- 空间猫发展(AWS3338)
如果你是第一次设置这个:
- 获取 AWS 证书 空间猫发展(AWS3338) 从
klam.corp.adobe.com。 。 。 . - 把它们添加到
~/.aws/credentials在您选择的配置名称下. - 在 ECR 登录命令中使用此配置文件 .
示例 ~/.aws/credentials 条目 :
[spacecat-dev]
aws_access_key_id = <your-access-key-id>
aws_secret_access_key = <your-secret-access-key>
仓库 :
682033462621.dkr.ecr.us-east-1.amazonaws.com/mysticat-data-service
然后认证 Docker 到 ECR :
aws ecr get-login-password --profile spacecat-dev --region us-east-1 \
| docker login --username AWS --password-stdin 682033462621.dkr.ecr.us-east-1.amazonaws.com
运行
npm run test:it
有用的覆盖
MYSTICAT_DATA_SERVICE_TAG: 覆盖图像标记( 推荐为版本的凸起)MYSTICAT_DATA_SERVICE_REPOSITORY: 覆盖图像存储器MYSTICAT_DATA_SERVICE_PLATFORM: 覆盖容器平台( 默认)linux/amd64(中文(简体) )IT_POSTGREST_PORT: 覆盖已曝光的 PostgREST 端口( 默认)3300(中文(简体) )IT_POSTGRES_PORT: 覆盖已曝光的 Postgres 端口( 默认)55432(中文(简体) )
export MYSTICAT_DATA_SERVICE_TAG=v1.11.0
export MYSTICAT_DATA_SERVICE_PLATFORM=linux/amd64
# optional if repository changes
export MYSTICAT_DATA_SERVICE_REPOSITORY=682033462621.dkr.ecr.us-east-1.amazonaws.com/mysticat-data-service
类型脚本
类型定义来自:
src/index.d.tssrc/service/index.d.ts—— 说DataAccess财务报告和审定财务报表DataAccessServices接口src/models/**/index.d.ts——实体收集和模型接口
这个 DataAccess 接口为所有实体集合提供完整的打字功能 services.postgrestClient (类型为: PostgrestClient 从 @supabase/postgrest-js) (中文(简体) ).
在 TS 项目中直接使用软件包;不需要额外的设置.
许可证
阿帕奇 - 2.0