mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-18 01:10:14 +01:00
feat: search bar working, still in progress
This commit is contained in:
parent
610cba4a60
commit
0f54ffd8b4
10 changed files with 147 additions and 24 deletions
|
|
@ -85,12 +85,6 @@ const createMeiliMongooseModel = function ({ index, indexName, client, attribute
|
||||||
// Push new document to Meili
|
// Push new document to Meili
|
||||||
async addObjectToMeili() {
|
async addObjectToMeili() {
|
||||||
const object = _.pick(this.toJSON(), attributesToIndex);
|
const object = _.pick(this.toJSON(), attributesToIndex);
|
||||||
// const title = (await this.getTitle()) || 'New Chat'; // Get title value
|
|
||||||
// const objectWithTitle = {
|
|
||||||
// ...this.toJSON(),
|
|
||||||
// title
|
|
||||||
// };
|
|
||||||
// const object = _.pick(objectWithTitle, attributesToIndex); // Pick desired attributes
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// console.log('Adding document to Meili', object);
|
// console.log('Adding document to Meili', object);
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,25 @@ module.exports = {
|
||||||
return { message: 'Error getting conversations' };
|
return { message: 'Error getting conversations' };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getConvosQueried: async (user, convoIds, pageNumber = 1, pageSize = 12) => {
|
||||||
|
try {
|
||||||
|
if (!convoIds || convoIds.length === 0) {
|
||||||
|
return { conversations: [], pages: 1, pageNumber, pageSize };
|
||||||
|
}
|
||||||
|
|
||||||
|
const promises = convoIds.map(convo => {
|
||||||
|
return Conversation.findOne({ user, conversationId: convo.conversationId}).exec();
|
||||||
|
});
|
||||||
|
const results = await Promise.all(promises);
|
||||||
|
const startIndex = (pageNumber - 1) * pageSize;
|
||||||
|
const convos = results.slice(startIndex, startIndex + pageSize);
|
||||||
|
const totalPages = Math.ceil(results.length / pageSize);
|
||||||
|
return { conversations: convos, pages: totalPages, pageNumber, pageSize };
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return { message: 'Error fetching conversations' };
|
||||||
|
}
|
||||||
|
},
|
||||||
getConvo,
|
getConvo,
|
||||||
getConvoTitle: async (user, conversationId) => {
|
getConvoTitle: async (user, conversationId) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const { Message } = require('../../models/Message');
|
const { Message } = require('../../models/Message');
|
||||||
const { Conversation } = require('../../models/Conversation');
|
const { Conversation, getConvosQueried } = require('../../models/Conversation');
|
||||||
const {reduceMessages, reduceHits} = require('../../lib/utils/reduceHits');
|
const {reduceMessages, reduceHits} = require('../../lib/utils/reduceHits');
|
||||||
// const { MeiliSearch } = require('meilisearch');
|
// const { MeiliSearch } = require('meilisearch');
|
||||||
|
|
||||||
|
|
@ -13,13 +13,18 @@ router.get('/sync', async function (req, res) {
|
||||||
|
|
||||||
router.get('/', async function (req, res) {
|
router.get('/', async function (req, res) {
|
||||||
const { q } = req.query;
|
const { q } = req.query;
|
||||||
const message = await Message.meiliSearch(q, { attributesToHighlight: ['text', 'sender'] });
|
console.log(req.query);
|
||||||
|
const pageNumber = req.query.pageNumber || 1;
|
||||||
|
// const message = await Message.meiliSearch(q, { attributesToHighlight: ['text', 'sender'] });
|
||||||
|
const message = await Message.meiliSearch(q);
|
||||||
const title = await Conversation.meiliSearch(q, { attributesToHighlight: ['title'] });
|
const title = await Conversation.meiliSearch(q, { attributesToHighlight: ['title'] });
|
||||||
// console.log('titles', title);
|
// console.log('titles', title);
|
||||||
// console.log(sortedHits);
|
// console.log(sortedHits);
|
||||||
const sortedHits = reduceHits(message.hits, title.hits);
|
const sortedHits = reduceHits(message.hits, title.hits);
|
||||||
|
const result = await getConvosQueried(req?.session?.user?.username, sortedHits, pageNumber);
|
||||||
// const sortedHits = reduceMessages(message.hits);
|
// const sortedHits = reduceMessages(message.hits);
|
||||||
res.status(200).send({sortedHits});
|
// res.status(200).send(sortedHits || result);
|
||||||
|
res.status(200).send(result);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/clear', async function (req, res) {
|
router.get('/clear', async function (req, res) {
|
||||||
|
|
|
||||||
7
client/package-lock.json
generated
7
client/package-lock.json
generated
|
|
@ -20,6 +20,7 @@
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
"crypto-browserify": "^3.12.0",
|
"crypto-browserify": "^3.12.0",
|
||||||
"highlight.js": "^11.7.0",
|
"highlight.js": "^11.7.0",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.113.0",
|
"lucide-react": "^0.113.0",
|
||||||
"markdown-to-jsx": "^7.1.9",
|
"markdown-to-jsx": "^7.1.9",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
|
@ -7469,8 +7470,7 @@
|
||||||
"node_modules/lodash": {
|
"node_modules/lodash": {
|
||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/lodash.debounce": {
|
"node_modules/lodash.debounce": {
|
||||||
"version": "4.0.8",
|
"version": "4.0.8",
|
||||||
|
|
@ -16943,8 +16943,7 @@
|
||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"lodash.debounce": {
|
"lodash.debounce": {
|
||||||
"version": "4.0.8",
|
"version": "4.0.8",
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
"crypto-browserify": "^3.12.0",
|
"crypto-browserify": "^3.12.0",
|
||||||
"highlight.js": "^11.7.0",
|
"highlight.js": "^11.7.0",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.113.0",
|
"lucide-react": "^0.113.0",
|
||||||
"markdown-to-jsx": "^7.1.9",
|
"markdown-to-jsx": "^7.1.9",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import NavLink from './NavLink';
|
import NavLink from './NavLink';
|
||||||
import LogOutIcon from '../svg/LogOutIcon';
|
import LogOutIcon from '../svg/LogOutIcon';
|
||||||
|
import SearchBar from './SearchBar';
|
||||||
import ClearConvos from './ClearConvos';
|
import ClearConvos from './ClearConvos';
|
||||||
import DarkMode from './DarkMode';
|
import DarkMode from './DarkMode';
|
||||||
import Logout from './Logout';
|
import Logout from './Logout';
|
||||||
|
|
@ -8,6 +9,7 @@ import Logout from './Logout';
|
||||||
export default function NavLinks() {
|
export default function NavLinks() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<SearchBar />
|
||||||
<ClearConvos />
|
<ClearConvos />
|
||||||
<DarkMode />
|
<DarkMode />
|
||||||
<Logout />
|
<Logout />
|
||||||
|
|
|
||||||
66
client/src/components/Nav/SearchBar.jsx
Normal file
66
client/src/components/Nav/SearchBar.jsx
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
import React, { useState, useCallback } from 'react';
|
||||||
|
import { debounce } from 'lodash';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { Search } from 'lucide-react';
|
||||||
|
import { setQuery } from '~/store/searchSlice';
|
||||||
|
import { setPage, refreshConversation } from '~/store/convoSlice';
|
||||||
|
|
||||||
|
export default function SearchBar() {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [inputValue, setInputValue] = useState('');
|
||||||
|
const { search } = useSelector((state) => state.search);
|
||||||
|
|
||||||
|
const debouncedChangeHandler = useCallback(
|
||||||
|
debounce((q) => {
|
||||||
|
dispatch(setQuery(q));
|
||||||
|
}, 750),
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleKeyUp = (e) => {
|
||||||
|
const { value } = e.target;
|
||||||
|
if (e.keyCode === 8 && value === '') {
|
||||||
|
// Value after clearing input: ""
|
||||||
|
console.log(`Value after clearing input: "${value}"`);
|
||||||
|
dispatch(setPage(1));
|
||||||
|
dispatch(setQuery(''));
|
||||||
|
dispatch(refreshConversation());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const changeHandler = (e) => {
|
||||||
|
if (!search) {
|
||||||
|
console.log('setting page to 1');
|
||||||
|
dispatch(setPage(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
let q = e.target.value;
|
||||||
|
setInputValue(q);
|
||||||
|
q = q.trim();
|
||||||
|
|
||||||
|
if (q === '' || !q) {
|
||||||
|
dispatch(setPage(1));
|
||||||
|
dispatch(setQuery(''));
|
||||||
|
dispatch(refreshConversation());
|
||||||
|
} else {
|
||||||
|
debouncedChangeHandler(q);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex cursor-pointer items-center gap-3 rounded-md py-3 px-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10">
|
||||||
|
{<Search className="h-4 w-4" />}
|
||||||
|
<input
|
||||||
|
// ref={inputRef}
|
||||||
|
type="text"
|
||||||
|
className="m-0 mr-0 w-full border-none bg-transparent p-0 text-sm leading-tight outline-none"
|
||||||
|
value={inputValue}
|
||||||
|
onChange={changeHandler}
|
||||||
|
placeholder="Search messages"
|
||||||
|
onKeyUp={handleKeyUp}
|
||||||
|
// onBlur={onRename}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -10,7 +10,8 @@ import { increasePage, decreasePage, setPage, setConvos, setPages } from '~/stor
|
||||||
export default function Nav({ navVisible, setNavVisible }) {
|
export default function Nav({ navVisible, setNavVisible }) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [isHovering, setIsHovering] = useState(false);
|
const [isHovering, setIsHovering] = useState(false);
|
||||||
const { conversationId, convos, search, pages, pageNumber, refreshConvoHint } = useSelector(
|
const { search, query } = useSelector((state) => state.search);
|
||||||
|
const { conversationId, convos, pages, pageNumber, refreshConvoHint } = useSelector(
|
||||||
(state) => state.convo
|
(state) => state.convo
|
||||||
);
|
);
|
||||||
const onSuccess = (data) => {
|
const onSuccess = (data) => {
|
||||||
|
|
@ -24,8 +25,12 @@ export default function Nav({ navVisible, setNavVisible }) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const { data, isLoading, mutate } = swr(`/api/${search ? 'search?q=' : `convos?pageNumber=${pageNumber}`}`, onSuccess, {
|
const { data, isLoading, mutate } = swr(`/api/${search ? `search?q=${query}&pageNumber=${pageNumber}` : `convos?pageNumber=${pageNumber}`}`, onSuccess, {
|
||||||
revalidateOnMount: false
|
revalidateOnMount: false,
|
||||||
|
revalidateIfStale: !search,
|
||||||
|
revalidateOnFocus: !search,
|
||||||
|
revalidateOnReconnect: !search,
|
||||||
|
populateCache: !search,
|
||||||
});
|
});
|
||||||
|
|
||||||
const containerRef = useRef(null);
|
const containerRef = useRef(null);
|
||||||
|
|
@ -103,7 +108,7 @@ export default function Nav({ navVisible, setNavVisible }) {
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
>
|
>
|
||||||
<div className={containerClasses}>
|
<div className={containerClasses}>
|
||||||
{isLoading && pageNumber === 1 ? (
|
{isLoading && (pageNumber === 1 || search) ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : (
|
) : (
|
||||||
<Conversations
|
<Conversations
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
import { configureStore } from '@reduxjs/toolkit';
|
import { configureStore } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
import convoReducer from './convoSlice.js';
|
import convoReducer from './convoSlice.js';
|
||||||
import messageReducer from './messageSlice.js'
|
import messageReducer from './messageSlice.js';
|
||||||
import modelReducer from './modelSlice.js'
|
import modelReducer from './modelSlice.js';
|
||||||
import submitReducer from './submitSlice.js'
|
import submitReducer from './submitSlice.js';
|
||||||
import textReducer from './textSlice.js'
|
import textReducer from './textSlice.js';
|
||||||
import userReducer from './userReducer.js'
|
import userReducer from './userReducer.js';
|
||||||
|
import searchReducer from './searchSlice.js';
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
|
|
@ -15,6 +16,7 @@ export const store = configureStore({
|
||||||
text: textReducer,
|
text: textReducer,
|
||||||
submit: submitReducer,
|
submit: submitReducer,
|
||||||
user: userReducer,
|
user: userReducer,
|
||||||
|
search: searchReducer
|
||||||
},
|
},
|
||||||
devTools: true,
|
devTools: true
|
||||||
});
|
});
|
||||||
|
|
|
||||||
30
client/src/store/searchSlice.js
Normal file
30
client/src/store/searchSlice.js
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
search: false,
|
||||||
|
query: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const currentSlice = createSlice({
|
||||||
|
name: 'search',
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
setSearchState: (state, action) => {
|
||||||
|
state.search = action.payload;
|
||||||
|
},
|
||||||
|
setQuery: (state, action) => {
|
||||||
|
const q = action.payload;
|
||||||
|
state.query = q;
|
||||||
|
|
||||||
|
if (!q || q === '') {
|
||||||
|
state.search = false;
|
||||||
|
} else {
|
||||||
|
state.search = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { setSearchState, setQuery } = currentSlice.actions;
|
||||||
|
|
||||||
|
export default currentSlice.reducer;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue