-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Serializes search props into a Solr search string
- Loading branch information
1 parent
777c6ab
commit 1660b09
Showing
2 changed files
with
196 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
<script lang="ts" context="module"> | ||
export function serialize({ | ||
query, | ||
filters, | ||
sort, | ||
order = "asc", | ||
}: { | ||
query?: string; | ||
filters?: FilterFields; | ||
sort?: SortField; | ||
order?: SortOrder; | ||
}): string { | ||
const parts: string[] = []; | ||
// Add main query | ||
if (query) parts.push(query); | ||
if (filters) { | ||
// Add access filter | ||
if (filters.access) { | ||
parts.push(`access:${filters.access}`); | ||
} | ||
// Add page count filters | ||
if (filters.minPages && filters.maxPages) { | ||
parts.push(`pages:[${filters.minPages} TO ${filters.maxPages}]`); | ||
} else if (filters.minPages) { | ||
parts.push(`pages:[${filters.minPages} TO *]`); | ||
} else if (filters.maxPages) { | ||
parts.push(`pages:[* TO ${filters.maxPages}]`); | ||
} | ||
// Add user filters | ||
if (filters.users?.length) { | ||
const userPart = filters.users.map((u) => `user:${u.id}`).join(" OR "); | ||
parts.push(filters.users.length > 1 ? `(${userPart})` : userPart); | ||
} | ||
// Add organization filters | ||
if (filters.orgs?.length) { | ||
const orgPart = filters.orgs | ||
.map((o) => `organization:${o.id}`) | ||
.join(" OR "); | ||
parts.push(filters.orgs.length > 1 ? `(${orgPart})` : orgPart); | ||
} | ||
// Add project filters | ||
if (filters.projects?.length) { | ||
const projectPart = filters.projects | ||
.map((p) => `project:${p.id}`) | ||
.join(" OR "); | ||
parts.push( | ||
filters.projects.length > 1 ? `(${projectPart})` : projectPart, | ||
); | ||
} | ||
} | ||
if (sort) { | ||
if (order === "asc") { | ||
parts.push(`sort:${sort}`); | ||
} else { | ||
parts.push(`sort:-${sort}`); | ||
} | ||
} | ||
return parts.join(" "); | ||
} | ||
</script> | ||
|
||
<script lang="ts"> | ||
import { _ } from "svelte-i18n"; | ||
import Search from "../forms/Search.svelte"; | ||
import Filter, { type FilterFields, defaultFilters } from "./Filter.svelte"; | ||
import Sort, { type SortField, type SortOrder } from "./Sort.svelte"; | ||
export let query = ""; | ||
export let filters: FilterFields = defaultFilters; | ||
export let sort: SortField = "created_at"; | ||
export let order: SortOrder = "desc"; | ||
export let fields: SortField[] = ["created_at", "title", "page_count"]; | ||
</script> | ||
|
||
<div class="search"> | ||
<Search bind:query name="q" /> | ||
<div class="controls"> | ||
<Filter bind:filters /> | ||
<Sort bind:order bind:sort {fields} {query} /> | ||
</div> | ||
</div> | ||
|
||
<style> | ||
.search { | ||
display: flex; | ||
flex-direction: row; | ||
align-items: center; | ||
gap: 0.5rem; | ||
} | ||
.controls { | ||
display: flex; | ||
gap: 0.5rem; | ||
align-items: center; | ||
} | ||
</style> |
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,92 @@ | ||
import { describe, it, expect } from "vitest"; | ||
import { serialize } from "../Search.svelte"; | ||
import type { FilterFields } from "../Filter.svelte"; | ||
import type { SortField, SortOrder } from "../Sort.svelte"; | ||
import type { Project, User } from "@/lib/api/types"; | ||
|
||
interface SearchProps { | ||
query?: string; | ||
filters?: FilterFields; | ||
sort?: SortField; | ||
order?: SortOrder; | ||
} | ||
|
||
describe("Search.svelte", () => { | ||
describe("serialize", () => { | ||
it("should serialize a basic query", () => { | ||
const props: SearchProps = { query: "foobar" }; | ||
expect(serialize(props)).toBe("foobar"); | ||
}); | ||
|
||
it("should serialize filters", () => { | ||
const props: SearchProps = { | ||
query: "document", | ||
filters: { | ||
users: [{ id: 12345, name: "Test User" }] as User[], | ||
}, | ||
}; | ||
expect(serialize(props)).toBe("document user:12345"); | ||
}); | ||
|
||
it("should serialize multiple filters with OR", () => { | ||
const props: SearchProps = { | ||
query: "report", | ||
filters: { | ||
projects: [ | ||
{ id: 45678, title: "Project 1" }, | ||
{ id: 121212, title: "Project 2" }, | ||
] as Project[], | ||
}, | ||
}; | ||
expect(serialize(props)).toBe("report (project:45678 OR project:121212)"); | ||
}); | ||
|
||
it("should serialize access filter", () => { | ||
const props: SearchProps = { | ||
query: "secret", | ||
filters: { access: "private" }, | ||
}; | ||
expect(serialize(props)).toBe("secret access:private"); | ||
}); | ||
|
||
it("should serialize page count filters", () => { | ||
// with both min and max | ||
let props: SearchProps = { | ||
query: "long", | ||
filters: { minPages: 10, maxPages: 20 }, | ||
}; | ||
expect(serialize(props)).toBe("long pages:[10 TO 20]"); | ||
|
||
// with only min | ||
props = { | ||
query: "long", | ||
filters: { minPages: 10 }, | ||
}; | ||
expect(serialize(props)).toBe("long pages:[10 TO *]"); | ||
|
||
// with only max | ||
props = { | ||
query: "long", | ||
filters: { maxPages: 20 }, | ||
}; | ||
expect(serialize(props)).toBe("long pages:[* TO 20]"); | ||
}); | ||
|
||
it("should serialize sort and order", () => { | ||
// in ascending order, the sort is positive | ||
let props: SearchProps = { | ||
query: "test", | ||
sort: "title", | ||
order: "asc", | ||
}; | ||
expect(serialize(props)).toBe("test sort:title"); | ||
// in descending order, the sort is negative | ||
props = { | ||
query: "test", | ||
sort: "title", | ||
order: "desc", | ||
}; | ||
expect(serialize(props)).toBe("test sort:-title"); | ||
}); | ||
}); | ||
}); |