document-dataply 0.0.2 → 0.0.3-alpha.1
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 +35 -48
- package/dist/cjs/index.js +161 -37
- package/dist/types/core/bptree/documentStrategy.d.ts +3 -3
- package/dist/types/core/document.d.ts +66 -8
- package/dist/types/types/index.d.ts +26 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,14 +33,14 @@ type MyDocument = {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
async function main() {
|
|
36
|
-
const db =
|
|
36
|
+
const db = DocumentDataply.Define<MyDocument>().Options({
|
|
37
37
|
wal: 'my-database.wal',
|
|
38
38
|
indices: {
|
|
39
39
|
name: true, // Index both existing and new data
|
|
40
40
|
age: false, // Index only new data
|
|
41
41
|
'tags.0': true // Index the first element of the 'tags' array
|
|
42
42
|
}
|
|
43
|
-
});
|
|
43
|
+
}).Open('my-database.db');
|
|
44
44
|
|
|
45
45
|
// Initialize database
|
|
46
46
|
await db.init();
|
|
@@ -78,7 +78,7 @@ main();
|
|
|
78
78
|
|
|
79
79
|
### Indexing Policies
|
|
80
80
|
|
|
81
|
-
When defining indices in the
|
|
81
|
+
When defining indices in the `options`, you can specify a boolean value.
|
|
82
82
|
|
|
83
83
|
- `true`: The library indexes all existing documents for that field during `init()`, and also indexes all subsequent insertions.
|
|
84
84
|
- `false`: The library only indexes documents inserted after this configuration.
|
|
@@ -99,54 +99,32 @@ const ids = await db.insertBatch([
|
|
|
99
99
|
|
|
100
100
|
### Querying
|
|
101
101
|
|
|
102
|
-
`document-dataply` supports
|
|
102
|
+
`document-dataply` supports powerful search capabilities based on B+Tree indexing.
|
|
103
103
|
|
|
104
104
|
| Operator | Description |
|
|
105
105
|
| :--- | :--- |
|
|
106
|
-
| `lt` |
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
110
|
-
| `equal` | Equal to |
|
|
111
|
-
| `notEqual` | Not equal to |
|
|
112
|
-
| `like` | SQL-style pattern matching (e.g., `Jo%`) |
|
|
113
|
-
| `or` | If any value in the array is satisfied |
|
|
114
|
-
|
|
115
|
-
Example of a complex query:
|
|
116
|
-
```typescript
|
|
117
|
-
const users = await db.select({
|
|
118
|
-
age: { gt: 18, lt: 65 },
|
|
119
|
-
'address.city': 'Seoul',
|
|
120
|
-
tags: { or: ['vip', 'premium'] }
|
|
121
|
-
}).drain();
|
|
122
|
-
```
|
|
106
|
+
| `lt`, `lte`, `gt`, `gte` | Comparison operations |
|
|
107
|
+
| `equal`, `notEqual` | Equality check |
|
|
108
|
+
| `like` | Pattern matching |
|
|
109
|
+
| `or` | Matching within an array |
|
|
123
110
|
|
|
124
|
-
|
|
125
|
-
> **Query Constraints**: Query conditions (`lt`, `gt`, `equal`, etc.) can only be used on fields explicitly indexed in the constructor.
|
|
126
|
-
>
|
|
127
|
-
> **If a field in the query is not indexed, that condition will be ignored.**
|
|
128
|
-
>
|
|
129
|
-
> If you need to filter by unindexed fields, you should first retrieve the documents and then use JavaScript's native `.filter()` method.
|
|
130
|
-
```typescript
|
|
131
|
-
const results = await db.select({ /* indexed fields only */ }).drain();
|
|
132
|
-
const filtered = results.filter(doc => doc.unindexedField === 'some-value');
|
|
133
|
-
```
|
|
111
|
+
For detailed operator usage, index constraints (including full scans), and sorting methods, see the [Query Guide (QUERY.md)](./docs/QUERY.md).
|
|
134
112
|
|
|
135
113
|
### Transactions
|
|
136
114
|
|
|
137
|
-
|
|
115
|
+
Ensure data integrity with ACID-compliant transactions. Use `commit()` and `rollback()` to process multiple operations atomically.
|
|
138
116
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
117
|
+
For detailed usage and error handling patterns, see the [Transaction Guide (TRANSACTION.md)](./docs/TRANSACTION.md).
|
|
118
|
+
|
|
119
|
+
### Updating and Deleting
|
|
120
|
+
|
|
121
|
+
`document-dataply` provides flexible ways to update or delete documents based on query results. All these operations are **Stream-based**, allowing you to handle millions of records without memory concerns.
|
|
122
|
+
|
|
123
|
+
- **Partial Update**: Modify only specific fields or use a function for dynamic updates.
|
|
124
|
+
- **Full Update**: Replace the entire document while preserving the original `_id`.
|
|
125
|
+
- **Delete**: Permanently remove matching documents from both storage and indices.
|
|
126
|
+
|
|
127
|
+
For details on streaming mechanisms and bandwidth optimization tips, see the [Stream Guide (STREAM.md)](./docs/STREAM.md).
|
|
150
128
|
|
|
151
129
|
## Tips and Advanced Features
|
|
152
130
|
|
|
@@ -160,25 +138,34 @@ For more information on performance optimization and advanced features, see [TIP
|
|
|
160
138
|
|
|
161
139
|
## API Reference
|
|
162
140
|
|
|
163
|
-
### `
|
|
164
|
-
Creates a
|
|
165
|
-
`options.indices` is an object where keys are field names and values are booleans indicating
|
|
141
|
+
### `DocumentDataply.Define<T>().Options(options).Open(file)`
|
|
142
|
+
Creates or opens a database instance. `T` defines the document structure.
|
|
143
|
+
`options.indices` is an object where keys are field names and values are booleans indicating the [Indexing Policy](#indexing-policies).
|
|
166
144
|
|
|
167
145
|
### `db.init()`
|
|
168
146
|
Initializes the database, sets up internal metadata, and prepares indices.
|
|
169
147
|
|
|
170
148
|
### `db.insert(document, tx?)`
|
|
171
|
-
Inserts a single document.
|
|
149
|
+
Inserts a single document. Each document is automatically assigned a unique, immutable `_id` field. The method returns this `_id` (`number`).
|
|
172
150
|
|
|
173
151
|
### `db.insertBatch(documents, tx?)`
|
|
174
152
|
Inserts multiple documents efficiently. Returns an array of `_ids` (`number[]`).
|
|
175
153
|
|
|
176
154
|
### `db.select(query, options?, tx?)`
|
|
177
|
-
Searches for documents matching the query.
|
|
155
|
+
Searches for documents matching the query. Passing an empty object (`{}`) as the `query` retrieves all documents.
|
|
178
156
|
Returns an object `{ stream, drain }`.
|
|
179
157
|
- `stream`: An async iterator to traverse results one by one.
|
|
180
158
|
- `drain()`: A promise that resolves to an array of all matching documents.
|
|
181
159
|
|
|
160
|
+
### `db.partialUpdate(query, newFields, tx?)`
|
|
161
|
+
Partially updates documents matching the query. `newFields` can be a partial object or a function that returns a partial object. Returns the number of updated documents.
|
|
162
|
+
|
|
163
|
+
### `db.fullUpdate(query, newDocument, tx?)`
|
|
164
|
+
Fully replaces documents matching the query while preserving their `_id`. Returns the number of updated documents.
|
|
165
|
+
|
|
166
|
+
### `db.delete(query, tx?)`
|
|
167
|
+
Deletes documents matching the query. Returns the number of deleted documents.
|
|
168
|
+
|
|
182
169
|
### `db.getMetadata(tx?)`
|
|
183
170
|
Returns physical storage information (number of pages, number of rows, etc.).
|
|
184
171
|
|
package/dist/cjs/index.js
CHANGED
|
@@ -9683,7 +9683,40 @@ var DocumentDataplyAPI = class extends import_dataply3.DataplyAPI {
|
|
|
9683
9683
|
await this.update(1, JSON.stringify(metadata), tx);
|
|
9684
9684
|
}
|
|
9685
9685
|
};
|
|
9686
|
-
var DocumentDataply = class {
|
|
9686
|
+
var DocumentDataply = class _DocumentDataply {
|
|
9687
|
+
/**
|
|
9688
|
+
* Starts the database definition by setting the document type.
|
|
9689
|
+
* This is used to ensure TypeScript type inference works correctly for the document structure.
|
|
9690
|
+
* @template T The structure of the document to be stored.
|
|
9691
|
+
*/
|
|
9692
|
+
static Define() {
|
|
9693
|
+
return {
|
|
9694
|
+
/**
|
|
9695
|
+
* Sets the options for the database, such as index configurations and WAL settings.
|
|
9696
|
+
* @template IC The configuration of indices.
|
|
9697
|
+
* @param options The database initialization options.
|
|
9698
|
+
*/
|
|
9699
|
+
Options: (options) => _DocumentDataply.Options(options)
|
|
9700
|
+
};
|
|
9701
|
+
}
|
|
9702
|
+
/**
|
|
9703
|
+
* Internal method used by the Define-chain to pass options.
|
|
9704
|
+
*/
|
|
9705
|
+
static Options(options) {
|
|
9706
|
+
return {
|
|
9707
|
+
/**
|
|
9708
|
+
* Creates or opens the database instance with the specified file path.
|
|
9709
|
+
* @param file The path to the database file.
|
|
9710
|
+
*/
|
|
9711
|
+
Open: (file) => _DocumentDataply.Open(file, options)
|
|
9712
|
+
};
|
|
9713
|
+
}
|
|
9714
|
+
/**
|
|
9715
|
+
* Internal method used to finalize construction and create the instance.
|
|
9716
|
+
*/
|
|
9717
|
+
static Open(file, options) {
|
|
9718
|
+
return new _DocumentDataply(file, options);
|
|
9719
|
+
}
|
|
9687
9720
|
api;
|
|
9688
9721
|
indexedFields;
|
|
9689
9722
|
operatorConverters = {
|
|
@@ -9869,6 +9902,112 @@ var DocumentDataply = class {
|
|
|
9869
9902
|
return ids;
|
|
9870
9903
|
}, tx));
|
|
9871
9904
|
}
|
|
9905
|
+
/**
|
|
9906
|
+
* Internal update method used by both fullUpdate and partialUpdate
|
|
9907
|
+
* @param query The query to use
|
|
9908
|
+
* @param computeUpdatedDoc Function that computes the updated document from the original
|
|
9909
|
+
* @param tx The transaction to use
|
|
9910
|
+
* @returns The number of updated documents
|
|
9911
|
+
*/
|
|
9912
|
+
async updateInternal(query, computeUpdatedDoc, tx) {
|
|
9913
|
+
const idTree = this.api.trees.get("_id");
|
|
9914
|
+
if (!idTree) {
|
|
9915
|
+
throw new Error("ID tree not found");
|
|
9916
|
+
}
|
|
9917
|
+
const { stream } = this.select(query, {}, tx);
|
|
9918
|
+
let updatedCount = 0;
|
|
9919
|
+
for await (const doc of stream) {
|
|
9920
|
+
const id = doc._id;
|
|
9921
|
+
let pk = null;
|
|
9922
|
+
for await (const [entryPk] of idTree.whereStream({ primaryEqual: { v: id } })) {
|
|
9923
|
+
pk = entryPk;
|
|
9924
|
+
break;
|
|
9925
|
+
}
|
|
9926
|
+
if (pk === null) continue;
|
|
9927
|
+
const updatedDoc = computeUpdatedDoc(doc);
|
|
9928
|
+
const oldFlatDoc = this.api.flattenDocument(doc);
|
|
9929
|
+
const newFlatDoc = this.api.flattenDocument(updatedDoc);
|
|
9930
|
+
for (const [field, tree] of this.api.trees) {
|
|
9931
|
+
const oldV = oldFlatDoc[field];
|
|
9932
|
+
const newV = newFlatDoc[field];
|
|
9933
|
+
if (oldV === newV) continue;
|
|
9934
|
+
if (oldV !== void 0) {
|
|
9935
|
+
await tree.delete(pk, { k: pk, v: oldV });
|
|
9936
|
+
}
|
|
9937
|
+
if (newV !== void 0) {
|
|
9938
|
+
await tree.insert(pk, { k: pk, v: newV });
|
|
9939
|
+
}
|
|
9940
|
+
}
|
|
9941
|
+
await this.api.update(pk, JSON.stringify(updatedDoc), tx);
|
|
9942
|
+
updatedCount++;
|
|
9943
|
+
}
|
|
9944
|
+
return updatedCount;
|
|
9945
|
+
}
|
|
9946
|
+
/**
|
|
9947
|
+
* Fully update documents from the database that match the query
|
|
9948
|
+
* @param query The query to use (only indexed fields + _id allowed)
|
|
9949
|
+
* @param newRecord Complete document to replace with, or function that receives current document and returns new document
|
|
9950
|
+
* @param tx The transaction to use
|
|
9951
|
+
* @returns The number of updated documents
|
|
9952
|
+
*/
|
|
9953
|
+
async fullUpdate(query, newRecord, tx) {
|
|
9954
|
+
return this.api.writeLock(() => this.api.runWithDefault(async (tx2) => {
|
|
9955
|
+
return this.updateInternal(query, (doc) => {
|
|
9956
|
+
const newDoc = typeof newRecord === "function" ? newRecord(doc) : newRecord;
|
|
9957
|
+
return { _id: doc._id, ...newDoc };
|
|
9958
|
+
}, tx2);
|
|
9959
|
+
}, tx));
|
|
9960
|
+
}
|
|
9961
|
+
/**
|
|
9962
|
+
* Partially update documents from the database that match the query
|
|
9963
|
+
* @param query The query to use (only indexed fields + _id allowed)
|
|
9964
|
+
* @param newRecord Partial document to merge, or function that receives current document and returns partial update
|
|
9965
|
+
* @param tx The transaction to use
|
|
9966
|
+
* @returns The number of updated documents
|
|
9967
|
+
*/
|
|
9968
|
+
async partialUpdate(query, newRecord, tx) {
|
|
9969
|
+
return this.api.writeLock(() => this.api.runWithDefault(async (tx2) => {
|
|
9970
|
+
return this.updateInternal(query, (doc) => {
|
|
9971
|
+
const partialUpdate = typeof newRecord === "function" ? newRecord(doc) : newRecord;
|
|
9972
|
+
delete partialUpdate._id;
|
|
9973
|
+
return { ...doc, ...partialUpdate };
|
|
9974
|
+
}, tx2);
|
|
9975
|
+
}, tx));
|
|
9976
|
+
}
|
|
9977
|
+
/**
|
|
9978
|
+
* Delete documents from the database that match the query
|
|
9979
|
+
* @param query The query to use (only indexed fields + _id allowed)
|
|
9980
|
+
* @param tx The transaction to use
|
|
9981
|
+
* @returns The number of deleted documents
|
|
9982
|
+
*/
|
|
9983
|
+
async delete(query, tx) {
|
|
9984
|
+
return this.api.writeLock(() => this.api.runWithDefault(async (tx2) => {
|
|
9985
|
+
const idTree = this.api.trees.get("_id");
|
|
9986
|
+
if (!idTree) {
|
|
9987
|
+
throw new Error("ID tree not found");
|
|
9988
|
+
}
|
|
9989
|
+
const { stream } = this.select(query, {}, tx2);
|
|
9990
|
+
let deletedCount = 0;
|
|
9991
|
+
for await (const doc of stream) {
|
|
9992
|
+
const id = doc._id;
|
|
9993
|
+
let pk = null;
|
|
9994
|
+
for await (const [entryPk] of idTree.whereStream({ primaryEqual: { v: id } })) {
|
|
9995
|
+
pk = entryPk;
|
|
9996
|
+
break;
|
|
9997
|
+
}
|
|
9998
|
+
if (pk === null) continue;
|
|
9999
|
+
const flatDoc = this.api.flattenDocument(doc);
|
|
10000
|
+
for (const [field, tree] of this.api.trees) {
|
|
10001
|
+
const v = flatDoc[field];
|
|
10002
|
+
if (v === void 0) continue;
|
|
10003
|
+
await tree.delete(pk, { k: pk, v });
|
|
10004
|
+
}
|
|
10005
|
+
await this.api.delete(pk, true, tx2);
|
|
10006
|
+
deletedCount++;
|
|
10007
|
+
}
|
|
10008
|
+
return deletedCount;
|
|
10009
|
+
}, tx));
|
|
10010
|
+
}
|
|
9872
10011
|
/**
|
|
9873
10012
|
* Select documents from the database
|
|
9874
10013
|
* @param query The query to use (only indexed fields + _id allowed)
|
|
@@ -9883,8 +10022,8 @@ var DocumentDataply = class {
|
|
|
9883
10022
|
throw new Error(`Query field "${field}" is not indexed. Available indexed fields: ${Array.from(this.indexedFields).join(", ")}`);
|
|
9884
10023
|
}
|
|
9885
10024
|
}
|
|
9886
|
-
const orderBy = options.orderBy
|
|
9887
|
-
if (!this.indexedFields.has(orderBy)) {
|
|
10025
|
+
const orderBy = options.orderBy;
|
|
10026
|
+
if (orderBy !== void 0 && !this.indexedFields.has(orderBy)) {
|
|
9888
10027
|
throw new Error(`orderBy field "${orderBy}" is not indexed. Available indexed fields: ${Array.from(this.indexedFields).join(", ")}`);
|
|
9889
10028
|
}
|
|
9890
10029
|
const {
|
|
@@ -9896,51 +10035,36 @@ var DocumentDataply = class {
|
|
|
9896
10035
|
const isQueryEmpty = Object.keys(query).length === 0;
|
|
9897
10036
|
const normalizedQuery = isQueryEmpty ? { _id: { gte: 0 } } : query;
|
|
9898
10037
|
const verbose = self.verboseQuery(normalizedQuery);
|
|
9899
|
-
const orderByTree = self.api.trees.get(orderBy);
|
|
9900
10038
|
const selectivity = await self.getSelectivityCandidate(
|
|
9901
10039
|
verbose,
|
|
9902
|
-
|
|
10040
|
+
orderBy
|
|
9903
10041
|
);
|
|
9904
10042
|
if (!selectivity) return;
|
|
9905
10043
|
const { driver, others } = selectivity;
|
|
9906
|
-
const isDriverOrderByField =
|
|
10044
|
+
const isDriverOrderByField = orderBy === void 0 || driver.field === orderBy;
|
|
9907
10045
|
if (isDriverOrderByField) {
|
|
9908
|
-
|
|
10046
|
+
let keys = await driver.tree.keys(driver.condition, void 0, sortOrder);
|
|
10047
|
+
for (const { tree, condition } of others) {
|
|
10048
|
+
keys = await tree.keys(condition, keys, sortOrder);
|
|
10049
|
+
}
|
|
9909
10050
|
let i = 0;
|
|
9910
|
-
for
|
|
10051
|
+
for (const key of keys) {
|
|
9911
10052
|
if (i >= limit) break;
|
|
9912
|
-
|
|
9913
|
-
|
|
9914
|
-
|
|
9915
|
-
|
|
9916
|
-
isMatch = false;
|
|
9917
|
-
break;
|
|
9918
|
-
}
|
|
9919
|
-
}
|
|
9920
|
-
if (isMatch) {
|
|
9921
|
-
const stringified = await self.api.select(pk, false, tx2);
|
|
9922
|
-
if (!stringified) continue;
|
|
9923
|
-
yield JSON.parse(stringified);
|
|
9924
|
-
i++;
|
|
9925
|
-
}
|
|
10053
|
+
const stringified = await self.api.select(key, false, tx2);
|
|
10054
|
+
if (!stringified) continue;
|
|
10055
|
+
yield JSON.parse(stringified);
|
|
10056
|
+
i++;
|
|
9926
10057
|
}
|
|
9927
10058
|
} else {
|
|
9928
10059
|
const results = [];
|
|
9929
|
-
|
|
9930
|
-
for
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
9935
|
-
|
|
9936
|
-
|
|
9937
|
-
}
|
|
9938
|
-
}
|
|
9939
|
-
if (isMatch) {
|
|
9940
|
-
const stringified = await self.api.select(pk, false, tx2);
|
|
9941
|
-
if (!stringified) continue;
|
|
9942
|
-
results.push(JSON.parse(stringified));
|
|
9943
|
-
}
|
|
10060
|
+
let keys = await driver.tree.keys(driver.condition, void 0);
|
|
10061
|
+
for (const { tree, condition } of others) {
|
|
10062
|
+
keys = await tree.keys(condition, keys);
|
|
10063
|
+
}
|
|
10064
|
+
for (const key of keys) {
|
|
10065
|
+
const stringified = await self.api.select(key, false, tx2);
|
|
10066
|
+
if (!stringified) continue;
|
|
10067
|
+
results.push(JSON.parse(stringified));
|
|
9944
10068
|
}
|
|
9945
10069
|
results.sort((a, b) => {
|
|
9946
10070
|
const aVal = a[orderBy] ?? a._id;
|
|
@@ -2,10 +2,10 @@ import type { DataplyTreeValue, Primitive } from '../../types';
|
|
|
2
2
|
import { BPTreeNode, SerializeStrategyAsync, type SerializeStrategyHead } from 'dataply';
|
|
3
3
|
import { DocumentDataplyAPI } from '../document';
|
|
4
4
|
export declare class DocumentSerializeStrategyAsync<T extends Primitive> extends SerializeStrategyAsync<number, DataplyTreeValue<T>> {
|
|
5
|
-
protected readonly api: DocumentDataplyAPI<any>;
|
|
6
|
-
protected readonly txContext: DocumentDataplyAPI<any>['txContext'];
|
|
5
|
+
protected readonly api: DocumentDataplyAPI<any, any>;
|
|
6
|
+
protected readonly txContext: DocumentDataplyAPI<any, any>['txContext'];
|
|
7
7
|
readonly treeKey: string;
|
|
8
|
-
constructor(order: number, api: DocumentDataplyAPI<any>, txContext: DocumentDataplyAPI<any>['txContext'], treeKey: string);
|
|
8
|
+
constructor(order: number, api: DocumentDataplyAPI<any, any>, txContext: DocumentDataplyAPI<any, any>['txContext'], treeKey: string);
|
|
9
9
|
id(isLeaf: boolean): Promise<string>;
|
|
10
10
|
read(id: string): Promise<BPTreeNode<number, DataplyTreeValue<T>>>;
|
|
11
11
|
write(id: string, node: BPTreeNode<number, DataplyTreeValue<T>>): Promise<void>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { DataplyTreeValue, DocumentDataplyInnerMetadata, DocumentDataplyOptions, DocumentJSON, FlattenedDocumentJSON, Primitive, DocumentDataplyQuery,
|
|
1
|
+
import type { DataplyTreeValue, DocumentDataplyInnerMetadata, DocumentDataplyOptions, DocumentJSON, FlattenedDocumentJSON, Primitive, DocumentDataplyQuery, DocumentDataplyIndexedQuery, DocumentDataplyCondition, DataplyDocument, DocumentDataplyMetadata, DocumentDataplyQueryOptions, IndexConfig } from '../types';
|
|
2
2
|
import { DataplyAPI, Transaction, BPTreeAsync } from 'dataply';
|
|
3
3
|
import { DocumentValueComparator } from './bptree/documentComparator';
|
|
4
|
-
export declare class DocumentDataplyAPI<T extends DocumentJSON
|
|
4
|
+
export declare class DocumentDataplyAPI<T extends DocumentJSON, IC extends IndexConfig<T>> extends DataplyAPI {
|
|
5
5
|
runWithDefault: <T_1>(callback: (tx: Transaction) => Promise<T_1>, tx?: Transaction) => Promise<T_1>;
|
|
6
6
|
streamWithDefault: <T_1>(callback: (tx: Transaction) => AsyncGenerator<T_1>, tx?: Transaction) => AsyncGenerator<T_1>;
|
|
7
7
|
indices: DocumentDataplyInnerMetadata['indices'];
|
|
@@ -9,7 +9,7 @@ export declare class DocumentDataplyAPI<T extends DocumentJSON> extends DataplyA
|
|
|
9
9
|
readonly comparator: DocumentValueComparator<DataplyTreeValue<Primitive>, Primitive>;
|
|
10
10
|
private pendingBackfillFields;
|
|
11
11
|
private readonly lock;
|
|
12
|
-
constructor(file: string, options: DocumentDataplyOptions<T>);
|
|
12
|
+
constructor(file: string, options: DocumentDataplyOptions<T, IC>);
|
|
13
13
|
readLock<T>(fn: () => T): Promise<T>;
|
|
14
14
|
writeLock<T>(fn: () => T): Promise<T>;
|
|
15
15
|
getDocument(pk: number, tx?: Transaction): Promise<DataplyDocument<T>>;
|
|
@@ -34,11 +34,38 @@ export declare class DocumentDataplyAPI<T extends DocumentJSON> extends DataplyA
|
|
|
34
34
|
getDocumentInnerMetadata(tx: Transaction): Promise<DocumentDataplyInnerMetadata>;
|
|
35
35
|
updateDocumentInnerMetadata(metadata: DocumentDataplyInnerMetadata, tx: Transaction): Promise<void>;
|
|
36
36
|
}
|
|
37
|
-
export declare class DocumentDataply<T extends DocumentJSON,
|
|
38
|
-
|
|
37
|
+
export declare class DocumentDataply<T extends DocumentJSON, IC extends IndexConfig<T>> {
|
|
38
|
+
/**
|
|
39
|
+
* Starts the database definition by setting the document type.
|
|
40
|
+
* This is used to ensure TypeScript type inference works correctly for the document structure.
|
|
41
|
+
* @template T The structure of the document to be stored.
|
|
42
|
+
*/
|
|
43
|
+
static Define<T extends DocumentJSON>(): {
|
|
44
|
+
/**
|
|
45
|
+
* Sets the options for the database, such as index configurations and WAL settings.
|
|
46
|
+
* @template IC The configuration of indices.
|
|
47
|
+
* @param options The database initialization options.
|
|
48
|
+
*/
|
|
49
|
+
Options: <IC extends IndexConfig<T>>(options: DocumentDataplyOptions<T, IC>) => {
|
|
50
|
+
/**
|
|
51
|
+
* Creates or opens the database instance with the specified file path.
|
|
52
|
+
* @param file The path to the database file.
|
|
53
|
+
*/
|
|
54
|
+
Open: (file: string) => DocumentDataply<T, IC>;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Internal method used by the Define-chain to pass options.
|
|
59
|
+
*/
|
|
60
|
+
private static Options;
|
|
61
|
+
/**
|
|
62
|
+
* Internal method used to finalize construction and create the instance.
|
|
63
|
+
*/
|
|
64
|
+
private static Open;
|
|
65
|
+
protected readonly api: DocumentDataplyAPI<T, IC>;
|
|
39
66
|
private readonly indexedFields;
|
|
40
67
|
private readonly operatorConverters;
|
|
41
|
-
constructor(file: string, options?: DocumentDataplyOptions<T>);
|
|
68
|
+
protected constructor(file: string, options?: DocumentDataplyOptions<T, IC>);
|
|
42
69
|
/**
|
|
43
70
|
* Initialize the document database
|
|
44
71
|
*/
|
|
@@ -58,7 +85,7 @@ export declare class DocumentDataply<T extends DocumentJSON, I extends string =
|
|
|
58
85
|
* @param orderByField Optional field name for orderBy optimization
|
|
59
86
|
* @returns Driver and other candidates for query execution
|
|
60
87
|
*/
|
|
61
|
-
getSelectivityCandidate<U extends Partial<
|
|
88
|
+
getSelectivityCandidate<U extends Partial<DocumentDataplyIndexedQuery<T, IC>>, V extends DataplyTreeValue<U>>(query: Partial<DocumentDataplyQuery<V>>, orderByField?: string): Promise<{
|
|
62
89
|
driver: {
|
|
63
90
|
tree: BPTreeAsync<number, V>;
|
|
64
91
|
condition: Partial<DocumentDataplyCondition<U>>;
|
|
@@ -85,6 +112,37 @@ export declare class DocumentDataply<T extends DocumentJSON, I extends string =
|
|
|
85
112
|
* @returns The primary keys of the inserted documents
|
|
86
113
|
*/
|
|
87
114
|
insertBatch(documents: T[], tx?: Transaction): Promise<number[]>;
|
|
115
|
+
/**
|
|
116
|
+
* Internal update method used by both fullUpdate and partialUpdate
|
|
117
|
+
* @param query The query to use
|
|
118
|
+
* @param computeUpdatedDoc Function that computes the updated document from the original
|
|
119
|
+
* @param tx The transaction to use
|
|
120
|
+
* @returns The number of updated documents
|
|
121
|
+
*/
|
|
122
|
+
private updateInternal;
|
|
123
|
+
/**
|
|
124
|
+
* Fully update documents from the database that match the query
|
|
125
|
+
* @param query The query to use (only indexed fields + _id allowed)
|
|
126
|
+
* @param newRecord Complete document to replace with, or function that receives current document and returns new document
|
|
127
|
+
* @param tx The transaction to use
|
|
128
|
+
* @returns The number of updated documents
|
|
129
|
+
*/
|
|
130
|
+
fullUpdate(query: Partial<DocumentDataplyIndexedQuery<T, IC>>, newRecord: T | ((document: DataplyDocument<T>) => T), tx?: Transaction): Promise<number>;
|
|
131
|
+
/**
|
|
132
|
+
* Partially update documents from the database that match the query
|
|
133
|
+
* @param query The query to use (only indexed fields + _id allowed)
|
|
134
|
+
* @param newRecord Partial document to merge, or function that receives current document and returns partial update
|
|
135
|
+
* @param tx The transaction to use
|
|
136
|
+
* @returns The number of updated documents
|
|
137
|
+
*/
|
|
138
|
+
partialUpdate(query: Partial<DocumentDataplyIndexedQuery<T, IC>>, newRecord: Partial<DataplyDocument<T>> | ((document: DataplyDocument<T>) => Partial<DataplyDocument<T>>), tx?: Transaction): Promise<number>;
|
|
139
|
+
/**
|
|
140
|
+
* Delete documents from the database that match the query
|
|
141
|
+
* @param query The query to use (only indexed fields + _id allowed)
|
|
142
|
+
* @param tx The transaction to use
|
|
143
|
+
* @returns The number of deleted documents
|
|
144
|
+
*/
|
|
145
|
+
delete(query: Partial<DocumentDataplyIndexedQuery<T, IC>>, tx?: Transaction): Promise<number>;
|
|
88
146
|
/**
|
|
89
147
|
* Select documents from the database
|
|
90
148
|
* @param query The query to use (only indexed fields + _id allowed)
|
|
@@ -93,7 +151,7 @@ export declare class DocumentDataply<T extends DocumentJSON, I extends string =
|
|
|
93
151
|
* @returns The documents that match the query
|
|
94
152
|
* @throws Error if query or orderBy contains non-indexed fields
|
|
95
153
|
*/
|
|
96
|
-
select(query: Partial<
|
|
154
|
+
select(query: Partial<DocumentDataplyIndexedQuery<T, IC>>, options?: DocumentDataplyQueryOptions<T, IC>, tx?: Transaction): {
|
|
97
155
|
stream: AsyncIterableIterator<DataplyDocument<T>>;
|
|
98
156
|
drain: () => Promise<DataplyDocument<T>[]>;
|
|
99
157
|
};
|
|
@@ -47,26 +47,26 @@ export type DocumentDataplyCondition<V> = {
|
|
|
47
47
|
or?: Partial<V>[];
|
|
48
48
|
like?: string;
|
|
49
49
|
};
|
|
50
|
-
export type DocumentDataplyQueryOptions<V, I extends string = string> = {
|
|
51
|
-
limit?: number;
|
|
52
|
-
orderBy?: I | '_id';
|
|
53
|
-
sortOrder?: BPTreeOrder;
|
|
54
|
-
};
|
|
55
50
|
export type DocumentDataplyQuery<T> = {
|
|
56
51
|
[key in keyof T]?: T[key] | DocumentDataplyCondition<T[key]>;
|
|
57
52
|
} & {
|
|
58
53
|
[key: string]: any;
|
|
59
54
|
};
|
|
60
55
|
/**
|
|
61
|
-
* Query type restricted to indexed fields only
|
|
56
|
+
* Query type restricted to indexed fields only
|
|
62
57
|
*/
|
|
63
|
-
export type
|
|
64
|
-
[key in
|
|
58
|
+
export type DocumentDataplyIndexedQuery<T extends DocumentJSON, IC extends IndexConfig<T>> = {
|
|
59
|
+
[key in keyof IC]: key extends keyof FinalFlatten<DataplyDocument<T>> ? FinalFlatten<DataplyDocument<T>>[key] | DocumentDataplyCondition<FinalFlatten<DataplyDocument<T>>[key]> : never;
|
|
65
60
|
};
|
|
66
61
|
export interface DataplyTreeValue<T> {
|
|
67
62
|
k: number;
|
|
68
63
|
v: T;
|
|
69
64
|
}
|
|
65
|
+
export type DocumentDataplyQueryOptions<T extends DocumentJSON, IC extends IndexConfig<T>> = {
|
|
66
|
+
limit?: number;
|
|
67
|
+
orderBy?: ExtractIndexKeys<T, IC> | '_id';
|
|
68
|
+
sortOrder?: BPTreeOrder;
|
|
69
|
+
};
|
|
70
70
|
/**
|
|
71
71
|
* T가 객체인지 확인하고, 객체라면 하위 키를 재귀적으로 탐색합니다.
|
|
72
72
|
*/
|
|
@@ -75,7 +75,7 @@ type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17
|
|
|
75
75
|
* T가 객체인지 확인하고, 객체라면 하위 키를 재귀적으로 탐색합니다.
|
|
76
76
|
* Depth 제한을 두어 "Type instantiation is excessively deep and possibly infinite" 에러를 방지합니다.
|
|
77
77
|
*/
|
|
78
|
-
export type DeepFlattenKeys<T, Prefix extends string = "", D extends number =
|
|
78
|
+
export type DeepFlattenKeys<T, Prefix extends string = "", D extends number = 5> = [
|
|
79
79
|
D
|
|
80
80
|
] extends [0] ? never : T extends Primitive ? (Prefix extends `${infer P}.` ? P : never) : T extends readonly any[] ? (DeepFlattenKeys<T[number], `${Prefix}${number}.`, Prev[D]>) : T extends object ? {
|
|
81
81
|
[K in keyof T & string]: NonNullable<T[K]> extends Primitive ? `${Prefix}${K}` : DeepFlattenKeys<NonNullable<T[K]>, `${Prefix}${K}.`, Prev[D]>;
|
|
@@ -88,15 +88,26 @@ type GetTypeByPath<T, Path extends string> = T extends readonly (infer U)[] ? Pa
|
|
|
88
88
|
export type FinalFlatten<T> = {
|
|
89
89
|
[P in DeepFlattenKeys<T>]: GetTypeByPath<T, P & string>;
|
|
90
90
|
};
|
|
91
|
-
export
|
|
91
|
+
export type DocumentDataplyIndices<T extends DocumentJSON, IC extends IndexConfig<T>> = {
|
|
92
|
+
[key in keyof IC & keyof FinalFlatten<T>]: GetTypeByPath<T, key>;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Index configuration type - keys are field names, values are boolean
|
|
96
|
+
*/
|
|
97
|
+
export type IndexConfig<T> = Partial<{
|
|
98
|
+
[key in keyof FinalFlatten<T>]: boolean;
|
|
99
|
+
}>;
|
|
100
|
+
/**
|
|
101
|
+
* Extract index keys from IndexConfig
|
|
102
|
+
*/
|
|
103
|
+
export type ExtractIndexKeys<T extends DocumentJSON, IC extends IndexConfig<T>> = keyof IC & keyof FinalFlatten<DataplyDocument<T>> & string;
|
|
104
|
+
export interface DocumentDataplyOptions<T, IC extends IndexConfig<T> = IndexConfig<T>> extends DataplyOptions {
|
|
92
105
|
/**
|
|
93
|
-
*
|
|
94
|
-
* If not specified, no
|
|
106
|
+
* Indices to create when initializing the database.
|
|
107
|
+
* If not specified, no indices will be created.
|
|
95
108
|
* If the value of the index is `true`, the index will be created for the already inserted data.
|
|
96
109
|
* If the value of the index is `false`, the index will not be created for the already inserted data.
|
|
97
110
|
*/
|
|
98
|
-
indices?:
|
|
99
|
-
[key in keyof FinalFlatten<T>]: boolean;
|
|
100
|
-
}>;
|
|
111
|
+
indices?: IC;
|
|
101
112
|
}
|
|
102
113
|
export {};
|
package/package.json
CHANGED