import { Injectable } from "@nestjs/common"; import { EntryStatus } from "@prisma/client"; import { PrismaService } from "../../prisma/prisma.service"; import type { KnowledgeStats } from "../entities/stats.entity"; /** * Service for knowledge base statistics */ @Injectable() export class StatsService { constructor(private readonly prisma: PrismaService) {} /** * Get comprehensive knowledge base statistics */ async getStats(workspaceId: string): Promise { // Run queries in parallel for better performance const [ totalEntries, totalTags, totalLinks, publishedEntries, draftEntries, archivedEntries, entriesWithLinkCounts, recentEntries, tagsWithCounts, ] = await Promise.all([ // Total entries this.prisma.knowledgeEntry.count({ where: { workspaceId }, }), // Total tags this.prisma.knowledgeTag.count({ where: { workspaceId }, }), // Total links this.prisma.knowledgeLink.count({ where: { source: { workspaceId }, }, }), // Published entries this.prisma.knowledgeEntry.count({ where: { workspaceId, status: EntryStatus.PUBLISHED, }, }), // Draft entries this.prisma.knowledgeEntry.count({ where: { workspaceId, status: EntryStatus.DRAFT, }, }), // Archived entries this.prisma.knowledgeEntry.count({ where: { workspaceId, status: EntryStatus.ARCHIVED, }, }), // Most connected entries this.prisma.knowledgeEntry.findMany({ where: { workspaceId }, include: { _count: { select: { incomingLinks: true, outgoingLinks: true, }, }, }, orderBy: { incomingLinks: { _count: "desc", }, }, take: 10, }), // Recent activity this.prisma.knowledgeEntry.findMany({ where: { workspaceId }, orderBy: { updatedAt: "desc", }, take: 10, select: { id: true, slug: true, title: true, updatedAt: true, status: true, }, }), // Tag distribution this.prisma.knowledgeTag.findMany({ where: { workspaceId }, include: { _count: { select: { entries: true, }, }, }, orderBy: { entries: { _count: "desc", }, }, }), ]); // Transform most connected entries const mostConnected = entriesWithLinkCounts.map((entry) => { const incomingLinks = entry._count.incomingLinks; const outgoingLinks = entry._count.outgoingLinks; return { id: entry.id, slug: entry.slug, title: entry.title, incomingLinks, outgoingLinks, totalConnections: incomingLinks + outgoingLinks, }; }); // Sort by total connections mostConnected.sort((a, b) => b.totalConnections - a.totalConnections); // Transform tag distribution const tagDistribution = tagsWithCounts.map((tag) => ({ id: tag.id, name: tag.name, slug: tag.slug, color: tag.color, entryCount: tag._count.entries, })); return { overview: { totalEntries, totalTags, totalLinks, publishedEntries, draftEntries, archivedEntries, }, mostConnected, recentActivity: recentEntries.map((entry) => ({ id: entry.id, slug: entry.slug, title: entry.title, updatedAt: entry.updatedAt, status: entry.status, })), tagDistribution, }; } }