diff --git a/api/models/Conversation.js b/api/models/Conversation.js index 4403c1e6ab..c8e9dd9eca 100644 --- a/api/models/Conversation.js +++ b/api/models/Conversation.js @@ -59,7 +59,7 @@ convoSchema.plugin(mongoMeili, { host: process.env.MEILI_HOST, apiKey: process.env.MEILI_KEY, indexName: 'convos', // Will get created automatically if it doesn't exist already - primaryKey: 'conversationId', + primaryKey: 'conversationId' }); const Conversation = @@ -142,15 +142,40 @@ module.exports = { return { conversations: [], pages: 1, pageNumber, pageSize }; } - const promises = convoIds.map(convo => { - return Conversation.findOne({ user, conversationId: convo.conversationId}).exec(); + const cache = {}; + const promises = []; + + convoIds.forEach((convo, i) => { + const page = Math.floor(i / pageSize) + 1; + if (!cache[page]) { + cache[page] = []; + } + + const conversation = Conversation.findOne({ + user, + conversationId: convo.conversationId + }).exec(); + + cache[page].push(conversation); + promises.push(conversation); }); - const results = (await Promise.all(promises)).filter(convo => convo); - const startIndex = (pageNumber - 1) * pageSize; - const convos = results.slice(startIndex, startIndex + pageSize); + const results = (await Promise.all(promises)).filter((convo) => convo); + for (const key in cache) { + const promises = cache[key]; + cache[key] = (await Promise.all(promises)).filter((convo) => convo); + } + // const startIndex = (pageNumber - 1) * pageSize; + // const convos = results.slice(startIndex, startIndex + pageSize); const totalPages = Math.ceil(results.length / pageSize); - console.log(results.length, totalPages, convos.length); - return { conversations: convos, pages: totalPages, pageNumber, pageSize }; + cache.pages = totalPages; + cache.pageSize = pageSize; + return { + cache, + conversations: cache[pageNumber], + pages: totalPages, + pageNumber, + pageSize + }; } catch (error) { console.log(error); return { message: 'Error fetching conversations' }; diff --git a/api/server/routes/search.js b/api/server/routes/search.js index d58c62919f..e563966a57 100644 --- a/api/server/routes/search.js +++ b/api/server/routes/search.js @@ -4,6 +4,7 @@ const { Message } = require('../../models/Message'); const { Conversation, getConvosQueried } = require('../../models/Conversation'); const { reduceMessages, reduceHits } = require('../../lib/utils/reduceHits'); // const { MeiliSearch } = require('meilisearch'); +const cache = new Map(); router.get('/sync', async function (req, res) { await Message.syncWithMeili(); @@ -13,18 +14,26 @@ router.get('/sync', async function (req, res) { router.get('/', async function (req, res) { try { + const user = req?.session?.user?.username; const { q } = req.query; - console.log(req.query, req.params); const pageNumber = req.query.pageNumber || 1; + const key = `${user || ''}${q}`; + + if (cache.has(key)) { + console.log('cache hit', key); + const cached = cache.get(key); + const { pages, pageSize } = cached; + res.status(200).send({ conversations: cached[pageNumber], pages, pageNumber, pageSize }); + return; + } else { + cache.clear(); + } const message = await Message.meiliSearch(q); const title = await Conversation.meiliSearch(q, { attributesToHighlight: ['title'] }); const sortedHits = reduceHits(message.hits, title.hits); - const result = await getConvosQueried( - req?.session?.user?.username, - sortedHits, - pageNumber - ); - console.log('result', result.pageNumber, result.pages, result.pageSize); + const result = await getConvosQueried(user, sortedHits, pageNumber); + cache.set(q, result.cache); + delete result.cache; res.status(200).send(result); } catch (error) { console.log(error); diff --git a/client/src/components/Nav/SearchBar.jsx b/client/src/components/Nav/SearchBar.jsx index fe08b61717..ddbe74ee2c 100644 --- a/client/src/components/Nav/SearchBar.jsx +++ b/client/src/components/Nav/SearchBar.jsx @@ -3,31 +3,15 @@ import { debounce } from 'lodash'; import { useDispatch } from 'react-redux'; import { Search } from 'lucide-react'; import { setQuery } from '~/store/searchSlice'; -import { setConvos, refreshConversation } from '~/store/convoSlice'; -import axios from 'axios'; -// const fetch = async (q, pageNumber, callback) => { -// const { data } = await axios.get(`/api/search?q=${q}&pageNumber=${pageNumber}`); -// console.log(data); -// callback(data); -// }; - -export default function SearchBar({ fetch, onSuccess, clearSearch }) { +export default function SearchBar({ fetch, clearSearch }) { const dispatch = useDispatch(); const [inputValue, setInputValue] = useState(''); - // const onSuccess = (data) => { - // const { conversations, pages, pageNumber } = data; - // dispatch(setConvos({ convos: conversations, searchFetch: true })); - // dispatch(setPage(pageNumber)); - // dispatch(setPages(pages)); - // }; - const debouncedChangeHandler = useCallback( debounce((q) => { dispatch(setQuery(q)); if (q.length > 0) { - // fetch(q, 1, onSuccess); fetch(q, 1); } }, 750), @@ -46,19 +30,12 @@ export default function SearchBar({ fetch, onSuccess, clearSearch }) { 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 === '') { dispatch(setQuery('')); - // dispatch(setPage(1)); - // dispatch(refreshConversation()); clearSearch(); } else { debouncedChangeHandler(q); diff --git a/client/src/components/Nav/index.jsx b/client/src/components/Nav/index.jsx index 247b0b7606..9701d554b0 100644 --- a/client/src/components/Nav/index.jsx +++ b/client/src/components/Nav/index.jsx @@ -6,17 +6,10 @@ import Spinner from '../svg/Spinner'; import Pages from '../Conversations/Pages'; import Conversations from '../Conversations'; import NavLinks from './NavLinks'; -import { swr } from '~/utils/fetchers'; +import { searchFetcher, swr } from '~/utils/fetchers'; import { useDispatch, useSelector } from 'react-redux'; import { setConvos, refreshConversation } from '~/store/convoSlice'; -const fetcher = async (pre, q, pageNumber, callback) => { - pre(); - const { data } = await axios.get(`/api/search?q=${q}&pageNumber=${pageNumber}`); - console.log(data); - callback(data); -}; - export default function Nav({ navVisible, setNavVisible }) { const dispatch = useDispatch(); const [isHovering, setIsHovering] = useState(false); @@ -51,7 +44,7 @@ export default function Nav({ navVisible, setNavVisible }) { setIsFetching(false); }; - const fetch = useCallback(_.partialRight(fetcher.bind(null, () => setIsFetching(true)), onSearchSuccess), [dispatch]); + const fetch = useCallback(_.partialRight(searchFetcher.bind(null, () => setIsFetching(true)), onSearchSuccess), [dispatch]); const clearSearch = () => { setPage(1); @@ -79,7 +72,6 @@ export default function Nav({ navVisible, setNavVisible }) { setPage((prev) => prev + 1); await mutate(); } else { - // await fetch(query, +pageNumber + 1, onSearchSuccess); await fetch(query, +pageNumber + 1); } }; @@ -91,7 +83,6 @@ export default function Nav({ navVisible, setNavVisible }) { setPage((prev) => prev - 1); await mutate(); } else { - // await fetch(query, +pageNumber - 1, onSearchSuccess); await fetch(query, +pageNumber - 1); } }; @@ -111,7 +102,7 @@ export default function Nav({ navVisible, setNavVisible }) { container.scrollTop = Math.min(maxScrollTop, scrollPositionRef.current); } - }, [data]); + }, [data, convos]); useEffect(() => { setNavVisible(false); diff --git a/client/src/utils/fetchers.js b/client/src/utils/fetchers.js index abe8f4a9a6..a82e7cc22d 100644 --- a/client/src/utils/fetchers.js +++ b/client/src/utils/fetchers.js @@ -13,6 +13,13 @@ const postRequest = async (url, { arg }) => { return await axios.post(url, { withCredentials: true, arg }); }; +export const searchFetcher = async (pre, q, pageNumber, callback) => { + pre(); + const { data } = await axios.get(`/api/search?q=${q}&pageNumber=${pageNumber}`); + console.log('search data', data); + callback(data); +}; + export const swr = (path, successCallback, options) => { const _options = { ...options };