Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for searching in PDFs #668

Merged
merged 12 commits into from
Jan 8, 2025
3 changes: 3 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,7 @@ MEILISEARCH_HOST=http://localhost:7700
MEILISEARCH_MASTER_KEY=masterKey


# POPPLER-SERVER
POPPLER_SERVER_URL=http://localhost:8800

PUBLIC_DISABLE_SSR_GLOBALLY=false
45 changes: 45 additions & 0 deletions src/lib/components/search/DocumentSearchResult.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<script lang="ts">
import type {
GoverningDocumentSearchReturnAttributes,
MeetingDocumentSearchReturnAttributes,
} from "$lib/search/searchTypes";

export let document: (
| GoverningDocumentSearchReturnAttributes
| MeetingDocumentSearchReturnAttributes
) & { type: "gov" | "meeting" };
alfredgrip marked this conversation as resolved.
Show resolved Hide resolved

const getUrl = (url: string) => {
if (url.startsWith("http")) {
return url;
} else {
// We store the url of governing documents as
// relative paths, and that logic is handled in the
// api/pdf endpoint
return `/api/pdf/${url}`;
}
};
</script>

<li>
<a
href={getUrl(document.url)}
class="search-result border border-transparent focus:border-primary"
>
<div class="avatar aspect-square w-8 overflow-hidden rounded-full">
{#if document.type === "gov"}
<span class="i-mdi-gavel text-2xl"></span>
{:else}
<span class="i-mdi-text-box-multiple-outline text-2xl"></span>
{/if}
</div>
<div>
<h4>
{document.title ? document.title : document.content.slice(0, 20)}
</h4>
<p class="line-clamp-1 text-gray-500">
{document.content}
</p>
</div>
</a>
</li>
7 changes: 6 additions & 1 deletion src/lib/components/search/SearchResultList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
import ArticleSearchResult from "$lib/components/search/ArticleSearchResult.svelte";
import EventSearchResult from "$lib/components/search/EventSearchResult.svelte";
import SongSearchResult from "$lib/components/search/SongSearchResult.svelte";
import CommitteeSearchResult from "./CommitteeSearchResult.svelte";
import CommitteeSearchResult from "$lib/components/search/CommitteeSearchResult.svelte";
import type { SearchDataWithType } from "$lib/search/searchTypes";
import DocumentSearchResult from "$lib/components/search/DocumentSearchResult.svelte";

export let results: SearchDataWithType[] = [];
</script>
Expand All @@ -23,5 +24,9 @@
<SongSearchResult song={searchValue.data} />
{:else if searchValue.type === "committees"}
<CommitteeSearchResult committee={searchValue.data} />
{:else if searchValue.type === "governingDocuments"}
<DocumentSearchResult document={{ ...searchValue.data, type: "gov" }} />
{:else if searchValue.type === "meetingDocuments"}
<DocumentSearchResult document={{ ...searchValue.data, type: "meeting" }} />
danieladugyan marked this conversation as resolved.
Show resolved Hide resolved
{/if}
{/each}
26 changes: 26 additions & 0 deletions src/lib/search/searchHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {
type SearchableArticleAttributes,
type SearchableCommitteeAttributes,
type SearchableEventAttributes,
type SearchableGoverningDocumentAttributes,
type SearchableIndex,
type SearchableMeetingDocumentAttributes,
type SearchableMemberAttributes,
type SearchablePositionAttributes,
type SearchableSongAttributes,
Expand Down Expand Up @@ -35,6 +37,10 @@ export function getFederatedWeight(index: SearchableIndex): number {
return 1;
case "committees":
return 2;
case "governingDocuments":
return 1;
case "meetingDocuments":
return 1;
default:
return 0;
}
Expand Down Expand Up @@ -114,6 +120,22 @@ export function getSearchableAttributes(
}
return res;
}
case "governingDocuments": {
// no language specific fields
const res: Array<keyof SearchableGoverningDocumentAttributes> = [
"title",
"content",
];
return res;
}
case "meetingDocuments": {
// no language specific fields
const res: Array<keyof SearchableMeetingDocumentAttributes> = [
"title",
"content",
];
return res;
}
default:
return [];
}
Expand All @@ -133,6 +155,10 @@ export function mapIndexToMessage(index: SearchableIndex) {
return m.search_songs();
case "committees":
return m.search_committees();
case "governingDocuments":
return m.search_governing_documents();
case "meetingDocuments":
return m.search_meeting_documents();
default:
return "";
}
Expand Down
108 changes: 104 additions & 4 deletions src/lib/search/searchTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* 3. which attributes are returned.
*
* Of course, all attributes that can be searched for, or are returned,
* must be stored in Meilisearch. However, it is not as simble as doing
* must be stored in Meilisearch. However, it is not as simple as doing
* a union of 2 and 3 to get 1, since some attributes (e.g `event.startDatetime`)
* are used purely for sorting and ranking purposes internally by Meilisearch.
*
Expand All @@ -24,6 +24,7 @@ import type {
Song,
Position,
Committee,
Document,
} from "@prisma/client";

