@dxos/index-core 0.0.0 → 0.8.4-main.59c2e9b

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.
Files changed (42) hide show
  1. package/dist/lib/browser/index.mjs +559 -0
  2. package/dist/lib/browser/index.mjs.map +7 -0
  3. package/dist/lib/browser/meta.json +1 -0
  4. package/dist/lib/node-esm/index.mjs +561 -0
  5. package/dist/lib/node-esm/index.mjs.map +7 -0
  6. package/dist/lib/node-esm/meta.json +1 -0
  7. package/dist/types/src/index-engine.d.ts +63 -0
  8. package/dist/types/src/index-engine.d.ts.map +1 -0
  9. package/dist/types/src/index-engine.test.d.ts +2 -0
  10. package/dist/types/src/index-engine.test.d.ts.map +1 -0
  11. package/dist/types/src/index-tracker.d.ts +44 -0
  12. package/dist/types/src/index-tracker.d.ts.map +1 -0
  13. package/dist/types/src/index-tracker.test.d.ts +2 -0
  14. package/dist/types/src/index-tracker.test.d.ts.map +1 -0
  15. package/dist/types/src/index.d.ts +7 -0
  16. package/dist/types/src/index.d.ts.map +1 -0
  17. package/dist/types/src/indexes/fts-index.d.ts +63 -0
  18. package/dist/types/src/indexes/fts-index.d.ts.map +1 -0
  19. package/dist/types/src/indexes/fts-index.test.d.ts +2 -0
  20. package/dist/types/src/indexes/fts-index.test.d.ts.map +1 -0
  21. package/dist/types/src/indexes/fts5.test.d.ts +2 -0
  22. package/dist/types/src/indexes/fts5.test.d.ts.map +1 -0
  23. package/dist/types/src/indexes/index.d.ts +5 -0
  24. package/dist/types/src/indexes/index.d.ts.map +1 -0
  25. package/dist/types/src/indexes/interface.d.ts +47 -0
  26. package/dist/types/src/indexes/interface.d.ts.map +1 -0
  27. package/dist/types/src/indexes/object-meta-index.d.ts +37 -0
  28. package/dist/types/src/indexes/object-meta-index.d.ts.map +1 -0
  29. package/dist/types/src/indexes/object-meta-index.test.d.ts +2 -0
  30. package/dist/types/src/indexes/object-meta-index.test.d.ts.map +1 -0
  31. package/dist/types/src/indexes/reverse-ref-index.d.ts +37 -0
  32. package/dist/types/src/indexes/reverse-ref-index.d.ts.map +1 -0
  33. package/dist/types/src/indexes/reverse-ref-index.test.d.ts +2 -0
  34. package/dist/types/src/indexes/reverse-ref-index.test.d.ts.map +1 -0
  35. package/dist/types/src/utils.d.ts +17 -0
  36. package/dist/types/src/utils.d.ts.map +1 -0
  37. package/dist/types/tsconfig.tsbuildinfo +1 -0
  38. package/package.json +7 -2
  39. package/src/index-engine.test.ts +8 -5
  40. package/src/index-engine.ts +18 -7
  41. package/src/index.ts +2 -2
  42. package/src/indexes/fts-index.ts +41 -2
package/package.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "@dxos/index-core",
3
- "version": "0.0.0",
3
+ "version": "0.8.4-main.59c2e9b",
4
4
  "description": "Indexing core.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/dxos/dxos"
10
+ },
7
11
  "license": "MIT",
8
12
  "author": "info@dxos.org",
9
13
  "sideEffects": false,
@@ -34,7 +38,8 @@
34
38
  "@effect/experimental": "0.57.11",
35
39
  "@effect/platform": "0.93.6",
36
40
  "@effect/sql": "0.48.6",
37
- "effect": "3.19.11"
41
+ "effect": "3.19.11",
42
+ "@dxos/sql-sqlite": "0.8.4-main.59c2e9b"
38
43
  },
