mirror of
https://github.com/BradNut/boredgame
synced 2025-09-08 17:40:22 +00:00
Changing search to currently just search by like. Adding functions to get order by and direction based on search inputs.
This commit is contained in:
parent
45e9e5adb7
commit
0b58b3ff8f
9 changed files with 195 additions and 347 deletions
473
pnpm-lock.yaml
473
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
|
@ -1,8 +1,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { superForm, type Infer, type SuperValidated } from 'sveltekit-superforms';
|
import { superForm, type Infer, type SuperValidated } from 'sveltekit-superforms';
|
||||||
import { search_schema, type SearchSchema } from '$lib/zodValidation';
|
|
||||||
import * as Form from "$lib/components/ui/form";
|
|
||||||
import { zodClient } from 'sveltekit-superforms/adapters';
|
import { zodClient } from 'sveltekit-superforms/adapters';
|
||||||
|
import { search_schema, type SearchSchema } from '$lib/zodValidation';
|
||||||
|
import * as Form from "$lib/components/ui/form";
|
||||||
import Input from '$components/ui/input/input.svelte';
|
import Input from '$components/ui/input/input.svelte';
|
||||||
import Checkbox from '$components/ui/checkbox/checkbox.svelte';
|
import Checkbox from '$components/ui/checkbox/checkbox.svelte';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ export async function createOrUpdateGameMinimal(locals: App.Locals, game: Games,
|
||||||
.insert(games)
|
.insert(games)
|
||||||
.values({
|
.values({
|
||||||
name: game.name,
|
name: game.name,
|
||||||
slug: kebabCase(game.name || game.slug || ''),
|
slug: kebabCase(game.name ?? game.slug ?? ''),
|
||||||
description: game.description,
|
description: game.description,
|
||||||
year_published: game.year_published,
|
year_published: game.year_published,
|
||||||
url: externalUrl,
|
url: externalUrl,
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ async function searchForGames(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers
|
headers
|
||||||
};
|
};
|
||||||
const url = `/api/game/search${urlQueryParams ? `?${urlQueryParams}` : ''}`;
|
const url = `/api/games/search${urlQueryParams ? `?${urlQueryParams}` : ''}`;
|
||||||
console.log('Calling internal api', url);
|
console.log('Calling internal api', url);
|
||||||
const response = await eventFetch(url, requestInit);
|
const response = await eventFetch(url, requestInit);
|
||||||
console.log('response from internal api', response);
|
console.log('response from internal api', response);
|
||||||
|
|
@ -44,9 +44,9 @@ async function searchForGames(
|
||||||
!games.find((game: GameType) => game.slug === kebabCase(gameNameSearch))
|
!games.find((game: GameType) => game.slug === kebabCase(gameNameSearch))
|
||||||
) {
|
) {
|
||||||
console.log('No games found in DB for', gameNameSearch);
|
console.log('No games found in DB for', gameNameSearch);
|
||||||
|
const searchQueryParams = urlQueryParams ? `?${urlQueryParams}` : '';
|
||||||
const externalResponse = await eventFetch(
|
const externalResponse = await eventFetch(
|
||||||
`/api/external/search${urlQueryParams ? `?${urlQueryParams}` : ''}`,
|
`/api/external/search${searchQueryParams}`,
|
||||||
requestInit
|
requestInit
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -102,7 +102,7 @@ const defaults = {
|
||||||
exact: false,
|
exact: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ locals, fetch, url }) => {
|
export const load = async ({ locals, fetch, url }) => {
|
||||||
const searchParams = Object.fromEntries(url?.searchParams);
|
const searchParams = Object.fromEntries(url?.searchParams);
|
||||||
console.log('searchParams', searchParams);
|
console.log('searchParams', searchParams);
|
||||||
searchParams.order = searchParams.order || defaults.order;
|
searchParams.order = searchParams.order || defaults.order;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { fail, error, type Actions } from '@sveltejs/kit';
|
import { fail, error, type Actions } from '@sveltejs/kit';
|
||||||
import { and, eq, ne } from 'drizzle-orm';
|
import { and, eq } from 'drizzle-orm';
|
||||||
import { Argon2id } from 'oslo/password';
|
import { Argon2id } from 'oslo/password';
|
||||||
import { decodeHex } from 'oslo/encoding';
|
import { decodeHex } from 'oslo/encoding';
|
||||||
import { TOTPController } from 'oslo/otp';
|
import { TOTPController } from 'oslo/otp';
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import { error, json } from '@sveltejs/kit';
|
import { error, json } from '@sveltejs/kit';
|
||||||
import db from '$lib/drizzle.js';
|
import db from '$lib/drizzle.js';
|
||||||
import { eq, sql } from 'drizzle-orm';
|
import {asc, desc, eq, ilike, or } from 'drizzle-orm';
|
||||||
import { games } from '../../../../schema.js';
|
import { games } from '../../../../schema.js';
|
||||||
|
import kebabCase from "just-kebab-case";
|
||||||
|
|
||||||
// Search a user's collection
|
// Search a user's collection
|
||||||
export const GET = async ({ url, locals }) => {
|
export const GET = async ({ url, locals }) => {
|
||||||
|
|
@ -9,14 +10,14 @@ export const GET = async ({ url, locals }) => {
|
||||||
const q = searchParams?.q?.trim() || '';
|
const q = searchParams?.q?.trim() || '';
|
||||||
const limit = parseInt(searchParams?.limit) || 10;
|
const limit = parseInt(searchParams?.limit) || 10;
|
||||||
const skip = parseInt(searchParams?.skip) || 0;
|
const skip = parseInt(searchParams?.skip) || 0;
|
||||||
const order = searchParams?.order || 'desc';
|
const order: OrderDirection = searchParams?.order === 'desc' ? 'desc' : 'asc';
|
||||||
const exact = searchParams?.exact === 'true';
|
const exact = searchParams?.exact === 'true';
|
||||||
let orderBy = searchParams?.orderBy || 'slug';
|
let orderBy = searchParams?.orderBy || 'slug';
|
||||||
|
|
||||||
if (orderBy === 'name') {
|
if (orderBy === 'name') {
|
||||||
orderBy = 'slug';
|
orderBy = 'slug';
|
||||||
}
|
}
|
||||||
console.log(`q: ${q}, limit: ${limit}, skip: ${skip}, order: ${order}, exact: ${exact}`);
|
console.log(`q: ${q}, limit: ${limit}, skip: ${skip}, order: ${order}, exact: ${exact}, orderBy: ${orderBy}`);
|
||||||
console.log(exact);
|
console.log(exact);
|
||||||
if (exact) {
|
if (exact) {
|
||||||
console.log('Exact Search API');
|
console.log('Exact Search API');
|
||||||
|
|
@ -44,9 +45,23 @@ export const GET = async ({ url, locals }) => {
|
||||||
slug: games.slug,
|
slug: games.slug,
|
||||||
thumb_url: games.thumb_url
|
thumb_url: games.thumb_url
|
||||||
})
|
})
|
||||||
.from(games)
|
.from(games)
|
||||||
.where(sql`to_tsvector('simple', ${games.name}) || to_tsvector('simple', ${games.slug}) @@ to_tsquery('simple', ${q})`)
|
.where(or(
|
||||||
.orderBy(sql`${orderBy} ${order}`).offset(skip).limit(limit) || [];
|
ilike(games.name, `%${q}%`),
|
||||||
|
ilike(games.slug, `%${kebabCase(q)}%`)
|
||||||
|
))
|
||||||
|
.orderBy(getOrderDirection(order)(getOrderBy(orderBy)))
|
||||||
|
.offset(skip)
|
||||||
|
.limit(limit) || [];
|
||||||
|
// const foundGames = await db.select({
|
||||||
|
// id: games.id,
|
||||||
|
// name: games.name,
|
||||||
|
// slug: games.slug,
|
||||||
|
// thumb_url: games.thumb_url
|
||||||
|
// })
|
||||||
|
// .from(games)
|
||||||
|
// .where(sql`to_tsvector('simple', ${games.name}) || to_tsvector('simple', ${games.slug}) @@ to_tsquery('simple', ${q})`)
|
||||||
|
// .orderBy(sql`${orderBy} ${order}`).offset(skip).limit(limit) || [];
|
||||||
if (foundGames.length === 0) {
|
if (foundGames.length === 0) {
|
||||||
error(404, { message: 'No games found' });
|
error(404, { message: 'No games found' });
|
||||||
}
|
}
|
||||||
|
|
@ -54,3 +69,20 @@ export const GET = async ({ url, locals }) => {
|
||||||
return json(foundGames);
|
return json(foundGames);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type OrderDirection = 'asc' | 'desc';
|
||||||
|
|
||||||
|
const getOrderDirection = (direction: OrderDirection) => {
|
||||||
|
return direction === 'asc' ? asc: desc;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getOrderBy = (orderBy: string) => {
|
||||||
|
switch (orderBy) {
|
||||||
|
case 'name':
|
||||||
|
return games.name;
|
||||||
|
case 'slug':
|
||||||
|
return games.slug;
|
||||||
|
default:
|
||||||
|
return games.slug;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
import { customType } from 'drizzle-orm/pg-core';
|
import { customType } from 'drizzle-orm/pg-core';
|
||||||
|
import {sql} from "drizzle-orm";
|
||||||
|
|
||||||
function genExpWithWeights(input: string[]) {
|
function genExpWithWeights(input: string[]) {
|
||||||
const columnExpressions = input.map((column, index) => {
|
const columnExpressions = input.map((column, index) => {
|
||||||
const weight = String.fromCharCode(index + 65);
|
const weight = String.fromCharCode(index + 65);
|
||||||
return `setweight(to_tsvector('english', coalesce(${column}, '')), '${weight}')`;
|
return sql`setweight(to_tsvector('english', coalesce(${column}, '')), '${weight}')`;
|
||||||
});
|
});
|
||||||
|
|
||||||
return `tsvector GENERATED ALWAYS AS (${columnExpressions.join(' || ')}) STORED`;
|
return sql`tsvector GENERATED ALWAYS AS (${columnExpressions.join(' || ')}) STORED`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const tsvector = customType<{
|
export const tsvector = customType<{
|
||||||
|
|
@ -18,9 +19,9 @@ export const tsvector = customType<{
|
||||||
const sources = config.sources.join(" || ' ' || ");
|
const sources = config.sources.join(" || ' ' || ");
|
||||||
return config.weighted
|
return config.weighted
|
||||||
? genExpWithWeights(config.sources)
|
? genExpWithWeights(config.sources)
|
||||||
: `tsvector generated always as (to_tsvector('english', ${sources})) stored`;
|
: sql`tsvector generated always as (to_tsvector('english', ${sources})) stored`;
|
||||||
} else {
|
} else {
|
||||||
return `tsvector`;
|
return sql`tsvector`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue