Merge branch 'feat/advanced-search' into 'develop'
Feat/advanced search See merge request !16
Showing
10 changed files
with
110 additions
and
39 deletions
... | @@ -126,7 +126,11 @@ const Header = () => { | ... | @@ -126,7 +126,11 @@ const Header = () => { |
126 | float="left" | 126 | float="left" |
127 | fontsize="20px" | 127 | fontsize="20px" |
128 | height="50px" | 128 | height="50px" |
129 | - options={[{ id: 1, name: '전체' }]} | 129 | + options={[ |
130 | + { id: 0, name: '전체' }, | ||
131 | + { id: 1, name: '작성자' }, | ||
132 | + { id: 2, name: '내용' }, | ||
133 | + ]} | ||
130 | /> | 134 | /> |
131 | </DropDownWrap> | 135 | </DropDownWrap> |
132 | </DropDownContainer> | 136 | </DropDownContainer> | ... | ... |
... | @@ -8,7 +8,7 @@ const Background = styled.div``; | ... | @@ -8,7 +8,7 @@ const Background = styled.div``; |
8 | 8 | ||
9 | const ModalWrapper = styled.div` | 9 | const ModalWrapper = styled.div` |
10 | width: 400px; | 10 | width: 400px; |
11 | - height: 300px; | 11 | + height: 194px; |
12 | `; | 12 | `; |
13 | 13 | ||
14 | const ModalContent = styled.div` | 14 | const ModalContent = styled.div` |
... | @@ -23,13 +23,13 @@ const ModalContent = styled.div` | ... | @@ -23,13 +23,13 @@ const ModalContent = styled.div` |
23 | const SearchWrap = styled.div` | 23 | const SearchWrap = styled.div` |
24 | position: absolute; | 24 | position: absolute; |
25 | top: 80%; | 25 | top: 80%; |
26 | - left: 20%; | 26 | + right: 20%; |
27 | `; | 27 | `; |
28 | 28 | ||
29 | const CloseWrap = styled.div` | 29 | const CloseWrap = styled.div` |
30 | position: absolute; | 30 | position: absolute; |
31 | top: 80%; | 31 | top: 80%; |
32 | - right: 20%; | 32 | + left: 20%; |
33 | `; | 33 | `; |
34 | 34 | ||
35 | const StandardWrap = styled.div` | 35 | const StandardWrap = styled.div` |
... | @@ -51,7 +51,6 @@ const TextWrap = styled.div` | ... | @@ -51,7 +51,6 @@ const TextWrap = styled.div` |
51 | width: 360px; | 51 | width: 360px; |
52 | top: ${props => props.top}; | 52 | top: ${props => props.top}; |
53 | right: ${props => props.right}; | 53 | right: ${props => props.right}; |
54 | - border-bottom: 2px solid #dee2e6; | ||
55 | padding-left: 20px; | 54 | padding-left: 20px; |
56 | padding-bottom: ${props => props.bottom}; | 55 | padding-bottom: ${props => props.bottom}; |
57 | `; | 56 | `; |
... | @@ -59,8 +58,7 @@ const TextWrap = styled.div` | ... | @@ -59,8 +58,7 @@ const TextWrap = styled.div` |
59 | const Modal = ({ showModal, setShowModal }) => { | 58 | const Modal = ({ showModal, setShowModal }) => { |
60 | const [query, setQuery] = useState(''); | 59 | const [query, setQuery] = useState(''); |
61 | const [keywordQuery, setKeywordQuery] = useState(''); | 60 | const [keywordQuery, setKeywordQuery] = useState(''); |
62 | - const [writerQuery, setWriterQuery] = useState(''); | 61 | + const [writer, setWriter] = useState(''); |
63 | - const [dateQuery, setDateQuery] = useState(''); | ||
64 | const history = useHistory(); | 62 | const history = useHistory(); |
65 | const modalRef = useRef(); | 63 | const modalRef = useRef(); |
66 | const closeModal = e => { | 64 | const closeModal = e => { |
... | @@ -76,7 +74,7 @@ const Modal = ({ showModal, setShowModal }) => { | ... | @@ -76,7 +74,7 @@ const Modal = ({ showModal, setShowModal }) => { |
76 | <Card shadow="lg"> | 74 | <Card shadow="lg"> |
77 | <ModalWrapper> | 75 | <ModalWrapper> |
78 | <ModalContent> | 76 | <ModalContent> |
79 | - <TextWrap top="6%" right="10%" bottom="4%" height="40px"> | 77 | + <TextWrap top="8%" right="10%" bottom="4%" height="40px"> |
80 | 기본검색 | 78 | 기본검색 |
81 | </TextWrap> | 79 | </TextWrap> |
82 | <StandardWrap> | 80 | <StandardWrap> |
... | @@ -103,7 +101,7 @@ const Modal = ({ showModal, setShowModal }) => { | ... | @@ -103,7 +101,7 @@ const Modal = ({ showModal, setShowModal }) => { |
103 | }} | 101 | }} |
104 | /> | 102 | /> |
105 | </StandardWrap> | 103 | </StandardWrap> |
106 | - <TextWrap top="24%" right="10%" bottom="31%"> | 104 | + <TextWrap top="34%" right="10%" bottom="31%"> |
107 | 고급검색 | 105 | 고급검색 |
108 | </TextWrap> | 106 | </TextWrap> |
109 | <AdvancedWrap> | 107 | <AdvancedWrap> |
... | @@ -124,28 +122,29 @@ const Modal = ({ showModal, setShowModal }) => { | ... | @@ -124,28 +122,29 @@ const Modal = ({ showModal, setShowModal }) => { |
124 | }} | 122 | }} |
125 | placeholder="작성자" | 123 | placeholder="작성자" |
126 | type="text" | 124 | type="text" |
127 | - value={decodeURIComponent(writerQuery)} | 125 | + value={decodeURIComponent(writer)} |
128 | - onChange={e => setWriterQuery(e.target.value)} | 126 | + onChange={e => setWriter(e.target.value)} |
129 | - /> | ||
130 | - <TextInput | ||
131 | - inputStyle={{ | ||
132 | - marginBottom: 18, | ||
133 | - fontSize: 15, | ||
134 | - }} | ||
135 | - placeholder="생성 날짜" | ||
136 | - type="text" | ||
137 | - value={decodeURIComponent(dateQuery)} | ||
138 | - onChange={e => setDateQuery(e.target.value)} | ||
139 | /> | 127 | /> |
140 | </AdvancedWrap> | 128 | </AdvancedWrap> |
141 | </ModalContent> | 129 | </ModalContent> |
142 | <CloseWrap onClick={() => setShowModal(prev => !prev)}> | 130 | <CloseWrap onClick={() => setShowModal(prev => !prev)}> |
143 | - <Button width="100px" color="blue"> | 131 | + <Button width="100px" color="gray"> |
144 | 닫기 | 132 | 닫기 |
145 | </Button> | 133 | </Button> |
146 | </CloseWrap> | 134 | </CloseWrap> |
147 | - <SearchWrap> | 135 | + <SearchWrap |
148 | - <Button width="100px" color="gray"> | 136 | + onClick={() => { |
137 | + const searchQuery = {}; | ||
138 | + if (query !== '') searchQuery.query = query; | ||
139 | + if (keywordQuery !== '') searchQuery.keyword = keywordQuery; | ||
140 | + if (writer !== '') searchQuery.writer = writer; | ||
141 | + const params = new URLSearchParams(searchQuery); | ||
142 | + history.push( | ||
143 | + `search?${decodeURIComponent(params.toString())}` | ||
144 | + ); | ||
145 | + }} | ||
146 | + > | ||
147 | + <Button width="100px" color="blue"> | ||
149 | 검색 | 148 | 검색 |
150 | </Button> | 149 | </Button> |
151 | </SearchWrap> | 150 | </SearchWrap> | ... | ... |
... | @@ -18,10 +18,16 @@ const Buttons = ({ | ... | @@ -18,10 +18,16 @@ const Buttons = ({ |
18 | width, | 18 | width, |
19 | fontsize, | 19 | fontsize, |
20 | icon = '', | 20 | icon = '', |
21 | + onClick, | ||
21 | }) => { | 22 | }) => { |
22 | return ( | 23 | return ( |
23 | <ButtonBlock float={float} width={width} fontsize={fontsize}> | 24 | <ButtonBlock float={float} width={width} fontsize={fontsize}> |
24 | - <Button size={size} color={color || 'blue'} rightIcon={icon}> | 25 | + <Button |
26 | + onClick={onClick} | ||
27 | + size={size} | ||
28 | + color={color || 'blue'} | ||
29 | + rightIcon={icon} | ||
30 | + > | ||
25 | {children} | 31 | {children} |
26 | </Button> | 32 | </Button> |
27 | </ButtonBlock> | 33 | </ButtonBlock> | ... | ... |
1 | import React, { useState, useEffect } from 'react'; | 1 | import React, { useState, useEffect } from 'react'; |
2 | import { TiArrowSortedDown } from 'react-icons/ti'; | 2 | import { TiArrowSortedDown } from 'react-icons/ti'; |
3 | import { Menu, MenuItem } from '@mantine/core'; | 3 | import { Menu, MenuItem } from '@mantine/core'; |
4 | +import { useDispatch } from 'react-redux'; | ||
4 | import styled from 'styled-components'; | 5 | import styled from 'styled-components'; |
5 | import { dropdownHeaderColorMap } from '../../lib/styles/palette'; | 6 | import { dropdownHeaderColorMap } from '../../lib/styles/palette'; |
7 | +import { setSearchOption } from '../../features/searchOption'; | ||
6 | 8 | ||
7 | const DropDownBlock = styled.div` | 9 | const DropDownBlock = styled.div` |
8 | margin: 0 auto; | 10 | margin: 0 auto; |
... | @@ -41,6 +43,7 @@ const DropDown = ({ | ... | @@ -41,6 +43,7 @@ const DropDown = ({ |
41 | size, | 43 | size, |
42 | }) => { | 44 | }) => { |
43 | const [menuTitle, setTitle] = useState(''); | 45 | const [menuTitle, setTitle] = useState(''); |
46 | + const dispatch = useDispatch(); | ||
44 | useEffect(() => { | 47 | useEffect(() => { |
45 | setTitle(title); | 48 | setTitle(title); |
46 | }, []); | 49 | }, []); |
... | @@ -62,14 +65,15 @@ const DropDown = ({ | ... | @@ -62,14 +65,15 @@ const DropDown = ({ |
62 | </DropDownWrap> | 65 | </DropDownWrap> |
63 | } | 66 | } |
64 | > | 67 | > |
65 | - {options.map(friend => ( | 68 | + {options.map(option => ( |
66 | <MenuItem | 69 | <MenuItem |
67 | - value={friend.id} | 70 | + value={option.id} |
68 | onClick={() => { | 71 | onClick={() => { |
69 | - setTitle(friend.name); | 72 | + dispatch(setSearchOption(option.id)); |
73 | + setTitle(option.name); | ||
70 | }} | 74 | }} |
71 | > | 75 | > |
72 | - {friend.name} | 76 | + {option.name} |
73 | </MenuItem> | 77 | </MenuItem> |
74 | ))} | 78 | ))} |
75 | </DropDownHeader> | 79 | </DropDownHeader> | ... | ... |
... | @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'; | ... | @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'; |
2 | import { TextInput } from '@mantine/core'; | 2 | import { TextInput } from '@mantine/core'; |
3 | import styled from 'styled-components'; | 3 | import styled from 'styled-components'; |
4 | import { useHistory, useLocation } from 'react-router-dom'; | 4 | import { useHistory, useLocation } from 'react-router-dom'; |
5 | -import { useDispatch } from 'react-redux'; | 5 | +import { useDispatch, useSelector } from 'react-redux'; |
6 | import SearchBox from './SearchBox'; | 6 | import SearchBox from './SearchBox'; |
7 | import { inputColorMap } from '../../lib/styles/palette'; | 7 | import { inputColorMap } from '../../lib/styles/palette'; |
8 | import { esApi } from '../../lib/api/elasticsearch'; | 8 | import { esApi } from '../../lib/api/elasticsearch'; |
... | @@ -43,18 +43,31 @@ const MyInput = ({ | ... | @@ -43,18 +43,31 @@ const MyInput = ({ |
43 | }) => { | 43 | }) => { |
44 | const [query, setQuery] = useState(''); | 44 | const [query, setQuery] = useState(''); |
45 | const history = useHistory(); | 45 | const history = useHistory(); |
46 | - const search = useLocation(); | 46 | + const { search } = useLocation(); |
47 | - const name = decodeURIComponent(search.search.substring(7)); | 47 | + const name = decodeURIComponent( |
48 | + new URLSearchParams(search).get('query') || '' | ||
49 | + ); | ||
50 | + const option = decodeURIComponent( | ||
51 | + new URLSearchParams(search).get('option') || '' | ||
52 | + ); | ||
48 | const dispatch = useDispatch(); | 53 | const dispatch = useDispatch(); |
54 | + const { option: currentSearchOption } = useSelector( | ||
55 | + state => state.searchOption | ||
56 | + ); | ||
49 | 57 | ||
50 | useEffect(() => { | 58 | useEffect(() => { |
51 | const setSearchDatas = async () => { | 59 | const setSearchDatas = async () => { |
60 | + if (option === '') { | ||
52 | const { results } = await esApi.search(name); | 61 | const { results } = await esApi.search(name); |
53 | dispatch(setParsedDocuments(results)); | 62 | dispatch(setParsedDocuments(results)); |
63 | + } else { | ||
64 | + const { results } = await esApi.searchWithOption(name, option); | ||
65 | + dispatch(setParsedDocuments(results)); | ||
66 | + } | ||
54 | }; | 67 | }; |
55 | setQuery(name); | 68 | setQuery(name); |
56 | setSearchDatas(); | 69 | setSearchDatas(); |
57 | - }, [name]); | 70 | + }, [name, option]); |
58 | 71 | ||
59 | return ( | 72 | return ( |
60 | <InputBlock | 73 | <InputBlock |
... | @@ -82,7 +95,13 @@ const MyInput = ({ | ... | @@ -82,7 +95,13 @@ const MyInput = ({ |
82 | alert('검색어를 입력 해 주세요.'); | 95 | alert('검색어를 입력 해 주세요.'); |
83 | return; | 96 | return; |
84 | } | 97 | } |
85 | - const params = new URLSearchParams({ query }); | 98 | + const searchRequest = {}; |
99 | + searchRequest.query = query; | ||
100 | + if (currentSearchOption === 'WRITER') | ||
101 | + searchRequest.option = 'writer'; | ||
102 | + if (currentSearchOption === 'CONTENT') | ||
103 | + searchRequest.option = 'content'; | ||
104 | + const params = new URLSearchParams(searchRequest); | ||
86 | history.push(`search?${decodeURIComponent(params.toString())}`); | 105 | history.push(`search?${decodeURIComponent(params.toString())}`); |
87 | } | 106 | } |
88 | }} | 107 | }} |
... | @@ -94,7 +113,13 @@ const MyInput = ({ | ... | @@ -94,7 +113,13 @@ const MyInput = ({ |
94 | alert('검색어를 입력 해 주세요.'); | 113 | alert('검색어를 입력 해 주세요.'); |
95 | return; | 114 | return; |
96 | } | 115 | } |
97 | - const params = new URLSearchParams({ query }); | 116 | + const searchRequest = {}; |
117 | + searchRequest.query = query; | ||
118 | + if (currentSearchOption === 'WRITER') searchRequest.option = 'writer'; | ||
119 | + if (currentSearchOption === 'CONTENT') | ||
120 | + searchRequest.option = 'content'; | ||
121 | + | ||
122 | + const params = new URLSearchParams(searchRequest); | ||
98 | history.push(`search?${decodeURIComponent(params.toString())}`); | 123 | history.push(`search?${decodeURIComponent(params.toString())}`); |
99 | }} | 124 | }} |
100 | > | 125 | > | ... | ... |
... | @@ -15,7 +15,6 @@ const ImageWrapper = styled.div` | ... | @@ -15,7 +15,6 @@ const ImageWrapper = styled.div` |
15 | `; | 15 | `; |
16 | const Thumbnails = ({ srcs }) => { | 16 | const Thumbnails = ({ srcs }) => { |
17 | const [lists, setLists] = useState([]); | 17 | const [lists, setLists] = useState([]); |
18 | - console.log(srcs); | ||
19 | const placeholder = () => { | 18 | const placeholder = () => { |
20 | if (srcs.length < 4) { | 19 | if (srcs.length < 4) { |
21 | const list = []; | 20 | const list = []; | ... | ... |
frontend/src/features/searchOption.js
0 → 100644
1 | +import { createSlice } from '@reduxjs/toolkit'; | ||
2 | + | ||
3 | +const OPTION = ['ALL', 'WRITER', 'CONTENT']; | ||
4 | + | ||
5 | +const searchOptionSlice = createSlice({ | ||
6 | + name: 'searchOption', | ||
7 | + initialState: { | ||
8 | + option: OPTION[0], | ||
9 | + }, | ||
10 | + reducers: { | ||
11 | + setSearchOption: (state, action) => { | ||
12 | + state.option = OPTION[action.payload]; | ||
13 | + }, | ||
14 | + }, | ||
15 | +}); | ||
16 | + | ||
17 | +export const { setSearchOption } = searchOptionSlice.actions; | ||
18 | + | ||
19 | +export default searchOptionSlice.reducer; |
... | @@ -19,4 +19,17 @@ export const esApi = { | ... | @@ -19,4 +19,17 @@ export const esApi = { |
19 | 19 | ||
20 | return res.data; | 20 | return res.data; |
21 | }, | 21 | }, |
22 | + searchWithOption: async (searchWord, option) => { | ||
23 | + const res = await esInstance.post( | ||
24 | + `/api/as/v1/engines/${process.env.REACT_APP_ENGINE_NAME}/search`, | ||
25 | + { | ||
26 | + query: searchWord, | ||
27 | + search_fields: { | ||
28 | + [option]: {}, | ||
29 | + }, | ||
30 | + } | ||
31 | + ); | ||
32 | + | ||
33 | + return res.data; | ||
34 | + }, | ||
22 | }; | 35 | }; | ... | ... |
... | @@ -51,9 +51,9 @@ const HomePage = () => { | ... | @@ -51,9 +51,9 @@ const HomePage = () => { |
51 | fontsize="20px" | 51 | fontsize="20px" |
52 | height="50px" | 52 | height="50px" |
53 | options={[ | 53 | options={[ |
54 | - { id: 1, name: '전체' }, | 54 | + { id: 0, name: '전체' }, |
55 | - { id: 2, name: '개인' }, | 55 | + { id: 1, name: '작성자' }, |
56 | - { id: 3, name: '부서' }, | 56 | + { id: 2, name: '내용' }, |
57 | ]} | 57 | ]} |
58 | /> | 58 | /> |
59 | <Input color="blue" paddingsize="10px" width="100%" display /> | 59 | <Input color="blue" paddingsize="10px" width="100%" display /> | ... | ... |
1 | import { combineReducers } from 'redux'; | 1 | import { combineReducers } from 'redux'; |
2 | import parsedDocumentsReducer from '../features/parsedDocumentsSlice'; | 2 | import parsedDocumentsReducer from '../features/parsedDocumentsSlice'; |
3 | +import searchOptionReducer from '../features/searchOption'; | ||
3 | 4 | ||
4 | export default combineReducers({ | 5 | export default combineReducers({ |
5 | parsedDocuments: parsedDocumentsReducer, | 6 | parsedDocuments: parsedDocumentsReducer, |
7 | + searchOption: searchOptionReducer, | ||
6 | }); | 8 | }); | ... | ... |
-
Please register or login to post a comment