@hollware/server 0.4.0 → 0.5.0

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.
@@ -558,6 +558,47 @@ const reviews = sqliteTable("reviews", {
558
558
  check("reviews_decided", sql`(${t.status} IN ('pending', 'cancelled')) OR (${t.decidedBy} IS NOT NULL AND ${t.decidedAt} IS NOT NULL)`),
559
559
  check("reviews_json", sql`json_valid(${t.acknowledged}) AND json_type(${t.acknowledged}) = 'array' AND (${t.requestedMeta} IS NULL OR json_valid(${t.requestedMeta}))`)
560
560
  ]);
561
+ /**
562
+ * The vectors of an entry, as derived data (v0.5): the published revision they were made from,
563
+ * with which model and which version of the chunker, and how many pieces there are. The vectors
564
+ * live in Vectorize under `<entry id>:<piece>`; this row says which exist and whether they are
565
+ * current. One row per entry: a new publication replaces it.
566
+ */
567
+ const entryEmbeddings = sqliteTable("entry_embeddings", {
568
+ id: id("emb"),
569
+ siteId: siteId(),
570
+ entryId: text("entry_id").notNull(),
571
+ revisionId: text("revision_id").notNull(),
572
+ model: text("model").notNull(),
573
+ chunkerVersion: text("chunker_version").notNull(),
574
+ /** How many pieces, so how many vectors: `<entry id>:0` to `<entry id>:<pieces - 1>`. */
575
+ pieces: integer("pieces").notNull(),
576
+ /**
577
+ * The entries closest to this one by their summaries, in its collection and locale, closest
578
+ * first, as JSON: `[{ "id": "ent_…", "score": 0.91 }]`. Found when its vectors were made.
579
+ */
580
+ related: text("related").notNull().default("[]"),
581
+ ...timestamps()
582
+ }, (t) => [
583
+ unique("entry_embeddings_id_site").on(t.id, t.siteId),
584
+ unique("entry_embeddings_entry").on(t.siteId, t.entryId),
585
+ foreignKey({
586
+ name: "entry_embeddings_site",
587
+ columns: [t.siteId],
588
+ foreignColumns: [sites.id]
589
+ }),
590
+ foreignKey({
591
+ name: "entry_embeddings_entry_fk",
592
+ columns: [t.entryId, t.siteId],
593
+ foreignColumns: [entries.id, entries.siteId]
594
+ }).onDelete("cascade"),
595
+ foreignKey({
596
+ name: "entry_embeddings_revision_fk",
597
+ columns: [t.revisionId, t.siteId],
598
+ foreignColumns: [revisions.id, revisions.siteId]
599
+ }),
600
+ check("entry_embeddings_pieces", sql`${t.pieces} >= 1`)
601
+ ]);
561
602
  //#endregion
562
603
  //#region src/identity/secrets.ts
563
604
  /**
@@ -757,4 +798,4 @@ async function invitationById(db, id) {
757
798
  };
758
799
  }
759
800
  //#endregion
760
- export { effectiveScopes as A, revisionTags as C, tokens as D, sites as E, users as O, reviews as S, schedules as T, entries as _, findInvitation as a, outbox as b, invitationById as c, redeemInvitation as d, saveChallenge as f, deliveries as g, userHandle as h, createUser as i, isRole as j, ROLE_SCOPES as k, membershipsOf as l, touchPasskey as m, createInvitation as n, findPasskey as o, takeChallenge as p, createSession as r, findSession as s, addPasskey as t, passkeysOf as u, invitations as v, revisions as w, publications as x, members as y };
801
+ export { ROLE_SCOPES as A, reviews as C, sites as D, schedules as E, isRole as M, tokens as O, publications as S, revisions as T, entries as _, findInvitation as a, members as b, invitationById as c, redeemInvitation as d, saveChallenge as f, deliveries as g, userHandle as h, createUser as i, effectiveScopes as j, users as k, membershipsOf as l, touchPasskey as m, createInvitation as n, findPasskey as o, takeChallenge as p, createSession as r, findSession as s, addPasskey as t, passkeysOf as u, entryEmbeddings as v, revisionTags as w, outbox as x, invitations as y };
@@ -0,0 +1,47 @@
1
+ -- Written by hand (ADR-023), as drizzle-kit has no virtual tables. Full-text search (v0.5): an
2
+ -- FTS5 index of the published revision of each entry, its title, its tags and its text. The text
3
+ -- is taken from the stored Comark document, the strings at the text positions of its nodes, so
4
+ -- the Markdown's own marks are never indexed. `unicode61 remove_diacritics 2` matches
5
+ -- "migracion" with "migración".
6
+ --
7
+ -- The triggers keep it in the step that changes what is published, whichever write does it:
8
+ -- publish, a group, a schedule that comes due, unpublish. Only published content is in it.
9
+ CREATE VIRTUAL TABLE `entries_fts` USING fts5(
10
+ `title`,
11
+ `tags`,
12
+ `body`,
13
+ `entry_id` UNINDEXED,
14
+ `site_id` UNINDEXED,
15
+ `collection` UNINDEXED,
16
+ `locale` UNINDEXED,
17
+ tokenize = 'unicode61 remove_diacritics 2'
18
+ );
19
+ --> statement-breakpoint
20
+ CREATE TRIGGER `entries_fts_publication` AFTER UPDATE OF `published_rev` ON `entries`
21
+ BEGIN
22
+ DELETE FROM `entries_fts` WHERE `entry_id` = OLD.`id`;
23
+ INSERT INTO `entries_fts` (`title`, `tags`, `body`, `entry_id`, `site_id`, `collection`, `locale`)
24
+ SELECT
25
+ coalesce(json_extract(r.`frontmatter`, '$.title'), ''),
26
+ coalesce((SELECT group_concat(t.`tag`, ' ') FROM `revision_tags` t WHERE t.`revision_id` = r.`id`), ''),
27
+ coalesce((SELECT group_concat(n.`value`, ' ') FROM json_tree(r.`document`, '$.nodes') n
28
+ WHERE n.`type` = 'text' AND typeof(n.`key`) = 'integer' AND n.`key` >= 2), ''),
29
+ NEW.`id`, NEW.`site_id`, NEW.`collection`, NEW.`locale`
30
+ FROM `revisions` r
31
+ WHERE NEW.`published_rev` IS NOT NULL AND r.`id` = NEW.`published_rev`;
32
+ END;
33
+ --> statement-breakpoint
34
+ CREATE TRIGGER `entries_fts_removal` AFTER DELETE ON `entries`
35
+ BEGIN
36
+ DELETE FROM `entries_fts` WHERE `entry_id` = OLD.`id`;
37
+ END;
38
+ --> statement-breakpoint
39
+ -- What is published already.
40
+ INSERT INTO `entries_fts` (`title`, `tags`, `body`, `entry_id`, `site_id`, `collection`, `locale`)
41
+ SELECT
42
+ coalesce(json_extract(r.`frontmatter`, '$.title'), ''),
43
+ coalesce((SELECT group_concat(t.`tag`, ' ') FROM `revision_tags` t WHERE t.`revision_id` = r.`id`), ''),
44
+ coalesce((SELECT group_concat(n.`value`, ' ') FROM json_tree(r.`document`, '$.nodes') n
45
+ WHERE n.`type` = 'text' AND typeof(n.`key`) = 'integer' AND n.`key` >= 2), ''),
46
+ e.`id`, e.`site_id`, e.`collection`, e.`locale`
47
+ FROM `entries` e JOIN `revisions` r ON r.`id` = e.`published_rev`;
@@ -0,0 +1,19 @@
1
+ CREATE TABLE `entry_embeddings` (
2
+ `id` text PRIMARY KEY NOT NULL,
3
+ `site_id` text NOT NULL,
4
+ `entry_id` text NOT NULL,
5
+ `revision_id` text NOT NULL,
6
+ `model` text NOT NULL,
7
+ `chunker_version` text NOT NULL,
8
+ `pieces` integer NOT NULL,
9
+ `related` text DEFAULT '[]' NOT NULL,
10
+ `created_at` text NOT NULL,
11
+ `updated_at` text NOT NULL,
12
+ FOREIGN KEY (`site_id`) REFERENCES `sites`(`id`) ON UPDATE no action ON DELETE no action,
13
+ FOREIGN KEY (`entry_id`,`site_id`) REFERENCES `entries`(`id`,`site_id`) ON UPDATE no action ON DELETE cascade,
14
+ FOREIGN KEY (`revision_id`,`site_id`) REFERENCES `revisions`(`id`,`site_id`) ON UPDATE no action ON DELETE no action,
15
+ CONSTRAINT "entry_embeddings_pieces" CHECK("entry_embeddings"."pieces" >= 1)
16
+ );
17
+ --> statement-breakpoint
18
+ CREATE UNIQUE INDEX `entry_embeddings_id_site` ON `entry_embeddings` (`id`,`site_id`);--> statement-breakpoint
19
+ CREATE UNIQUE INDEX `entry_embeddings_entry` ON `entry_embeddings` (`site_id`,`entry_id`);