adds search api

This commit is contained in:
Christian Beutel
2024-03-09 19:05:21 +01:00
parent 1b493f576e
commit 963b589b2d
8 changed files with 133 additions and 69 deletions

View File

@@ -1,17 +1,16 @@
<script lang="ts"> <script lang="ts">
import { ms } from "$lib/meilisearch";
import type { Category } from "$lib/models/category"; import type { Category } from "$lib/models/category";
import type { TrailFilter } from "$lib/models/trail"; import type { TrailFilter } from "$lib/models/trail";
import { country_codes } from "$lib/util/country_code_util"; import { country_codes } from "$lib/util/country_code_util";
import { formatDistance, formatElevation } from "$lib/util/format_util"; import { formatDistance, formatElevation } from "$lib/util/format_util";
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import { _ } from "svelte-i18n";
import { slide } from "svelte/transition";
import DoubleSlider from "../base/double_slider.svelte"; import DoubleSlider from "../base/double_slider.svelte";
import type { RadioItem } from "../base/radio_group.svelte"; import type { RadioItem } from "../base/radio_group.svelte";
import RadioGroup from "../base/radio_group.svelte"; import RadioGroup from "../base/radio_group.svelte";
import Search, { type SearchItem } from "../base/search.svelte"; import Search, { type SearchItem } from "../base/search.svelte";
import Slider from "../base/slider.svelte"; import Slider from "../base/slider.svelte";
import { slide } from "svelte/transition";
import { _ } from "svelte-i18n";
export let categories: Category[]; export let categories: Category[];
export let filterExpanded: boolean = true; export let filterExpanded: boolean = true;
@@ -87,8 +86,13 @@
return; return;
} }
const result = await ms.index("cities500").search(q, { limit: 5 }); const r = await fetch("/api/v1/search/cities500", {
searchDropdownItems = result.hits.map((h) => ({ method: "POST",
body: JSON.stringify({ q: q, options: { limit: 5 } }),
});
const result = await r.json();
searchDropdownItems = result.hits.map((h: Record<string, any>) => ({
text: h.name, text: h.name,
description: description:
country_codes[h["country code"] as keyof typeof country_codes], country_codes[h["country code"] as keyof typeof country_codes],

View File

@@ -7,7 +7,6 @@ import { util } from "$lib/vendor/svelte-form-lib/util";
import { get, writable, type Writable } from "svelte/store"; import { get, writable, type Writable } from "svelte/store";
import { summit_logs_create, summit_logs_delete, summit_logs_update } from "./summit_log_store"; import { summit_logs_create, summit_logs_delete, summit_logs_update } from "./summit_log_store";
import { waypoints_create, waypoints_delete, waypoints_update } from "./waypoint_store"; import { waypoints_create, waypoints_delete, waypoints_update } from "./waypoint_store";
import { ms } from "$lib/meilisearch";
import type { LatLng } from "leaflet"; import type { LatLng } from "leaflet";
import { ClientResponseError } from "pocketbase"; import { ClientResponseError } from "pocketbase";
@@ -45,19 +44,22 @@ export async function trails_search_filter(filter: TrailFilter, f: (url: Request
if (filter.near.lat && filter.near.lon) { if (filter.near.lat && filter.near.lon) {
filterText += ` AND _geoRadius(${filter.near.lat}, ${filter.near.lon}, ${filter.near.radius})` filterText += ` AND _geoRadius(${filter.near.lat}, ${filter.near.lon}, ${filter.near.radius})`
} }
const indexResponse = await ms let r = await f("/api/v1/search/trails", {
.index("trails") method: "POST",
.search(filter.q, { filter: filterText }); body: JSON.stringify({ q: filter.q, options: { filter: filterText } }),
const trailIds = indexResponse.hits.map((h) => h.id); });
const result = await r.json();
const trailIds = result.hits.map((h: Record<string, any>) => h.id);
if (trailIds.length == 0) { if (trailIds.length == 0) {
trails.set([]); trails.set([]);
return []; return [];
} }
const r = await f('/api/v1/trail?' + new URLSearchParams({ r = await f('/api/v1/trail?' + new URLSearchParams({
expand: "category,waypoints,summit_logs", expand: "category,waypoints,summit_logs",
filter: trailIds.map((id) => `id="${id}"`).join('||'), filter: trailIds.map((id: Record<string, any>) => `id="${id}"`).join('||'),
sort: `${filter.sortOrder}${filter.sort}` sort: `${filter.sortOrder}${filter.sort}`
}), { }), {
method: 'GET', method: 'GET',
@@ -87,13 +89,20 @@ export async function trails_search_bounding_box(northEast: LatLng, southWest: L
} }
} }
const indexResponse = await ms.index("trails").search("", { let r = await fetch("/api/v1/search/trails", {
filter: [ method: "POST",
`_geoBoundingBox([${northEast.lat}, ${northEast.lng}], [${southWest.lat}, ${southWest.lng}])`, body: JSON.stringify({
filterText q: "", options: {
], filter: [
`_geoBoundingBox([${northEast.lat}, ${northEast.lng}], [${southWest.lat}, ${southWest.lng}])`,
filterText
],
}
}),
}); });
const trailIds = indexResponse.hits.map((h) => h.id); const result = await r.json();
const trailIds = result.hits.map((h: Record<string, any>) => h.id);
if (trailIds.length == 0) { if (trailIds.length == 0) {
const currentTrails: Trail[] = get(trails); const currentTrails: Trail[] = get(trails);
@@ -101,8 +110,8 @@ export async function trails_search_bounding_box(northEast: LatLng, southWest: L
return compareObjectArrays<Trail>(currentTrails, []); return compareObjectArrays<Trail>(currentTrails, []);
} }
const r = await fetch('/api/v1/trail?' + new URLSearchParams({ r = await fetch('/api/v1/trail?' + new URLSearchParams({
filter: trailIds.map((id) => `id="${id}"`).join("||"), filter: trailIds.map((id: Record<string, any>) => `id="${id}"`).join("||"),
expand: "category,waypoints,summit_logs", expand: "category,waypoints,summit_logs",
sort: `+name`, sort: `+name`,
}), { }), {

View File

@@ -6,7 +6,6 @@
import CategoryCard from "$lib/components/category_card.svelte"; import CategoryCard from "$lib/components/category_card.svelte";
import Scene from "$lib/components/scene.svelte"; import Scene from "$lib/components/scene.svelte";
import TrailCard from "$lib/components/trail/trail_card.svelte"; import TrailCard from "$lib/components/trail/trail_card.svelte";
import { ms } from "$lib/meilisearch";
import { categories } from "$lib/stores/category_store"; import { categories } from "$lib/stores/category_store";
import { trails } from "$lib/stores/trail_store"; import { trails } from "$lib/stores/trail_store";
import { currentUser } from "$lib/stores/user_store"; import { currentUser } from "$lib/stores/user_store";
@@ -17,33 +16,38 @@
let searchDropdownItems: SearchItem[] = []; let searchDropdownItems: SearchItem[] = [];
async function search(q: string) { async function search(q: string) {
const response = await ms.multiSearch({ const r = await fetch("/api/v1/search/multi", {
queries: [ method: "POST",
{ body: JSON.stringify({
indexUid: "trails", queries: [
q: q, {
limit: 3, indexUid: "trails",
}, q: q,
{ limit: 3,
indexUid: "cities500", },
q: q, {
limit: 3, indexUid: "cities500",
}, q: q,
], limit: 3,
},
],
}),
}); });
const trailItems = response.results[0].hits.map((t) => ({ const response = await r.json();
const trailItems = response.results[0].hits.map((t: Record<string, any>) => ({
text: t.name, text: t.name,
description: `Trail | ${t.location}`, description: `Trail | ${t.location}`,
value: t.id, value: t.id,
icon: "route", icon: "route",
})); }));
const cityItems = response.results[1].hits.map((t) => ({ const cityItems = response.results[1].hits.map((c: Record<string, any>) => ({
text: t.name, text: c.name,
description: `City | ${ description: `City | ${
country_codes[t["country code"] as keyof typeof country_codes] country_codes[c["country code"] as keyof typeof country_codes]
}`, }`,
value: t, value: c,
icon: "city", icon: "city",
})); }));

View File

@@ -0,0 +1,13 @@
import { ms } from "$lib/meilisearch";
import { error, json, type RequestEvent } from "@sveltejs/kit";
export async function POST(event: RequestEvent) {
const data = await event.request.json()
try {
const r = await ms.index(event.params.index as string).search(data.q, data.options);
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}

View File

@@ -0,0 +1,15 @@
import { ms } from "$lib/meilisearch";
import { error, json, type RequestEvent } from "@sveltejs/kit";
export async function POST(event: RequestEvent) {
const data = await event.request.json()
try {
const r = await ms.multiSearch({
queries: data.queries
});
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}

View File

@@ -8,7 +8,6 @@
import EmptyStateSearch from "$lib/components/empty_states/empty_state_search.svelte"; import EmptyStateSearch from "$lib/components/empty_states/empty_state_search.svelte";
import TrailCard from "$lib/components/trail/trail_card.svelte"; import TrailCard from "$lib/components/trail/trail_card.svelte";
import TrailFilterPanel from "$lib/components/trail/trail_filter_panel.svelte"; import TrailFilterPanel from "$lib/components/trail/trail_filter_panel.svelte";
import { ms } from "$lib/meilisearch";
import type { Trail, TrailFilter } from "$lib/models/trail"; import type { Trail, TrailFilter } from "$lib/models/trail";
import { categories } from "$lib/stores/category_store"; import { categories } from "$lib/stores/category_store";
import { import {
@@ -111,28 +110,33 @@
}); });
async function search(q: string) { async function search(q: string) {
const response = await ms.multiSearch({ const r = await fetch("/api/v1/search/multi", {
queries: [ method: "POST",
{ body: JSON.stringify({
indexUid: "trails", queries: [
q: q, {
limit: 3, indexUid: "trails",
}, q: q,
{ limit: 3,
indexUid: "cities500", },
q: q, {
limit: 3, indexUid: "cities500",
}, q: q,
], limit: 3,
},
],
}),
}); });
const trailItems = response.results[0].hits.map((t) => ({ const response = await r.json();
const trailItems = response.results[0].hits.map((t: Record<string, any>) => ({
text: t.name, text: t.name,
description: `Trail | ${t.location}`, description: `Trail | ${t.location}`,
value: t, value: t,
icon: "route", icon: "route",
})); }));
const cityItems = response.results[1].hits.map((c) => ({ const cityItems = response.results[1].hits.map((c: Record<string, any>) => ({
text: c.name, text: c.name,
description: `City | ${ description: `City | ${
country_codes[c["country code"] as keyof typeof country_codes] country_codes[c["country code"] as keyof typeof country_codes]

View File

@@ -10,7 +10,6 @@
type SelectItem, type SelectItem,
} from "$lib/components/base/select.svelte"; } from "$lib/components/base/select.svelte";
import ConfirmModal from "$lib/components/confirm_modal.svelte"; import ConfirmModal from "$lib/components/confirm_modal.svelte";
import { ms } from "$lib/meilisearch";
import { import {
currentUser, currentUser,
logout, logout,
@@ -45,8 +44,12 @@
}); });
async function searchCities(q: string) { async function searchCities(q: string) {
const result = await ms.index("cities500").search(q, { limit: 5 }); const r = await fetch("/api/v1/search/cities500", {
searchDropdownItems = result.hits.map((h) => ({ method: "POST",
body: JSON.stringify({ q: q, options: { limit: 5 } }),
});
const result = await r.json();
searchDropdownItems = result.hits.map((h: Record<string, any>) => ({
text: h.name, text: h.name,
description: description:
country_codes[h["country code"] as keyof typeof country_codes], country_codes[h["country code"] as keyof typeof country_codes],

View File

@@ -9,7 +9,6 @@
import SummitLogModal from "$lib/components/summit_log/summit_log_modal.svelte"; import SummitLogModal from "$lib/components/summit_log/summit_log_modal.svelte";
import WaypointCard from "$lib/components/waypoint/waypoint_card.svelte"; import WaypointCard from "$lib/components/waypoint/waypoint_card.svelte";
import WaypointModal from "$lib/components/waypoint/waypoint_modal.svelte"; import WaypointModal from "$lib/components/waypoint/waypoint_modal.svelte";
import { ms } from "$lib/meilisearch";
import { SummitLog } from "$lib/models/summit_log"; import { SummitLog } from "$lib/models/summit_log";
import { Trail, trailSchema } from "$lib/models/trail"; import { Trail, trailSchema } from "$lib/models/trail";
import { Waypoint } from "$lib/models/waypoint"; import { Waypoint } from "$lib/models/waypoint";
@@ -106,7 +105,12 @@
); );
$form.id = createdTrail.id; $form.id = createdTrail.id;
} else { } else {
await trails_update($trail, submittedTrail, photoFiles, gpxFile); await trails_update(
$trail,
submittedTrail,
photoFiles,
gpxFile,
);
} }
show_toast({ show_toast({
@@ -222,13 +226,20 @@
reader.onload = async function (e) { reader.onload = async function (e) {
await addGPXLayer(e.target?.result as string); await addGPXLayer(e.target?.result as string);
const closestCity = ( const r = await fetch("/api/v1/search/cities500", {
await ms.index("cities500").search("", { method: "POST",
filter: [`_geoRadius(${$form.lat}, ${$form.lon}, 10000)`], body: JSON.stringify({
sort: [`_geoPoint(${$form.lat}, ${$form.lon}):asc`], q: "",
limit: 1, options: {
}) filter: [
).hits[0]; `_geoRadius(${$form.lat}, ${$form.lon}, 10000)`,
],
sort: [`_geoPoint(${$form.lat}, ${$form.lon}):asc`],
limit: 1,
},
}),
});
const closestCity = (await r.json()).hits[0];
$form.location = closestCity.name; $form.location = closestCity.name;
}; };
@@ -318,7 +329,6 @@
if ($form.thumbnail == index) { if ($form.thumbnail == index) {
$form.thumbnail = 0; $form.thumbnail = 0;
console.log("huere"); console.log("huere");
} }
if (index >= $form.photos.length) { if (index >= $form.photos.length) {
@@ -491,7 +501,9 @@
{#each ($form.photos ?? []).concat(photoPreviews) as photo, i} {#each ($form.photos ?? []).concat(photoPreviews) as photo, i}
<div class="shrink-0 grow-0 basis-auto m-2"> <div class="shrink-0 grow-0 basis-auto m-2">
<PhotoCard <PhotoCard
src={i >= $form.photos.length ? photo : getFileURL($form, photo)} src={i >= $form.photos.length
? photo
: getFileURL($form, photo)}
on:delete={() => handlePhotoDelete(i)} on:delete={() => handlePhotoDelete(i)}
isThumbnail={$form.thumbnail === i} isThumbnail={$form.thumbnail === i}
on:thumbnail={() => makePhotoThumbnail(i)} on:thumbnail={() => makePhotoThumbnail(i)}