Skip to content

Commit 95f8a41

Browse files
authored
Added complex r/w-aware Table & Collection types (#87)
* advanced typings for tables/colls; typings for includeSimialrity * fixed tables typing test file * update readme
1 parent f31127b commit 95f8a41

36 files changed

+646
-803
lines changed

README.md

Lines changed: 74 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -27,71 +27,55 @@ Get the *API endpoint* and your *application token* for your Astra DB instance @
2727
### Collections
2828

2929
```typescript
30-
import { DataAPIClient, VectorDoc, UUID, ObjectId } from '@datastax/astra-db-ts';
31-
32-
// Schema for the collections (VectorDoc adds the $vector field)
33-
interface Idea extends VectorDoc {
34-
idea: string,
35-
}
30+
import { DataAPIClient, ObjectId, vector, VectorDoc, oid } from '@datastax/astra-db-ts';
3631

3732
// Connect to the db
38-
const client = new DataAPIClient('*TOKEN*');
39-
const db = client.db('*ENDPOINT*', { namespace: '*NAMESPACE*' });
33+
const client = new DataAPIClient({ logging: 'all' });
34+
const db = client.db(process.env.CLIENT_DB_URL!, { token: process.env.CLIENT_DB_TOKEN! });
35+
36+
// The `VectorDoc` interface adds `$vector?: DataAPIVector` as a field to the collection type
37+
interface Dream extends VectorDoc {
38+
_id: ObjectId, // Uses an `astra-db-ts` provided type here (NOT the `bson` version)
39+
summary: string,
40+
tags?: string[], // No sets/maps available without creating custom ser/des rules
41+
}
4042

4143
(async () => {
42-
// Creates collections, or gets it if it already exists with same options
43-
const collection = await db.createCollection<Idea>('vector_5_collection', {
44-
vector: {
45-
dimension: 5,
46-
metric: 'cosine',
47-
},
48-
checkExists: false,
44+
// Create the table using our helper function.
45+
// The _id should be an `ObjectId` type, as specified by `defaultId.type`
46+
const collection = await db.createCollection<Dream>('dreams', {
47+
defaultId: { type: 'objectId' },
4948
});
5049

51-
// Insert many ideas into the collections
52-
const ideas = [
53-
{
54-
idea: 'An AI quilt to help you sleep forever',
55-
$vector: [0.1, 0.15, 0.3, 0.12, 0.05],
56-
},
57-
{
58-
_id: new UUID('e7f1f3a0-7e3d-11eb-9439-0242ac130002'),
59-
idea: 'Vision Vector Frame—A deep learning display that controls your mood',
60-
$vector: [0.1, 0.05, 0.08, 0.3, 0.6],
61-
},
62-
{
63-
idea: 'A smartwatch that tells you what to eat based on your mood',
64-
$vector: [0.2, 0.3, 0.1, 0.4, 0.15],
65-
},
66-
];
67-
await collection.insertMany(ideas);
68-
69-
// Insert a specific idea into the collections
70-
const sneakersIdea = {
71-
_id: new ObjectId('507f191e810c19729de860ea'),
72-
idea: 'ChatGPT-integrated sneakers that talk to you',
73-
$vector: [0.45, 0.09, 0.01, 0.2, 0.11],
74-
}
75-
await collection.insertOne(sneakersIdea);
50+
// Batch-insert some rows into the table
51+
// _id can be optionally provided, or be auto-generated @ the server side
52+
await collection.insertMany([{
53+
summary: 'A dinner on the Moon',
54+
$vector: vector([0.2, -0.3, -0.5]), // Shorthand for `new DataAPIVector([0.2, -0.3, -0.5])`
55+
}, {
56+
summary: 'Riding the waves',
57+
$vector: vector([0, 0.2, 1]),
58+
tags: ['sport'],
59+
}, {
60+
_id: oid('674f0f5c1c162131319fa09e'), // Shorthand for `new ObjectId('674f0f5c1c162131319fa09e')`
61+
summary: 'Meeting Beethoven at the dentist',
62+
$vector: vector([0.2, 0.6, 0]),
63+
}]);
7664

77-
// Actually, let's change that idea
78-
await collection.updateOne(
79-
{ _id: sneakersIdea._id },
80-
{ $set: { idea: 'Gemini-integrated sneakers that talk to you' } },
81-
);
65+
// Hm, changed my mind
66+
await collection.updateOne({ id: 103 }, { $set: { summary: 'Surfers\' paradise' } });
8267

83-
// Get similar results as desired
84-
const cursor = collection.find({}, {
85-
vector: [0.1, 0.15, 0.3, 0.12, 0.05],
86-
includeSimilarity: true,
87-
limit: 2,
88-
});
68+
// Let's see what we've got
69+
const cursor = collection.find({})
70+
.sort({ vector: vector([0, 0.2, 0.4]) }) // Performing a vector search
71+
.includeSimilarity(true) // The found doc is inferred to have `$similarity` as a property now
72+
.limit(2);
8973

90-
for await (const doc of cursor) {
91-
// Prints the following:
92-
// - An AI quilt to help you sleep forever: 1
93-
// - A smartwatch that tells you what to eat based on your mood: 0.85490346
94-
console.log(`${doc.idea}: ${doc.$similarity}`);
74+
// This would print:
75+
// - Surfers' paradise: 0.98238194
76+
// - Friendly aliens in town: 0.91873914
77+
for await (const result of cursor) {
78+
console.log(`${result.summary}: ${result.$similarity}`);
9579
}
9680

9781
// Cleanup (if desired)
@@ -102,57 +86,59 @@ const db = client.db('*ENDPOINT*', { namespace: '*NAMESPACE*' });
10286
### Tables
10387

10488
```typescript
105-
import { CreateTableDefinition, DataAPIClient, InferTableSchema, vector } from '@datastax/astra-db-ts';
89+
import { DataAPIClient, InferTableSchema, vector } from '@datastax/astra-db-ts';
10690

10791
// Connect to the db
108-
const client = new DataAPIClient();
109-
const db = client.db('https://178cf75b-ec19-4d13-9ca7-b6dcfa613157-us-west-2.apps.astra-dev.datastax.com', { token: 'AstraCS:EFZEBUXuMzwyFntFYcroNsWQ:e473c9b244253e3c873346cc8584d4767789d93e54c6c5453c996fbd5ab4605d' });
110-
111-
// Example table schema using bespoke Data API table definition syntax
112-
const TableDefinition = <const>{
113-
columns: {
114-
id: 'int',
115-
summary: 'text',
116-
tags: { type: 'set', valueType: 'text' },
117-
vector: { type: 'vector', dimension: 3 },
118-
},
119-
primaryKey: {
120-
partitionBy: ['id'],
92+
const client = new DataAPIClient({ logging: 'all' });
93+
const db = client.db(process.env.CLIENT_DB_URL!, { token: process.env.CLIENT_DB_TOKEN! });
94+
95+
// Create a table through the Data API if it does not yet exist.
96+
// Returns the created table through a function so we can use the inferred type of the table ourselves
97+
// (instead of having to manually define it)
98+
const mkDreamsTable = async () => await db.createTable('dreams', {
99+
definition: {
100+
columns: {
101+
id: 'int', // Shorthand notation for { type: 'int' }
102+
summary: 'text',
103+
tags: { type: 'set', valueType: 'text' }, // Collection types require additional type information
104+
vector: { type: 'vector', dimension: 3 }, // Auto-embedding-generation can be enabled through a `service` block
105+
},
106+
primaryKey: 'id', // Shorthand for { partitionBy: ['id'] }
121107
},
122-
} satisfies CreateTableDefinition;
108+
ifNotExists: true, // If any table with the same name exists, do nothing
109+
}); // (note that this does not check if the tables are the same)
123110

124111
// Infer the TS-equivalent type from the table definition (like zod or arktype). Equivalent to:
125112
//
126-
// interface TableSchema extends Row<'id'> { // Types 'id' as a primary key for insertion command return types
127-
// summary?: string | null; // Not a primary key, so it's optional and may return as null when found
128-
// tags?: Set<string>; // Sets/maps/lists are optional to insert, but will actually be returned as empty collections instead of null
129-
// vector?: DataAPIVector | null; // Vectors, however, may be null.
113+
// interface TableSchema extends Row<'id'> {
114+
// id: number, -- A primary key component, so it's required
115+
// summary?: string | null, -- Not a primary key, so it's optional and may return as null when found
116+
// tags?: Set<string>, -- Sets/maps/lists are optional to insert, but will actually be returned as empty collections instead of null
117+
// vector?: DataAPIVector | null, -- Vectors, however, may be null.
130118
// }
131-
type TableSchema = InferTableSchema<typeof TableDefinition>;
119+
type Dream = InferTableSchema<typeof mkDreamsTable>;
132120

133121
(async () => {
134-
// Create the table if it doesn't alredy exist
135-
const table = await db.createTable('dreams', {
136-
definition: TableDefinition,
137-
ifNotExists: true,
138-
});
122+
// Create the table using our helper function.
123+
// Table will be typed as `Table<Dream, { id: number }>`, where the former is the schema, and the latter is the primary key
124+
const table = await mkDreamsTable();
139125

140126
// Enables vector search on the table (on the 'vector' column)
141127
await table.createVectorIndex('dreams_vector_idx', 'vector', {
142128
options: { metric: 'cosine' },
143129
ifNotExists: true,
144130
});
145131

146-
// Insert some rows into the table
147-
const rows: TableSchema[] = [{
132+
// Batch-insert some rows into the table
133+
const rows: Dream[] = [{
148134
id: 102,
149135
summary: 'A dinner on the Moon',
150136
vector: vector([0.2, -0.3, -0.5]), // Shorthand for `new DataAPIVector([0.2, -0.3, -0.5])`
151137
}, {
152138
id: 103,
153139
summary: 'Riding the waves',
154-
tags: new Set(['sport']),
155140
vector: vector([0, 0.2, 1]),
141+
tags: new Set(['sport']), // Collection types use native JS collections
156142
}, {
157143
id: 37,
158144
summary: 'Meeting Beethoven at the dentist',
@@ -164,11 +150,10 @@ type TableSchema = InferTableSchema<typeof TableDefinition>;
164150
await table.updateOne({ id: 103 }, { $set: { summary: 'Surfers\' paradise' } });
165151

166152
// Let's see what we've got
167-
const cursor = table.find({}, {
168-
sort: { vector: vector([0, 0.2, 0.4]) },
169-
includeSimilarity: true,
170-
limit: 2,
171-
});
153+
const cursor = table.find({})
154+
.sort({ vector: vector([0, 0.2, 0.4]) }) // Performing a vector search
155+
.includeSimilarity(true) // The found doc is inferred to have `$similarity` as a property now
156+
.limit(2);
172157

173158
// This would print:
174159
// - Surfers' paradise: 0.98238194

0 commit comments

Comments
 (0)