Skip to content

Commit

Permalink
feat: add RankingRules & StopWords components.
Browse files Browse the repository at this point in the history
add moveable property into ArrayInput component.
fix config edit type switch tabs height.
  • Loading branch information
riccox committed Mar 11, 2023
1 parent 99e21bc commit ad27fd7
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 5 deletions.
54 changes: 51 additions & 3 deletions src/components/Settings/config/detail/arrayInput.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import clsx from 'clsx';
import { useForm } from 'react-hook-form';
import { IconPlus, IconCircleMinus } from '@tabler/icons-react';
import { IconPlus, IconCircleMinus, IconArrowUp, IconArrowDown } from '@tabler/icons-react';
import { PropsWithoutRef, useCallback, useMemo, useState } from 'react';
import { openConfirmModal } from '@mantine/modals';
import _ from 'lodash';
import { arrayMove } from '@/src/utils/array';

export function ArrayInput<V extends string>({
defaultValue,
onMutation,
className,
moveable = false,
}: PropsWithoutRef<{
className?: string;
defaultValue: V[];
onMutation: (value: V[]) => void;
moveable?: boolean;
}>) {
const [array, setArray] = useState<V[]>(defaultValue);
const [isAdding, setIsAdding] = useState<boolean>(false);
Expand All @@ -34,6 +37,30 @@ export function ArrayInput<V extends string>({
onCompleteInput();
});

const onClickItemUp = useCallback(
(index: number) => {
console.debug('🚀 ~ file: ArrayInput onClickItemUp', index);
if (!moveable || index <= 0) return;

const updated = arrayMove(array, index, index - 1);
setArray(updated);
onMutation(updated);
},
[array, moveable, onMutation]
);

const onClickItemDown = useCallback(
(index: number) => {
console.debug('🚀 ~ file: ArrayInput onClickItemDown', index);
if (!moveable || index >= array.length - 1) return;

const updated = arrayMove(array, index, index + 1);
setArray(updated);
onMutation(updated);
},
[array, moveable, onMutation]
);

const onClickItemDel = useCallback(
(index: number) => {
console.debug('🚀 ~ file: ArrayInput onClickItemDel', index);
Expand Down Expand Up @@ -63,7 +90,16 @@ export function ArrayInput<V extends string>({
className="w-full flex justify-center items-center p-2 bg-primary-200 text-primary-1000 rounded-xl"
>
<p className="flex-1 text-center">{item}</p>
<button className={'ml-auto'} onClick={() => onClickItemDel(i)}>
<button className={clsx((!moveable || i <= 0) && 'hidden', 'ml-auto')} onClick={() => onClickItemUp(i)}>
<IconArrowUp />
</button>
<button
className={clsx((!moveable || i >= array.length - 1) && 'hidden')}
onClick={() => onClickItemDown(i)}
>
<IconArrowDown />
</button>
<button onClick={() => onClickItemDel(i)}>
<IconCircleMinus />
</button>
</div>
Expand Down Expand Up @@ -94,6 +130,18 @@ export function ArrayInput<V extends string>({
</button>
</div>
),
[addForm, array, className, defaultValue, isAdding, onClickItemDel, onCompleteInput, onSubmit]
[
addForm,
array,
className,
defaultValue,
isAdding,
moveable,
onClickItemDel,
onClickItemDown,
onClickItemUp,
onCompleteInput,
onSubmit,
]
);
}
65 changes: 65 additions & 0 deletions src/components/Settings/config/detail/rankingRules.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { useMutation, useQuery } from '@tanstack/react-query';
import clsx from 'clsx';
import { RankingRules as TRankingRules } from 'meilisearch';
import { FC, useEffect, useMemo } from 'react';
import { IndexSettingConfigComponentProps } from '../..';
import { ArrayInput } from './arrayInput';
import _ from 'lodash';

export const RankingRules: FC<IndexSettingConfigComponentProps> = ({ client, className, host, toggleLoading }) => {
const query = useQuery({
queryKey: ['getRankingRules', host, client.uid],
refetchInterval: 4500,
async queryFn(ctx) {
return await client.getRankingRules();
},
});

const mutation = useMutation({
mutationKey: ['updateRankingRules', host, client.uid],
async mutationFn(variables: TRankingRules) {
console.debug('🚀 ~ file: rankingRules.tsx:19 ~ mutationFn ~ variables:', variables);
if (_.isEmpty(variables)) {
// empty to reset
return await client.resetRankingRules();
}
return await client.updateRankingRules(variables);
},
});

useEffect(() => {
const isLoading = query.isLoading || query.isFetching || mutation.isLoading;
toggleLoading(isLoading);
}, [mutation.isLoading, query.isFetching, query.isLoading, toggleLoading]);

return useMemo(
() => (
<div className={clsx(className)}>
<h2 className="font-semibold">Ranking Rules</h2>
<span className="text-sm flex gap-2">
<p>
Ranking rules are built-in rules that rank search results according to certain criteria. They are applied in
the same order in which they appear in the rankingRules array.
</p>
<a
className="link info text-info-800"
href="https://docs.meilisearch.com/learn/core_concepts/relevancy.html"
target={'_blank'}
rel="noreferrer"
>
Learn more
</a>
</span>
<ArrayInput
className="py-2"
defaultValue={query.data || []}
onMutation={(value) => {
mutation.mutate(value);
}}
moveable
/>
</div>
),
[className, mutation, query.data]
);
};
73 changes: 73 additions & 0 deletions src/components/Settings/config/detail/stopWords.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { useMutation, useQuery } from '@tanstack/react-query';
import clsx from 'clsx';
import { StopWords as TStopWords } from 'meilisearch';
import { FC, useEffect, useMemo } from 'react';
import { IndexSettingConfigComponentProps } from '../..';
import { ArrayInput } from './arrayInput';
import _ from 'lodash';
import { IconAlertTriangleFilled, IconInfoCircleFilled } from '@tabler/icons-react';

export const StopWords: FC<IndexSettingConfigComponentProps> = ({ client, className, host, toggleLoading }) => {
const query = useQuery({
queryKey: ['getStopWords', host, client.uid],
refetchInterval: 4500,
async queryFn(ctx) {
return await client.getStopWords();
},
});

const mutation = useMutation({
mutationKey: ['updateStopWords', host, client.uid],
async mutationFn(variables: TStopWords) {
console.debug('🚀 ~ file: stopWords.tsx:19 ~ mutationFn ~ variables:', variables);
if (_.isEmpty(variables)) {
// empty to reset
return await client.resetStopWords();
}
return await client.updateStopWords(variables);
},
});

useEffect(() => {
const isLoading = query.isLoading || query.isFetching || mutation.isLoading;
toggleLoading(isLoading);
}, [mutation.isLoading, query.isFetching, query.isLoading, toggleLoading]);

return useMemo(
() => (
<div className={clsx(className)}>
<h2 className="font-semibold">Displayed Attributes</h2>
<span className="text-sm flex gap-2">
<p>Words added to the stopWords list are ignored in future search queries.</p>
</span>
<span className="prompt justify-start warn sm">
<span className="icon">
<IconAlertTriangleFilled />
</span>
<p className="content">
Updating stop words will re-index all documents in the index, which can take some time. We recommend
updating your index settings first and then adding documents as this reduces RAM consumption.
</p>
</span>
<span className="prompt info sm">
<span className="icon">
<IconInfoCircleFilled />
</span>
<p className="content">
Stop words are strongly related to the language used in your dataset. For example, most datasets containing
English documents will have countless occurrences of 'the' and 'of'. Italian datasets, instead, will benefit
from ignoring words like 'a', 'la', or 'il'.
</p>
</span>
<ArrayInput
className="py-2"
defaultValue={query.data || []}
onMutation={(value) => {
mutation.mutate(value);
}}
/>
</div>
),
[className, mutation, query.data]
);
};
14 changes: 12 additions & 2 deletions src/components/Settings/config/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { DistinctAttribute } from './detail/distinctAttribute';
import { SortableAttributes } from './detail/sortableAttributes';
import { SearchableAttributes } from './detail/searchableAttributes';
import { DisplayedAttributes } from './detail/displayedAttributes';
import { RankingRules } from './detail/rankingRules';
import { StopWords } from './detail/stopWords';

const tabs = [
'Filterable Attributes',
Expand Down Expand Up @@ -37,13 +39,13 @@ export const IndexConfiguration: FC<IndexSettingComponentProps> = ({ host, clien
</div>
<div className="ml-auto tabs boxed bw pill">
<div
className={clsx('tab px-3 !py-1 min-h-0 h-fit', inputType === 'visualization' && 'active')}
className={clsx('tab px-3 !py-1 !min-h-0 !h-fit', inputType === 'visualization' && 'active')}
onClick={() => setInputType('visualization')}
>
Visualization
</div>
<div
className={clsx('tab px-3 !py-1 min-h-0 h-fit', inputType === 'json' && 'active')}
className={clsx('tab px-3 !py-1 !min-h-0 !h-fit', inputType === 'json' && 'active')}
onClick={() => setInputType('json')}
>
JSON
Expand Down Expand Up @@ -83,6 +85,14 @@ export const IndexConfiguration: FC<IndexSettingComponentProps> = ({ host, clien
className={clsx(selectTab !== 4 && 'hidden', 'flex-1 flex flex-col gap-2 p-2')}
{...{ client, host, toggleLoading }}
/>
<RankingRules
className={clsx(selectTab !== 5 && 'hidden', 'flex-1 flex flex-col gap-2 p-2')}
{...{ client, host, toggleLoading }}
/>
<StopWords
className={clsx(selectTab !== 6 && 'hidden', 'flex-1 flex flex-col gap-2 p-2')}
{...{ client, host, toggleLoading }}
/>
</div>
</div>
<Editor className={clsx(inputType !== 'json' && 'hidden')} {...{ host, client, toggleLoading }} />
Expand Down
17 changes: 17 additions & 0 deletions src/utils/array.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const arrayMove = <T = unknown>(arr: T[], old_index: number, new_index: number): T[] => {
while (old_index < 0) {
old_index += arr.length;
}
while (new_index < 0) {
new_index += arr.length;
}
if (new_index >= arr.length) {
var k = new_index - arr.length + 1;
while (k--) {
// @ts-ignore
arr.push(undefined);
}
}
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
return arr;
};

0 comments on commit ad27fd7

Please sign in to comment.