feat(repo): implémentation MongoDB de CardRepository

This commit is contained in:
devcodetools committed 2026-09-21 17:47:23 +02:00
1 parent ab24d329b9
commit dca234f298
1 file changed
+126
+126
View File
@@ -0,0 +1,126 @@
import { MongoClient, type Collection } from 'mongodb'
import type { Card, CardInput } from '../domain/card'
import type { CardRepository } from '../domain/card-repository'
interface CardDocument {
c_id: string
question: string
response: string
theme: string
tags: string[]
createdAt: Date
updatedAt: Date
}
const DOC_FIELDS = {
_id: 0,
c_id: 1,
question: 1,
response: 1,
theme: 1,
tags: 1,
createdAt: 1,
updatedAt: 1,
}
export class MongoCardRepository implements CardRepository {
private readonly collection: Collection<CardDocument>
constructor(collection?: Collection<CardDocument>) {
if (collection) {
this.collection = collection
} else {
const client = new MongoClient(
process.env.MONGODB_URI ?? 'mongodb://flashcards:flashcards@localhost:127017/flashcards',
)
this.collection = client
.db(process.env.MONGODB_DATABASE ?? 'flashcards')
.collection<CardDocument>('cards')
}
}
private mapDoc(doc: CardDocument): Card {
return {
id: doc.c_id,
question: doc.question,
response: doc.response,
theme: doc.theme,
tags: doc.tags,
createdAt: doc.createdAt,
updatedAt: doc.updatedAt,
}
}
async findAll(): Promise<Card[]> {
const docs = await this.collection.find({}, { projection: DOC_FIELDS }).toArray()
return docs.map((doc) => this.mapDoc(doc))
}
async findById(id: string): Promise<Card | null> {
const doc = await this.collection.findOne({ c_id: id }, { projection: DOC_FIELDS })
return doc ? this.mapDoc(doc) : null
}
async create(input: CardInput): Promise<Card> {
const doc: CardDocument = {
c_id: crypto.randomUUID(),
question: input.question,
response: input.response,
theme: input.theme,
tags: input.tags ?? [],
createdAt: new Date(),
updatedAt: new Date(),
}
await this.collection.insertOne(doc)
return this.mapDoc(doc)
}
async update(id: string, input: Partial<CardInput>): Promise<Card | null> {
const set: Partial<CardDocument> = { updatedAt: new Date() }
if (input.question !== undefined) {
set.question = input.question
}
if (input.response !== undefined) {
set.response = input.response
}
if (input.theme !== undefined) {
set.theme = input.theme
}
if (input.tags !== undefined) {
set.tags = input.tags
}
const result = await this.collection.updateOne({ c_id: id }, { $set: set })
if (result.matchedCount === 0) {
return null
}
return this.findById(id)
}
async delete(id: string): Promise<boolean> {
const result = await this.collection.deleteOne({ c_id: id })
return result.deletedCount > 0
}
async findRandom(): Promise<Card | null> {
const docs = await this.collection
.aggregate<CardDocument>([{ $sample: { size: 1 } }, { $project: DOC_FIELDS }])
.toArray()
return docs[0] ? this.mapDoc(docs[0]) : null
}
async findRandomByTheme(theme: string): Promise<Card | null> {
const docs = await this.collection
.aggregate<CardDocument>([
{ $match: { theme } },
{ $sample: { size: 1 } },
{ $project: DOC_FIELDS },
])
.toArray()
return docs[0] ? this.mapDoc(docs[0]) : null
}
async findThemes(): Promise<string[]> {
const themes: string[] = await this.collection.distinct('theme')
return themes.sort()
}
}