mirror of
https://github.com/tauri-apps/tauri-search.git
synced 2026-02-04 02:41:20 +01:00
chore(docs): 2nd round of de-linting once React type bleed-in was removed
This commit is contained in:
@@ -3,11 +3,11 @@
|
||||
import { useSearch } from "~/modules/search";
|
||||
import { format } from "date-fns";
|
||||
import { PropType } from "vue";
|
||||
import { MeiliSearchInterface } from "~/types/meilisearch";
|
||||
import { ApiModel } from "tauri-search";
|
||||
import type { IMeilisearchInterface} from "tauri-search";
|
||||
const s = useSearch();
|
||||
const props = defineProps({
|
||||
idx: {type: Object as PropType<MeiliSearchInterface>, required: true},
|
||||
idx: {type: Object as PropType<IMeilisearchInterface>, required: true},
|
||||
unknown: {type: Boolean, default: false, required: false}
|
||||
});
|
||||
const settings = computed(() => s.$state.indexSettings[props.idx.name]);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { PropType } from "vue";
|
||||
import { GenericDoc } from "~/types/meilisearch";
|
||||
import { GenericDoc } from "tauri-search";
|
||||
|
||||
const props = defineProps({
|
||||
document: {
|
||||
@@ -84,7 +84,7 @@ const details = () => {
|
||||
{{apiKind}}
|
||||
</div>
|
||||
<div class="flex px-1 hover:text-green-600 hover:font-bold ">
|
||||
<a :href="doc.url" target="_new">
|
||||
<a :href="(doc.url as string)" target="_new">
|
||||
<ph:link-light class="flex" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { Keys } from "inferred-types";
|
||||
import { useSearch } from "~/modules/search";
|
||||
import type { IMeilisearchInterface} from "tauri-search";
|
||||
const s = useSearch();
|
||||
|
||||
/**
|
||||
* this allows all expected indexes to be shown and in a sensible order
|
||||
*/
|
||||
const knownIndexes = ["api", "prose", "repo", "consolidated"];
|
||||
const knownIndexes = ["api", "prose", "repo", "consolidated"] as const;
|
||||
/** the name of the indexes current known by MeiliSearch */
|
||||
const currentIndexes = computed(() => s.$state.indexes.map(i => i.name));
|
||||
|
||||
@@ -14,7 +15,7 @@ const currentIndexes = computed(() => s.$state.indexes.map(i => i.name));
|
||||
* we do want to see ANY index that exists though too
|
||||
*/
|
||||
const unknownIndexes = computed(() => {
|
||||
return s.$state.indexes.filter(i => !knownIndexes.includes(i.name));
|
||||
return s.$state.indexes.filter(i => !knownIndexes.includes(i.name as Keys<typeof knownIndexes>));
|
||||
});
|
||||
|
||||
const indexStateOptions = ["consolidated", "individual", "bespoke"] as const;
|
||||
@@ -80,7 +81,7 @@ const optionStyle = (opt: IndexState) => computed(() => {
|
||||
</div>
|
||||
|
||||
<template v-for="idx in knownIndexes" :key="idx">
|
||||
<current-index v-if="currentIndexes.includes(idx)" :idx="s.$state.indexes.find(i => i.name === idx)"/>
|
||||
<current-index v-if="currentIndexes.includes(idx)" :idx="((s.indexes.find(i => i.name === idx))as IMeilisearchInterface)"/>
|
||||
<missing-index v-else :idx="idx" />
|
||||
</template>
|
||||
<template v-for="idx in unknownIndexes" :key="idx">
|
||||
|
||||
@@ -2,9 +2,15 @@
|
||||
/* eslint-disable no-use-before-define */
|
||||
import { acceptHMRUpdate, defineStore } from "pinia";
|
||||
import type { UserModule } from "~/types";
|
||||
import { MeiliSearchHealth, MeiliSearchIndex, MeiliSearchInterface, MeiliSearchResponse, MeiliSearchStats } from "~/types/meilisearch";
|
||||
import { ApiModel, IMeilisearchIndexSettings } from "tauri-search";
|
||||
|
||||
import type {
|
||||
IMeiliSearchHealth,
|
||||
IMeilisearchIndex,
|
||||
IMeilisearchInterface,
|
||||
IMeilisearchSearchResponse,
|
||||
IMeiliSearchStats,
|
||||
IMeilisearchIndexSettings,
|
||||
} from "tauri-search";
|
||||
import { ApiModel } from "tauri-search";
|
||||
|
||||
//#region STORE
|
||||
export interface SearchState {
|
||||
@@ -12,13 +18,13 @@ export interface SearchState {
|
||||
health: boolean | "initializing";
|
||||
|
||||
/** the indexes which are defined on MeiliSearch */
|
||||
indexes: MeiliSearchInterface[];
|
||||
indexes: IMeilisearchInterface[];
|
||||
|
||||
/** indexes to use when searching */
|
||||
searchUsing: string[];
|
||||
|
||||
/** database stats for MeiliSearch */
|
||||
stats?: MeiliSearchStats;
|
||||
stats?: IMeiliSearchStats;
|
||||
|
||||
/** index settings */
|
||||
indexSettings: Record<string, IMeilisearchIndexSettings<any>>;
|
||||
@@ -27,28 +33,29 @@ export interface SearchState {
|
||||
|
||||
searchQuery: string;
|
||||
|
||||
searchResults: {id: string; _idx: string; [key: string]: unknown}[];
|
||||
searchResults: { id: string; _idx: string; [key: string]: unknown }[];
|
||||
}
|
||||
|
||||
export const useSearch = defineStore("search", ({
|
||||
state: () => ({
|
||||
health: "initializing",
|
||||
indexes: [],
|
||||
indexSettings: {},
|
||||
searchUsing: ["consolidated"],
|
||||
stats: undefined,
|
||||
searchQuery: "",
|
||||
searchStatus: "not-ready",
|
||||
searchResults: [],
|
||||
}) as SearchState,
|
||||
export const useSearch = defineStore("search", {
|
||||
state: () =>
|
||||
({
|
||||
health: "initializing",
|
||||
indexes: [],
|
||||
indexSettings: {},
|
||||
searchUsing: ["consolidated"],
|
||||
stats: undefined,
|
||||
searchQuery: "",
|
||||
searchStatus: "not-ready",
|
||||
searchResults: [],
|
||||
} as SearchState),
|
||||
actions: {
|
||||
async search(text: string, force: boolean = false) {
|
||||
if(text.trim()==="") {
|
||||
if (text.trim() === "") {
|
||||
this.$state.searchResults = [];
|
||||
this.$state.searchQuery = text.trim();
|
||||
return;
|
||||
}
|
||||
if(text.trim() === this.searchQuery.trim() && !force) {
|
||||
if (text.trim() === this.searchQuery.trim() && !force) {
|
||||
// no change
|
||||
return;
|
||||
} else {
|
||||
@@ -59,30 +66,28 @@ export const useSearch = defineStore("search", ({
|
||||
console.info(`search on ${indexes.length} indexes`, indexes);
|
||||
console.time("search");
|
||||
this.$state.searchStatus = "searching";
|
||||
|
||||
|
||||
const waitFor: Promise<any>[] = [];
|
||||
for (const idx of indexes) {
|
||||
const addIndex =
|
||||
(result: MeiliSearchResponse): MeiliSearchResponse => ({
|
||||
...result,
|
||||
hits: result.hits.map(i =>
|
||||
({...i, _idx: idx})
|
||||
)
|
||||
});
|
||||
const addIndex = (
|
||||
result: IMeilisearchSearchResponse
|
||||
): IMeilisearchSearchResponse => ({
|
||||
...result,
|
||||
hits: result.hits.map((i) => ({ ...i, _idx: idx })),
|
||||
});
|
||||
|
||||
waitFor.push(
|
||||
get<
|
||||
MeiliSearchResponse,
|
||||
MeiliSearchResponse
|
||||
>
|
||||
(api().search(idx,text), addIndex)
|
||||
get<IMeilisearchSearchResponse, IMeilisearchSearchResponse>(
|
||||
api().search(idx, text),
|
||||
addIndex
|
||||
)
|
||||
);
|
||||
}
|
||||
const results = await Promise.all(waitFor);
|
||||
this.$state.searchStatus = "ready";
|
||||
console.timeEnd("search");
|
||||
|
||||
const hits = results.flatMap(i => i.hits);
|
||||
const hits = results.flatMap((i) => i.hits);
|
||||
console.info(`found ${hits.length} documents`);
|
||||
console.groupEnd();
|
||||
this.$state.searchResults = hits;
|
||||
@@ -91,15 +96,17 @@ export const useSearch = defineStore("search", ({
|
||||
},
|
||||
/** updates settings for all active indexes */
|
||||
async updateIndexSettings() {
|
||||
for (const idx of this.indexes.map(i => i.name)) {
|
||||
const result = await ApiModel.query.getIndexSettings(idx) as IMeilisearchIndexSettings<any>;
|
||||
|
||||
this.$state.indexSettings = {...this.$state.indexSettings, [idx]:result} ;
|
||||
for (const idx of this.indexes.map((i) => i.name)) {
|
||||
const result = (await ApiModel.query.getIndexSettings(
|
||||
idx
|
||||
)) as IMeilisearchIndexSettings<any>;
|
||||
|
||||
this.$state.indexSettings = { ...this.$state.indexSettings, [idx]: result };
|
||||
}
|
||||
},
|
||||
toggleUseOfIndex(idx: string) {
|
||||
if(this.$state.searchUsing.includes(idx)) {
|
||||
this.$state.searchUsing = this.$state.searchUsing.filter(i => i !== idx);
|
||||
if (this.$state.searchUsing.includes(idx)) {
|
||||
this.$state.searchUsing = this.$state.searchUsing.filter((i) => i !== idx);
|
||||
this.search(this.searchQuery, true);
|
||||
this.updateIndexSettings();
|
||||
} else {
|
||||
@@ -113,36 +120,37 @@ export const useSearch = defineStore("search", ({
|
||||
this.search(this.searchQuery, true);
|
||||
this.updateIndexSettings();
|
||||
},
|
||||
statsUpdate(s: MeiliSearchStats) {
|
||||
statsUpdate(s: IMeiliSearchStats) {
|
||||
this.$state.stats = s;
|
||||
},
|
||||
healthUpdate(h: boolean) {
|
||||
this.$state.health = h;
|
||||
},
|
||||
indexUpdate(idx: MeiliSearchInterface[]) {
|
||||
const current = this.$state.indexes.map(i => i.name);
|
||||
const newList = idx.map(i => i.name);
|
||||
if(current.length === newList.length && newList.every(i => current.includes(i))) {
|
||||
indexUpdate(idx: IMeilisearchInterface[]) {
|
||||
const current = this.$state.indexes.map((i) => i.name);
|
||||
const newList = idx.map((i) => i.name);
|
||||
if (
|
||||
current.length === newList.length &&
|
||||
newList.every((i) => current.includes(i))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.$state.indexes = idx;
|
||||
}
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
indexIsUsed: (state) => (idx: string) => state.searchUsing.includes(idx),
|
||||
dbSize: (state) => state.stats?.databaseSize || 0,
|
||||
indexInfo: (state) => (idx: string): MeiliSearchIndex | undefined => state.stats?.indexes[idx]
|
||||
}
|
||||
}));
|
||||
|
||||
dbSize: (state) => state.stats?.databaseSize || 0,
|
||||
indexInfo:
|
||||
(state) =>
|
||||
(idx: string): IMeilisearchIndex | undefined =>
|
||||
state.stats?.indexes[idx],
|
||||
},
|
||||
});
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(
|
||||
acceptHMRUpdate(
|
||||
useSearch,
|
||||
import.meta.hot)
|
||||
);
|
||||
import.meta.hot.accept(acceptHMRUpdate(useSearch, import.meta.hot));
|
||||
}
|
||||
|
||||
//#endregion STORE
|
||||
@@ -157,43 +165,44 @@ export const install: UserModule = ({ isClient }) => {
|
||||
if (isClient) {
|
||||
const s = useSearch();
|
||||
|
||||
const { pause, resume, isActive } = useIntervalFn(async() => {
|
||||
s.indexUpdate(await indexes());
|
||||
s.statsUpdate(await stats());
|
||||
}, 2000, {immediate: true});
|
||||
|
||||
const { pause, resume, isActive } = useIntervalFn(
|
||||
async () => {
|
||||
s.indexUpdate(await indexes());
|
||||
s.statsUpdate(await stats());
|
||||
},
|
||||
2000,
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// check health of MeiliSearch
|
||||
useIntervalFn(async() => {
|
||||
const h = await health();
|
||||
s.healthUpdate(h);
|
||||
s.$state.searchStatus = !h ? "not-ready": "ready";
|
||||
useIntervalFn(
|
||||
async () => {
|
||||
const h = await health();
|
||||
s.healthUpdate(h);
|
||||
s.$state.searchStatus = !h ? "not-ready" : "ready";
|
||||
|
||||
if(h === true) {
|
||||
s.updateIndexSettings();
|
||||
if(!isActive.value) {
|
||||
resume();
|
||||
if (h === true) {
|
||||
s.updateIndexSettings();
|
||||
if (!isActive.value) {
|
||||
resume();
|
||||
}
|
||||
}
|
||||
};
|
||||
if(h === false && isActive.value) pause();
|
||||
}, 1000, {immediate: true});
|
||||
|
||||
if (h === false && isActive.value) pause();
|
||||
},
|
||||
1000,
|
||||
{ immediate: true }
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
async function get<T extends {}, U extends {} = never>(
|
||||
url: string,
|
||||
cb?: ((r: T) => U)
|
||||
) {
|
||||
async function get<T extends {}, U extends {} = never>(url: string, cb?: (r: T) => U) {
|
||||
const res = await fetch(url);
|
||||
if(res.ok) {
|
||||
if (res.ok) {
|
||||
const result = res.json();
|
||||
|
||||
return (
|
||||
cb
|
||||
? result.then(r => cb(r)) as Promise<U>
|
||||
: result as Promise<T>
|
||||
) as never extends U ? Promise<T> : Promise<U>;
|
||||
|
||||
return (
|
||||
cb ? (result.then((r) => cb(r)) as Promise<U>) : (result as Promise<T>)
|
||||
) as never extends U ? Promise<T> : Promise<U>;
|
||||
} else {
|
||||
console.groupCollapsed(`Error with API`);
|
||||
console.info(`Request: GET ${url}`);
|
||||
@@ -207,15 +216,13 @@ function api(base: string = "http://localhost:7700") {
|
||||
search: (idx: string, text: string) => `${base}/indexes/${idx}/search?q=${text}`,
|
||||
stats: `${base}/stats`,
|
||||
health: `${base}/health`,
|
||||
indexes: `${base}/indexes`
|
||||
indexes: `${base}/indexes`,
|
||||
};
|
||||
}
|
||||
|
||||
async function health(): Promise<boolean> {
|
||||
try {
|
||||
const response = await (
|
||||
await fetch(api().health)
|
||||
).json() as MeiliSearchHealth;
|
||||
const response = (await (await fetch(api().health)).json()) as IMeiliSearchHealth;
|
||||
return response.status === "available";
|
||||
} catch {
|
||||
return false;
|
||||
@@ -223,15 +230,11 @@ async function health(): Promise<boolean> {
|
||||
}
|
||||
|
||||
async function indexes() {
|
||||
return await (
|
||||
await fetch(api().indexes)
|
||||
).json() as MeiliSearchInterface[];
|
||||
return (await (await fetch(api().indexes)).json()) as IMeilisearchInterface[];
|
||||
}
|
||||
|
||||
async function stats() {
|
||||
return await (
|
||||
await fetch(api().stats)
|
||||
).json() as MeiliSearchStats;
|
||||
return (await (await fetch(api().stats)).json()) as IMeiliSearchStats;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#endregion
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
export interface MeiliSearchHealth {
|
||||
/** the current health status; is "available" when healthy */
|
||||
status: string;
|
||||
}
|
||||
|
||||
export type datetime = string;
|
||||
|
||||
export interface MeiliSearchInterface {
|
||||
uid: string;
|
||||
name: string;
|
||||
/** datetime string (aka., 2022-01-23T22:47:42.745395044Z) */
|
||||
createdAt: datetime;
|
||||
/** datetime string (aka., 2022-01-23T22:47:42.745395044Z) */
|
||||
updatedAt: datetime;
|
||||
/**
|
||||
* the property serving as the primary key for the index,
|
||||
* use `id` unless there's a good reason not to
|
||||
*/
|
||||
primaryKey: string;
|
||||
}
|
||||
|
||||
export interface MeiliSearchIndex {
|
||||
numberOfDocuments: number;
|
||||
isIndexing: false;
|
||||
fieldDistribution: Record<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Global MeiliSearch Stats
|
||||
*/
|
||||
export interface MeiliSearchStats {
|
||||
databaseSize: number;
|
||||
lastUpdate: datetime;
|
||||
indexes: Record<string, MeiliSearchIndex>;
|
||||
}
|
||||
|
||||
export type GenericDoc = {id: string; _idx?: string; [key:string]: unknown};
|
||||
|
||||
|
||||
export interface MeiliSearchResponse<T extends {} = GenericDoc> {
|
||||
hits: T[];
|
||||
limit: number;
|
||||
nbHits: number;
|
||||
offset: number;
|
||||
processingTimeMs: number;
|
||||
query: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The search results but _with_ the index used inserted on
|
||||
* each "hit"
|
||||
*/
|
||||
export type WithIndex<T extends MeiliSearchResponse> =
|
||||
Omit<T, "hits"> & { hits:
|
||||
{
|
||||
[K in keyof T["hits"]]: T["hits"][K] & Record<K, {_idx: string}>
|
||||
}[number];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user