Compare commits

...
Sign in to create a new pull request.

4 commits

Author SHA1 Message Date
ThatOneCalculator
c489a2d448
logging 2023-05-20 18:24:30 -07:00
ThatOneCalculator
3edfdc5e98
logs 2023-05-20 18:14:52 -07:00
ThatOneCalculator
1e7cbe9045
docs: searc providers 2023-05-19 20:41:19 -07:00
Kainoa Kanter
2d431ef341 feat: meilisearch 2023-05-19 11:11:10 -07:00
13 changed files with 166 additions and 7 deletions

View file

@ -28,6 +28,12 @@ redis:
# user: # user:
# pass: # pass:
#meilisearch
# host: localhost
# port: 7700
# ssl: false
# apiKey: ''
id: 'aid' id: 'aid'
reservedUsernames: reservedUsernames:

View file

@ -82,6 +82,15 @@ redis:
# user: # user:
# pass: # pass:
# ┌───────────────────────────┐
#───┘ MeiliSearch configuration └─────────────────────────────
#meilisearch:
# host: meilisearch
# port: 7700
# apiKey: ''
# ssl: false
# ┌───────────────┐ # ┌───────────────┐
#───┘ ID generation └─────────────────────────────────────────── #───┘ ID generation └───────────────────────────────────────────

1
.gitignore vendored
View file

@ -33,6 +33,7 @@ coverage
built built
db db
elasticsearch elasticsearch
meili_data
redis redis
npm-debug.log npm-debug.log
*.pem *.pem

View file

@ -124,6 +124,7 @@
- Improve system emails - Improve system emails
- Mod mail - Mod mail
- Focus trapping and button labels - Focus trapping and button labels
- Meilisearch
## Implemented (remote) ## Implemented (remote)

View file

