imean-cassandra-orm
Version:
cassandra orm
348 lines (272 loc) • 9.54 kB
Markdown
# Cassandra ORM
一个简单易用的 Cassandra ORM 库,提供类型安全的 API。
## 安装
```bash
npm install imean-cassandra-orm
```
## 使用示例
```typescript
import { Client as CassandraClient, auth } from "cassandra-driver";
import { Client, Field, uuid, type Infer } from "imean-cassandra-orm";
// 创建原始 Cassandra 客户端
const cassandraClient = new CassandraClient({
contactPoints: ["localhost"],
localDataCenter: "datacenter1",
authProvider: new auth.PlainTextAuthProvider("cassandra", "cassandra"),
});
// 创建我们的客户端
const client = new Client(cassandraClient);
// 设置 keyspace
await client.useKeyspace("my_keyspace", {
class: "SimpleStrategy",
replication_factor: 1,
});
// 创建用户模型
const userModel = client.createModel(
{
id: Field.uuid().partitionKey(), // 分区键,必填
name: Field.text().clusteringKey(), // 聚类键,必填
age: Field.int().optional(), // 可选字段
email: Field.text(), // 普通字段,必填
},
"users"
);
type User = Infer<typeof userModel>;
// 创建文章模型
const postModel = client.createModel(
{
id: Field.uuid().partitionKey(), // 分区键,必填
title: Field.text(), // 普通字段,必填
content: Field.text(), // 普通字段,必填
created_at: Field.timestamp(), // 普通字段,必填
},
"posts"
);
// 同步所有表结构
await client.syncAllSchemas();
// 创建用户
const user = await userModel.create({
id: uuid(), // 必须提供分区键
name: "张三", // 必须提供聚类键
age: 25, // 可选
email: "zhangsan@example.com" // 必填
});
// 创建文章
await postModel.create({
id: uuid(),
title: "测试文章",
content: "内容",
created_at: new Date(),
});
// 查询用户
const foundUser = await userModel.findOne({ id: user.id });
console.log(foundUser);
// 关闭连接
await client.close();
```
## API 参考
### 类型推导工具
ORM 库提供了一系列类型推导工具,可以帮助开发者在上层 API 中实现类型安全。
#### Infer<M>
从 Model 实例中推断出原始字段类型。
```typescript
const userModel = client.createModel({
id: Field.uuid().partitionKey(),
name: Field.text().clusteringKey(),
age: Field.int().optional(),
email: Field.text(),
}, "users");
type User = Infer<typeof userModel>;
// 推导结果: { id: string; name: string; age?: number; email: string }
```
#### InferPartitionKey<M>
推导分区键字段类型。
```typescript
type UserPartitionKey = InferPartitionKey<typeof userModel>;
// 推导结果: { id: string }
```
#### InferClusteringKey<M>
推导聚类键字段类型。
```typescript
type UserClusteringKey = InferClusteringKey<typeof userModel>;
// 推导结果: { name: string }
```
#### InferNonPartitionFields<S>
推导非分区键字段类型。
```typescript
type UserFields = {
id: { type: "uuid"; flags: { partitionKey: true } };
name: { type: "text" };
age: { type: "int" };
};
type NonPartitionFields = InferNonPartitionFields<UserFields>;
// 推导结果: { name: string; age: number }
```
#### InferStaticFields<M>
推导静态字段类型。
```typescript
const userModel = client.createModel({
id: Field.uuid().partitionKey(),
name: Field.text().static(), // 静态字段
age: Field.int().optional(),
email: Field.text(),
}, "users");
type UserStaticFields = InferStaticFields<typeof userModel>;
// 推导结果: { name: string }
```
静态列的特点:
1. 在分区级别存储数据,而不是在每一行重复存储
2. 适用于在分区内所有行共享相同值的字段
3. 可以节省存储空间
4. 提高查询效率
这些类型工具可以用于:
1. 在 API 层定义类型安全的接口
2. 在服务层进行类型检查
3. 在数据访问层确保数据一致性
4. 在业务逻辑层进行类型推导
### Client
`Client` 类是对原始 Cassandra 客户端的封装,提供了更高级的 API。
#### 构造函数
```typescript
constructor(cassandraClient: CassandraClient)
```
#### 方法
- `useKeyspace(keyspace: string, options: KeyspaceOptions): Promise<void>`
- 设置当前使用的 keyspace,如果不存在则创建
- `options`:
- `class`: 复制策略,可以是 `"SimpleStrategy"` 或 `"NetworkTopologyStrategy"`
- `replication_factor`: 复制因子(仅用于 `SimpleStrategy`)
- `datacenters`: 数据中心配置(仅用于 `NetworkTopologyStrategy`)
- `createModel<S extends Record<string, FieldConfig<keyof TypeMap>>>(schema: S, tableName: string): Model<S>`
- 创建并注册一个新的模型
- 返回的模型实例可以直接使用,不需要额外获取
- `syncAllSchemas(): Promise<void>`
- 同步所有已注册模型的结构到数据库
- `close(): Promise<void>`
- 关闭数据库连接
### Model
`Model` 类提供了对数据库表的操作接口。
#### 类型推导示例
```typescript
// 定义模型 schema
const userModel = client.createModel({
id: Field.uuid().partitionKey(), // 分区键,必填
name: Field.text().clusteringKey(), // 聚类键,必填
age: Field.int().optional(), // 可选字段
email: Field.text(), // 普通字段,必填
}, "users");
// 推导出的类型:
type User = {
id: string; // 分区键,必填
name: string; // 聚类键,必填
age?: number; // 可选字段
email: string; // 普通字段,必填
};
// 分区键类型(用于查询和删除)
type PartitionKey = {
id: string;
};
// 聚类键类型(用于查询和删除)
type ClusteringKey = {
name: string;
};
// 可更新字段类型(用于更新操作)
type UpdatableFields = {
age?: number; // 可选字段
email: string; // 普通字段
};
```
#### 方法
- `create(data: User): Promise<void>`
- 创建新记录
- 示例:
```typescript
await userModel.create({
id: uuid(), // 必须提供分区键
name: "张三", // 必须提供聚类键
age: 25, // 可选
email: "zhangsan@example.com" // 必填
});
```
- `findOne(partitionKey: PartitionKey, clusteringKey?: Partial<ClusteringKey>): Promise<User | null>`
- 查询单条记录
- 示例:
```typescript
// 只使用分区键查询
const user = await userModel.findOne({ id: "some-uuid" });
// 使用分区键和聚类键查询
const user = await userModel.findOne(
{ id: "some-uuid" },
{ name: "张三" }
);
```
- `findAll(partitionKey: PartitionKey, clusteringKey?: Partial<ClusteringKey>): Promise<User[]>`
- 查询多条记录
- 参数同 `findOne`
- `update(partitionKey: PartitionKey, data: Partial<UpdatableFields>): Promise<void>`
- 更新记录
- 示例:
```typescript
await userModel.update(
{ id: "some-uuid" }, // 必须提供分区键
{
age: 26, // 可以更新可选字段
email: "new@example.com" // 可以更新普通字段
}
);
```
- `delete(partitionKey: PartitionKey, clusteringKey?: Partial<ClusteringKey>): Promise<void>`
- 删除记录
- 参数同 `findOne`
### Field
`Field` 类提供了创建字段配置的方法。
#### 静态方法
- `uuid(): FieldBuilder<"uuid">`
- `text(): FieldBuilder<"text">`
- `int(): FieldBuilder<"int">`
- `bigint(): FieldBuilder<"bigint">`
- `float(): FieldBuilder<"float">`
- `double(): FieldBuilder<"double">`
- `boolean(): FieldBuilder<"boolean">`
- `timestamp(): FieldBuilder<"timestamp">`
#### 实例方法
- `partitionKey(): this`
- 将字段设置为分区键
- 分区键字段是必填的,不能为 null
- 示例:
```typescript
Field.uuid().partitionKey() // 将 UUID 字段设置为分区键
```
- `clusteringKey(order?: "ASC" | "DESC"): this`
- 将字段设置为聚类键
- 聚类键字段是否必填取决于是否设置了 `optional()`
- `order` 可选,指定排序方向
- 示例:
```typescript
Field.text().clusteringKey() // 默认升序,必填
Field.text().clusteringKey("DESC") // 降序,必填
Field.text().clusteringKey().optional() // 可选聚类键
```
- `optional(): this`
- 将字段设置为可选
- 可以用于任何类型的字段,包括聚类键
- 示例:
```typescript
Field.int().optional() // 将整数字段设置为可选
```
- `static(): this`
- 将字段设置为静态列
- 静态列在分区级别存储,而不是在每一行重复存储
- 适用于在分区内所有行共享相同值的字段
- 示例:
```typescript
Field.text().static() // 将文本字段设置为静态列
```
## 注意事项
1. 所有模型必须在调用 `syncAllSchemas` 之前创建
2. 创建记录时必须提供所有分区键字段
3. 聚类键字段是否必填取决于是否设置了 `optional()`
4. 更新记录时可以更新非分区键字段(包括聚类键和普通字段)
5. 删除记录时必须提供所有分区键字段
## 许可证
MIT