rudeshark.net/packages/backend/src/db/meilisearch.ts

116 lines
2.9 KiB
TypeScript
Raw Normal View History

2023-05-25 09:53:04 +02:00
import {Health, MeiliSearch, Stats } from 'meilisearch';
2023-05-25 00:55:33 +02:00
import { dbLogger } from "./logger.js";
import config from "@/config/index.js";
import {Note} from "@/models/entities/note";
const logger = dbLogger.createSubLogger("meilisearch", "gray", false);
logger.info("Connecting to MeiliSearch");
const hasConfig =
config.meilisearch && (config.meilisearch.host || config.meilisearch.port || config.meilisearch.apiKey);
const host = hasConfig ? config.meilisearch.host ?? "localhost" : "";
const port = hasConfig ? config.meilisearch.port ?? 7700 : 0;
const auth = hasConfig ? config.meilisearch.apiKey ?? "" : "";
const client: MeiliSearch = new MeiliSearch({
2023-05-25 02:19:42 +02:00
host: `http://${host}:${port}`,
apiKey: auth,
2023-05-25 00:55:33 +02:00
})
const posts = client.index('posts');
posts.updateSearchableAttributes(['text']);
posts.updateFilterableAttributes(["userId", "userHost", "mediaAttachment"])
2023-05-25 00:55:33 +02:00
logger.info("Connected to MeiliSearch");
export type MeilisearchNote = {
id: string;
text: string;
userId: string;
userHost: string;
channelId: string;
mediaAttachment: string;
2023-05-25 00:55:33 +02:00
}
export default hasConfig ? {
search: (query : string, limit : number, offset : number) => {
logger.info(`Searching for ${query}`);
2023-05-25 02:19:42 +02:00
logger.info(`Limit: ${limit}`);
logger.info(`Offset: ${offset}`);
2023-05-25 00:55:33 +02:00
/// Advanced search syntax
/// from:user => filter by user + optional domain
/// has:image/video/audio/text/file => filter by attachment types
/// domain:domain.com => filter by domain
let constructedFilters: string[] = [];
let splitSearch = query.split(" ");
splitSearch.forEach(term => {
if (term.startsWith("has:")) {
let fileType = term.slice(4);
constructedFilters.push(`mediaAttachment = "${fileType}"`)
}
if (term.startsWith("from:")) {
let user = term.slice(5);
constructedFilters.push(`userId = ${user}`)
}
if (term.startsWith("domain:")) {
let domain = term.slice(7);
constructedFilters.push(`userHost = ${domain}`)
}
})
2023-05-25 00:55:33 +02:00
return posts.search(query, {
limit: limit,
offset: offset,
filter: constructedFilters
2023-05-25 00:55:33 +02:00
});
},
ingestNote: (note : Note) => {
logger.info("Indexing note in MeiliSearch: " + note.id);
let attachmentType = "";
if (note.attachedFileTypes.length > 0) {
attachmentType = note.attachedFileTypes[0].split("/")[0];
switch (attachmentType) {
case "image":
case "video":
case "audio":
case "text":
break;
default:
attachmentType = "file"
break
}
}
2023-05-25 00:55:33 +02:00
return posts.addDocuments([
{
id: note.id.toString(),
text: note.text,
userId: note.userId,
userHost: note.userHost,
channelId: note.channelId,
mediaAttachment: attachmentType
2023-05-25 00:55:33 +02:00
}
2023-05-25 09:53:04 +02:00
]);
2023-05-25 00:55:33 +02:00
},
2023-05-25 09:53:04 +02:00
serverStats: async () => {
let health : Health = await client.health();
let stats: Stats = await client.getStats();
return {
health: health.status,
size: stats.databaseSize,
indexed_count: stats.indexes["posts"].numberOfDocuments
}
}
2023-05-25 00:55:33 +02:00
} : null;