/**
Expand Down Expand Up @@ -68,6 +69,8 @@ export const availableSearchIndexes = [
"positions",
"songs",
"committees",
"governingDocuments",
"meetingDocuments",
] as const;
export type SearchableIndex = (typeof availableSearchIndexes)[number];

Expand Down Expand Up @@ -257,13 +260,64 @@ export type SongSearchReturnAttributes = OnlySwedishAttributes<
SearchableSongAttributes & Pick<SongDataInMeilisearch, "slug">
>;

// --------------------------------------------------
// GOVERNING DOCUMENT
// --------------------------------------------------

// The order of the attributes in the array is important for ranking
// The lower the index, the higher the weight
export const governingDocumentSearchableAttributes = [
"title",
"content",
] as const satisfies Array<keyof GoverningDocumentDataInMeilisearch>;
export type SearchableGoverningDocumentAttributes = Pick<
GoverningDocumentDataInMeilisearch,
(typeof governingDocumentSearchableAttributes)[number]
>;
export type GoverningDocumentDataInMeilisearch = Prettify<
Pick<Document, "id" | "title" | "url" | "type"> & {
content: string;
}
>;
export type GoverningDocumentSearchReturnAttributes = OnlySwedishAttributes<
SearchableGoverningDocumentAttributes &
Pick<GoverningDocumentDataInMeilisearch, "url">
>;

// --------------------------------------------------
// MEETING DOCUMENT
// --------------------------------------------------

// The order of the attributes in the array is important for ranking
// The lower the index, the higher the weight
export const meetingDocumentSearchableAttributes = [
"title",
"content",
] as const satisfies Array<keyof MeetingDocumentDataInMeilisearch>;
export type SearchableMeetingDocumentAttributes = Pick<
MeetingDocumentDataInMeilisearch,
(typeof meetingDocumentSearchableAttributes)[number]
>;
export type MeetingDocumentDataInMeilisearch = Prettify<{
id: string;
title?: string;
url: string;
content: string;
}>;
export type MeetingDocumentSearchReturnAttributes = OnlySwedishAttributes<
SearchableMeetingDocumentAttributes &
Pick<MeetingDocumentDataInMeilisearch, "url">
>;

export type AnySearchReturnAttributes =
| SongSearchReturnAttributes
| ArticleSearchReturnAttributes
| EventSearchReturnAttributes
| MemberSearchReturnAttributes
| PositionSearchReturnAttributes
| CommitteeSearchReturnAttributes;
| CommitteeSearchReturnAttributes
| GoverningDocumentSearchReturnAttributes
| MeetingDocumentSearchReturnAttributes;

export type SearchDataWithType =
| {
Expand All @@ -289,6 +343,14 @@ export type SearchDataWithType =
| {
type: "committees";
data: CommitteeSearchReturnAttributes;
}
| {
type: "governingDocuments";
data: GoverningDocumentSearchReturnAttributes;
}
| {
type: "meetingDocuments";
data: MeetingDocumentSearchReturnAttributes;
};

export const attributesUsedAsLink: {
Expand All @@ -298,18 +360,22 @@ export const attributesUsedAsLink: {
songs: keyof SongSearchReturnAttributes;
positions: keyof PositionSearchReturnAttributes;
committees: keyof CommitteeSearchReturnAttributes;
governingDocuments: keyof GoverningDocumentSearchReturnAttributes;
meetingDocuments: keyof MeetingDocumentSearchReturnAttributes;
} = {
members: "studentId",
events: "slug",
articles: "slug",
songs: "slug",
positions: "dsekId",
committees: "shortName",
governingDocuments: "url",
meetingDocuments: "url",
};

export const listOfattributesUsedAsLink: string[] = Object.values(
export const listOfAttributesUsedAsLink: string[] = Object.values(
attributesUsedAsLink,
) satisfies string[];
) as string[];

type DefaultRankingRules =
| "words"
Expand Down Expand Up @@ -374,6 +440,18 @@ type CommitteeConstantsMeilisearch = Prettify<
type SongConstantsMeilisearch = Prettify<
IndexConstantsMeilisearch<SearchableSongAttributes, SongDataInMeilisearch>
>;
type GoverningDocumentConstantsMeilisearch = Prettify<
IndexConstantsMeilisearch<
SearchableGoverningDocumentAttributes,
GoverningDocumentDataInMeilisearch
>
>;
type MeetingDocumentConstantsMeilisearch = Prettify<
IndexConstantsMeilisearch<
SearchableMeetingDocumentAttributes,
MeetingDocumentDataInMeilisearch
>
>;

const memberMeilisearchConstants: MemberConstantsMeilisearch = {
searchableAttributes: memberSearchableAttributes,
Expand Down Expand Up @@ -423,13 +501,27 @@ const songMeilisearchConstants: SongConstantsMeilisearch = {
rankingRules: defaultRankingRules,
};

const governingDocumentMeilisearchConstants: GoverningDocumentConstantsMeilisearch =
{
searchableAttributes: governingDocumentSearchableAttributes,
rankingRules: defaultRankingRules,
};

const meetingDocumentMeilisearchConstants: MeetingDocumentConstantsMeilisearch =
{
searchableAttributes: meetingDocumentSearchableAttributes,
rankingRules: defaultRankingRules,
};

export const meilisearchConstants = {
member: memberMeilisearchConstants,
article: articleMeilisearchConstants,
event: eventMeilisearchConstants,
position: positionMeilisearchConstants,
committee: committeeMeilisearchConstants,
song: songMeilisearchConstants,
governingDocument: governingDocumentMeilisearchConstants,
meetingDocument: meetingDocumentMeilisearchConstants,
};

export type MeilisearchConstants =
Expand All @@ -456,4 +548,12 @@ export type MeilisearchConstants =
| {
constants: SongConstantsMeilisearch;
data: SongDataInMeilisearch;
}
| {
constants: GoverningDocumentConstantsMeilisearch;
data: GoverningDocumentDataInMeilisearch;
}
| {
constants: MeetingDocumentConstantsMeilisearch;
data: MeetingDocumentDataInMeilisearch;
};
Loading
Loading