mvp
This commit is contained in:
81
src/lib/related.ts
Normal file
81
src/lib/related.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
interface ScoredNote {
|
||||
id: string
|
||||
title: string
|
||||
type: string
|
||||
tags: string[]
|
||||
score: number
|
||||
reason: string
|
||||
}
|
||||
|
||||
export async function getRelatedNotes(noteId: string, limit = 5): Promise<ScoredNote[]> {
|
||||
const note = await prisma.note.findUnique({
|
||||
where: { id: noteId },
|
||||
include: { tags: { include: { tag: true } } },
|
||||
})
|
||||
|
||||
if (!note) return []
|
||||
|
||||
const noteTagNames = note.tags.map(t => t.tag.name)
|
||||
const noteWords = note.title.toLowerCase().split(/\s+/).filter(w => w.length > 2)
|
||||
const noteContentWords = note.content.toLowerCase().split(/\s+/).filter(w => w.length > 4)
|
||||
|
||||
const allNotes = await prisma.note.findMany({
|
||||
where: { id: { not: noteId } },
|
||||
include: { tags: { include: { tag: true } } },
|
||||
})
|
||||
|
||||
const scored: ScoredNote[] = []
|
||||
|
||||
for (const other of allNotes) {
|
||||
let score = 0
|
||||
const reasons: string[] = []
|
||||
|
||||
// +3 si comparten tipo
|
||||
if (other.type === note.type) {
|
||||
score += 3
|
||||
reasons.push(`Same type (${note.type})`)
|
||||
}
|
||||
|
||||
// +2 por cada tag compartido
|
||||
const sharedTags = noteTagNames.filter(t => other.tags.some(ot => ot.tag.name === t))
|
||||
score += sharedTags.length * 2
|
||||
if (sharedTags.length > 0) {
|
||||
reasons.push(`Shared tags: ${sharedTags.join(', ')}`)
|
||||
}
|
||||
|
||||
// +1 por palabra relevante compartida en título
|
||||
const sharedTitleWords = noteWords.filter(w =>
|
||||
other.title.toLowerCase().includes(w)
|
||||
)
|
||||
score += Math.min(sharedTitleWords.length, 2) // max +2
|
||||
if (sharedTitleWords.length > 0) {
|
||||
reasons.push(`Title match: ${sharedTitleWords.slice(0, 2).join(', ')}`)
|
||||
}
|
||||
|
||||
// +1 si keyword del contenido aparece en ambas
|
||||
const sharedContentWords = noteContentWords.filter(w =>
|
||||
other.content.toLowerCase().includes(w)
|
||||
)
|
||||
score += Math.min(sharedContentWords.length, 2) // max +2
|
||||
if (sharedContentWords.length > 0) {
|
||||
reasons.push(`Content: ${sharedContentWords.slice(0, 2).join(', ')}`)
|
||||
}
|
||||
|
||||
if (score > 0) {
|
||||
scored.push({
|
||||
id: other.id,
|
||||
title: other.title,
|
||||
type: other.type,
|
||||
tags: other.tags.map(t => t.tag.name),
|
||||
score,
|
||||
reason: reasons.join(' | '),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return scored
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.slice(0, limit)
|
||||
}
|
||||
Reference in New Issue
Block a user