@shirokuma-library/mcp-knowledge-base
Version:
MCP server for AI-powered knowledge management with semantic search, graph analysis, and automatic enrichment
121 lines (120 loc) • 4.97 kB
JavaScript
import { Item } from '../entities/Item.js';
import { AppDataSource } from '../data-source.js';
import { SearchQueryParser } from '../services/SearchQueryParser.js';
import { StatusRepository } from './StatusRepository.js';
export class ItemRepository {
repository;
constructor() {
this.repository = AppDataSource.getRepository(Item);
}
async create(data) {
const item = this.repository.create(data);
return await this.repository.save(item);
}
async findById(id) {
return await this.repository.findOne({ where: { id } });
}
async findAll(options) {
const query = this.repository.createQueryBuilder('item');
let needsStatusJoin = false;
if (options?.status) {
query.leftJoinAndSelect('item.status', 'status')
.andWhere('LOWER(status.name) = LOWER(:statusName)', {
statusName: options.status
});
needsStatusJoin = true;
}
else if (options?.onlyActive || (!options?.includeClosable && options?.includeClosable !== true)) {
query.leftJoinAndSelect('item.status', 'status')
.andWhere('status.isClosable = :isClosable', { isClosable: false });
needsStatusJoin = true;
}
else if (options?.includeClosable === true) {
query.leftJoinAndSelect('item.status', 'status');
needsStatusJoin = true;
}
if (options?.type) {
query.andWhere('item.type = :type', { type: options.type });
}
if (options?.limit) {
query.limit(options.limit);
}
if (options?.offset) {
query.offset(options.offset);
}
query.orderBy('item.createdAt', 'DESC');
return await query.getMany();
}
async update(id, data) {
await this.repository.update(id, data);
return await this.findById(id);
}
async delete(id) {
const result = await this.repository.delete(id);
return result.affected !== 0;
}
async searchAdvanced(parsedQuery) {
const queryBuilder = this.repository.createQueryBuilder('item')
.leftJoinAndSelect('item.status', 'status');
if (parsedQuery.filters.status?.length) {
const statusRepo = new StatusRepository();
const statusIds = [];
for (const statusName of parsedQuery.filters.status) {
const status = await statusRepo.findByName(statusName);
if (status)
statusIds.push(status.id);
}
if (statusIds.length > 0) {
queryBuilder.andWhere('item.statusId IN (:...statusIds)', { statusIds });
}
}
if (parsedQuery.filters.type?.length) {
queryBuilder.andWhere('item.type IN (:...types)', { types: parsedQuery.filters.type });
}
if (parsedQuery.filters.is) {
const isClosable = parsedQuery.filters.is === 'closed';
queryBuilder.andWhere('status.isClosable = :isClosable', { isClosable });
}
if (parsedQuery.keywords.length > 0) {
const keywordConditions = parsedQuery.keywords.map((_, index) => `(item.title LIKE :kw${index} OR item.description LIKE :kw${index} OR item.content LIKE :kw${index})`).join(' AND ');
const keywordParams = {};
parsedQuery.keywords.forEach((kw, index) => {
keywordParams[`kw${index}`] = `%${kw}%`;
});
if (keywordConditions) {
queryBuilder.andWhere(`(${keywordConditions})`, keywordParams);
}
}
return queryBuilder.orderBy('item.updatedAt', 'DESC').getMany();
}
async searchLegacy(query) {
return await this.repository
.createQueryBuilder('item')
.where('item.title LIKE :query', { query: `%${query}%` })
.orWhere('item.description LIKE :query', { query: `%${query}%` })
.orWhere('item.content LIKE :query', { query: `%${query}%` })
.orderBy('item.updatedAt', 'DESC')
.getMany();
}
async search(query) {
const parser = new SearchQueryParser();
const parsed = parser.parse(query);
if (Object.keys(parsed.filters).length === 0 && parsed.keywords.length === 0) {
return this.searchLegacy(query);
}
if (Object.keys(parsed.filters).length === 0 && parsed.keywords.length > 0) {
return this.searchLegacy(query);
}
return this.searchAdvanced(parsed);
}
async count(options) {
const query = this.repository.createQueryBuilder('item');
if (options?.type) {
query.andWhere('item.type = :type', { type: options.type });
}
if (options?.status) {
query.andWhere('item.status = :status', { status: options.status });
}
return await query.getCount();
}
}