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 package info.magnolia.module.templatingkit.search;
35
36 import info.magnolia.cms.core.Content;
37 import info.magnolia.context.MgnlContext;
38 import info.magnolia.jcr.util.NodeTypes;
39 import info.magnolia.jcr.util.NodeUtil;
40 import info.magnolia.jcr.wrapper.I18nNodeWrapper;
41 import info.magnolia.module.templatingkit.functions.STKTemplatingFunctions;
42 import info.magnolia.module.templatingkit.templates.AbstractSTKTemplateModel;
43 import info.magnolia.rendering.model.RenderingModel;
44 import info.magnolia.rendering.template.TemplateDefinition;
45 import info.magnolia.repository.RepositoryConstants;
46 import info.magnolia.templating.functions.TemplatingFunctions;
47
48 import java.util.ArrayList;
49 import java.util.Collection;
50 import java.util.HashSet;
51 import java.util.List;
52 import java.util.Set;
53
54 import javax.inject.Inject;
55 import javax.jcr.Node;
56 import javax.jcr.NodeIterator;
57 import javax.jcr.RepositoryException;
58 import javax.jcr.Session;
59 import javax.jcr.query.Query;
60 import javax.jcr.query.QueryManager;
61 import javax.jcr.query.QueryResult;
62
63 import org.apache.commons.lang3.StringUtils;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
66
67
68
69
70 public class SearchResultModel extends AbstractSTKTemplateModel<TemplateDefinition> {
71
72 private static final String SEARCH_QUERY_PATTERN = "SELECT * from [nt:base] AS t WHERE ISDESCENDANTNODE([%s]) AND contains(t.*, '%s') ORDER BY score(t) DESC";
73
74 protected String repository = RepositoryConstants.WEBSITE;
75
76
77
78 protected List<Content> result = new ArrayList<Content>();
79 protected Set<Node> queryResult = new HashSet<Node>();
80 protected long count;
81 protected int maxResultsPerPage;
82 protected int currentPage = 1;
83 protected long numPages = 0;
84
85 private static final Logger log = LoggerFactory.getLogger(SearchResultModel.class);
86
87 @Inject
88 public SearchResultModel(Node content, TemplateDefinition definition, RenderingModel<?> parent, STKTemplatingFunctions stkFunctions, TemplatingFunctions templatingFunctions) {
89 super(content, definition, parent, stkFunctions, templatingFunctions);
90 }
91
92 protected int getMaxResultsPerPage() {
93
94 maxResultsPerPage = Integer.MAX_VALUE;
95 try {
96 if (content.hasProperty("maxResultsPerPage")) {
97 maxResultsPerPage = Integer.parseInt(content.getProperty("maxResultsPerPage").getString());
98 }
99 } catch (Exception e) {
100
101 }
102 return maxResultsPerPage;
103 }
104
105
106
107
108
109
110 @Override
111 public String execute() {
112
113 String queryString = generateSimpleQuery(this.getQueryStr());
114
115 if (StringUtils.isBlank(queryString)) {
116 return null;
117 }
118
119 try {
120 count = getResultSetSize(queryString);
121
122 QueryResult qr = doQuery(queryString, getMaxResultsPerPage(), getOffset());
123 Iterable<Node> results = NodeUtil.asIterable(NodeUtil.filterDuplicates(NodeUtil.filterParentNodeType(qr.getNodes(), NodeTypes.Page.NAME)));
124 this.queryResult = getQueryResult(results);
125
126 numPages = count / maxResultsPerPage;
127 if ((count % maxResultsPerPage) > 0) {
128 numPages++;
129 }
130
131 } catch (RepositoryException e) {
132 log.error("Could not execute query.", e);
133 }
134
135 return "";
136
137 }
138
139 protected int getOffset() {
140 return ((getCurrentPage() - 1) * maxResultsPerPage);
141 }
142
143
144
145
146 @Deprecated
147 protected int pagedQuery(Collection<Content> queryResult, int offset, int limit) throws Exception {
148 int total = queryResult.size();
149 int newLimit = limit;
150 if (total > offset) {
151 if (total < offset + limit) {
152 newLimit = total - offset;
153 }
154
155 result = ((List<Content>) queryResult).subList(offset, offset + newLimit);
156 }
157
158 return total;
159 }
160
161 protected String generateSimpleQuery(String input) {
162 if (StringUtils.isBlank(input)) {
163 return null;
164 }
165
166 String searchString = input.replace("'", "''");
167 return String.format(SEARCH_QUERY_PATTERN, this.getPath(), searchString);
168 }
169
170 public String getPath() {
171 try {
172 return stkFunctions.siteRoot(content).getPath();
173
174 } catch (Exception e) {
175 log.warn("no site");
176 }
177 return "";
178 }
179
180
181
182
183 @Deprecated
184 public List<Node> getQueryResult() {
185 return new ArrayList<Node>(queryResult);
186 }
187
188 public Set<Node> getQueryResult(Iterable<Node> results) throws RepositoryException {
189 HashSet<Node> res = new HashSet<Node>();
190 for (Node page : results) {
191 res.add(new I18nNodeWrapper(page));
192 }
193 return res;
194 }
195
196 public Collection<SearchResultItem> getResult() {
197 final Collection<SearchResultItem> searchResults = new ArrayList<SearchResultItem>();
198 for (Node content : queryResult) {
199 searchResults.add(new SearchResultItem(content, this.getQueryStr(), templatingFunctions));
200 }
201 return searchResults;
202 }
203
204 public String getQueryStr() {
205 return MgnlContext.getParameter("queryStr");
206 }
207
208
209 public long getCount() {
210 return this.count;
211 }
212
213 public int getCurrentPage() {
214
215 if (MgnlContext.getParameter("currentPage") != null) {
216 currentPage = Integer.parseInt(MgnlContext.getParameter("currentPage"));
217 }
218
219 return currentPage;
220 }
221
222 public long getNumPages() {
223 return numPages;
224 }
225
226 public String getPageLink(int i) {
227 String link = "";
228 try {
229 link = stkFunctions.searchPageLink(content);
230 String current = "&currentPage=";
231 link = link + "?queryStr=" + getQueryStr() + current + i;
232 } catch (Exception e) {
233 log.error("could not find search result page");
234 }
235 return link;
236 }
237
238 public int getBeginIndex() {
239 if (currentPage - 2 <= 1) {
240 return 1;
241 } else {
242 return currentPage - 2;
243 }
244 }
245
246 public long getEndIndex() {
247 if (currentPage + 2 >= numPages) {
248 return numPages;
249 } else {
250 return currentPage + 2;
251 }
252 }
253
254 public String getPosition() {
255 try {
256 if (content.hasProperty("pager")) {
257 return content.getProperty("pager").getString();
258 }
259 } catch (Exception e) {
260 log.debug("no pagination position found");
261 }
262 return "";
263 }
264
265
266
267
268 private long getResultSetSize(String queryString) throws RepositoryException {
269 QueryResult qr = doQuery(queryString, Integer.MAX_VALUE, 0);
270
271 NodeIterator iter = NodeUtil.filterDuplicates(NodeUtil.filterParentNodeType(qr.getNodes(), NodeTypes.Page.NAME));
272 long count = 0;
273 while (iter.hasNext()) {
274 iter.nextNode();
275 count++;
276 }
277 return count;
278 }
279
280 private QueryResult doQuery(String queryString, long limit, long offset) throws RepositoryException {
281 try {
282 final Session jcrSession = MgnlContext.getJCRSession(repository);
283 final QueryManager jcrQueryManager = jcrSession.getWorkspace().getQueryManager();
284 final Query query = jcrQueryManager.createQuery(queryString, Query.JCR_SQL2);
285 query.setLimit(limit);
286 query.setOffset(offset);
287
288 log.debug("Executing query against workspace [{}] with statement [{}] and limit {} and offset {}...", repository, queryString, limit, offset);
289 long start = System.currentTimeMillis();
290 QueryResult qr = query.execute();
291 log.debug("Query execution took {} ms", System.currentTimeMillis() - start);
292 return qr;
293
294 } catch (RepositoryException e) {
295 log.error("An error occurred while performing a query against workspace [{}] with statement [{}] and limit {} and offset {}.", repository, queryString, limit, offset);
296 throw new RepositoryException(e);
297 }
298 }
299 }