Files
outline/server/migrations/20260416000000-create-document-insights.js
T
Tom Moor 600108bc43 feat: Document insight rollups (#12086)
* First pass

* Remove popularity changes

* Address review feedback

- Compute retention cutoff in UTC from the database rather than worker-local TZ
- Push partition predicate into rollup source CTEs to avoid full-table scans per partition

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Anchor insight rollups to UTC and include today

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 08:11:15 -04:00

97 lines
2.4 KiB
JavaScript

"use strict";
module.exports = {
async up(queryInterface, Sequelize) {
return queryInterface.sequelize.transaction(async (transaction) => {
await queryInterface.createTable(
"document_insights",
{
id: {
type: Sequelize.UUID,
allowNull: false,
defaultValue: Sequelize.UUIDV4,
primaryKey: true,
},
documentId: {
type: Sequelize.UUID,
allowNull: false,
references: {
model: "documents",
key: "id",
},
onDelete: "CASCADE",
},
teamId: {
type: Sequelize.UUID,
allowNull: false,
references: {
model: "teams",
key: "id",
},
onDelete: "CASCADE",
},
date: {
type: Sequelize.DATEONLY,
allowNull: false,
},
viewCount: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0,
},
viewerCount: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0,
},
commentCount: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0,
},
reactionCount: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0,
},
revisionCount: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0,
},
editorCount: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0,
},
createdAt: {
type: Sequelize.DATE,
allowNull: false,
},
updatedAt: {
type: Sequelize.DATE,
allowNull: false,
},
},
{ transaction }
);
await queryInterface.addIndex(
"document_insights",
["documentId", "date"],
{ unique: true, transaction }
);
await queryInterface.addIndex("document_insights", ["teamId", "date"], {
transaction,
});
});
},
async down(queryInterface) {
return queryInterface.sequelize.transaction(async (transaction) => {
await queryInterface.dropTable("document_insights", { transaction });
});
},
};