Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Semantic search backend integration #653

Merged
merged 1 commit into from
Dec 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions prediction-polls/frontend/src/Components/SearchBar/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import styles from "./SearchBar.module.css";
function SearchBar({ onSearch }) {
const [searchText, setSearchText] = useState('');

const handleSearch = value => {
setSearchText(value);
onSearch(value);
const handleInputChange = (e) => {
setSearchText(e.target.value);
};

const handleSearchTrigger = () => {
onSearch(searchText);
};

return (
Expand All @@ -17,8 +20,13 @@ function SearchBar({ onSearch }) {
style={{ padding: '10px' }}
placeholder="Search with question, tag, and creator name"
value={searchText}
onChange={e => handleSearch(e.target.value)}
prefix={<SearchOutlined />}
onChange={handleInputChange}
onPressEnter={handleSearchTrigger}
suffix={
<span onClick={handleSearchTrigger} style={{ cursor: 'pointer' }}>
<SearchOutlined />
</span>
}
allowClear
/>
</div>
Expand Down
87 changes: 65 additions & 22 deletions prediction-polls/frontend/src/Pages/Feed/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import pointData from "../../MockData/PointList.json";
import SearchBar from "../../Components/SearchBar";
import getProfileMe from "../../api/requests/profileMe.jsx";
import { useNavigate } from "react-router-dom";
import { Spin } from 'antd';

function Feed() {
const [pollData, setPollData] = useState({ pollList: [] });
const [filteredPolls, setFilteredPolls] = useState(pollData.pollList);
const [userData, setUserData] = useState({});
const [spinning, setSpinning] = useState(false);

React.useEffect(() => {
const data = getProfileMe();
Expand All @@ -24,6 +26,7 @@ function Feed() {

useEffect(() => {
const fetchData = async () => {
setSpinning(true);
try {
const response = await fetch(url + "/polls", {
method: "GET",
Expand Down Expand Up @@ -58,6 +61,7 @@ function Feed() {
} catch (error) {
console.error("Error fetching polls:", error);
}
setSpinning(false);
};

fetchData();
Expand All @@ -66,32 +70,70 @@ function Feed() {


const handleSearch = (searchText) => {
if (!searchText.trim()) {
setFilteredPolls(pollData.pollList);
return;
}
setSpinning(true);
if (!searchText.trim()) {
setFilteredPolls(pollData.pollList);
setSpinning(false);
return;
}

const lowerCaseSearchText = searchText.toLowerCase();

// Filter local polls
const localFiltered = pollData.pollList.filter((poll) => {
const questionMatch = poll.question
.toLowerCase()
.includes(lowerCaseSearchText);
const tagsMatch =
poll.tags &&
poll.tags.some((tag) =>
tag.toLowerCase().includes(lowerCaseSearchText)
);
const creatorNameMatch =
poll.creatorName &&
poll.creatorName.toLowerCase().includes(lowerCaseSearchText);

return questionMatch || tagsMatch || creatorNameMatch;
});

// Make a GET request to the semantic search endpoint
const fetchSemanticSearch = async () => {
try {
const response = await fetch(`${url}/semantic/pollsearch?keyword=${encodeURIComponent(lowerCaseSearchText)}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${localStorage.getItem("accessToken")}`,
}
});

const lowerCaseSearchText = searchText.toLowerCase();

const filtered = pollData.pollList.filter((poll) => {
const questionMatch = poll.question
.toLowerCase()
.includes(lowerCaseSearchText);
const tagsMatch =
poll.tags &&
poll.tags.some((tag) =>
tag.toLowerCase().includes(lowerCaseSearchText)
);
const creatorNameMatch =
poll.creatorName &&
poll.creatorName.toLowerCase().includes(lowerCaseSearchText);

return questionMatch || tagsMatch || creatorNameMatch;
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

const semanticData = await response.json();

setFilteredPolls(filtered);
// Combine local filtered polls with semantic search results
const combinedPolls = [...localFiltered];

// Add polls from semanticData if they don't already exist in combinedPolls
semanticData.forEach((semanticPoll) => {
const exists = combinedPolls.some(poll => poll.id === semanticPoll.id);
if (!exists) {
combinedPolls.push(semanticPoll);
}
});
setFilteredPolls(combinedPolls);
} catch (error) {
// Fallback to local filtered results if semantic search fails
setFilteredPolls(localFiltered);
}
setSpinning(false);
};

fetchSemanticSearch();
};

return (
<div className={styles.page}>
<Menu currentPage="Feed" />
Expand All @@ -108,6 +150,7 @@ function Feed() {
<div className={styles.pointsButton}>
<PointsButton point={userData?.points ?? 0} />
</div>
<Spin spinning={spinning} fullscreen />
</div>
);
}
Expand Down