-
Notifications
You must be signed in to change notification settings - Fork 46
Expand file tree
/
Copy pathNewSuggestionsController.java
More file actions
183 lines (167 loc) · 9.01 KB
/
NewSuggestionsController.java
File metadata and controls
183 lines (167 loc) · 9.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package org.ayfaar.app.controllers;
import lombok.extern.slf4j.Slf4j;
import one.util.streamex.StreamEx;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.ayfaar.app.dao.TermDao;
import org.ayfaar.app.model.Term;
import org.ayfaar.app.services.ItemService;
import org.ayfaar.app.services.document.DocumentService;
import org.ayfaar.app.services.record.RecordService;
import org.ayfaar.app.services.topics.TopicService;
import org.ayfaar.app.services.videoResource.VideoResourceService;
import org.ayfaar.app.utils.ContentsService;
import org.ayfaar.app.utils.TermService;
import org.ayfaar.app.utils.UriGenerator;
import org.ayfaar.app.utils.contents.ContentsUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.lang.Math.min;
import static java.util.Arrays.asList;
import static java.util.regex.Pattern.CASE_INSENSITIVE;
import static java.util.regex.Pattern.UNICODE_CASE;
@Slf4j
@RestController
@RequestMapping("api/suggestions")
public class NewSuggestionsController {
@Autowired TermService termService;
@Autowired TopicService topicService;
@Autowired ContentsService contentsService;
@Autowired DocumentService documentService;
@Autowired VideoResourceService videoResourceService;
@Autowired RecordService recordService;
@Autowired ItemService itemService;
@Autowired ContentsUtils contentsUtils;
private List<String> escapeChars = Arrays.asList("(", ")", "[", "]", "{", "}");
private static final int MAX_SUGGESTIONS = 5;
private static final int MAX_WORDS_PARAGRAPH_AFTER_SEARCH = 4;
public Map<String, String> suggestions(String q) {
return suggestions(q, false, false, false, false, false, false, false, false, false);
}
@RequestMapping("all")
@ResponseBody
public Map<String, String> suggestions(@RequestParam String q,
@RequestParam(required = false, defaultValue = "true") boolean with_terms,
@RequestParam(required = false, defaultValue = "true") boolean with_topic,
@RequestParam(required = false, defaultValue = "true") boolean with_category_name,
@RequestParam(required = false, defaultValue = "true") boolean with_category_description,
@RequestParam(required = false, defaultValue = "true") boolean with_doc,
@RequestParam(required = false, defaultValue = "true") boolean with_video,
@RequestParam(required = false, defaultValue = "true") boolean with_item,
@RequestParam(required = false, defaultValue = "true") boolean with_record_name,
@RequestParam(required = false, defaultValue = "true") boolean with_record_code
) {
Map<String, String> allSuggestions = new LinkedHashMap<>();
List<Suggestions> items = new ArrayList<>();
if (with_terms) items.add(Suggestions.TERM); //default
if (with_topic) items.add(Suggestions.TOPIC);
if (with_category_name) items.add(Suggestions.CATEGORY_NAME);
if (with_category_description) items.add(Suggestions.CATEGORY_DESCRIPTION);
if (with_doc) items.add(Suggestions.DOCUMENT);
if (with_video) items.add(Suggestions.VIDEO);
if (with_record_name) items.add(Suggestions.RECORD_NAME);
if (with_record_code) items.add(Suggestions.RECORD_CODE);
if (with_item) items.add(Suggestions.ITEM);
for (Suggestions item : items) {
Queue<String> queriesQueue = getQueue(q);
for (Map.Entry<String, String> suggestion : getSuggestions(queriesQueue, item)) {
String key = suggestion.getKey();
String value = suggestion.getValue();
if(key.contains("ии:пункты:")) {
String suggestionParagraph = contentsUtils.filterLengthWordsAfter(value, q, MAX_WORDS_PARAGRAPH_AFTER_SEARCH);
if(suggestionParagraph != "")allSuggestions.put(key, key.substring(10) + ":" + suggestionParagraph);
}
else allSuggestions.put(key, value);
}
}
return allSuggestions;
}
private Queue<String> getQueue(String q) {
q = q.replace("*", ".*");
q = q.replaceAll("\\s+", ".*");
q = escapeRegexp(q);
q = addDuplications(q);
return new LinkedList<>(asList(
"^" + q,
"[\\s\\-]" + q,
q
));
}
private List<Map.Entry<String, String>> getSuggestions(Queue<String> queriesQueue, Suggestions item) {
List<Map.Entry<String, String>> suggestions = new ArrayList<>();
while (suggestions.size() < MAX_SUGGESTIONS && queriesQueue.peek() != null) {
List<? extends Map.Entry<String, String>> founded = null;
Map<String, String> mapUriWithNames = null;
// fixme: в некоторых методах getAllUriNames при каждом вызове getSuggestions, происходит запрос в БД для получения всех имён, это не рационально, я бы сделал логику кеширования имён и обновления кеша в случае добавления/изменения видео или документа
// или можно сделать RegExp запрос в БД и тогда не нужен кеш вовсе, просто из соображений скорости я стараюсь уменьшить запросы в БД
switch (item) {
case TERM:
Collection<TermDao.TermInfo> allInfoTerms = termService.getAllInfoTerms();
final List<TermDao.TermInfo> suggested = getSuggested(queriesQueue.poll(), suggestions, allInfoTerms, TermDao.TermInfo::getName);
founded = StreamEx.of(suggested)
.map((i) -> new ImmutablePair<>(UriGenerator.generate(Term.class, i.getName()), i.getName()))
.toList();
break;
case TOPIC:
mapUriWithNames = topicService.getAllUriNames();
break;
case CATEGORY_NAME:
mapUriWithNames = contentsService.getAllUriNames();
break;
case CATEGORY_DESCRIPTION:
mapUriWithNames = contentsService.getAllUriDescription();
break;
case DOCUMENT:
mapUriWithNames = documentService.getAllUriNames();
break;
case VIDEO:
mapUriWithNames = videoResourceService.getAllUriNames();
break;
case ITEM:
mapUriWithNames = itemService.getAllUriNumbers();
break;
case RECORD_NAME:
mapUriWithNames = recordService.getAllUriNames();
break;
case RECORD_CODE:
mapUriWithNames = recordService.getAllUriCodes();
break;
}
if (item != Suggestions.TERM)
founded = getSuggested(queriesQueue.poll(), suggestions, mapUriWithNames.entrySet(), Map.Entry::getValue);
suggestions.addAll(founded.subList(0, min(MAX_SUGGESTIONS - suggestions.size(), founded.size())));
}
Collections.sort(suggestions, (e1, e2) -> Integer.valueOf(e1.getValue().length()).compareTo(e2.getValue().length()));
return suggestions;
}
protected static String addDuplications(String q) {
return q.replaceAll("([A-Za-zА-Яа-яЁё])", "$1+-*$1*");
}
private <T> List<T> getSuggested(String query, List<Map.Entry<String, String>> suggestions, Collection<T> uriWithNames, Function<T, String> nameProvider) {
List<T> list = new ArrayList<>();
Pattern pattern = Pattern.compile(query, CASE_INSENSITIVE + UNICODE_CASE);
for (T entry : uriWithNames) {
String name = nameProvider.apply(entry);
Matcher matcher = pattern.matcher(name);
if (matcher.find() && !suggestions.contains(name) && !list.contains(name)) {
list.add(entry);
}
}
Collections.reverse(list);
return list;
}
private String escapeRegexp(String query) {
for (String bracket : escapeChars) {
if (query.contains(bracket)) {
query = query.replace(bracket, "\\" + bracket);
}
}
return query;
}
}