@undefineds.co/drizzle-solid 0.2.11 → 0.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +255 -439
- package/dist/core/expressions.d.ts +4 -0
- package/dist/core/expressions.d.ts.map +1 -1
- package/dist/core/expressions.js +8 -1
- package/dist/core/expressions.js.map +1 -1
- package/dist/core/order-by.d.ts +9 -0
- package/dist/core/order-by.d.ts.map +1 -0
- package/dist/core/order-by.js +20 -0
- package/dist/core/order-by.js.map +1 -0
- package/dist/core/pod-database.d.ts +6 -0
- package/dist/core/pod-database.d.ts.map +1 -1
- package/dist/core/pod-database.js +121 -31
- package/dist/core/pod-database.js.map +1 -1
- package/dist/core/pod-dialect.d.ts +10 -1
- package/dist/core/pod-dialect.d.ts.map +1 -1
- package/dist/core/pod-dialect.js +81 -2
- package/dist/core/pod-dialect.js.map +1 -1
- package/dist/core/query-builders/delete-query-builder.d.ts +7 -12
- package/dist/core/query-builders/delete-query-builder.d.ts.map +1 -1
- package/dist/core/query-builders/delete-query-builder.js +47 -20
- package/dist/core/query-builders/delete-query-builder.js.map +1 -1
- package/dist/core/query-builders/helpers.d.ts +7 -0
- package/dist/core/query-builders/helpers.d.ts.map +1 -1
- package/dist/core/query-builders/helpers.js +169 -0
- package/dist/core/query-builders/helpers.js.map +1 -1
- package/dist/core/query-builders/insert-query-builder.d.ts +10 -3
- package/dist/core/query-builders/insert-query-builder.d.ts.map +1 -1
- package/dist/core/query-builders/insert-query-builder.js +50 -6
- package/dist/core/query-builders/insert-query-builder.js.map +1 -1
- package/dist/core/query-builders/select-query-builder.d.ts +29 -1
- package/dist/core/query-builders/select-query-builder.d.ts.map +1 -1
- package/dist/core/query-builders/select-query-builder.js +405 -85
- package/dist/core/query-builders/select-query-builder.js.map +1 -1
- package/dist/core/query-builders/types.d.ts +5 -3
- package/dist/core/query-builders/types.d.ts.map +1 -1
- package/dist/core/query-builders/update-query-builder.d.ts +8 -12
- package/dist/core/query-builders/update-query-builder.d.ts.map +1 -1
- package/dist/core/query-builders/update-query-builder.js +63 -21
- package/dist/core/query-builders/update-query-builder.js.map +1 -1
- package/dist/core/query-conditions.d.ts +17 -16
- package/dist/core/query-conditions.d.ts.map +1 -1
- package/dist/core/query-conditions.js.map +1 -1
- package/dist/core/resource-resolver/base-resolver.d.ts +8 -4
- package/dist/core/resource-resolver/base-resolver.d.ts.map +1 -1
- package/dist/core/resource-resolver/base-resolver.js +39 -36
- package/dist/core/resource-resolver/base-resolver.js.map +1 -1
- package/dist/core/resource-resolver/document-resolver.d.ts.map +1 -1
- package/dist/core/resource-resolver/document-resolver.js +75 -78
- package/dist/core/resource-resolver/document-resolver.js.map +1 -1
- package/dist/core/schema/pod-table.d.ts +1 -0
- package/dist/core/schema/pod-table.d.ts.map +1 -1
- package/dist/core/schema/pod-table.js +67 -0
- package/dist/core/schema/pod-table.js.map +1 -1
- package/dist/core/select-plan.d.ts +6 -3
- package/dist/core/select-plan.d.ts.map +1 -1
- package/dist/core/sparql/builder/expression-builder.d.ts +10 -0
- package/dist/core/sparql/builder/expression-builder.d.ts.map +1 -1
- package/dist/core/sparql/builder/expression-builder.js +27 -4
- package/dist/core/sparql/builder/expression-builder.js.map +1 -1
- package/dist/core/sparql/builder/select-builder.d.ts +9 -0
- package/dist/core/sparql/builder/select-builder.d.ts.map +1 -1
- package/dist/core/sparql/builder/select-builder.js +147 -2
- package/dist/core/sparql/builder/select-builder.js.map +1 -1
- package/dist/core/utils/debug-logger.d.ts +19 -0
- package/dist/core/utils/debug-logger.d.ts.map +1 -0
- package/dist/core/utils/debug-logger.js +59 -0
- package/dist/core/utils/debug-logger.js.map +1 -0
- package/dist/driver.d.ts +4 -0
- package/dist/driver.d.ts.map +1 -1
- package/dist/driver.js +1 -0
- package/dist/driver.js.map +1 -1
- package/dist/esm/core/expressions.d.ts +4 -0
- package/dist/esm/core/expressions.d.ts.map +1 -1
- package/dist/esm/core/expressions.js +6 -0
- package/dist/esm/core/expressions.js.map +1 -1
- package/dist/esm/core/order-by.d.ts +9 -0
- package/dist/esm/core/order-by.d.ts.map +1 -0
- package/dist/esm/core/order-by.js +15 -0
- package/dist/esm/core/order-by.js.map +1 -0
- package/dist/esm/core/pod-database.d.ts +6 -0
- package/dist/esm/core/pod-database.d.ts.map +1 -1
- package/dist/esm/core/pod-database.js +122 -32
- package/dist/esm/core/pod-database.js.map +1 -1
- package/dist/esm/core/pod-dialect.d.ts +10 -1
- package/dist/esm/core/pod-dialect.d.ts.map +1 -1
- package/dist/esm/core/pod-dialect.js +81 -2
- package/dist/esm/core/pod-dialect.js.map +1 -1
- package/dist/esm/core/query-builders/delete-query-builder.d.ts +7 -12
- package/dist/esm/core/query-builders/delete-query-builder.d.ts.map +1 -1
- package/dist/esm/core/query-builders/delete-query-builder.js +48 -21
- package/dist/esm/core/query-builders/delete-query-builder.js.map +1 -1
- package/dist/esm/core/query-builders/helpers.d.ts +7 -0
- package/dist/esm/core/query-builders/helpers.d.ts.map +1 -1
- package/dist/esm/core/query-builders/helpers.js +164 -0
- package/dist/esm/core/query-builders/helpers.js.map +1 -1
- package/dist/esm/core/query-builders/insert-query-builder.d.ts +10 -3
- package/dist/esm/core/query-builders/insert-query-builder.d.ts.map +1 -1
- package/dist/esm/core/query-builders/insert-query-builder.js +50 -6
- package/dist/esm/core/query-builders/insert-query-builder.js.map +1 -1
- package/dist/esm/core/query-builders/select-query-builder.d.ts +29 -1
- package/dist/esm/core/query-builders/select-query-builder.d.ts.map +1 -1
- package/dist/esm/core/query-builders/select-query-builder.js +407 -87
- package/dist/esm/core/query-builders/select-query-builder.js.map +1 -1
- package/dist/esm/core/query-builders/types.d.ts +5 -3
- package/dist/esm/core/query-builders/types.d.ts.map +1 -1
- package/dist/esm/core/query-builders/update-query-builder.d.ts +8 -12
- package/dist/esm/core/query-builders/update-query-builder.d.ts.map +1 -1
- package/dist/esm/core/query-builders/update-query-builder.js +64 -22
- package/dist/esm/core/query-builders/update-query-builder.js.map +1 -1
- package/dist/esm/core/query-conditions.d.ts +17 -16
- package/dist/esm/core/query-conditions.d.ts.map +1 -1
- package/dist/esm/core/query-conditions.js.map +1 -1
- package/dist/esm/core/resource-resolver/base-resolver.d.ts +8 -4
- package/dist/esm/core/resource-resolver/base-resolver.d.ts.map +1 -1
- package/dist/esm/core/resource-resolver/base-resolver.js +39 -36
- package/dist/esm/core/resource-resolver/base-resolver.js.map +1 -1
- package/dist/esm/core/resource-resolver/document-resolver.d.ts.map +1 -1
- package/dist/esm/core/resource-resolver/document-resolver.js +75 -78
- package/dist/esm/core/resource-resolver/document-resolver.js.map +1 -1
- package/dist/esm/core/schema/pod-table.d.ts +1 -0
- package/dist/esm/core/schema/pod-table.d.ts.map +1 -1
- package/dist/esm/core/schema/pod-table.js +66 -0
- package/dist/esm/core/schema/pod-table.js.map +1 -1
- package/dist/esm/core/select-plan.d.ts +6 -3
- package/dist/esm/core/select-plan.d.ts.map +1 -1
- package/dist/esm/core/sparql/builder/expression-builder.d.ts +10 -0
- package/dist/esm/core/sparql/builder/expression-builder.d.ts.map +1 -1
- package/dist/esm/core/sparql/builder/expression-builder.js +27 -4
- package/dist/esm/core/sparql/builder/expression-builder.js.map +1 -1
- package/dist/esm/core/sparql/builder/select-builder.d.ts +9 -0
- package/dist/esm/core/sparql/builder/select-builder.d.ts.map +1 -1
- package/dist/esm/core/sparql/builder/select-builder.js +148 -3
- package/dist/esm/core/sparql/builder/select-builder.js.map +1 -1
- package/dist/esm/core/utils/debug-logger.d.ts +19 -0
- package/dist/esm/core/utils/debug-logger.d.ts.map +1 -0
- package/dist/esm/core/utils/debug-logger.js +53 -0
- package/dist/esm/core/utils/debug-logger.js.map +1 -0
- package/dist/esm/driver.d.ts +4 -0
- package/dist/esm/driver.d.ts.map +1 -1
- package/dist/esm/driver.js +1 -0
- package/dist/esm/driver.js.map +1 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +3 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/package.json +17 -6
package/README.md
CHANGED
|
@@ -1,563 +1,379 @@
|
|
|
1
|
-
#
|
|
1
|
+
# drizzle-solid
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`drizzle-solid` is a Drizzle-aligned adapter for application-owned data in Solid Pods.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
It helps you define typed models, map them to RDF resources/documents/IRIs, and read or write them with a familiar query builder — without pretending Solid is a relational database.
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
- 🧭 **Drizzle 对齐**: 沿用 Drizzle ORM 的查询构建器与错误形态,降低迁移成本
|
|
9
|
-
- 🌐 **Solid 实测**: CSS 集成测试覆盖 CRUD、条件组合、聚合与联结场景
|
|
10
|
-
- 🔁 **智能回退**: SQL 查询自动转换为 SPARQL;当 CSS/Comunica 无法处理过滤器或聚合时由方言拉取数据并在内存中回放
|
|
11
|
-
- 🔧 **灵活映射**: 自定义命名空间、谓词和列类型(字符串、数字、布尔、时间、JSON/Object)
|
|
7
|
+
The library is intentionally **document-first**, **IRI-native**, **Pod-aware**, and **SPARQL-native**.
|
|
12
8
|
|
|
13
|
-
##
|
|
9
|
+
## What it helps with
|
|
14
10
|
|
|
15
|
-
|
|
11
|
+
- Defining typed application data for Solid Pods
|
|
12
|
+
- Mapping fields to RDF predicates and resource layouts
|
|
13
|
+
- Using a Drizzle-style CRUD/query builder over Pod data
|
|
14
|
+
- Performing exact entity operations through full IRIs
|
|
15
|
+
- Building discovery, federation, and notification flows on top of Solid-native storage
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
## Design principles
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
| **条件查询** (where) | Comunica 读文件到内存 | 索引下推(单 Pod) |
|
|
25
|
-
| **聚合函数** (count/sum/avg) | Comunica 读文件到内存 | 索引下推(单 Pod) |
|
|
26
|
-
| **GROUP BY / JOIN** | Comunica 读文件到内存 | 索引下推(单 Pod) |
|
|
27
|
-
| **联邦查询** (跨 Pod) | Comunica 内存处理 | Comunica 内存处理 |
|
|
28
|
-
| **SPARQL 端点** | ❌ 不支持 | ✅ `/-/sparql` Sidecar |
|
|
29
|
-
| **SPARQL 1.1 覆盖率** | - | ~90% |
|
|
19
|
+
- **Document-first**: model around Solid resources and document boundaries
|
|
20
|
+
- **IRI-native**: treat IRIs as first-class identity, not as an afterthought over local IDs
|
|
21
|
+
- **Pod-aware**: keep resource layout, links, network, and access constraints explicit
|
|
22
|
+
- **Drizzle-aligned**: stay close to Drizzle's builder surface and ergonomics where it makes sense
|
|
23
|
+
- **SPARQL-native**: the native query model is SPARQL/Solid, not SQL emulation
|
|
30
24
|
|
|
31
|
-
|
|
25
|
+
## What it is
|
|
32
26
|
|
|
33
|
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
- ⚠️ 所有查询由 Comunica 客户端在内存中执行
|
|
27
|
+
- A typed persistence layer for application-owned data in Solid Pods
|
|
28
|
+
- A Drizzle-style API surface over RDF resources and documents
|
|
29
|
+
- A practical bridge for teams migrating Drizzle mental models into Solid-native data flows
|
|
37
30
|
|
|
38
|
-
|
|
31
|
+
## What it isn't
|
|
39
32
|
|
|
40
|
-
|
|
33
|
+
- Not a general-purpose SQL ORM
|
|
34
|
+
- Not a universal abstraction over arbitrary RDF on the open web
|
|
35
|
+
- Not a graph database or SPARQL engine replacement
|
|
36
|
+
- Not a black box that hides Pod boundaries, permissions, or network behavior
|
|
37
|
+
- Not a promise that every SQL/driver-specific Drizzle feature maps cleanly to Solid
|
|
41
38
|
|
|
42
|
-
|
|
43
|
-
- ✅ **Sidecar API**:`/-/sparql` 端点,权限继承自资源路径
|
|
44
|
-
- ✅ **混合存储**:RDF → QuintStore 索引,非 RDF → 文件系统
|
|
45
|
-
- ⚠️ **联邦查询**:跨 Pod 查询仍需 Comunica 内存处理
|
|
46
|
-
- ⚠️ **SPARQL 覆盖**:约 90% W3C SPARQL 1.1 测试通过
|
|
39
|
+
## Core concepts
|
|
47
40
|
|
|
48
|
-
|
|
41
|
+
### Resource
|
|
49
42
|
|
|
50
|
-
|
|
43
|
+
A resource is the persisted unit in a Pod, usually a Turtle or JSON-LD document addressed by URL.
|
|
51
44
|
|
|
52
|
-
###
|
|
45
|
+
### Document
|
|
53
46
|
|
|
54
|
-
|
|
55
|
-
yarn add drizzle-solid
|
|
56
|
-
```
|
|
47
|
+
A document may contain one entity or many entities. Document layout is part of application design, not an implementation detail.
|
|
57
48
|
|
|
58
|
-
###
|
|
49
|
+
### Entity
|
|
59
50
|
|
|
60
|
-
|
|
61
|
-
import { drizzle } from 'drizzle-solid';
|
|
62
|
-
import { podTable, string, int } from 'drizzle-solid';
|
|
63
|
-
import { Session } from '@inrupt/solid-client-authn-node';
|
|
51
|
+
An entity is a typed RDF subject stored in a document. It is closer to a linked-data subject than to a relational row.
|
|
64
52
|
|
|
65
|
-
|
|
66
|
-
const profileTable = podTable('profile', {
|
|
67
|
-
name: string('name'),
|
|
68
|
-
email: string('email'),
|
|
69
|
-
age: int('age')
|
|
70
|
-
}, {
|
|
71
|
-
// 相对 Pod 根的容器/资源路径,不需要 idp:// 前缀
|
|
72
|
-
base: '/profiles/',
|
|
73
|
-
type: 'https://schema.org/Person'
|
|
74
|
-
});
|
|
53
|
+
### IRI
|
|
75
54
|
|
|
76
|
-
|
|
77
|
-
const logsTable = podTable('logs', {
|
|
78
|
-
id: string('id').primaryKey(),
|
|
79
|
-
message: string('message')
|
|
80
|
-
}, {
|
|
81
|
-
type: 'https://schema.org/DigitalDocument',
|
|
82
|
-
base: '/logs/',
|
|
83
|
-
subjectTemplate: '{yyyy}/{MM}/{dd}/{id}.ttl'
|
|
84
|
-
});
|
|
55
|
+
IRIs are native identity. `drizzle-solid` keeps the full subject IRI available and provides explicit IRI APIs for exact operations.
|
|
85
56
|
|
|
86
|
-
|
|
87
|
-
// - 作用:控制每条记录主体 URI 的形态,支持占位符 {yyyy}/{MM}/{dd}/{HH}/{mm}/{ss}/{id}。
|
|
88
|
-
// - 推断:未指定时按 base 判断:base 是容器(以 / 结尾且包含表名)→ document 模式;base 是具体文件 → fragment 模式。模板含 #(如 '#{id}')也强制 fragment;其他模板按 document 处理,可用 {yyyy}/{MM}/{dd}/{id}.ttl 等。
|
|
89
|
-
// - document 模式:每条记录独立文件,URI 形如 .../users/{id}.ttl,SELECT/UPDATE/DELETE 由 Comunica dereference 目标文件后本地求值;适合记录级隔离/按目录分片。
|
|
90
|
-
// - fragment 模式:多条记录共享同一文件,主体是片段(.../file.ttl#{id}),SELECT 同样由 Comunica 拉取该文件求值;适合小表或聚合存放的数据。
|
|
91
|
-
// - 示例:subjectTemplate: '{yyyy}/{MM}/{dd}/{id}.ttl'(document 分日期分片);subjectTemplate: '#{id}'(fragment,共享文件)。
|
|
92
|
-
|
|
93
|
-
// 创建数据库连接
|
|
94
|
-
const session = new Session(); // 已认证的session
|
|
95
|
-
const db = drizzle(session);
|
|
57
|
+
### Link
|
|
96
58
|
|
|
97
|
-
|
|
98
|
-
await db.init([profileTable]);
|
|
59
|
+
Links between entities are represented as IRIs and may cross documents or Pods.
|
|
99
60
|
|
|
100
|
-
|
|
101
|
-
const profiles = await db.select().from(profileTable);
|
|
61
|
+
### Model
|
|
102
62
|
|
|
103
|
-
|
|
104
|
-
await db.insert(profileTable).values({
|
|
105
|
-
name: 'Alice',
|
|
106
|
-
email: 'alice@example.com',
|
|
107
|
-
age: 30
|
|
108
|
-
});
|
|
63
|
+
`drizzle-solid` supports two modeling styles:
|
|
109
64
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
id: string('id').primaryKey(),
|
|
113
|
-
title: string('title'),
|
|
114
|
-
}, {
|
|
115
|
-
type: 'https://schema.org/CreativeWork',
|
|
116
|
-
base: '/data/posts.ttl', // 资源路径
|
|
117
|
-
sparqlEndpoint: '/data/posts.ttl/-/sparql' // xpod Sidecar 模式端点
|
|
118
|
-
// 💡 xpod 使用 Sidecar 模式:在资源路径后添加 /-/sparql 后缀
|
|
119
|
-
// - 文件资源: /data/posts.ttl/-/sparql
|
|
120
|
-
// - 容器: /data/users/-/sparql
|
|
121
|
-
});
|
|
122
|
-
await db.init([sparqlTable]); // 不会创建容器,直接使用端点执行 SPARQL UPDATE/SELECT
|
|
123
|
-
await db.insert(sparqlTable).values({ id: 'post-1', title: 'Hello SPARQL' });
|
|
65
|
+
- `podTable(...)`: schema plus concrete storage location
|
|
66
|
+
- `solidSchema(...)` + `db.createTable(...)`: reusable schema separated from where data is stored
|
|
124
67
|
|
|
125
|
-
###
|
|
68
|
+
### Pod client
|
|
126
69
|
|
|
127
|
-
|
|
70
|
+
`drizzle(session)` creates the database client bound to an authenticated Solid session.
|
|
128
71
|
|
|
129
|
-
|
|
130
|
-
* **定义**: 当表的 `base` 指向一个具体资源 URL (例如 `/data/posts.ttl`),或者 `subjectTemplate` 使用片段 (例如 `'#{id}'`) 时,Drizzle Solid 会将所有数据操作集中在 `base` 对应的单一 Named Graph (`GRAPH <base_url>`) 中。
|
|
131
|
-
* **行为**: 在此模式下,LDP 读写与 SPARQL 读写**完全兼容且互操作**。LDP 对 `base` 资源的 PATCH/GET 操作,与 SPARQL 对 `GRAPH <base_url>` 的 DML/SELECT 操作,都作用于相同的数据集,并能互相可见。**这是 SPARQL Endpoint 模式下的推荐用法**。
|
|
132
|
-
* **示例**: `base: '/data/posts.ttl'`, `subjectTemplate: '#{id}'`。所有 posts 都存储在 `posts.ttl` 文件对应的 Named Graph 中。
|
|
72
|
+
## Modeling vs persistence
|
|
133
73
|
|
|
134
|
-
|
|
135
|
-
* **定义**: 当表的 `base` 指向一个容器 URL (例如 `/data/users/`),且 `subjectTemplate` 生成文件路径 (例如 `'{id}.ttl'`) 时。
|
|
136
|
-
* **读操作 (SELECT)**: SPARQL SELECT 查询指向容器的 `/sparql` 端点时,Community Solid Server (CSS) 会**聚合该容器内所有 LDP 子文件**的数据,形成一个统一的视图。因此,通过 LDP PUT 写入的子文件 (例如 `alice.ttl`),**可以被 SPARQL SELECT 查询到**。
|
|
137
|
-
* **写操作 (DML)**: SPARQL INSERT/UPDATE/DELETE 操作是针对**容器的聚合 Named Graph** (`GRAPH <container_url>`)。这些操作**不会**直接创建、修改或删除 LDP 子文件。
|
|
138
|
-
* 例如,通过 SPARQL INSERT 写入 `bob`,数据会存在容器 Graph 中,但**不会**创建 `bob.ttl` 文件 (LDP GET `/data/users/bob.ttl` 将返回 404)。
|
|
139
|
-
* 通过 SPARQL UPDATE `alice`,只会修改容器 Graph 中的 `alice` 数据(SPARQL 视图),而**不会**更新 `alice.ttl` 文件本身 (LDP GET `/data/users/alice.ttl` 将显示旧数据)。
|
|
140
|
-
* **结论**: 在 Document Mode 下,SPARQL 读操作可以兼容 LDP 资源(因为聚合),但 **写操作与 LDP 资源的物理文件视图不兼容**。这意味着 LDP 客户端可能无法看到 SPARQL DML 操作的影响,反之亦然。**不建议混合使用 LDP Document Mode 和 SPARQL DML 操作**,除非您明确理解其数据存储的视图差异,并仅利用 SPARQL 作为聚合查询工具。
|
|
74
|
+
A Solid application usually needs to answer two different questions:
|
|
141
75
|
|
|
76
|
+
1. **What is the shape of my data?**
|
|
77
|
+
2. **Where does that data live in Pods?**
|
|
142
78
|
|
|
143
|
-
|
|
79
|
+
`drizzle-solid` keeps those concerns explicit.
|
|
144
80
|
|
|
145
|
-
|
|
81
|
+
- Use `podTable(...)` when your app owns the storage location and you want one place to define shape + location.
|
|
82
|
+
- Use `solidSchema(...)` when the same schema should be reused across different Pod locations, profiles, or discovered data sources.
|
|
83
|
+
- Use `db.createTable(schema, { base, ... })` when you want to bind a reusable schema to a concrete place at runtime.
|
|
146
84
|
|
|
147
|
-
|
|
85
|
+
This keeps Solid's persistence model visible instead of flattening everything into table-only abstractions.
|
|
148
86
|
|
|
149
|
-
|
|
87
|
+
## Why document-first
|
|
150
88
|
|
|
151
|
-
|
|
152
|
-
yarn example:quick
|
|
153
|
-
```
|
|
89
|
+
Solid stores data in resources, not in tables.
|
|
154
90
|
|
|
155
|
-
|
|
156
|
-
- 如何连接到 Solid Pod 并定义表结构
|
|
157
|
-
- 使用 Drizzle 风格 API 执行插入、查询、更新、删除
|
|
91
|
+
That means application design starts with questions like:
|
|
158
92
|
|
|
159
|
-
|
|
93
|
+
- which entities belong in the same document
|
|
94
|
+
- which entities should be split into separate resources
|
|
95
|
+
- which resources should be public, private, or shared
|
|
96
|
+
- how links should work across documents and Pods
|
|
160
97
|
|
|
161
|
-
|
|
162
|
-
yarn example:query
|
|
163
|
-
```
|
|
98
|
+
`drizzle-solid` follows that reality. It provides a structured API, but keeps resource identity and document layout as part of the model.
|
|
164
99
|
|
|
165
|
-
|
|
166
|
-
- 如何定义表之间的关系
|
|
167
|
-
- 使用 `with` 进行关联查询
|
|
168
|
-
- 嵌套数据的加载方式
|
|
100
|
+
## Quick start
|
|
169
101
|
|
|
170
|
-
###
|
|
102
|
+
### Install
|
|
171
103
|
|
|
172
104
|
```bash
|
|
173
|
-
yarn
|
|
105
|
+
yarn add @undefineds.co/drizzle-solid
|
|
106
|
+
# or
|
|
107
|
+
npm install @undefineds.co/drizzle-solid
|
|
174
108
|
```
|
|
175
109
|
|
|
176
|
-
|
|
177
|
-
- 如何使用 TypeIndex 自动发现数据位置
|
|
178
|
-
- SAI (Solid Application Interoperability) 数据授权
|
|
179
|
-
- 无需硬编码路径的数据访问
|
|
110
|
+
> In this repository, the examples import `drizzle-solid` through a local TypeScript path alias. In external applications, import the published package name: `@undefineds.co/drizzle-solid`.
|
|
180
111
|
|
|
181
|
-
|
|
112
|
+
### Define a model and run CRUD
|
|
182
113
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
114
|
+
```ts
|
|
115
|
+
import {
|
|
116
|
+
drizzle,
|
|
117
|
+
podTable,
|
|
118
|
+
string,
|
|
119
|
+
datetime,
|
|
120
|
+
eq,
|
|
121
|
+
} from '@undefineds.co/drizzle-solid';
|
|
122
|
+
|
|
123
|
+
const posts = podTable('posts', {
|
|
124
|
+
id: string('id').primaryKey(),
|
|
125
|
+
title: string('title').predicate('http://schema.org/headline'),
|
|
126
|
+
content: string('content').predicate('http://schema.org/text'),
|
|
127
|
+
createdAt: datetime('createdAt').predicate('http://schema.org/dateCreated'),
|
|
197
128
|
}, {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
type: 'https://schema.org/Person',
|
|
202
|
-
// 可选:注册 TypeIndex(仅在提供 typeIndex 时才会尝试)
|
|
203
|
-
typeIndex: 'private' // 'public' | 'private' | undefined
|
|
129
|
+
base: 'https://alice.example/data/posts/',
|
|
130
|
+
subjectTemplate: '{id}.ttl',
|
|
131
|
+
type: 'http://schema.org/CreativeWork',
|
|
204
132
|
});
|
|
205
133
|
|
|
206
|
-
|
|
207
|
-
await db.init([
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
- 使用 `.inverse()` 可以把列映射为 `<object> predicate <subject>` 方向,适合例如 `<org> schema:member <person>` 这样的反向边;查询/写入都会自动交换 RDF 三元组的主体和宾语。
|
|
211
|
-
|
|
212
|
-
### Drizzle 风格查询:`db.query` + `findByIRI`
|
|
213
|
-
|
|
214
|
-
将 schema 传入 `drizzle(session, { schema })` 后,可通过 Drizzle 对齐的查询助手调用:
|
|
134
|
+
const db = drizzle(session);
|
|
135
|
+
await db.init([posts]);
|
|
215
136
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
where: { verified: true },
|
|
222
|
-
orderBy: [{ column: schema.users.name, direction: 'asc' }],
|
|
223
|
-
with: {
|
|
224
|
-
posts: true // 根据子表 referenceTarget + @id 预加载关联行
|
|
225
|
-
}
|
|
137
|
+
await db.insert(posts).values({
|
|
138
|
+
id: 'post-1',
|
|
139
|
+
title: 'Hello Solid',
|
|
140
|
+
content: 'Stored as RDF in a Pod document.',
|
|
141
|
+
createdAt: new Date(),
|
|
226
142
|
});
|
|
227
143
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
144
|
+
const rows = await db.select()
|
|
145
|
+
.from(posts)
|
|
146
|
+
.where(eq(posts.id, 'post-1'));
|
|
231
147
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
- TypeIndex 注册策略:仅当表配置了 `typeIndex: 'private' | 'public'` 时才会尝试写入 TypeIndex;未配置则跳过。
|
|
236
|
-
|
|
237
|
-
### 支持的列类型
|
|
238
|
-
|
|
239
|
-
Drizzle Solid 完全兼容所有 Drizzle ORM 数据库方言的列类型:
|
|
240
|
-
|
|
241
|
-
#### 基础类型
|
|
242
|
-
```typescript
|
|
243
|
-
// 字符串类型
|
|
244
|
-
string('name') // 通用字符串
|
|
245
|
-
text('content') // 文本内容
|
|
246
|
-
varchar('title') // 可变长度字符串
|
|
247
|
-
char('code') // 固定长度字符串
|
|
248
|
-
|
|
249
|
-
// 数字类型
|
|
250
|
-
int('count') // MySQL 风格整数
|
|
251
|
-
integer('id') // PostgreSQL 风格整数
|
|
252
|
-
bigint('large') // 大整数
|
|
253
|
-
smallint('small') // 小整数
|
|
254
|
-
tinyint('tiny') // 微整数 (MySQL)
|
|
255
|
-
mediumint('medium') // 中等整数 (MySQL)
|
|
256
|
-
serial('auto') // 自增序列
|
|
257
|
-
|
|
258
|
-
// 浮点数类型(内部映射为 xsd:decimal)
|
|
259
|
-
real('price') // 实数
|
|
260
|
-
decimal('amount') // 十进制数
|
|
261
|
-
numeric('value') // 数值
|
|
262
|
-
float('ratio') // 单精度浮点
|
|
263
|
-
double('precise') // 双精度浮点
|
|
264
|
-
|
|
265
|
-
// 布尔类型
|
|
266
|
-
boolean('active') // 布尔值
|
|
267
|
-
|
|
268
|
-
// 日期时间类型
|
|
269
|
-
date('birthday') // 日期
|
|
270
|
-
datetime('event') // 日期时间
|
|
271
|
-
timestamp('created') // 时间戳
|
|
272
|
-
|
|
273
|
-
// JSON 类型
|
|
274
|
-
json('data') // JSON 数据
|
|
275
|
-
jsonb('config') // 二进制 JSON (PostgreSQL)
|
|
276
|
-
object('metadata') // 对象类型 (扩展)
|
|
277
|
-
```
|
|
148
|
+
await db.update(posts)
|
|
149
|
+
.set({ title: 'Updated title' })
|
|
150
|
+
.where(eq(posts.id, 'post-1'));
|
|
278
151
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
```typescript
|
|
282
|
-
// 查询所有记录
|
|
283
|
-
const users = await db.select().from(userTable);
|
|
284
|
-
|
|
285
|
-
// 条件查询
|
|
286
|
-
const adults = await db.select()
|
|
287
|
-
.from(userTable)
|
|
288
|
-
.where(gte(userTable.age, 18));
|
|
289
|
-
|
|
290
|
-
// 选择特定字段
|
|
291
|
-
const names = await db.select({ name: userTable.name })
|
|
292
|
-
.from(userTable);
|
|
293
|
-
|
|
294
|
-
// 使用条件构建器
|
|
295
|
-
const verifiedAdults = await db.select()
|
|
296
|
-
.from(userTable)
|
|
297
|
-
.where(and(gte(userTable.age, 18), eq(userTable.verified, true)));
|
|
298
|
-
|
|
299
|
-
// 排序、分页查询
|
|
300
|
-
const recentUsers = await db.select()
|
|
301
|
-
.from(userTable)
|
|
302
|
-
.orderBy(userTable.createdAt, 'desc') // 默认升序,可显式指定 'desc'
|
|
303
|
-
.limit(10) // 取前 10 条
|
|
304
|
-
.offset(10); // 跳过前 10 条,实现分页
|
|
305
|
-
|
|
306
|
-
// DISTINCT 查询,去重后返回唯一记录
|
|
307
|
-
const uniqueEmails = await db.select({ email: userTable.email })
|
|
308
|
-
.from(userTable)
|
|
309
|
-
.distinct();
|
|
152
|
+
await db.delete(posts)
|
|
153
|
+
.where(eq(posts.id, 'post-1'));
|
|
310
154
|
```
|
|
311
155
|
|
|
312
|
-
###
|
|
156
|
+
### Reusable schema + runtime binding
|
|
313
157
|
|
|
314
|
-
```
|
|
315
|
-
import {
|
|
158
|
+
```ts
|
|
159
|
+
import {
|
|
160
|
+
drizzle,
|
|
161
|
+
solidSchema,
|
|
162
|
+
id,
|
|
163
|
+
string,
|
|
164
|
+
} from '@undefineds.co/drizzle-solid';
|
|
165
|
+
|
|
166
|
+
const profileSchema = solidSchema({
|
|
167
|
+
id: id(),
|
|
168
|
+
name: string('name').predicate('http://xmlns.com/foaf/0.1/name'),
|
|
169
|
+
bio: string('bio').predicate('http://schema.org/description'),
|
|
170
|
+
}, {
|
|
171
|
+
type: 'http://xmlns.com/foaf/0.1/Person',
|
|
172
|
+
});
|
|
316
173
|
|
|
317
|
-
const
|
|
318
|
-
.select({
|
|
319
|
-
totalUsers: count(),
|
|
320
|
-
oldestAge: max(userTable.age)
|
|
321
|
-
})
|
|
322
|
-
.from(userTable)
|
|
323
|
-
.where(gte(userTable.age, 18));
|
|
174
|
+
const db = drizzle(session);
|
|
324
175
|
|
|
325
|
-
|
|
326
|
-
|
|
176
|
+
const profileTable = db.createTable(profileSchema, {
|
|
177
|
+
base: 'https://alice.example/profile/card',
|
|
178
|
+
});
|
|
327
179
|
```
|
|
328
180
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
### 插入数据
|
|
181
|
+
## Exact IRI operations
|
|
332
182
|
|
|
333
|
-
|
|
334
|
-
// 插入单条记录
|
|
335
|
-
await db.insert(userTable).values({
|
|
336
|
-
name: 'Bob',
|
|
337
|
-
email: 'bob@example.com',
|
|
338
|
-
age: 25
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
// 批量插入
|
|
342
|
-
await db.insert(userTable).values([
|
|
343
|
-
{ name: 'Alice', email: 'alice@example.com', age: 30 },
|
|
344
|
-
{ name: 'Charlie', email: 'charlie@example.com', age: 35 }
|
|
345
|
-
]);
|
|
346
|
-
```
|
|
183
|
+
For detail-page flows, remote links, shared resources, or exact mutation targets, use the IRI APIs:
|
|
347
184
|
|
|
348
|
-
|
|
185
|
+
- `db.findByIri(table, iri)`
|
|
186
|
+
- `db.updateByIri(table, iri, data)`
|
|
187
|
+
- `db.deleteByIri(table, iri)`
|
|
188
|
+
- `db.subscribeByIri(table, iri, options)`
|
|
349
189
|
|
|
350
|
-
```
|
|
351
|
-
await db.
|
|
352
|
-
.set({ age: 26 })
|
|
353
|
-
.where(eq(userTable.name, 'Bob'));
|
|
354
|
-
```
|
|
190
|
+
```ts
|
|
191
|
+
const agent = await db.findByIri(agentTable, 'https://alice.example/data/agents.ttl#assistant');
|
|
355
192
|
|
|
356
|
-
|
|
193
|
+
await db.updateByIri(
|
|
194
|
+
agentTable,
|
|
195
|
+
'https://alice.example/data/agents.ttl#assistant',
|
|
196
|
+
{ description: 'Updated through explicit IRI' },
|
|
197
|
+
);
|
|
357
198
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
.
|
|
199
|
+
await db.deleteByIri(
|
|
200
|
+
agentTable,
|
|
201
|
+
'https://alice.example/data/agents.ttl#assistant',
|
|
202
|
+
);
|
|
361
203
|
```
|
|
362
204
|
|
|
363
|
-
##
|
|
205
|
+
## Subject templates and identity
|
|
364
206
|
|
|
365
|
-
|
|
207
|
+
`subjectTemplate` controls how record identity maps to Pod resources.
|
|
366
208
|
|
|
367
|
-
|
|
209
|
+
Common patterns:
|
|
368
210
|
|
|
369
|
-
-
|
|
370
|
-
-
|
|
211
|
+
- `#{id}`: fragment mode, many entities in one RDF file
|
|
212
|
+
- `{id}.ttl`: document mode, one file per entity
|
|
213
|
+
- `{id}.ttl#it`: document mode with stable in-document fragment
|
|
214
|
+
- `{chatId}/{yyyy}/{MM}/{dd}/messages.ttl#{id}`: multi-variable, date-partitioned layouts
|
|
371
215
|
|
|
372
|
-
|
|
216
|
+
Example:
|
|
373
217
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
id: id(),
|
|
218
|
+
```ts
|
|
219
|
+
const Message = podTable('Message', {
|
|
220
|
+
id: string('id').primaryKey(),
|
|
221
|
+
chatId: string('chatId').predicate('http://example.org/chatId'),
|
|
379
222
|
content: string('content').predicate('http://schema.org/text'),
|
|
223
|
+
timestamp: datetime('timestamp').predicate('http://schema.org/dateCreated'),
|
|
380
224
|
}, {
|
|
225
|
+
base: 'https://alice.example/data/chats/',
|
|
226
|
+
subjectTemplate: '{chatId}/messages.ttl#{id}',
|
|
381
227
|
type: 'http://schema.org/Message',
|
|
382
|
-
typeIndex: 'private' // ✨ 开启自动发现魔法
|
|
383
228
|
});
|
|
384
|
-
|
|
385
|
-
// 当您执行查询时,Drizzle 会自动:
|
|
386
|
-
// 1. 查您的 Private TypeIndex
|
|
387
|
-
// 2. 如果没找到,查 SAI Agent Registry (查看是否有授权给您的数据)
|
|
388
|
-
// 3. 找到数据位置后,自动定向查询
|
|
389
|
-
const messages = await db.select().from(messageTable);
|
|
390
229
|
```
|
|
391
230
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
这是一个展示如何利用 SAI 发现机制构建“零配置”聊天应用的完整示例。Bob 不需要知道 Alice 的聊天室 URL,只要 Alice 授权了,Bob 的应用就能自动发现并加入聊天。
|
|
395
|
-
|
|
396
|
-
> 查看完整可运行代码:[`examples/04-sai-chat.ts`](examples/04-sai-chat.ts)
|
|
397
|
-
|
|
398
|
-
```typescript
|
|
399
|
-
// 1. 定义通用的消息表(不绑定具体路径)
|
|
400
|
-
const messageTable = podTable('message', {
|
|
401
|
-
id: id(),
|
|
402
|
-
content: string('content').predicate('http://schema.org/text'),
|
|
403
|
-
author: uri('author').predicate('http://schema.org/author'),
|
|
404
|
-
createdAt: datetime('createdAt').predicate('http://schema.org/dateCreated')
|
|
405
|
-
}, {
|
|
406
|
-
type: 'http://schema.org/Message',
|
|
407
|
-
typeIndex: 'private', // 启用发现
|
|
408
|
-
autoRegister: false // 对于访客 Bob,不仅不需要注册,而且不能注册到自己的 TypeIndex
|
|
409
|
-
});
|
|
231
|
+
For reads, the library can often work from:
|
|
410
232
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
...messageTable.config,
|
|
415
|
-
base: 'https://alice.pod/data/chat/room.ttl',
|
|
416
|
-
subjectTemplate: '#{id}' // 强制单文件存储模式
|
|
417
|
-
});
|
|
418
|
-
await db.insert(aliceChatTable).values({ ... });
|
|
233
|
+
- a full IRI
|
|
234
|
+
- all template variables
|
|
235
|
+
- a narrower partial condition that scans a smaller bounded area
|
|
419
236
|
|
|
420
|
-
|
|
237
|
+
Read-path and write-path semantics are intentionally different:
|
|
421
238
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
239
|
+
- **Read queries** (`select`, `db.query.*`) may use partial template information for list/filter flows when the resolver can keep the scan bounded.
|
|
240
|
+
- **Mutations** (`update`, `delete`) require deterministic targets by default. If `where()` cannot uniquely resolve the subject from the template, the dialect throws and tells you to use `updateByIri()` / `deleteByIri()` (or to provide the missing template variables).
|
|
241
|
+
- **No implicit scan-based `updateMany/deleteMany` fallback** is performed on backends that do not natively support set-based mutation.
|
|
242
|
+
- **Capability-based enhancement**: if a backend explicitly supports filter-based multi-row mutation in the future (for example, an xpod capability), `drizzle-solid` can expose that path there without pretending every Solid server supports it.
|
|
425
243
|
|
|
426
|
-
|
|
427
|
-
const messages = await bobDb.select().from(messageTable);
|
|
428
|
-
console.log(messages); // 包含 Alice 的消息
|
|
244
|
+
## Server support
|
|
429
245
|
|
|
430
|
-
|
|
431
|
-
await bobDb.insert(messageTable).values({
|
|
432
|
-
content: 'Hi Alice! I found your chat room!',
|
|
433
|
-
author: bobSession.info.webId
|
|
434
|
-
});
|
|
435
|
-
```
|
|
246
|
+
### Capability matrix
|
|
436
247
|
|
|
437
|
-
|
|
248
|
+
| Capability | Community Solid Server | xpod |
|
|
249
|
+
| --- | --- | --- |
|
|
250
|
+
| Basic CRUD | ✅ LDP mode | ✅ LDP mode |
|
|
251
|
+
| SPARQL SELECT pushdown | ❌ Client-side / resolver-driven | ✅ `/-/sparql` sidecar |
|
|
252
|
+
| SPARQL UPDATE | ⚠️ Limited / write path stays LDP-oriented | ✅ Better server-side support |
|
|
253
|
+
| Filter / aggregation pushdown | ❌ Fallbacks and client execution | ✅ Single-Pod pushdown |
|
|
254
|
+
| Federated queries | ⚠️ Client-side federation | ⚠️ Client-side federation |
|
|
255
|
+
| Notifications | ✅ | ✅ |
|
|
256
|
+
| In-process test runtime | ⚠️ External CSS setup | ✅ via `@undefineds.co/xpod` |
|
|
438
257
|
|
|
439
|
-
|
|
440
|
-
1. **TypeIndex 优先**:检查用户的 `privateTypeIndex.ttl` 和 `publicTypeIndex.ttl`。
|
|
441
|
-
2. **SAI 后备**:如果 TypeIndex 未命中,检查用户的 Profile -> RegistrySet -> Agent Registry,寻找匹配当前 ClientID 的 Data Grant。
|
|
258
|
+
### Community Solid Server (CSS)
|
|
442
259
|
|
|
443
|
-
|
|
260
|
+
On plain CSS, `drizzle-solid` keeps working, but many queries are resolved through client-side execution and Solid-aware fallbacks. This is the compatibility baseline.
|
|
444
261
|
|
|
445
|
-
|
|
262
|
+
### xpod
|
|
446
263
|
|
|
447
|
-
|
|
264
|
+
`xpod` adds a Solid-compatible SPARQL sidecar and is the recommended development/runtime target when you want stronger query pushdown and a lower-friction local setup.
|
|
448
265
|
|
|
449
|
-
|
|
450
|
-
- 运行策略:
|
|
451
|
-
- **xpod + 单 Pod**:查询下推到 QuintStore 索引执行
|
|
452
|
-
- **原生 CSS / 联邦查询**:Comunica 将数据读入内存后执行
|
|
453
|
-
- 未实现:`rightJoin`/`fullJoin`(待评估 xpod 支持情况)
|
|
454
|
-
- 未覆盖:`HAVING`、窗口函数、`UNION/UNION ALL`、子查询与跨容器联结;如需这些能力,请暂时改用手写 SPARQL 或拆分查询。
|
|
266
|
+
See `docs/xpod-features.md` for more detail.
|
|
455
267
|
|
|
456
|
-
##
|
|
268
|
+
## Verified examples
|
|
457
269
|
|
|
458
|
-
|
|
270
|
+
The canonical examples in `examples/` are verified in the test suite.
|
|
459
271
|
|
|
460
|
-
|
|
272
|
+
- `examples/01-quick-start.ts` — CRUD quick start
|
|
273
|
+
- `examples/02-relational-query.ts` — relational query API surface
|
|
274
|
+
- `examples/03-zero-config-discovery.ts` — zero-config discovery flow
|
|
275
|
+
- `examples/04-notifications.ts` — Solid notifications
|
|
276
|
+
- `examples/05-data-discovery.ts` — discovery API
|
|
277
|
+
- `examples/06-federated-query.ts` — federated query across Pods
|
|
278
|
+
- `examples/07-hooks-and-profile.ts` — hooks and profile management
|
|
279
|
+
- `examples/08-iri-based-operations.ts` — explicit IRI operations
|
|
280
|
+
- `examples/08-schema-inheritance.ts` — schema inheritance
|
|
281
|
+
- `examples/09-multi-variable-templates.ts` — multi-variable subject templates
|
|
461
282
|
|
|
462
|
-
|
|
283
|
+
The example manifest lives at `examples/manifest.json`, and the integration verification lives at `tests/integration/css/examples-verification.test.ts`.
|
|
463
284
|
|
|
464
|
-
|
|
285
|
+
## Query surface and SQL scope
|
|
465
286
|
|
|
466
|
-
|
|
467
|
-
import { podTable, string, extendNamespace } from 'drizzle-solid';
|
|
468
|
-
import { SCHEMA_INRUPT as SCHEMA } from '@inrupt/vocab-common-rdf';
|
|
287
|
+
`drizzle-solid` aims to stay close to Drizzle's public builder surface where that maps cleanly to Solid.
|
|
469
288
|
|
|
470
|
-
|
|
471
|
-
{ prefix: 'linq', uri: 'https://linq.dev/ns/' },
|
|
472
|
-
{ favorite: 'profile#favorite' },
|
|
473
|
-
{ namespace: 'https://linq.dev/ns/' }
|
|
474
|
-
);
|
|
289
|
+
Current supported surface includes:
|
|
475
290
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
});
|
|
484
|
-
```
|
|
291
|
+
- CRUD builders: `select`, `insert`, `update`, `delete`
|
|
292
|
+
- Read-oriented query facade: `db.query.*.findMany`, `findFirst`, `findById`, `findByIRI`, `count`
|
|
293
|
+
- Conditions: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `like`, `ilike`, `inArray`, `exists`, `and`, `or`, `not`
|
|
294
|
+
- Query features: joins, ordering, limits, offsets, distinct
|
|
295
|
+
- Aggregations: `count`, `sum`, `avg`, `min`, `max`
|
|
296
|
+
- Batch and `returning()` support where implemented by the dialect
|
|
297
|
+
- Raw **SPARQL** workflows where you need explicit graph-native control
|
|
485
298
|
|
|
486
|
-
|
|
299
|
+
Not the mainline contract:
|
|
487
300
|
|
|
488
|
-
|
|
489
|
-
|
|
301
|
+
- raw SQL as the primary abstraction
|
|
302
|
+
- transaction semantics identical to SQL databases
|
|
303
|
+
- implicit scan-based `updateMany/deleteMany` on backends that do not support set-based mutation
|
|
304
|
+
- driver-specific DDL, auto-increment, foreign keys, or SQL engine behaviors
|
|
490
305
|
|
|
491
|
-
|
|
492
|
-
await session.login({
|
|
493
|
-
oidcIssuer: 'https://solidcommunity.net',
|
|
494
|
-
redirectUrl: 'http://localhost:3000/callback',
|
|
495
|
-
clientName: 'My Solid App'
|
|
496
|
-
});
|
|
306
|
+
The migration goal is API familiarity with low migration cost, but the execution model remains Solid/SPARQL-native rather than semantics-faking SQL emulation.
|
|
497
307
|
|
|
498
|
-
|
|
499
|
-
```
|
|
308
|
+
## Discovery, interoperability, and federation
|
|
500
309
|
|
|
501
|
-
|
|
310
|
+
The library also includes higher-level Solid workflows:
|
|
502
311
|
|
|
503
|
-
-
|
|
504
|
-
-
|
|
505
|
-
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
- 查询:
|
|
509
|
-
- `where({ '@id': 'https://…#foo' })` 精确匹配该 subject;
|
|
510
|
-
- `where({ id: 'foo' })` 匹配 fragment 为 `foo` 且落在该表 `base` 下的 subject。
|
|
511
|
-
- `base` 同时决定存储地址(PUT/PATCH 目标)和 subject 生成;`podUrl` 只负责解析相对的 `base`。
|
|
312
|
+
- data discovery and interop metadata
|
|
313
|
+
- TypeIndex-driven location discovery
|
|
314
|
+
- shape-aware table generation
|
|
315
|
+
- federated queries across discovered Pod locations
|
|
316
|
+
- notifications for resource/entity updates
|
|
512
317
|
|
|
513
|
-
|
|
318
|
+
Useful starting points:
|
|
514
319
|
|
|
515
|
-
|
|
320
|
+
- `docs/guides/data-discovery.md`
|
|
321
|
+
- `docs/guides/notifications.md`
|
|
322
|
+
- `docs/guides/css-notifications.md`
|
|
323
|
+
- `docs/federated-queries.md`
|
|
324
|
+
- `docs/guides/multi-variable-templates.md`
|
|
516
325
|
|
|
517
|
-
|
|
518
|
-
- **SPARQL转换器**: 将Drizzle查询转换为SPARQL
|
|
519
|
-
- **Comunica执行器**: 执行SPARQL查询
|
|
520
|
-
- **类型系统**: 完整的TypeScript类型支持
|
|
326
|
+
## Documentation map
|
|
521
327
|
|
|
522
|
-
|
|
328
|
+
- `examples/README.md` — curated walkthroughs and runnable examples
|
|
329
|
+
- `docs/quick-start-local.md` — local setup guide
|
|
330
|
+
- `docs/guides/data-discovery.md` — discovery workflows
|
|
331
|
+
- `docs/guides/issue-handling.md` — issue reproduction and regression workflow
|
|
332
|
+
- `docs/guides/testing.md` — test conventions and coverage strategy
|
|
333
|
+
- `ACTION-PLAN.md` — current parity and implementation plan
|
|
523
334
|
|
|
524
|
-
|
|
525
|
-
- `PodDialect` 会自动推导目标容器与 `.ttl` 资源文件路径,必要时发送 `HEAD`/`PUT` 请求确保容器和资源已经存在,再交由 Comunica 处理数据修改。
|
|
526
|
-
- 插入会预先读取现有资源以检测重复 subject,避免重复写入;删除或更新只针对匹配的 subject 生成最小化补丁。
|
|
527
|
-
- 对于 `JOIN`、`GROUP BY` 与聚合,选取的数据仍由 SPARQL 拉取,但结果会在内存中组合或聚合,直到 CSS 升级到支持完整 SPARQL 1.1 为止。
|
|
335
|
+
## Planned scope
|
|
528
336
|
|
|
529
|
-
|
|
337
|
+
### Current direction
|
|
530
338
|
|
|
531
|
-
|
|
339
|
+
The current focus is:
|
|
532
340
|
|
|
533
|
-
|
|
341
|
+
- typed models for application-owned Pod data
|
|
342
|
+
- explicit document and subject placement
|
|
343
|
+
- Drizzle-aligned query ergonomics
|
|
344
|
+
- practical CRUD, discovery, federation, and notification flows
|
|
534
345
|
|
|
535
|
-
|
|
536
|
-
SOLID_ENABLE_REAL_TESTS=true npx vitest run tests/integration/css --runInBand
|
|
537
|
-
```
|
|
346
|
+
### Likely next steps
|
|
538
347
|
|
|
539
|
-
|
|
348
|
+
- better modeling ergonomics around reusable schemas and links
|
|
349
|
+
- clearer guidance around multi-resource layouts and identity strategies
|
|
350
|
+
- continued Drizzle parity on Solid-relevant behaviors
|
|
351
|
+
- stronger documentation around Solid-native tradeoffs and migration paths
|
|
540
352
|
|
|
541
|
-
|
|
353
|
+
### Out of scope
|
|
542
354
|
|
|
543
|
-
|
|
355
|
+
- hiding Solid behind a fake SQL database mental model
|
|
356
|
+
- universal querying over arbitrary open-world RDF as the primary goal
|
|
357
|
+
- promising full relational/database feature parity where Solid semantics differ
|
|
544
358
|
|
|
545
|
-
##
|
|
359
|
+
## Contributing
|
|
546
360
|
|
|
547
|
-
|
|
548
|
-
- [Solid Project](https://solidproject.org/)
|
|
549
|
-
- [Community Solid Server](https://github.com/CommunitySolidServer/CommunitySolidServer)
|
|
550
|
-
- [Inrupt Solid Client](https://github.com/inrupt/solid-client-js)
|
|
361
|
+
Before contributing:
|
|
551
362
|
|
|
363
|
+
```bash
|
|
364
|
+
yarn build
|
|
365
|
+
yarn lint
|
|
366
|
+
SOLID_ENABLE_REAL_TESTS=true SOLID_SERIAL_TESTS=true yarn vitest --run --silent
|
|
367
|
+
```
|
|
552
368
|
|
|
553
|
-
|
|
369
|
+
Examples must remain runnable and verified.
|
|
554
370
|
|
|
555
|
-
|
|
371
|
+
## License
|
|
556
372
|
|
|
557
|
-
|
|
558
|
-
2. `examples/README.md` 了解脚本入口与运行方式
|
|
559
|
-
3. [Issue 列表](https://github.com/undefinedsco/drizzle-solid/issues) 提交复现步骤与日志
|
|
373
|
+
MIT
|
|
560
374
|
|
|
561
|
-
|
|
375
|
+
## Related links
|
|
562
376
|
|
|
563
|
-
|
|
377
|
+
- GitHub: https://github.com/undefinedsco/drizzle-solid
|
|
378
|
+
- npm: https://www.npmjs.com/package/@undefineds.co/drizzle-solid
|
|
379
|
+
- xpod: https://github.com/undefinedsco/xpod
|