@ -89,7 +89,8 @@ If you have access to a server that supports one of the sources below, I recomme
- [FFmpeg](https://ffmpeg.org/) for video transcoding - [FFmpeg](https://ffmpeg.org/) for video transcoding
- Full text search (one of the following) - Full text search (one of the following)
- 🦔 [Sonic](https://crates.io/crates/sonic-server) (recommended) - 🦔 [Sonic](https://crates.io/crates/sonic-server)
- [MeiliSearch](https://www.meilisearch.com/)
- [ElasticSearch](https://www.elastic.co/elasticsearch/) - [ElasticSearch](https://www.elastic.co/elasticsearch/)
### 🏗️ Build dependencies ### 🏗️ Build dependencies
@ -140,7 +141,11 @@ psql postgres -c "create database calckey with encoding = 'UTF8';"
In Calckey's directory, fill out the `db` section of `.config/default.yml` with the correct information, where the `db` key is `calckey`. In Calckey's directory, fill out the `db` section of `.config/default.yml` with the correct information, where the `db` key is `calckey`.
## 🦔 Set up search ## 🔎 Set up search
### 🦔 Sonic
Sonic is better suited for self hosters with smaller deployments. It's easier to use, uses almost no resources, and takes barely any any disk space.
Follow sonic's [installation guide](https://github.com/valeriansaliou/sonic#installation) Follow sonic's [installation guide](https://github.com/valeriansaliou/sonic#installation)
@ -148,6 +153,17 @@ If you use IPv4: in Sonic's directory, edit the `config.cfg` file to change `ine
In Calckey's directory, fill out the `sonic` section of `.config/default.yml` with the correct information. In Calckey's directory, fill out the `sonic` section of `.config/default.yml` with the correct information.
### Meilisearch
Meilisearch is better suited for larger deployments. It's faster but uses far more resources and disk space.
Follow Meilisearch's [quick start guide](https://www.meilisearch.com/docs/learn/getting_started/quick_start)
In Calckey's directory, fill out the `meilisearch` section of `.config/default.yml` with the correct information.
### ElasticSearch
Please don't use ElasticSearch unless you already have an ElasticSearch setup and want to continue using it for Calckey. ElasticSearch is slow, heavy, and offers very few benefits over Sonic/Meilisearch.
## 💅 Customize ## 💅 Customize

View file

@ -164,6 +164,15 @@ redis:
# collection: notes # collection: notes
# bucket: default # bucket: default
# ┌───────────────────────────┐
#───┘ MeiliSearch configuration └─────────────────────────────
#meilisearch:
# host: meilisearch
# port: 7700
# apiKey: ''
# ssl: false
# ┌─────────────────────────────┐ # ┌─────────────────────────────┐
#───┘ Elasticsearch configuration └───────────────────────────── #───┘ Elasticsearch configuration └─────────────────────────────

View file

@ -186,6 +186,7 @@
"execa": "6.1.0", "execa": "6.1.0",
"json5": "2.2.3", "json5": "2.2.3",
"json5-loader": "4.0.1", "json5-loader": "4.0.1",
"meilisearch": "^0.32.3",
"mocha": "10.2.0", "mocha": "10.2.0",
"pug": "3.0.2", "pug": "3.0.2",
"strict-event-emitter-types": "2.0.0", "strict-event-emitter-types": "2.0.0",

View file

@ -39,6 +39,12 @@ export type Source = {
collection?: string; collection?: string;
bucket?: string; bucket?: string;
}; };
meilisearch: {
host: string;
port: number;
apiKey?: string;
ssl?: boolean;
};
proxy?: string; proxy?: string;
proxySmtp?: string; proxySmtp?: string;
@ -81,6 +87,8 @@ export type Source = {
reservedUsernames?: string[]; reservedUsernames?: string[];
logLevel?: "error" | "warn" | "info" | "debug" | "trace";
// Managed hosting stuff // Managed hosting stuff
maxUserSignups?: number; maxUserSignups?: number;
isManagedHosting?: boolean; isManagedHosting?: boolean;

View file

@ -0,0 +1,23 @@
import * as MeiliSearch from "meilisearch";
import { dbLogger } from "./logger.js";
import config from "@/config/index.js";
const logger = dbLogger.createSubLogger("meilisearch", "gray", false);
logger.info("Connecting to Meilisearch");
const hasConfig =
config.meilisearch && (config.meilisearch.host || config.meilisearch.port);
const host = hasConfig ? config.meilisearch.host ?? "localhost" : "";
const port = hasConfig ? config.meilisearch.port ?? 7700 : 0;
const ssl = hasConfig ? config.meilisearch.ssl ?? false : true;
const apiKey = hasConfig ? config.meilisearch.apiKey ?? "" : "";
export default hasConfig
? new MeiliSearch.MeiliSearch({
host: `${ssl ? "https" : "http"}://${host}:${port}`,
apiKey,
})
: null;

View file

@ -2,13 +2,17 @@ import { In } from "typeorm";
import { Notes } from "@/models/index.js"; import { Notes } from "@/models/index.js";
import { Note } from "@/models/entities/note.js"; import { Note } from "@/models/entities/note.js";
import config from "@/config/index.js"; import config from "@/config/index.js";
import es from "../../../../db/elasticsearch.js"; import es from "@/db/elasticsearch.js";
import sonic from "../../../../db/sonic.js"; import sonic from "@/db/sonic.js";
import meilisearch from "@/db/meilisearch.js";
import define from "../../define.js"; import define from "../../define.js";
import { makePaginationQuery } from "../../common/make-pagination-query.js"; import { makePaginationQuery } from "../../common/make-pagination-query.js";
import { generateVisibilityQuery } from "../../common/generate-visibility-query.js"; import { generateVisibilityQuery } from "../../common/generate-visibility-query.js";
import { generateMutedUserQuery } from "../../common/generate-muted-user-query.js"; import { generateMutedUserQuery } from "../../common/generate-muted-user-query.js";
import { generateBlockedUserQuery } from "../../common/generate-block-query.js"; import { generateBlockedUserQuery } from "../../common/generate-block-query.js";
import { apiLogger } from "../../logger.js";
const logger = apiLogger.createSubLogger("search", "blue", true);
export const meta = { export const meta = {
tags: ["notes"], tags: ["notes"],
@ -61,7 +65,7 @@ export const paramDef = {
} as const; } as const;
export default define(meta, paramDef, async (ps, me) => { export default define(meta, paramDef, async (ps, me) => {
if (es == null && sonic == null) { if (es == null && sonic == null && meilisearch == null) {
const query = makePaginationQuery( const query = makePaginationQuery(
Notes.createQueryBuilder("note"), Notes.createQueryBuilder("note"),
ps.sinceId, ps.sinceId,
@ -97,6 +101,36 @@ export default define(meta, paramDef, async (ps, me) => {
const notes: Note[] = await query.take(ps.limit).getMany(); const notes: Note[] = await query.take(ps.limit).getMany();
return await Notes.packMany(notes, me); return await Notes.packMany(notes, me);
} else if (meilisearch) {
//search in meilisearch
try {
const result = await meilisearch.index("notes").search(ps.query, {
limit: ps.limit,
offset: ps.offset,
filters: ps.userId,
// ? `userId = ${ps.userId}`
// : ps.channelId
// ? `channelId = ${ps.channelId}`
// : undefined,
});
const ids = result.hits.map((hit: { id: string }) => hit.id);
// Fetch the notes from the database until we have enough to satisfy the limit
const notes: Note[] = await Notes.find({
where: {
id: In(ids),
},
order: {
id: "DESC",
},
});
return await Notes.packMany(notes, me);
} catch (error) {
logger.error(error.message);
return [];
}
} else if (sonic) { } else if (sonic) {
let start = 0; let start = 0;
const chunkSize = 100; const chunkSize = 100;

View file

@ -60,6 +60,26 @@ export default class Logger {
if (!this.store) store = false; if (!this.store) store = false;
if (level === "debug") store = false; if (level === "debug") store = false;
// filter out logs based on config log level
if (config.logLevel === "error" && level !== "error") return;
if (config.logLevel === "warn" && level !== "error" && level !== "warning")
return;
if (
config.logLevel === "info" &&
level !== "error" &&
level !== "warning" &&
level !== "info"
)
return;
if (
config.logLevel === "debug" &&
level !== "error" &&
level !== "warning" &&
level !== "info" &&
level !== "debug"
)
return;
if (this.parentLogger) { if (this.parentLogger) {
this.parentLogger.log( this.parentLogger.log(
level, level,

View file

@ -1,6 +1,7 @@
import * as mfm from "mfm-js"; import * as mfm from "mfm-js";
import es from "../../db/elasticsearch.js"; import es from "@/db/elasticsearch.js";
import sonic from "../../db/sonic.js"; import sonic from "@/db/sonic.js";
import meilisearch from "@/db/meilisearch.js";
import { import {
publishMainStream, publishMainStream,
publishNotesStream, publishNotesStream,
@ -776,6 +777,25 @@ export async function index(note: Note): Promise<void> {
note.text, note.text,
); );
} }
if (meilisearch) {
await meilisearch.index("notes").addDocuments(
[
{
id: note.id.toString(),
text: note.text,
userId: note.userId,
userHost: note.userHost,
channelId: note.channelId,
cw: note.cw,
createdAt: note.createdAt.getTime(),
},
],
{
primaryKey: "id",
},
);
}
} }
async function notifyToWatchersOfRenotee( async function notifyToWatchersOfRenotee(

11
pnpm-lock.yaml generated
View file

@ -565,6 +565,9 @@ importers:
json5-loader: json5-loader:
specifier: 4.0.1 specifier: 4.0.1
version: 4.0.1(webpack@5.75.0) version: 4.0.1(webpack@5.75.0)
meilisearch:
specifier: ^0.32.3
version: 0.32.3
mocha: mocha:
specifier: 10.2.0 specifier: 10.2.0
version: 10.2.0 version: 10.2.0
@ -10367,6 +10370,14 @@ packages:
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
dev: false dev: false
/meilisearch@0.32.3:
resolution: {integrity: sha512-EOgfBuRE5SiIPIpEDYe2HO0D7a4z5bexIgaAdJFma/dH5hx1kwO+u/qb2g3qKyjG+iA3l8MlmTj/Xd72uahaAw==}
dependencies:
cross-fetch: 3.1.5
transitivePeerDependencies:
- encoding
dev: true
/meow@9.0.0: /meow@9.0.0:
resolution: {integrity: sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==} resolution: {integrity: sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==}
engines: {node: '>=10'} engines: {node: '>=10'}