-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add meilisearch to project * set up endpoint for search and sync * fix issues with sync and refactor * added simple documentation * removed missing refactor * handle createIndex at endpoint instead * big refactor, use federated search instead * add language support and searchable attributes * move sync endpoint to sync function * improve type definitions * small tweak to sync and update comments * refactor searchable attribute getter and docs --------- Co-authored-by: Daniel Adu-Gyan <26229521+danieladugyan@users.noreply.github.com>
- Loading branch information
1 parent
703979b
commit 51738b8
Showing
16 changed files
with
613 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { Meilisearch } from "meilisearch"; | ||
import { env } from "$env/dynamic/private"; | ||
|
||
export const meilisearch = new Meilisearch({ | ||
host: env.MEILISEARCH_HOST, | ||
apiKey: env.MEILISEARCH_MASTER_KEY, | ||
requestConfig: { | ||
headers: { | ||
Authorization: `Bearer ${env.MEILISEARCH_MASTER_KEY}`, | ||
}, | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import type { AvailableLanguageTag } from "$paraglide/runtime"; | ||
import { | ||
availableSearchIndexes, | ||
type SearchableArticleAttributes, | ||
type SearchableEventAttributes, | ||
type SearchableIndex, | ||
type SearchableMemberAttributes, | ||
type SearchablePositionAttributes, | ||
type SearchableSongAttributes, | ||
} from "./searchTypes"; | ||
|
||
export type SearchIndex = | ||
(typeof availableSearchIndexes)[keyof typeof availableSearchIndexes]; | ||
|
||
/** | ||
* Get the federated weight for a given index | ||
* This is used to give a higher weight to certain indexes | ||
* when searching. For example, we want members to be more | ||
* important than events. This is done by giving members | ||
* a higher weight. | ||
*/ | ||
export function getFederatedWeight(index: SearchableIndex): number { | ||
switch (index) { | ||
case "members": | ||
return 5; | ||
case "events": | ||
return 1; | ||
case "articles": | ||
return 1; | ||
case "positions": | ||
return 5; | ||
case "songs": | ||
return 1; | ||
default: | ||
return 1; | ||
} | ||
} | ||
|
||
/** | ||
* Get the searchable attributes for a given index and language | ||
* If for whatever reason the language is not supported, or our | ||
* systems fail to provide a language, swedish will be used as default. | ||
*/ | ||
export function getSearchableAttributes( | ||
index: SearchableIndex, | ||
language: AvailableLanguageTag, | ||
) { | ||
switch (index) { | ||
case "members": { | ||
// no language specific fields | ||
const res: Array<keyof SearchableMemberAttributes> = [ | ||
"firstName", | ||
"lastName", | ||
"nickname", | ||
"studentId", | ||
"fullName", | ||
]; | ||
return res; | ||
} | ||
case "events": { | ||
// default is swedish | ||
let res: Array<keyof SearchableEventAttributes> = [ | ||
"title", | ||
"description", | ||
]; | ||
if (language === "en") { | ||
res = ["titleEn", "descriptionEn"]; | ||
} | ||
return res; | ||
} | ||
case "articles": { | ||
// default is swedish | ||
let res: Array<keyof SearchableArticleAttributes> = ["header", "body"]; | ||
if (language === "en") { | ||
res = ["headerEn", "bodyEn"]; | ||
} | ||
return res; | ||
} | ||
case "positions": { | ||
// default is swedish | ||
let res: Array<keyof SearchablePositionAttributes> = [ | ||
"name", | ||
"description", | ||
"committeeName", | ||
"dsekId", | ||
]; | ||
if (language === "en") { | ||
res = ["nameEn", "descriptionEn", "committeeNameEn", "dsekId"]; | ||
} | ||
return res; | ||
} | ||
case "songs": { | ||
// no language specific fields | ||
const res: Array<keyof SearchableSongAttributes> = [ | ||
"title", | ||
"lyrics", | ||
"melody", | ||
"category", | ||
]; | ||
return res; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import type { | ||
Article, | ||
Member, | ||
Event, | ||
Song, | ||
Position, | ||
Committee, | ||
} from "@prisma/client"; | ||
|
||
/** | ||
* Utility type that creates a new object type based on a union of keys (Keys). | ||
* | ||
* For each key in Keys: | ||
* - If the key exists in T, its value type is preserved from T. | ||
* - If the key does not exist in T, its value type is set to `string`. | ||
* | ||
* This is useful for creating a new type that includes specific keys (from Keys), | ||
* ensuring compatibility with an existing type (T), while accounting for missing keys. | ||
*/ | ||
type FilterKeys<T extends Record<string, unknown>, Keys extends string> = { | ||
[Key in Keys]: Key extends keyof T ? T[Key] : string; | ||
}; | ||
|
||
export const availableSearchIndexes = [ | ||
"members", | ||
"events", | ||
"articles", | ||
"positions", | ||
"songs", | ||
] as const; | ||
export type SearchableIndex = (typeof availableSearchIndexes)[number]; | ||
|
||
export const memberSearchableAttributes = [ | ||
"id", | ||
"firstName", | ||
"lastName", | ||
"nickname", | ||
"studentId", | ||
"fullName", | ||
] as const satisfies Array<keyof Member | "fullName">; | ||
export type SearchableMemberAttributes = FilterKeys< | ||
Member, | ||
(typeof memberSearchableAttributes)[number] | ||
>; | ||
|
||
export const eventSearchableAttributes = [ | ||
"title", | ||
"titleEn", | ||
"description", | ||
"descriptionEn", | ||
] as const satisfies Array<keyof Event>; | ||
export type SearchableEventAttributes = Pick< | ||
Event, | ||
(typeof eventSearchableAttributes)[number] | ||
>; | ||
|
||
export const articleSearchableAttributes = [ | ||
"header", | ||
"headerEn", | ||
"body", | ||
"bodyEn", | ||
] as const satisfies Array<keyof Article>; | ||
export type SearchableArticleAttributes = Pick< | ||
Article, | ||
(typeof articleSearchableAttributes)[number] | ||
>; | ||
|
||
export const positionSearchableAttributes = [ | ||
"name", | ||
"nameEn", | ||
"description", | ||
"descriptionEn", | ||
"dsekId", | ||
"committeeName", | ||
"committeeNameEn", | ||
] as const satisfies Array< | ||
keyof Position | "dsekId" | "committeeName" | "committeeNameEn" | ||
>; | ||
export type SearchablePositionAttributes = FilterKeys< | ||
Position, | ||
(typeof positionSearchableAttributes)[number] | ||
>; | ||
|
||
export const songSearchableAttributes = [ | ||
"title", | ||
"lyrics", | ||
"melody", | ||
"category", | ||
] as const satisfies Array<keyof Song>; | ||
export type SearchableSongAttributes = Pick< | ||
Song, | ||
(typeof songSearchableAttributes)[number] | ||
>; | ||
|
||
export type SongSearchReturnAttributes = SearchableSongAttributes & | ||
Pick<Song, "slug">; | ||
export type ArticleSearchReturnAttributes = SearchableArticleAttributes & | ||
Pick<Article, "slug">; | ||
export type EventSearchReturnAttributes = SearchableEventAttributes & | ||
Pick<Event, "slug">; | ||
export type MemberSearchReturnAttributes = SearchableMemberAttributes & { | ||
picturePath: string; | ||
classYear: number; | ||
}; | ||
export type PositionSearchReturnAttributes = SearchablePositionAttributes & | ||
Pick<Position, "committeeId"> & { | ||
committee: Committee | null; | ||
}; | ||
|
||
export type SearchDataWithType = | ||
| { | ||
type: "members"; | ||
data: MemberSearchReturnAttributes; | ||
} | ||
| { | ||
type: "events"; | ||
data: EventSearchReturnAttributes; | ||
} | ||
| { | ||
type: "articles"; | ||
data: ArticleSearchReturnAttributes; | ||
} | ||
| { | ||
type: "songs"; | ||
data: SongSearchReturnAttributes; | ||
} | ||
| { | ||
type: "positions"; | ||
data: PositionSearchReturnAttributes; | ||
}; |
Oops, something went wrong.