modify search result page

This commit is contained in:
Shinsuke Sugaya 2015-09-24 07:22:36 +09:00
parent f7e43cf005
commit 2799bf6975
9 changed files with 130 additions and 77 deletions

View file

@ -20,6 +20,7 @@ import java.util.Map;
import org.codelibs.fess.Constants;
import org.codelibs.fess.app.web.base.FessSearchAction;
import org.codelibs.fess.util.DocumentUtil;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.lastaflute.web.Execute;
@ -73,7 +74,7 @@ public class CacheAction extends FessSearchAction {
}, () -> asHtml(path_ErrorJsp));
}
return asStream((String) doc.get(fieldHelper.docIdField)).contentType("text/html; charset=UTF-8").data(
return asStream(DocumentUtil.getValue(doc, fieldHelper.docIdField, String.class)).contentType("text/html; charset=UTF-8").data(
content.getBytes(Constants.CHARSET_UTF_8));
}

View file

@ -33,10 +33,12 @@ import org.codelibs.fess.es.exentity.ClickLog;
import org.codelibs.fess.helper.SearchLogHelper;
import org.codelibs.fess.helper.ViewHelper;
import org.codelibs.fess.util.ComponentUtil;
import org.codelibs.fess.util.DocumentUtil;
import org.codelibs.robot.util.CharUtil;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.lastaflute.web.Execute;
import org.lastaflute.web.response.ActionResponse;
import org.lastaflute.web.response.HtmlResponse;
import org.lastaflute.web.util.LaResponseUtil;
import org.slf4j.Logger;
@ -61,7 +63,7 @@ public class GoAction extends FessSearchAction {
// Search Execute
// ==============
@Execute
public HtmlResponse index(final GoForm form) throws IOException {
public ActionResponse index(final GoForm form) throws IOException {
searchAvailable();
Map<String, Object> doc = null;
@ -80,13 +82,12 @@ public class GoAction extends FessSearchAction {
messages.addErrorsDocidNotFound(GLOBAL, form.docId);
}, () -> asHtml(path_ErrorJsp));
}
final Object urlObj = doc.get(fieldHelper.urlField);
if (urlObj == null) {
final String url = DocumentUtil.getValue(doc, fieldHelper.urlField, String.class);
if (url == null) {
throwValidationError(messages -> {
messages.addErrorsDocumentNotFound(GLOBAL, form.docId);
}, () -> asHtml(path_ErrorJsp));
}
final String url = urlObj.toString();
if (Constants.TRUE.equals(crawlerProperties.getProperty(Constants.SEARCH_LOG_PROPERTY, Constants.TRUE))) {
final String userSessionId = userInfoHelper.getUserCode();
@ -100,9 +101,9 @@ public class GoAction extends FessSearchAction {
clickLog.setUserSessionId(userSessionId);
clickLog.setDocId(form.docId);
long clickCount = 0;
final Object count = doc.get(fieldHelper.clickCountField);
if (count instanceof Long) {
clickCount = ((Long) count).longValue();
final Integer count = DocumentUtil.getValue(doc, fieldHelper.clickCountField, Integer.class);
if (count != null) {
clickCount = count.longValue();
}
clickLog.setClickCount(clickCount);
searchLogHelper.addClickLog(clickLog);
@ -133,13 +134,13 @@ public class GoAction extends FessSearchAction {
if (Constants.TRUE.equals(crawlerProperties.getProperty(Constants.SEARCH_FILE_PROXY_PROPERTY, Constants.TRUE))) {
final ViewHelper viewHelper = ComponentUtil.getViewHelper();
try {
viewHelper.writeContent(doc);
return null;
return viewHelper.asContentResponse(doc);
} catch (final Exception e) {
logger.error("Failed to load: " + doc, e);
throwValidationError(messages -> {
messages.addErrorsNotLoadFromServer(GLOBAL, url);
}, () -> asHtml(path_ErrorJsp));
return null; // workaround
}
} else if (Constants.TRUE.equals(crawlerProperties.getProperty(Constants.SEARCH_DESKTOP_PROPERTY, Constants.FALSE))) {
final String path = url.replaceFirst("file:/+", "//");
@ -159,15 +160,13 @@ public class GoAction extends FessSearchAction {
}, () -> asHtml(path_ErrorJsp));
}
LaResponseUtil.getResponse().setStatus(HttpServletResponse.SC_NO_CONTENT);
return null;
return HtmlResponse.asEmptyBody().httpStatus(HttpServletResponse.SC_NO_CONTENT);
} else {
LaResponseUtil.getResponse().sendRedirect(url + hash);
return newHtmlResponseAsRediect(url + hash);
}
} else {
LaResponseUtil.getResponse().sendRedirect(url + hash);
return newHtmlResponseAsRediect(url + hash);
}
return null;
}
protected boolean isFileSystemPath(final String url) {

View file

@ -30,6 +30,7 @@ import org.codelibs.core.io.CopyUtil;
import org.codelibs.core.io.OutputStreamUtil;
import org.codelibs.core.lang.StringUtil;
import org.codelibs.fess.app.web.base.FessSearchAction;
import org.codelibs.fess.util.DocumentUtil;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.lastaflute.web.Execute;
@ -70,7 +71,7 @@ public class ScreenshotAction extends FessSearchAction {
queryRequestBuilder.addFields(queryHelper.getResponseFields());
return true;
}).get();
final String url = doc == null ? null : (String) doc.get(fieldHelper.urlField);
final String url = DocumentUtil.getValue(doc, fieldHelper.urlField, String.class);
if (StringUtil.isBlank(form.queryId) || StringUtil.isBlank(url) || screenShotManager == null) {
// 404
response.sendError(HttpServletResponse.SC_NOT_FOUND);

View file

@ -11,6 +11,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@ -131,6 +132,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.get.GetField;
import org.elasticsearch.index.query.BoolFilterBuilder;
import org.elasticsearch.index.query.FilterBuilders;
import org.elasticsearch.index.query.QueryBuilder;
@ -138,6 +140,7 @@ import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.indices.IndexAlreadyExistsException;
import org.elasticsearch.indices.IndexMissingException;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHitField;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder;
@ -501,7 +504,15 @@ public class FessEsClient implements Client {
public Optional<Map<String, Object>> getDocument(final String index, final String type,
final SearchCondition<SearchRequestBuilder> condition) {
return getDocument(index, type, condition, (response, hit) -> {
return hit.getSource();
Map<String, Object> source = hit.getSource();
if (source != null) {
return source;
}
Map<String, SearchHitField> fields = hit.getFields();
if (fields != null) {
return fields.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> (Object) e.getValue().getValues()));
}
return null;
});
}
@ -521,7 +532,15 @@ public class FessEsClient implements Client {
public Optional<Map<String, Object>> getDocument(final String index, final String type, final String id,
final SearchCondition<GetRequestBuilder> condition) {
return getDocument(index, type, id, condition, (response, result) -> {
return response.getSource();
Map<String, Object> source = response.getSource();
if (source != null) {
return source;
}
Map<String, GetField> fields = response.getFields();
if (fields != null) {
return fields.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> (Object) e.getValue().getValues()));
}
return null;
});
}

View file

@ -17,11 +17,9 @@
package org.codelibs.fess.helper;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
@ -39,12 +37,9 @@ import java.util.regex.Pattern;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.codelibs.core.CoreLibConstants;
import org.codelibs.core.io.CopyUtil;
import org.codelibs.core.lang.StringUtil;
import org.codelibs.core.misc.Base64Util;
import org.codelibs.core.misc.DynamicProperties;
@ -59,6 +54,7 @@ import org.codelibs.fess.es.exentity.CrawlingConfig;
import org.codelibs.fess.es.exentity.CrawlingConfig.ConfigType;
import org.codelibs.fess.helper.UserAgentHelper.UserAgentType;
import org.codelibs.fess.util.ComponentUtil;
import org.codelibs.fess.util.DocumentUtil;
import org.codelibs.fess.util.ResourceUtil;
import org.codelibs.robot.builder.RequestDataBuilder;
import org.codelibs.robot.client.S2RobotClient;
@ -67,6 +63,7 @@ import org.codelibs.robot.entity.ResponseData;
import org.codelibs.robot.util.CharUtil;
import org.lastaflute.di.core.SingletonLaContainer;
import org.lastaflute.taglib.function.LaFunctions;
import org.lastaflute.web.response.StreamResponse;
import org.lastaflute.web.util.LaRequestUtil;
import org.lastaflute.web.util.LaResponseUtil;
import org.lastaflute.web.util.LaServletContextUtil;
@ -398,18 +395,20 @@ public class ViewHelper implements Serializable {
if (locale == null) {
locale = Locale.ENGLISH;
}
String url = (String) doc.get("urlLink");
String url = DocumentUtil.getValue(doc, "urlLink", String.class);
if (url == null) {
url = ComponentUtil.getMessageManager().getMessage(locale, "labels.search_unknown");
}
Object created = doc.get(fieldHelper.createdField);
if (created instanceof Date) {
String createdStr;
Long created = DocumentUtil.getValue(doc, fieldHelper.createdField, Long.class);
if (created != null) {
final SimpleDateFormat sdf = new SimpleDateFormat(CoreLibConstants.DATE_FORMAT_ISO_8601_EXTEND);
created = sdf.format((Date) created);
createdStr = sdf.format(new Date(created.longValue()));
} else {
created = ComponentUtil.getMessageManager().getMessage(locale, "labels.search_unknown");
createdStr = ComponentUtil.getMessageManager().getMessage(locale, "labels.search_unknown");
}
doc.put("cacheMsg", ComponentUtil.getMessageManager().getMessage(locale, "labels.search_cache_msg", new Object[] { url, created }));
doc.put("cacheMsg",
ComponentUtil.getMessageManager().getMessage(locale, "labels.search_cache_msg", new Object[] { url, createdStr }));
doc.put("queries", queries);
@ -476,7 +475,7 @@ public class ViewHelper implements Serializable {
return null;
}
public void writeContent(final Map<String, Object> doc) {
public StreamResponse asContentResponse(final Map<String, Object> doc) {
if (logger.isDebugEnabled()) {
logger.debug("writing the content of: " + doc);
}
@ -516,37 +515,32 @@ public class ViewHelper implements Serializable {
throw new FessSystemException("No S2RobotClient: " + configIdObj + ", url: " + url);
}
final ResponseData responseData = client.execute(RequestDataBuilder.newRequestData().get().url(url).build());
final HttpServletResponse response = LaResponseUtil.getResponse();
StreamResponse response = new StreamResponse(StringUtil.EMPTY);
writeFileName(response, responseData);
writeContentType(response, responseData);
writeNoCache(response, responseData);
InputStream is = null;
OutputStream os = null;
try {
is = new BufferedInputStream(responseData.getResponseBody());
os = new BufferedOutputStream(response.getOutputStream());
CopyUtil.copy(is, os);
os.flush();
} catch (final IOException e) {
if (!"ClientAbortException".equals(e.getClass().getSimpleName())) {
throw new FessSystemException("Failed to write a content. configId: " + configIdObj + ", url: " + url, e);
response.stream(out -> {
try (InputStream is = new BufferedInputStream(responseData.getResponseBody())) {
out.write(is);
} catch (final IOException e) {
if (!"ClientAbortException".equals(e.getClass().getSimpleName())) {
throw new FessSystemException("Failed to write a content. configId: " + configIdObj + ", url: " + url, e);
}
}
} finally {
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(os);
}
if (logger.isDebugEnabled()) {
logger.debug("Finished to write " + url);
}
if (logger.isDebugEnabled()) {
logger.debug("Finished to write " + url);
}
});
return response;
}
protected void writeNoCache(final HttpServletResponse response, final ResponseData responseData) {
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Expires", "Thu, 01 Dec 1994 16:00:00 GMT");
protected void writeNoCache(final StreamResponse response, final ResponseData responseData) {
response.header("Pragma", "no-cache");
response.header("Cache-Control", "no-cache");
response.header("Expires", "Thu, 01 Dec 1994 16:00:00 GMT");
}
protected void writeFileName(final HttpServletResponse response, final ResponseData responseData) {
protected void writeFileName(final StreamResponse response, final ResponseData responseData) {
final UserAgentHelper userAgentHelper = ComponentUtil.getUserAgentHelper();
final UserAgentType userAgentType = userAgentHelper.getUserAgentType();
String charset = responseData.getCharSet();
@ -569,19 +563,19 @@ public class ViewHelper implements Serializable {
switch (userAgentType) {
case IE:
response.setHeader("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(name, Constants.UTF_8) + "\"");
response.header("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(name, Constants.UTF_8) + "\"");
break;
case OPERA:
response.setHeader("Content-Disposition", "attachment; filename*=utf-8'ja'" + URLEncoder.encode(name, Constants.UTF_8));
response.header("Content-Disposition", "attachment; filename*=utf-8'ja'" + URLEncoder.encode(name, Constants.UTF_8));
break;
case SAFARI:
response.setHeader("Content-Disposition", "attachment; filename=\"" + name + "\"");
response.header("Content-Disposition", "attachment; filename=\"" + name + "\"");
break;
case CHROME:
case FIREFOX:
case OTHER:
default:
response.setHeader("Content-Disposition",
response.header("Content-Disposition",
"attachment; filename=\"=?utf-8?B?" + Base64Util.encode(name.getBytes(Constants.UTF_8)) + "?=\"");
break;
}
@ -590,22 +584,23 @@ public class ViewHelper implements Serializable {
}
}
protected void writeContentType(final HttpServletResponse response, final ResponseData responseData) {
protected void writeContentType(final StreamResponse response, final ResponseData responseData) {
final String mimeType = responseData.getMimeType();
if (logger.isDebugEnabled()) {
logger.debug("mimeType: " + mimeType);
}
if (mimeType == null) {
response.contentTypeOctetStream();
return;
}
if (mimeType.startsWith("text/")) {
final String charset = response.getCharacterEncoding();
final String charset = LaResponseUtil.getResponse().getCharacterEncoding();
if (charset != null) {
response.setContentType(mimeType + "; charset=" + charset);
response.contentType(mimeType + "; charset=" + charset);
return;
}
}
response.setContentType(mimeType);
response.contentType(mimeType);
}
public boolean isUseSession() {

View file

@ -36,6 +36,7 @@ import org.codelibs.fess.Constants;
import org.codelibs.fess.FessSystemException;
import org.codelibs.fess.helper.FieldHelper;
import org.codelibs.fess.util.ComponentUtil;
import org.codelibs.fess.util.DocumentUtil;
import org.lastaflute.web.util.LaRequestUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -112,7 +113,7 @@ public class ScreenShotManager {
final FieldHelper fieldHelper = ComponentUtil.getFieldHelper();
for (final ScreenShotGenerator generator : generatorList) {
if (generator.isTarget(docMap)) {
final String url = (String) docMap.get(fieldHelper.urlField);
final String url = DocumentUtil.getValue(docMap, fieldHelper.urlField, String.class);
final String path = getImageFilename(docMap);
if (!screenShotTaskQueue.offer(new ScreenShotTask(url, new File(baseDir, path), generator))) {
logger.warn("Failed to offer a screenshot task: " + url + " -> " + path);
@ -125,7 +126,7 @@ public class ScreenShotManager {
protected String getImageFilename(final Map<String, Object> docMap) {
final StringBuilder buf = new StringBuilder(50);
final FieldHelper fieldHelper = ComponentUtil.getFieldHelper();
final String docid = (String) docMap.get(fieldHelper.docIdField);
final String docid = DocumentUtil.getValue(docMap, fieldHelper.docIdField, String.class);
for (int i = 0; i < docid.length(); i++) {
if (i > 0 && i % splitSize == 0) {
buf.append('/');

View file

@ -0,0 +1,35 @@
package org.codelibs.fess.util;
import java.util.List;
import java.util.Map;
public final class DocumentUtil {
private DocumentUtil() {
}
public static <T> T getValue(Map<String, Object> doc, String key, Class<T> clazz) {
if (doc == null || key == null) {
return null;
}
Object value = doc.get(key);
if (value == null) {
return null;
}
if (value instanceof List) {
if (clazz.isAssignableFrom(List.class)) {
return (T) value;
}
if (((List<?>) value).isEmpty()) {
return null;
}
return (T) ((List) value).get(0);
}
return (T) value;
}
}

View file

@ -29,15 +29,15 @@
<c:forEach var="doc" varStatus="s" items="${documentItems}">
<li id="result${s.index}">
<h3 class="title ellipsis">
<a class="link" href="${doc.urlLink}" data-uri="${doc.urlLink}" data-id="${doc.docId}">${f:h(doc.contentTitle)}</a>
<a class="link" href="${doc.urlLink}" data-uri="${doc.urlLink}" data-id="${doc.doc_id}">${f:h(doc.contentTitle)}</a>
</h3>
<div class="body">
<div class="description">${doc.contentDescription}</div>
<div class="site ellipsis">
<cite>${f:h(doc.sitePath)}</cite>
<c:if test="${doc.hasCache_s_s=='true'}">
<a href="cache?docId=${doc.docId}${appendHighlightQueries}" class="cache"><la:message
key="labels.search_result_cache" /></a>
<la:link href="/cache/?docId=${doc.doc_id}${appendHighlightQueries}" class="cache"><la:message
key="labels.search_result_cache" /></la:link>
</c:if>
</div>
<div class="more visible-phone">
@ -47,27 +47,27 @@
<c:if test="${doc.created!=null && doc.created!=''}">
<c:set var="hasInfo" value="true"/>
<la:message key="labels.search_result_created" />
<fmt:formatDate value="${fe:parseDate(doc.created)}" type="BOTH" />
<fmt:formatDate value="${fe:date(doc.created)}" type="BOTH" />
</c:if>
<c:if test="${doc.lastModified!=null && doc.lastModified!=''}">
<c:if test="${doc.last_modified!=null && doc.last_modified!=''}">
<c:if test="${hasInfo}"><span class="br-phone"></span><span class="hidden-phone">-</span></c:if><c:set var="hasInfo" value="true"/>
<la:message key="labels.search_result_lastModified" />
<fmt:formatDate value="${fe:parseDate(doc.lastModified)}" type="BOTH" />
<la:message key="labels.search_result_last_modified" />
<fmt:formatDate value="${fe:date(doc.last_modified)}" type="BOTH" />
</c:if>
<c:if test="${doc.contentLength!=null && doc.contentLength!=''}">
<c:if test="${doc.content_length!=null && doc.content_length!=''}">
<c:if test="${hasInfo}"><span class="br-phone"></span><span class="hidden-phone">-</span></c:if><c:set var="hasInfo" value="true"/>
<la:message key="labels.search_result_size"
arg0="${f:h(doc.contentLength)}" />
arg0="${f:h(doc.content_length)}" />
</c:if>
<c:if test="${searchLogSupport}">
<c:if test="${hasInfo}"><span class="br-phone"></span><span class="hidden-phone">-</span></c:if><c:set var="hasInfo" value="true"/>
<la:message key="labels.search_click_count"
arg0="${f:h(doc.clickCount_l_x_dv)}" />
arg0="${f:h(doc.click_count)}" />
</c:if>
<c:if test="${favoriteSupport}">
<c:if test="${hasInfo}"><span class="br-phone"></span><span class="hidden-phone">-</span></c:if><c:set var="hasInfo" value="true"/>
<a href="#${doc.docId}" class="favorite"><la:message
key="labels.search_result_favorite" /> (${f:h(doc.favoriteCount_l_x_dv)})</a>
<a href="#${doc.doc_id}" class="favorite"><la:message
key="labels.search_result_favorite" /> (${f:h(doc.favorite_count)})</a>
<span class="favorited"><la:message
key="labels.search_result_favorited"/> <span class="favorited-count">(${f:h(doc.favoriteCount_l_x_dv)})</span></span>
</c:if>

View file

@ -27,7 +27,8 @@ $(function(){
rt = $('#rt').val(),
url = $(this).attr('href'),
buf = [];
buf.push('go?rt=');
buf.push(contextPath);
buf.push('/go/?rt=');
buf.push(rt);
buf.push('&docId=');
buf.push(docId);
@ -50,7 +51,8 @@ $(function(){
url = $(this).attr('href'),
queryId = $queryId.val(),
buf = [];
buf.push('go?rt=');
buf.push(contextPath);
buf.push('/go/?rt=');
buf.push(rt);
buf.push('&docId=');
buf.push(docId);