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:
Bradley Shellnut 2024-04-27 19:52:57 -07:00
parent 45e9e5adb7
commit 0b58b3ff8f
9 changed files with 195 additions and 347 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,8 @@
<script lang="ts">
import { superForm, type Infer, type SuperValidated } from 'sveltekit-superforms';
import { zodClient } from 'sveltekit-superforms/adapters';
import { search_schema, type SearchSchema } from '$lib/zodValidation';
import * as Form from "$lib/components/ui/form";
import { zodClient } from 'sveltekit-superforms/adapters';
import Input from '$components/ui/input/input.svelte';
import Checkbox from '$components/ui/checkbox/checkbox.svelte';

View file

@ -118,7 +118,7 @@ export async function createOrUpdateGameMinimal(locals: App.Locals, game: Games,
.insert(games)
.values({
name: game.name,
slug: kebabCase(game.name || game.slug || ''),
slug: kebabCase(game.name ?? game.slug ?? ''),
description: game.description,
year_published: game.year_published,
url: externalUrl,

View file

@ -23,7 +23,7 @@ async function searchForGames(
method: 'GET',
headers
};
const url = `/api/game/search${urlQueryParams ? `?${urlQueryParams}` : ''}`;
const url = `/api/games/search${urlQueryParams ? `?${urlQueryParams}` : ''}`;
console.log('Calling internal api', url);
const response = await eventFetch(url, requestInit);
console.log('response from internal api', response);
@ -44,9 +44,9 @@ async function searchForGames(
!games.find((game: GameType) => game.slug === kebabCase(gameNameSearch))
) {
console.log('No games found in DB for', gameNameSearch);
const searchQueryParams = urlQueryParams ? `?${urlQueryParams}` : '';
const externalResponse = await eventFetch(
`/api/external/search${urlQueryParams ? `?${urlQueryParams}` : ''}`,
`/api/external/search${searchQueryParams}`,
requestInit
);
@ -102,7 +102,7 @@ const defaults = {
exact: false,
};
export const load: PageServerLoad = async ({ locals, fetch, url }) => {
export const load = async ({ locals, fetch, url }) => {
const searchParams = Object.fromEntries(url?.searchParams);
console.log('searchParams', searchParams);
searchParams.order = searchParams.order || defaults.order;

View file

@ -1,5 +1,5 @@
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 { decodeHex } from 'oslo/encoding';
import { TOTPController } from 'oslo/otp';

View file

@ -1,7 +1,8 @@
import { error, json } from '@sveltejs/kit';
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 kebabCase from "just-kebab-case";
// Search a user's collection
export const GET = async ({ url, locals }) => {
@ -9,14 +10,14 @@ export const GET = async ({ url, locals }) => {
const q = searchParams?.q?.trim() || '';
const limit = parseInt(searchParams?.limit) || 10;
const skip = parseInt(searchParams?.skip) || 0;
const order = searchParams?.order || 'desc';
const order: OrderDirection = searchParams?.order === 'desc' ? 'desc' : 'asc';
const exact = searchParams?.exact === 'true';
let orderBy = searchParams?.orderBy || 'slug';
if (orderBy === 'name') {
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);
if (exact) {
console.log('Exact Search API');
@ -45,8 +46,22 @@ export const GET = async ({ url, locals }) => {
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) || [];
.where(or(
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) {
error(404, { message: 'No games found' });
}
@ -54,3 +69,20 @@ export const GET = async ({ url, locals }) => {
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;
}
}

View file

@ -1,12 +1,13 @@
import { customType } from 'drizzle-orm/pg-core';
import {sql} from "drizzle-orm";
function genExpWithWeights(input: string[]) {
const columnExpressions = input.map((column, index) => {
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<{
@ -18,9 +19,9 @@ export const tsvector = customType<{
const sources = config.sources.join(" || ' ' || ");
return config.weighted
? genExpWithWeights(config.sources)
: `tsvector generated always as (to_tsvector('english', ${sources})) stored`;
: sql`tsvector generated always as (to_tsvector('english', ${sources})) stored`;
} else {
return `tsvector`;
return sql`tsvector`;
}
}
});