mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-02-11 20:14:24 +01:00
* 🕵️ feat: Enhance Index Sync and MeiliSearch filtering for User Field
- Implemented `ensureFilterableAttributes` function to configure MeiliSearch indexes for messages and conversations to filter by user.
- Updated sync logic to trigger a full re-sync if the user field is missing or index settings are modified.
- Adjusted search queries in Conversation and Message models to include user filtering.
- Ensured 'user' field is marked as filterable in MongoDB schema for both messages and conversations.
This update improves data integrity and search capabilities by ensuring user-related data is properly indexed and retrievable.
* fix: message processing in Search component to use linear list and not tree
* feat: Implement user filtering in MeiliSearch for shared links
* refactor: Optimize message search retrieval by batching database calls
* chore: Update MeiliSearch parameters type to use SearchParams for improved type safety
104 lines
3.2 KiB
TypeScript
104 lines
3.2 KiB
TypeScript
import { useEffect, useMemo } from 'react';
|
|
import { useRecoilValue } from 'recoil';
|
|
import { Spinner, useToastContext } from '@librechat/client';
|
|
import MinimalMessagesWrapper from '~/components/Chat/Messages/MinimalMessages';
|
|
import { useNavScrolling, useLocalize, useAuthContext } from '~/hooks';
|
|
import SearchMessage from '~/components/Chat/Messages/SearchMessage';
|
|
import { useMessagesInfiniteQuery } from '~/data-provider';
|
|
import { useFileMapContext } from '~/Providers';
|
|
import store from '~/store';
|
|
|
|
export default function Search() {
|
|
const localize = useLocalize();
|
|
const fileMap = useFileMapContext();
|
|
const { showToast } = useToastContext();
|
|
const { isAuthenticated } = useAuthContext();
|
|
const search = useRecoilValue(store.search);
|
|
const searchQuery = search.debouncedQuery;
|
|
|
|
const {
|
|
data: searchMessages,
|
|
isLoading,
|
|
isError,
|
|
fetchNextPage,
|
|
isFetchingNextPage,
|
|
hasNextPage: _hasNextPage,
|
|
} = useMessagesInfiniteQuery(
|
|
{
|
|
search: searchQuery || undefined,
|
|
},
|
|
{
|
|
enabled: isAuthenticated && !!searchQuery,
|
|
staleTime: 30000,
|
|
cacheTime: 300000,
|
|
},
|
|
);
|
|
|
|
const { containerRef } = useNavScrolling({
|
|
nextCursor: searchMessages?.pages[searchMessages.pages.length - 1]?.nextCursor,
|
|
setShowLoading: () => ({}),
|
|
fetchNextPage: fetchNextPage,
|
|
isFetchingNext: isFetchingNextPage,
|
|
});
|
|
|
|
const messages = useMemo(() => {
|
|
const msgs =
|
|
searchMessages?.pages.flatMap((page) =>
|
|
page.messages.map((message) => {
|
|
if (!message.files || !fileMap) {
|
|
return message;
|
|
}
|
|
return {
|
|
...message,
|
|
files: message.files.map((file) => fileMap[file.file_id ?? ''] ?? file),
|
|
};
|
|
}),
|
|
) || [];
|
|
|
|
return msgs.length === 0 ? null : msgs;
|
|
}, [fileMap, searchMessages?.pages]);
|
|
|
|
useEffect(() => {
|
|
if (isError && searchQuery) {
|
|
showToast({ message: 'An error occurred during search', status: 'error' });
|
|
}
|
|
}, [isError, searchQuery, showToast]);
|
|
|
|
const isSearchLoading = search.isTyping || isLoading || isFetchingNextPage;
|
|
|
|
if (isSearchLoading) {
|
|
return (
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<Spinner className="text-text-primary" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!searchQuery) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<MinimalMessagesWrapper ref={containerRef} className="relative flex h-full pt-4">
|
|
{(messages && messages.length === 0) || messages == null ? (
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<div className="rounded-lg bg-white p-6 text-lg text-gray-500 dark:border-gray-800/50 dark:bg-gray-800 dark:text-gray-300">
|
|
{localize('com_ui_nothing_found')}
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<>
|
|
{messages.map((msg) => (
|
|
<SearchMessage key={msg.messageId} message={msg} />
|
|
))}
|
|
{isFetchingNextPage && (
|
|
<div className="flex justify-center py-4">
|
|
<Spinner className="text-text-primary" />
|
|
</div>
|
|
)}
|
|
</>
|
|
)}
|
|
<div className="absolute bottom-0 left-0 right-0 h-[5%] bg-gradient-to-t from-gray-50 to-transparent dark:from-gray-800" />
|
|
</MinimalMessagesWrapper>
|
|
);
|
|
}
|