39
44
  "devDependencies": {
40
45
  "@effect/sql-sqlite-node": "0.49.1"
@@ -11,6 +11,7 @@ import * as Layer from 'effect/Layer';
11
11
  import { ATTR_TYPE } from '@dxos/echo/internal';
12
12
  import { invariant } from '@dxos/invariant';
13
13
  import { DXN, ObjectId, SpaceId } from '@dxos/keys';
14
+ import * as SqlTransaction from '@dxos/sql-sqlite/SqlTransaction';
14
15
 
15
16
  import { type DataSourceCursor, type IndexDataSource, IndexEngine } from './index-engine';
16
17
  import { type IndexCursor, IndexTracker } from './index-tracker';
@@ -20,11 +21,13 @@ const TYPE_DEFAULT = DXN.parse('dxn:type:test.com/type/Type:0.1.0').toString();
20
21
  const TYPE_A = DXN.parse('dxn:type:test.com/type/TypeA:0.1.0').toString();
21
22
  const TYPE_B = DXN.parse('dxn:type:test.com/type/TypeB:0.1.0').toString();
22
23
 
23
- const TestLayer = Layer.merge(
24
- SqliteClient.layer({
25
- filename: ':memory:',
26
- }),
27
- Reactivity.layer,
24
+ const TestLayer = SqlTransaction.layer.pipe(
25
+ Layer.provideMerge(
26
+ SqliteClient.layer({
27
+ filename: ':memory:',
28
+ }),
29
+ ),
30
+ Layer.provideMerge(Reactivity.layer),
28
31
  );
29
32
 
30
33
  class MockIndexDataSource implements IndexDataSource {
@@ -2,16 +2,18 @@
2
2
  // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
- import * as SqlClient from '@effect/sql/SqlClient';
5
+ import type * as SqlClient from '@effect/sql/SqlClient';
6
6
  import type * as SqlError from '@effect/sql/SqlError';
7
7
  import * as Effect from 'effect/Effect';
8
8
 
9
9
  import type { SpaceId } from '@dxos/keys';
10
+ import * as SqlTransaction from '@dxos/sql-sqlite/SqlTransaction';
10
11
 
11
12
  import { type IndexCursor, IndexTracker } from './index-tracker';
12
13
  import {
13
14
  FtsIndex,
14
15
  type FtsQuery,
16
+ type FtsQueryResult,
15
17
  type Index,
16
18
  type IndexerObject,
17
19
  type ObjectMeta,
@@ -76,9 +78,9 @@ export class IndexEngine {
76
78
  }
77
79
 
78
80
  /**
79
- * Query text index and return full object metadata.
81
+ * Query text index and return full object metadata with rank.
80
82
  */
81
- queryText(query: FtsQuery): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> {
83
+ queryText(query: FtsQuery): Effect.Effect<readonly FtsQueryResult[], SqlError.SqlError, SqlClient.SqlClient> {
82
84
  return Effect.gen(this, function* () {
83
85
  return yield* this.#ftsIndex.query(query);
84
86
  });
@@ -106,7 +108,11 @@ export class IndexEngine {
106
108
  update(
107
109
  dataSource: IndexDataSource,
108
110
  opts: { spaceId: SpaceId | null; limit?: number },
109
- ): Effect.Effect<{ updated: number; done: boolean }, SqlError.SqlError, SqlClient.SqlClient> {
111
+ ): Effect.Effect<
112
+ { updated: number; done: boolean },
113
+ SqlError.SqlError,
114
+ SqlTransaction.SqlTransaction | SqlClient.SqlClient
115
+ > {
110
116
  return Effect.gen(this, function* () {
111
117
  let updated = 0;
112
118
 
@@ -145,10 +151,15 @@ export class IndexEngine {
145
151
  index: Index,
146
152
  source: IndexDataSource,
147
153
  opts: { indexName: string; spaceId: SpaceId | null; limit?: number },
148
- ): Effect.Effect<{ updated: number; done: boolean }, SqlError.SqlError, SqlClient.SqlClient> {
154
+ ): Effect.Effect<
155
+ { updated: number; done: boolean },
156
+ SqlError.SqlError,
157
+ SqlTransaction.SqlTransaction | SqlClient.SqlClient
158
+ > {
149
159
  return Effect.gen(this, function* () {
150
- const sql = yield* SqlClient.SqlClient;
151
- return yield* sql.withTransaction(
160
+ const sqlTransaction = yield* SqlTransaction.SqlTransaction;
161
+
162
+ return yield* sqlTransaction.withTransaction(
152
163
  Effect.gen(this, function* () {
153
164
  const cursors = yield* this.#tracker.queryCursors({
154
165
  indexName: opts.indexName,
package/src/index.ts CHANGED
@@ -5,6 +5,6 @@
5
5
  export { IndexEngine, type IndexDataSource, type DataSourceCursor, type IndexEngineParams } from './index-engine';
6
6
  export { IndexTracker, type IndexCursor } from './index-tracker';
7
7
  export { type IndexerObject, type Index } from './indexes/interface';
8
- export { FtsIndex } from './indexes/fts-index';
8
+ export { FtsIndex, type FtsQuery } from './indexes/fts-index';
9
9
  export { ObjectMetaIndex, type ObjectMeta } from './indexes/object-meta-index';
10
- export { ReverseRefIndex, type ReverseRef } from './indexes/reverse-ref-index';
10
+ export { ReverseRefIndex, type ReverseRef, type ReverseRefQuery } from './indexes/reverse-ref-index';
@@ -49,6 +49,18 @@ export interface FtsResult extends ObjectMeta {
49
49
  snapshot: string;
50
50
  }
51
51
 
52
+ /**
53
+ * Result of FTS query with rank.
54
+ */
55
+ export interface FtsQueryResult extends ObjectMeta {
56
+ /**
57
+ * Relevance rank from FTS5.
58
+ * Higher values indicate better matches.
59
+ * Uses BM25 algorithm when available, falls back to 1 for non-BM25 queries.
60
+ */
61
+ rank: number;
62
+ }
63
+
52
64
  /**
53
65
  * Escapes user input for safe FTS5 queries.
54
66
  *
@@ -92,7 +104,7 @@ export class FtsIndex implements Index {
92
104
  spaceId,
93
105
  includeAllQueues,
94
106
  queueIds,
95
- }: FtsQuery): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> {
107
+ }: FtsQuery): Effect.Effect<readonly FtsQueryResult[], SqlError.SqlError, SqlClient.SqlClient> {
96
108
  return Effect.gen(function* () {
97
109
  const trimmed = query.trim();
98
110
  if (trimmed.length === 0) {
@@ -106,6 +118,11 @@ export class FtsIndex implements Index {
106
118
  const terms = trimmed.split(/\s+/).filter(Boolean);
107
119
  const minTermLength = Math.min(...terms.map((t) => t.length));
108
120
 
121
+ // Use BM25 ranking for FTS5 MATCH queries, fall back to rank 1 for LIKE queries.
122
+ // BM25 returns negative values where lower (more negative) means better match,
123
+ // so we negate it to get higher = better.
124
+ const useBm25 = minTermLength >= 3;
125
+
109
126
  const conditions =
110
127
  minTermLength < 3
111
128
  ? // LIKE fallback - scan the entire table, AND all terms.
@@ -135,7 +152,29 @@ export class FtsIndex implements Index {
135
152
  conditions.push(sql`(${sql.or(sourceConditions)})`);
136
153
  }
137
154
 
138
- return yield* sql<ObjectMeta>`SELECT m.* FROM ftsIndex AS f JOIN objectMeta AS m ON f.rowid = m.recordId WHERE ${sql.and(conditions)}`;
155
+ if (useBm25) {
156
+ // Use BM25 ranking for FTS5 MATCH queries.
157
+ // BM25 returns negative values, negate to get higher = better match.
158
+ // Order by rank descending so best matches come first.
159
+ // Note: bm25() requires the actual table name, not an alias.
160
+ const rows = yield* sql<ObjectMeta & { rank: number }>`
161
+ SELECT m.*, -bm25(ftsIndex) AS rank
162
+ FROM ftsIndex AS f
163
+ JOIN objectMeta AS m ON f.rowid = m.recordId
164
+ WHERE ${sql.and(conditions)}
165
+ ORDER BY rank DESC
166
+ `;
167
+ return rows;
168
+ } else {
169
+ // LIKE fallback - no ranking available, default to 1.
170
+ const rows = yield* sql<ObjectMeta>`
171
+ SELECT m.*
172
+ FROM ftsIndex AS f
173
+ JOIN objectMeta AS m ON f.rowid = m.recordId
174
+ WHERE ${sql.and(conditions)}
175
+ `;
176
+ return rows.map((row) => ({ ...row, rank: 1 }));
177
+ }
139
178
  });
140
179
  }
141
180