Shinsuke Sugaya преди 11 години
родител
ревизия
568f345a5a
променени са 27 файла, в които са добавени 292 реда и са изтрити 192 реда
  1. 3 1
      src/main/java/jp/sf/fess/Constants.java
  2. 97 58
      src/main/java/jp/sf/fess/action/IndexAction.java
  3. 1 1
      src/main/java/jp/sf/fess/action/MobileAction.java
  4. 1 1
      src/main/java/jp/sf/fess/action/admin/SearchListAction.java
  5. 2 2
      src/main/java/jp/sf/fess/ds/impl/AbstractDataStoreImpl.java
  6. 39 22
      src/main/java/jp/sf/fess/ds/impl/IndexUpdateCallbackImpl.java
  7. 2 2
      src/main/java/jp/sf/fess/form/IndexForm.java
  8. 11 0
      src/main/java/jp/sf/fess/helper/SystemHelper.java
  9. 1 1
      src/main/java/jp/sf/fess/helper/UserInfoHelper.java
  10. 24 24
      src/main/java/jp/sf/fess/helper/impl/CookieUserInfoHelperImpl.java
  11. 7 7
      src/main/java/jp/sf/fess/helper/impl/QueryHelperImpl.java
  12. 2 1
      src/main/java/jp/sf/fess/job/TriggeredJob.java
  13. 10 11
      src/main/java/jp/sf/fess/service/FavoriteLogService.java
  14. 30 3
      src/main/java/jp/sf/fess/service/SearchService.java
  15. 4 0
      src/main/java/jp/sf/fess/solr/IndexUpdater.java
  16. 2 2
      src/main/java/jp/sf/fess/transformer/AbstractFessFileTransformer.java
  17. 2 4
      src/main/java/jp/sf/fess/transformer/FessXpathTransformer.java
  18. 4 4
      src/main/resources/app.dicon
  19. 5 3
      src/main/resources/application.properties
  20. 5 3
      src/main/resources/application_ja.properties
  21. 1 1
      src/main/webapp/WEB-INF/view/common/help.jsp
  22. 1 1
      src/main/webapp/WEB-INF/view/common/help_ja.jsp
  23. 4 4
      src/main/webapp/WEB-INF/view/error/header.jsp
  24. 4 4
      src/main/webapp/WEB-INF/view/header.jsp
  25. 4 4
      src/main/webapp/WEB-INF/view/index.jsp
  26. 7 8
      src/main/webapp/WEB-INF/view/searchResults.jsp
  27. 19 20
      src/main/webapp/js/search.js

+ 3 - 1
src/main/java/jp/sf/fess/Constants.java

@@ -271,7 +271,7 @@ public class Constants extends CoreLibConstants {
 
     public static final String STATS_REPORT_TYPE = "reportType";
 
-    public static final String RESULT_URL_CACHE = "resultUrls";
+    public static final String RESULT_DOC_ID_CACHE = "resultDocIds";
 
     public static final String SCREEN_SHOT_PATH_CACHE = "screenShotPaths";
 
@@ -322,4 +322,6 @@ public class Constants extends CoreLibConstants {
 
     public static final String WEB_CONFIG_ID_PREFIX = "W";
 
+    public static final String DOC_ID = "docId";
+
 }

+ 97 - 58
src/main/java/jp/sf/fess/action/IndexAction.java

@@ -81,7 +81,6 @@ import org.seasar.framework.beans.util.Beans;
 import org.seasar.framework.container.SingletonS2Container;
 import org.seasar.framework.container.annotation.tiger.Binding;
 import org.seasar.framework.container.annotation.tiger.BindingType;
-import org.seasar.framework.exception.IORuntimeException;
 import org.seasar.framework.util.InputStreamUtil;
 import org.seasar.framework.util.OutputStreamUtil;
 import org.seasar.framework.util.StringUtil;
@@ -279,6 +278,27 @@ public class IndexAction {
 
     @Execute(validator = true, input = "index")
     public String go() throws IOException {
+        Map<String, Object> doc = null;
+        try {
+            doc = searchService.getDocument("docId:" + indexForm.docId);
+        } catch (final Exception e) {
+            logger.warn("Failed to request: " + indexForm.docId, e);
+        }
+        if (doc == null) {
+            errorMessage = MessageResourcesUtil.getMessage(RequestUtil
+                    .getRequest().getLocale(), "errors.docid_not_found",
+                    indexForm.docId);
+            return "error.jsp";
+        }
+        final Object urlObj = doc.get("url");
+        if (urlObj == null) {
+            errorMessage = MessageResourcesUtil.getMessage(RequestUtil
+                    .getRequest().getLocale(), "errors.document_not_found",
+                    indexForm.docId);
+            return "error.jsp";
+        }
+        final String url = urlObj.toString();
+
         if (Constants.TRUE.equals(crawlerProperties.getProperty(
                 Constants.SEARCH_LOG_PROPERTY, Constants.TRUE))) {
             final String userSessionId = userInfoHelper.getUserCode();
@@ -286,7 +306,7 @@ public class IndexAction {
                 final SearchLogHelper searchLogHelper = SingletonS2Container
                         .getComponent(SearchLogHelper.class);
                 final ClickLog clickLog = new ClickLog();
-                clickLog.setUrl(indexForm.u);
+                clickLog.setUrl(url);
                 clickLog.setRequestedTime(new Timestamp(System
                         .currentTimeMillis()));
                 clickLog.setQueryRequestedTime(new Timestamp(Long
@@ -295,15 +315,15 @@ public class IndexAction {
                 searchLogHelper.addClickLog(clickLog);
             }
         }
-        if (indexForm.u.startsWith("file:")) {
+        if (url.startsWith("file:")) {
             if (Constants.TRUE.equals(crawlerProperties.getProperty(
                     Constants.SEARCH_DESKTOP_PROPERTY, Constants.FALSE))) {
-                final String path = indexForm.u.replaceFirst("file:/+", "//");
+                final String path = url.replaceFirst("file:/+", "//");
                 final File file = new File(path);
                 if (!file.exists()) {
                     errorMessage = MessageResourcesUtil.getMessage(RequestUtil
                             .getRequest().getLocale(),
-                            "errors.not_found_on_file_system", indexForm.u);
+                            "errors.not_found_on_file_system", url);
                     return "error.jsp";
                 }
                 final Desktop desktop = Desktop.getDesktop();
@@ -312,7 +332,7 @@ public class IndexAction {
                 } catch (final Exception e) {
                     errorMessage = MessageResourcesUtil.getMessage(RequestUtil
                             .getRequest().getLocale(),
-                            "errors.could_not_open_on_system", indexForm.u);
+                            "errors.could_not_open_on_system", url);
                     logger.warn("Could not open " + path, e);
                     return "error.jsp";
                 }
@@ -324,13 +344,12 @@ public class IndexAction {
                     Constants.SEARCH_FILE_LAUNCHER_PROPERTY, Constants.TRUE))) {
                 ResponseUtil.getResponse().sendRedirect(
                         RequestUtil.getRequest().getContextPath()
-                                + "/applet/launcher?uri="
-                                + S2Functions.u(indexForm.u));
+                                + "/applet/launcher?uri=" + S2Functions.u(url));
             } else {
-                ResponseUtil.getResponse().sendRedirect(indexForm.u);
+                ResponseUtil.getResponse().sendRedirect(url);
             }
         } else {
-            ResponseUtil.getResponse().sendRedirect(indexForm.u);
+            ResponseUtil.getResponse().sendRedirect(url);
         }
         return null;
     }
@@ -413,8 +432,9 @@ public class IndexAction {
         final int pageStart = Integer.parseInt(indexForm.start);
         final int pageNum = Integer.parseInt(indexForm.num);
         try {
-            documentItems = searchService.selectList(query, indexForm.facet,
-                    pageStart, pageNum, indexForm.geo, indexForm.mlt);
+            documentItems = searchService.getDocumentList(query,
+                    indexForm.facet, pageStart, pageNum, indexForm.geo,
+                    indexForm.mlt);
         } catch (final SolrLibQueryException e) {
             if (logger.isDebugEnabled()) {
                 logger.debug(e.getMessage(), e);
@@ -1882,10 +1902,13 @@ public class IndexAction {
             errMsg = "Not supported.";
             status = 10;
         } else {
-            final String userCode = userInfoHelper.getUserCode();
-            final String favoriteUrl = indexForm.u;
-
             try {
+                final Map<String, Object> doc = indexForm.docId == null ? null
+                        : searchService.getDocument("docId:" + indexForm.docId);
+                final String userCode = userInfoHelper.getUserCode();
+                final String favoriteUrl = doc == null ? null : (String) doc
+                        .get("url");
+
                 if (StringUtil.isBlank(userCode)) {
                     errMsg = "No user session.";
                     status = 2;
@@ -1893,21 +1916,20 @@ public class IndexAction {
                     errMsg = "URL is null.";
                     status = 3;
                 } else {
-                    final String[] urls = userInfoHelper
-                            .getResultUrls(URLDecoder.decode(indexForm.queryId,
-                                    Constants.UTF_8));
-                    if (urls != null) {
-                        String targetUrl = null;
-                        final String matchUrl = favoriteUrl.replaceFirst(":/+",
-                                ":/");
-                        for (final String url : urls) {
-                            if (url.replaceFirst(":/+", ":/").equals(matchUrl)) {
-                                targetUrl = url;
+                    final String[] docIds = userInfoHelper
+                            .getResultDocIds(URLDecoder.decode(
+                                    indexForm.queryId, Constants.UTF_8));
+                    if (docIds != null) {
+                        boolean found = false;
+                        for (final String docId : docIds) {
+                            if (indexForm.docId.equals(docId)) {
+                                found = true;
                                 break;
                             }
                         }
-                        if (targetUrl != null) {
-                            if (favoriteLogService.addUrl(userCode, targetUrl)) {
+                        if (found) {
+                            if (favoriteLogService
+                                    .addUrl(userCode, favoriteUrl)) {
                                 body = "\"result\":\"ok\"";
                             } else {
                                 errMsg = "Failed to add url.";
@@ -1954,19 +1976,41 @@ public class IndexAction {
                     errMsg = "Query ID is null.";
                     status = 3;
                 } else {
-                    String[] urls = userInfoHelper.getResultUrls(URLDecoder
-                            .decode(indexForm.queryId, Constants.UTF_8));
-                    urls = favoriteLogService.getUrls(userCode, urls);
+                    final String[] docIds = userInfoHelper
+                            .getResultDocIds(indexForm.queryId);
+                    final List<Map<String, Object>> docList = searchService
+                            .getDocumentListByDocIds(docIds, MAX_PAGE_SIZE);
+                    List<String> urlList = new ArrayList<String>(docList.size());
+                    for (final Map<String, Object> doc : docList) {
+                        final Object urlObj = doc.get("url");
+                        if (urlObj != null) {
+                            urlList.add(urlObj.toString());
+                        }
+                    }
+                    urlList = favoriteLogService.getUrlList(userCode, urlList);
+                    final List<String> docIdList = new ArrayList<String>(
+                            urlList.size());
+                    for (final Map<String, Object> doc : docList) {
+                        final Object urlObj = doc.get("url");
+                        if (urlObj != null
+                                && urlList.contains(urlObj.toString())) {
+                            final Object docIdObj = doc.get(Constants.DOC_ID);
+                            if (docIdObj != null) {
+                                docIdList.add(docIdObj.toString());
+                            }
+                        }
+                    }
+
                     final StringBuilder buf = new StringBuilder();
-                    buf.append("\"num\":").append(urls.length);
-                    if (urls.length > 0) {
-                        buf.append(", \"urls\":[");
-                        for (int i = 0; i < urls.length; i++) {
+                    buf.append("\"num\":").append(docIdList.size());
+                    if (!docIdList.isEmpty()) {
+                        buf.append(", \"docIds\":[");
+                        for (int i = 0; i < docIdList.size(); i++) {
                             if (i > 0) {
                                 buf.append(',');
                             }
                             buf.append('"');
-                            buf.append(escapeJsonString(urls[i]));
+                            buf.append(docIdList.get(i));
                             buf.append('"');
                         }
                         buf.append(']');
@@ -1986,40 +2030,35 @@ public class IndexAction {
 
     @Execute(validator = false)
     public String screenshot() {
-        if (StringUtil.isBlank(indexForm.queryId)
-                || StringUtil.isBlank(indexForm.u) || screenShotManager == null) {
-            // 404
-            try {
+        OutputStream out = null;
+        BufferedInputStream in = null;
+        try {
+            final Map<String, Object> doc = searchService.getDocument("docId:"
+                    + indexForm.docId);
+            final String url = doc == null ? null : (String) doc.get("url");
+            if (StringUtil.isBlank(indexForm.queryId)
+                    || StringUtil.isBlank(url) || screenShotManager == null) {
+                // 404
                 response.sendError(HttpServletResponse.SC_NOT_FOUND);
-            } catch (final IOException e) {
-                throw new IORuntimeException(e);
+                return null;
             }
-            return null;
-        }
 
-        final File screenShotFile = screenShotManager.getScreenShotFile(
-                indexForm.queryId, indexForm.u);
-        if (screenShotFile == null) {
-            // 404
-            try {
+            final File screenShotFile = screenShotManager.getScreenShotFile(
+                    indexForm.queryId, url);
+            if (screenShotFile == null) {
+                // 404
                 response.sendError(HttpServletResponse.SC_NOT_FOUND);
-            } catch (final IOException e) {
-                throw new IORuntimeException(e);
+                return null;
             }
-            return null;
-        }
 
-        response.setContentType(getImageMimeType(screenShotFile));
+            response.setContentType(getImageMimeType(screenShotFile));
 
-        OutputStream out = null;
-        BufferedInputStream in = null;
-        try {
             out = response.getOutputStream();
             in = new BufferedInputStream(new FileInputStream(screenShotFile));
             InputStreamUtil.copy(in, out);
             OutputStreamUtil.flush(out);
-        } catch (final IOException e) {
-            throw new IORuntimeException(e);
+        } catch (final Exception e) {
+            logger.error("Failed to response: " + indexForm.docId, e);
         } finally {
             IOUtils.closeQuietly(in);
             IOUtils.closeQuietly(out);

+ 1 - 1
src/main/java/jp/sf/fess/action/MobileAction.java

@@ -144,7 +144,7 @@ public class MobileAction {
         final int pageNum = Integer.parseInt(mobileForm.num);
         // TODO add GeoInfo if needed...
         try {
-            documentItems = searchService.selectList(mobileForm.query, null,
+            documentItems = searchService.getDocumentList(mobileForm.query, null,
                     pageStart, pageNum, null, null);
         } catch (final InvalidQueryException e) {
             if (logger.isDebugEnabled()) {

+ 1 - 1
src/main/java/jp/sf/fess/action/admin/SearchListAction.java

@@ -150,7 +150,7 @@ public class SearchListAction implements Serializable {
         final int offset = Integer.parseInt(searchListForm.start);
         final int size = Integer.parseInt(searchListForm.num);
         try {
-            documentItems = searchService.selectList(query, null, offset, size,
+            documentItems = searchService.getDocumentList(query, null, offset, size,
                     null, null, false);
         } catch (final InvalidQueryException e) {
             if (logger.isDebugEnabled()) {

+ 2 - 2
src/main/java/jp/sf/fess/ds/impl/AbstractDataStoreImpl.java

@@ -83,8 +83,8 @@ public abstract class AbstractDataStoreImpl implements DataStore {
         }
         // segment
         defaultDataMap.put("segment", initParamMap.get(Constants.SESSION_ID));
-        // tstamp
-        defaultDataMap.put("tstamp", "NOW");
+        // created
+        defaultDataMap.put("created", "NOW");
         // boost
         defaultDataMap.put("boost", config.getBoost().toString());
         // type: browserType

+ 39 - 22
src/main/java/jp/sf/fess/ds/impl/IndexUpdateCallbackImpl.java

@@ -21,6 +21,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicLong;
 
+import jp.sf.fess.Constants;
 import jp.sf.fess.FessSystemException;
 import jp.sf.fess.db.bsbhv.BsFavoriteLogBhv;
 import jp.sf.fess.db.cbean.ClickLogCB;
@@ -30,6 +31,7 @@ import jp.sf.fess.db.exbhv.pmbean.FavoriteUrlCountPmb;
 import jp.sf.fess.db.exentity.customize.FavoriteUrlCount;
 import jp.sf.fess.ds.IndexUpdateCallback;
 import jp.sf.fess.helper.CrawlingSessionHelper;
+import jp.sf.fess.helper.SystemHelper;
 
 import org.apache.solr.common.SolrInputDocument;
 import org.codelibs.solr.lib.SolrGroup;
@@ -83,28 +85,7 @@ public class IndexUpdateCallbackImpl implements IndexUpdateCallback {
                 .getComponent(CrawlingSessionHelper.class);
         dataMap.put("id", crawlingSessionHelper.generateId(dataMap));
 
-        final SolrInputDocument doc = new SolrInputDocument();
-        for (final Map.Entry<String, Object> entry : dataMap.entrySet()) {
-            if ("boost".equals(entry.getKey())) {
-                // boost
-                final float documentBoost = Float.valueOf(entry.getValue()
-                        .toString());
-                doc.setDocumentBoost(documentBoost);
-                if (logger.isDebugEnabled()) {
-                    logger.debug("Set a document boost (" + documentBoost
-                            + ").");
-                }
-            }
-            doc.addField(entry.getKey(), entry.getValue());
-        }
-
-        if (clickCountEnabled) {
-            addClickCountField(doc, urlObj.toString());
-        }
-
-        if (favoriteCountEnabled) {
-            addFavoriteCountField(doc, urlObj.toString());
-        }
+        final SolrInputDocument doc = createSolrDocument(dataMap);
 
         docList.add(doc);
         if (logger.isDebugEnabled()) {
@@ -133,6 +114,42 @@ public class IndexUpdateCallbackImpl implements IndexUpdateCallback {
         return true;
     }
 
+    protected SolrInputDocument createSolrDocument(
+            final Map<String, Object> dataMap) {
+        final String url = dataMap.get("url").toString();
+
+        final SolrInputDocument doc = new SolrInputDocument();
+        for (final Map.Entry<String, Object> entry : dataMap.entrySet()) {
+            if ("boost".equals(entry.getKey())) {
+                // boost
+                final float documentBoost = Float.valueOf(entry.getValue()
+                        .toString());
+                doc.setDocumentBoost(documentBoost);
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Set a document boost (" + documentBoost
+                            + ").");
+                }
+            }
+            doc.addField(entry.getKey(), entry.getValue());
+        }
+
+        if (clickCountEnabled) {
+            addClickCountField(doc, url);
+        }
+
+        if (favoriteCountEnabled) {
+            addFavoriteCountField(doc, url);
+        }
+
+        if (!dataMap.containsKey(Constants.DOC_ID)) {
+            final SystemHelper systemHelper = SingletonS2Container
+                    .getComponent(SystemHelper.class);
+            doc.addField(Constants.DOC_ID, systemHelper.generateDocId(dataMap));
+        }
+
+        return doc;
+    }
+
     @Override
     public void commit() {
         if (!docList.isEmpty()) {

+ 2 - 2
src/main/java/jp/sf/fess/form/IndexForm.java

@@ -60,8 +60,8 @@ public class IndexForm implements Serializable {
     public String rt;
 
     @Required(target = "go")
-    @Maxbytelength(maxbytelength = 4000)
-    public String u;
+    @Maxbytelength(maxbytelength = 100)
+    public String docId;
 
     // xml/json
 

+ 11 - 0
src/main/java/jp/sf/fess/helper/SystemHelper.java

@@ -28,6 +28,7 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -116,6 +117,8 @@ public class SystemHelper implements Serializable {
 
     private final AtomicBoolean forceStop = new AtomicBoolean(false);
 
+    private final int maxTextLength = 4000;
+
     @InitMethod
     public void init() {
         final File[] files = ResourceUtil.getJarFiles(launcherFileNamePrefix);
@@ -624,4 +627,12 @@ public class SystemHelper implements Serializable {
         return runningJobExecutorMap.get(id);
     }
 
+    public String generateDocId(final Map<String, Object> map) {
+        return UUID.randomUUID().toString().replace("-", "");
+    }
+
+    public String abbreviateLongText(final String str) {
+        return StringUtils.abbreviate(str, maxTextLength);
+    }
+
 }

+ 1 - 1
src/main/java/jp/sf/fess/helper/UserInfoHelper.java

@@ -25,6 +25,6 @@ public interface UserInfoHelper {
 
     String generateQueryId(String query, List<Map<String, Object>> documentItems);
 
-    String[] getResultUrls(String decode);
+    String[] getResultDocIds(String queryId);
 
 }

+ 24 - 24
src/main/java/jp/sf/fess/helper/impl/CookieUserInfoHelperImpl.java

@@ -20,6 +20,7 @@ import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.UUID;
 
 import javax.annotation.Resource;
 import javax.servlet.http.Cookie;
@@ -33,7 +34,6 @@ import jp.sf.fess.service.UserInfoService;
 
 import org.seasar.framework.container.annotation.tiger.InitMethod;
 import org.seasar.framework.util.StringUtil;
-import org.seasar.framework.util.UUID;
 import org.seasar.robot.util.LruHashMap;
 import org.seasar.struts.util.RequestUtil;
 import org.seasar.struts.util.ResponseUtil;
@@ -45,7 +45,7 @@ public class CookieUserInfoHelperImpl implements UserInfoHelper {
 
     public int userInfoCacheSize = 1000;
 
-    public int resultUrlCacheSize = 10;
+    public int resultDocIdsCacheSize = 20;
 
     public String cookieName = "fsid";
 
@@ -90,7 +90,7 @@ public class CookieUserInfoHelperImpl implements UserInfoHelper {
     }
 
     protected String getId() {
-        return UUID.create();
+        return UUID.randomUUID().toString().replace("-", "");
     }
 
     protected void updateUserSessionId(final String userCode) {
@@ -143,22 +143,20 @@ public class CookieUserInfoHelperImpl implements UserInfoHelper {
             final List<Map<String, Object>> documentItems) {
         final HttpSession session = RequestUtil.getRequest().getSession(false);
         if (session != null) {
-            String queryId = query.hashCode() + session.getId()
-                    + System.currentTimeMillis();
-            queryId = Integer.toString(queryId.hashCode());
+            final String queryId = getId();
 
-            final List<String> urlList = new ArrayList<String>();
+            final List<String> docIdList = new ArrayList<String>();
             for (final Map<String, Object> map : documentItems) {
-                final Object url = map.get("url");
-                if (url != null && url.toString().length() > 0) {
-                    urlList.add(url.toString());
+                final Object docId = map.get(Constants.DOC_ID);
+                if (docId != null && docId.toString().length() > 0) {
+                    docIdList.add(docId.toString());
                 }
             }
 
-            if (!urlList.isEmpty()) {
-                final Map<String, String[]> resultUrlCache = getResultUrlCache(session);
-                resultUrlCache.put(queryId,
-                        urlList.toArray(new String[urlList.size()]));
+            if (!docIdList.isEmpty()) {
+                final Map<String, String[]> resultDocIdsCache = getResultDocIdsCache(session);
+                resultDocIdsCache.put(queryId,
+                        docIdList.toArray(new String[docIdList.size()]));
                 return queryId;
             }
         }
@@ -166,10 +164,10 @@ public class CookieUserInfoHelperImpl implements UserInfoHelper {
     }
 
     @Override
-    public String[] getResultUrls(final String queryId) {
+    public String[] getResultDocIds(final String queryId) {
         final HttpSession session = RequestUtil.getRequest().getSession(false);
         if (session != null) {
-            final Map<String, String[]> resultUrlCache = getResultUrlCache(session);
+            final Map<String, String[]> resultUrlCache = getResultDocIdsCache(session);
             final String[] urls = resultUrlCache.get(queryId);
             if (urls != null) {
                 return urls;
@@ -178,14 +176,16 @@ public class CookieUserInfoHelperImpl implements UserInfoHelper {
         return new String[0];
     }
 
-    private Map<String, String[]> getResultUrlCache(final HttpSession session) {
-        Map<String, String[]> resultUrlCache = (Map<String, String[]>) session
-                .getAttribute(Constants.RESULT_URL_CACHE);
-        if (resultUrlCache == null) {
-            resultUrlCache = new LruHashMap<String, String[]>(
-                    resultUrlCacheSize);
-            session.setAttribute(Constants.RESULT_URL_CACHE, resultUrlCache);
+    private Map<String, String[]> getResultDocIdsCache(final HttpSession session) {
+        @SuppressWarnings("unchecked")
+        Map<String, String[]> resultDocIdsCache = (Map<String, String[]>) session
+                .getAttribute(Constants.RESULT_DOC_ID_CACHE);
+        if (resultDocIdsCache == null) {
+            resultDocIdsCache = new LruHashMap<String, String[]>(
+                    resultDocIdsCacheSize);
+            session.setAttribute(Constants.RESULT_DOC_ID_CACHE,
+                    resultDocIdsCache);
         }
-        return resultUrlCache;
+        return resultDocIdsCache;
     }
 }

+ 7 - 7
src/main/java/jp/sf/fess/helper/impl/QueryHelperImpl.java

@@ -77,16 +77,16 @@ public class QueryHelperImpl implements QueryHelper, Serializable {
     @Resource
     protected RoleQueryHelper roleQueryHelper;
 
-    protected String[] responseFields = new String[] { "id", "score", "boost",
-            "contentLength", "host", "site", "lastModified", "mimetype",
-            "tstamp", "title", "digest", "url", "clickCount_i",
+    protected String[] responseFields = new String[] { "id", "docId", "score",
+            "boost", "contentLength", "host", "site", "lastModified",
+            "mimetype", "created", "title", "digest", "url", "clickCount_i",
             "favoriteCount_i", "screenshot_s_s" };
 
     protected String[] highlightingFields = new String[] { "content" };
 
-    protected String[] searchFields = new String[] { "url", "host", "title",
-            "content", "contentLength", "lastModified", "mimetype", "label",
-            "segment", "clickCount_i", "favoriteCount_i", "inurl" };
+    protected String[] searchFields = new String[] { "url", "docId", "host",
+            "title", "content", "contentLength", "lastModified", "mimetype",
+            "label", "segment", "clickCount_i", "favoriteCount_i", "inurl" };
 
     protected String[] facetFields = new String[] { "url", "host", "title",
             "content", "contentLength", "lastModified", "mimetype", "label",
@@ -94,7 +94,7 @@ public class QueryHelperImpl implements QueryHelper, Serializable {
 
     protected String sortPrefix = "sort:";
 
-    protected String[] supportedSortFields = new String[] { "tstamp",
+    protected String[] supportedSortFields = new String[] { "created",
             "contentLength", "lastModified", "clickCount_i", "favoriteCount_i" };
 
     protected String[] supportedMltFields = new String[] { "content",

+ 2 - 1
src/main/java/jp/sf/fess/job/TriggeredJob.java

@@ -91,7 +91,8 @@ public class TriggeredJob implements Job {
         } catch (final Throwable e) {
             logger.error("Failed to execute " + jobId + ": " + script, e);
             jobLog.setJobStatus(Constants.FAIL);
-            jobLog.setScriptResult(e.getLocalizedMessage());
+            jobLog.setScriptResult(systemHelper.abbreviateLongText(e
+                    .getLocalizedMessage()));
         } finally {
             systemHelper.finishJobExecutoer(id);
             jobLog.setEndTime(new Timestamp(System.currentTimeMillis()));

+ 10 - 11
src/main/java/jp/sf/fess/service/FavoriteLogService.java

@@ -22,6 +22,7 @@ import java.io.Writer;
 import java.sql.Timestamp;
 import java.text.ParseException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
@@ -115,9 +116,10 @@ public class FavoriteLogService extends BsFavoriteLogService implements
         return false;
     }
 
-    public String[] getUrls(final String userCode, final String[] urls) {
-        if (urls.length == 0) {
-            return urls;
+    public List<String> getUrlList(final String userCode,
+            final List<String> urlList) {
+        if (urlList.isEmpty()) {
+            return urlList;
         }
 
         final UserInfoCB cb = new UserInfoCB();
@@ -125,10 +127,6 @@ public class FavoriteLogService extends BsFavoriteLogService implements
         final UserInfo userInfo = userInfoBhv.selectEntity(cb);
 
         if (userInfo != null) {
-            final List<String> urlList = new ArrayList<String>();
-            for (final String url : urls) {
-                urlList.add(url);
-            }
             final FavoriteLogCB cb2 = new FavoriteLogCB();
             cb2.query().setUserId_Equal(userInfo.getId());
             cb2.query().setUrl_InScope(urlList);
@@ -136,15 +134,16 @@ public class FavoriteLogService extends BsFavoriteLogService implements
             final ListResultBean<FavoriteLog> list = favoriteLogBhv
                     .selectList(cb2);
             if (!list.isEmpty()) {
-                urlList.clear();
+                final List<String> newUrlList = new ArrayList<String>(
+                        list.size());
                 for (final FavoriteLog favoriteLog : list) {
-                    urlList.add(favoriteLog.getUrl());
+                    newUrlList.add(favoriteLog.getUrl());
                 }
-                return urlList.toArray(new String[urlList.size()]);
+                return newUrlList;
             }
         }
 
-        return new String[0];
+        return Collections.emptyList();
     }
 
     public void deleteAll(final FavoriteLogPager favoriteLogPager) {

+ 30 - 3
src/main/java/jp/sf/fess/service/SearchService.java

@@ -17,6 +17,7 @@
 package jp.sf.fess.service;
 
 import java.io.Serializable;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -59,13 +60,39 @@ public class SearchService implements Serializable {
     @Resource
     protected QueryHelper queryHelper;
 
-    public List<Map<String, Object>> selectList(final String query,
+    public Map<String, Object> getDocument(final String query) {
+        final List<Map<String, Object>> docList = getDocumentList(query, null,
+                0, 1, null, null);
+        if (!docList.isEmpty()) {
+            return docList.get(0);
+        }
+        return null;
+    }
+
+    public List<Map<String, Object>> getDocumentListByDocIds(
+            final String[] docIds, final int pageSize) {
+        if (docIds == null || docIds.length == 0) {
+            return Collections.emptyList();
+        }
+
+        final StringBuilder buf = new StringBuilder(1000);
+        for (int i = 0; i < docIds.length; i++) {
+            if (i != 0) {
+                buf.append(" OR ");
+            }
+            buf.append("docId:").append(docIds[i]);
+        }
+        return getDocumentList(buf.toString(), null, 0, pageSize, null, null);
+    }
+
+    public List<Map<String, Object>> getDocumentList(final String query,
             final FacetInfo facetInfo, final int start, final int rows,
             final GeoInfo geoInfo, final MoreLikeThisInfo mltInfo) {
-        return selectList(query, facetInfo, start, rows, geoInfo, mltInfo, true);
+        return getDocumentList(query, facetInfo, start, rows, geoInfo, mltInfo,
+                true);
     }
 
-    public List<Map<String, Object>> selectList(final String query,
+    public List<Map<String, Object>> getDocumentList(final String query,
             final FacetInfo facetInfo, final int start, final int rows,
             final GeoInfo geoInfo, final MoreLikeThisInfo mltInfo,
             final boolean forUser) {

+ 4 - 0
src/main/java/jp/sf/fess/solr/IndexUpdater.java

@@ -474,6 +474,10 @@ public class IndexUpdater extends Thread {
             addBoostValue(map, documentBoost, doc);
         }
 
+        if (!map.containsKey(Constants.DOC_ID)) {
+            doc.addField(Constants.DOC_ID, systemHelper.generateDocId(map));
+        }
+
         return doc;
     }
 

+ 2 - 2
src/main/java/jp/sf/fess/transformer/AbstractFessFileTransformer.java

@@ -250,8 +250,8 @@ public abstract class AbstractFessFileTransformer extends
                 getSite(url, responseData.getCharSet()));
         // url
         putResultDataBody(dataMap, "url", url);
-        // tstamp
-        putResultDataBody(dataMap, "tstamp", "NOW");
+        // created
+        putResultDataBody(dataMap, "created", "NOW");
         // TODO anchor
         putResultDataBody(dataMap, "anchor", "");
         // mimetype

+ 2 - 4
src/main/java/jp/sf/fess/transformer/FessXpathTransformer.java

@@ -251,8 +251,8 @@ public class FessXpathTransformer extends AbstractFessXpathTransformer {
                 getSite(url, responseData.getCharSet()));
         // url
         putResultDataBody(dataMap, "url", url);
-        // tstamp
-        putResultDataBody(dataMap, "tstamp", "NOW");
+        // created
+        putResultDataBody(dataMap, "created", "NOW");
         // anchor
         putResultDataBody(dataMap, "anchor",
                 getAnchorList(document, responseData));
@@ -290,8 +290,6 @@ public class FessXpathTransformer extends AbstractFessXpathTransformer {
             roleTypeList.add(roleType);
         }
         putResultDataBody(dataMap, "role", roleTypeList);
-        // TODO date
-        // TODO lang
         // id
         putResultDataBody(dataMap, "id",
                 crawlingSessionHelper.generateId(dataMap));

+ 4 - 4
src/main/resources/app.dicon

@@ -57,19 +57,19 @@
 		</initMethod>
 		<!-- 
 		<property name="additionalGeoQuery">"location_i_i:1"</property>
-		<property name="responseFields">new String[]{"id", "score", "boost",
+		<property name="responseFields">new String[]{"id", "docId", "score", "boost",
             "contentLength", "host", "site", "lastModified", "mimetype",
-            "tstamp", "title", "digest", "url", "clickCount_i", "favoriteCount_i",
+            "created", "title", "digest", "url", "clickCount_i", "favoriteCount_i",
             "screenshot_s_s"}</property>
 		<property name="highlightingFields">new String[]{"digest", "cache" }</property>
-		<property name="searchFields">new String[]{"url", "host", 
+		<property name="searchFields">new String[]{"url", "docId", "host", 
             "title", "content", "contentLength", "lastModified", "mimetype",
             "label", "segment" }</property>
 		<property name="facetFields">new String[]{"url", "host", 
             "title", "content", "contentLength", "lastModified", "mimetype",
             "label", "segment" }</property>
 		<property name="sortPrefix">"sort:"</property>
-		<property name="supportedSortFields">new String[]{"tstamp",
+		<property name="supportedSortFields">new String[]{"created",
             "contentLength", "lastModified", "clickCount_i" }</property>
 		<property name="supportedMltFields">new String[]{"content",
             "content_ja" }</property>

+ 5 - 3
src/main/resources/application.properties

@@ -66,6 +66,8 @@ errors.failed_to_delete_file=Failed to delete {0} file.
 errors.failed_to_redirect=Failed to redirect {0}.
 errors.no_launcher_applet_jar=No launcher file.
 errors.unsupported_encoding={0} is not supported as encoding.
+errors.docid_not_found=Not found Doc ID:{0}
+errors.document_not_found=Not found URL of Doc ID:{0}
 
 errors.invalid_query_unknown=The given query is invalid.
 errors.invalid_query_quoted=An invalid quote character is used.
@@ -331,14 +333,14 @@ labels.search_title=Fess
 labels.search_hot_search_word=Hot Keywords: 
 labels.search_result_select_sort=-- Sort --
 labels.search_result_select_num=-- Results per page --
-labels.search_result_sort_tstamp_asc=Date (ascending)
-labels.search_result_sort_tstamp_desc=Date (descending)
+labels.search_result_sort_created_asc=Date (ascending)
+labels.search_result_sort_created_desc=Date (descending)
 labels.search_result_sort_contentLength_asc=Size (ascending)
 labels.search_result_sort_contentLength_desc=Size (descending)
 labels.search_result_sort_lastModified_asc=Last Modified (ascending)
 labels.search_result_sort_lastModified_desc=Last Modified (descending)
 labels.search_result_size={0} bytes
-labels.search_result_tstamp=Registered: 
+labels.search_result_created=Registered: 
 labels.search_result_lastModified=Last Modified: 
 labels.search_result_favorite=Vote it
 labels.search_result_favorited=Voted

+ 5 - 3
src/main/resources/application_ja.properties

@@ -66,6 +66,8 @@ errors.failed_to_delete_file=\u30d5\u30a1\u30a4\u30eb {0} \u306e\u524a\u9664\u30
 errors.failed_to_redirect={0}\u3078\u306e\u30ea\u30c0\u30a4\u30ec\u30af\u30c8\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002
 errors.no_launcher_applet_jar=\u8d77\u52d5\u30d5\u30a1\u30a4\u30eb\u304c\u3042\u308a\u307e\u305b\u3093\u3002
 errors.unsupported_encoding={0}\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u306a\u3044\u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0\u3067\u3059\u3002
+errors.docid_not_found=ID:{0}\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002
+errors.document_not_found=ID:{0}\u306eURL\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002
 
 errors.invalid_query_unknown=\u691c\u7d22\u30af\u30a8\u30ea\u304c\u6b63\u3057\u304f\u3042\u308a\u307e\u305b\u3093\u3002
 errors.invalid_query_quoted=\u30af\u30aa\u30fc\u30c8\u6587\u5b57(")\u306e\u5229\u7528\u65b9\u6cd5\u304c\u6b63\u3057\u304f\u3042\u308a\u307e\u305b\u3093\u3002
@@ -331,14 +333,14 @@ labels.search_title=Fess
 labels.search_hot_search_word=\u6ce8\u76ee\u30ad\u30fc\u30ef\u30fc\u30c9: 
 labels.search_result_select_sort=-- \u30bd\u30fc\u30c8 --
 labels.search_result_select_num=-- \u8868\u793a\u4ef6\u6570 --
-labels.search_result_sort_tstamp_asc=\u767b\u9332\u65e5\u6642 (\u6607\u9806)
-labels.search_result_sort_tstamp_desc=\u767b\u9332\u65e5\u6642 (\u964d\u9806)
+labels.search_result_sort_created_asc=\u767b\u9332\u65e5\u6642 (\u6607\u9806)
+labels.search_result_sort_created_desc=\u767b\u9332\u65e5\u6642 (\u964d\u9806)
 labels.search_result_sort_contentLength_asc=\u30b5\u30a4\u30ba (\u6607\u9806)
 labels.search_result_sort_contentLength_desc=\u30b5\u30a4\u30ba (\u964d\u9806)
 labels.search_result_sort_lastModified_asc=\u66f4\u65b0\u65e5\u6642 (\u6607\u9806)
 labels.search_result_sort_lastModified_desc=\u66f4\u65b0\u65e5\u6642 (\u964d\u9806)
 labels.search_result_size={0} \u30d0\u30a4\u30c8
-labels.search_result_tstamp=\u767b\u9332\u65e5\u6642: 
+labels.search_result_created=\u767b\u9332\u65e5\u6642: 
 labels.search_result_lastModified=\u66f4\u65b0\u65e5\u6642: 
 labels.search_result_favorite=1\u7968\u5165\u308c\u308b
 labels.search_result_favorited=\u6295\u7968\u6e08\u307f

+ 1 - 1
src/main/webapp/WEB-INF/view/common/help.jsp

@@ -18,7 +18,7 @@
 		desc. If you want to find documents which has "Fess" and sort them in
 		descending order, you can enter:
 		<pre>Fess sort:contentLength.desc</pre>
-		The available sort field are "tstamp", "contentLength" and
+		The available sort field are "created", "contentLength" and
 		"lastModified", and they are customizable.
 	</dd>
 	<dt>AND</dt>

+ 1 - 1
src/main/webapp/WEB-INF/view/common/help_ja.jsp

@@ -17,7 +17,7 @@
 		&lt;order&gt; は asc または desc で昇順・降順を指定できます。
 		たとえば、Fess を含むドキュメントでサイズの降順にソートしたい場合、次のように入力します。
 		<pre>Fess sort:contentLength.desc</pre>
-		標準で利用可能なフィールドは tstamp, contentLength および lastModified になります。
+		標準で利用可能なフィールドは created, contentLength および lastModified になります。
 		設定で指定するフィールドは変更することができます。
 	</dd>
 	<dt>AND検索</dt>

+ 4 - 4
src/main/webapp/WEB-INF/view/error/header.jsp

@@ -59,11 +59,11 @@
 							<option value="">
 								<bean:message key="labels.search_result_select_sort" />
 							</option>
-							<html:option value="tstamp.asc">
-								<bean:message key="labels.search_result_sort_tstamp_asc" />
+							<html:option value="created.asc">
+								<bean:message key="labels.search_result_sort_created_asc" />
 							</html:option>
-							<html:option value="tstamp.desc">
-								<bean:message key="labels.search_result_sort_tstamp_desc" />
+							<html:option value="created.desc">
+								<bean:message key="labels.search_result_sort_created_desc" />
 							</html:option>
 							<html:option value="contentLength.asc">
 								<bean:message key="labels.search_result_sort_contentLength_asc" />

+ 4 - 4
src/main/webapp/WEB-INF/view/header.jsp

@@ -63,11 +63,11 @@ ${fe:facetForm()}${fe:mltForm()}${fe:geoForm()}
 							<option value="">
 								<bean:message key="labels.search_result_select_sort" />
 							</option>
-							<html:option value="tstamp.asc">
-								<bean:message key="labels.search_result_sort_tstamp_asc" />
+							<html:option value="created.asc">
+								<bean:message key="labels.search_result_sort_created_asc" />
 							</html:option>
-							<html:option value="tstamp.desc">
-								<bean:message key="labels.search_result_sort_tstamp_desc" />
+							<html:option value="created.desc">
+								<bean:message key="labels.search_result_sort_created_desc" />
 							</html:option>
 							<html:option value="contentLength.asc">
 								<bean:message key="labels.search_result_sort_contentLength_asc" />

+ 4 - 4
src/main/webapp/WEB-INF/view/index.jsp

@@ -115,11 +115,11 @@
 											<option value="">
 												<bean:message key="labels.search_result_select_sort" />
 											</option>
-											<html:option value="tstamp.asc">
-												<bean:message key="labels.search_result_sort_tstamp_asc" />
+											<html:option value="created.asc">
+												<bean:message key="labels.search_result_sort_created_asc" />
 											</html:option>
-											<html:option value="tstamp.desc">
-												<bean:message key="labels.search_result_sort_tstamp_desc" />
+											<html:option value="created.desc">
+												<bean:message key="labels.search_result_sort_created_desc" />
 											</html:option>
 											<html:option value="contentLength.asc">
 												<bean:message

+ 7 - 8
src/main/webapp/WEB-INF/view/searchResults.jsp

@@ -22,16 +22,15 @@
 	</div>
 </c:if>
 <div id="result" class="row content">
-	<input type="hidden" id="queryId" value="${f:u(queryId)}" /> <input
-		type="hidden" id="contextPath" value="<%=request.getContextPath()%>" />
+	<input type="hidden" id="queryId" value="${f:u(queryId)}" />
+	<input type="hidden" id="contextPath" value="<%=request.getContextPath()%>" />
 	<input type="hidden" id="rt" value="${f:u(rt)}" />
 	<div class="span8">
 		<ol>
 			<c:forEach var="doc" varStatus="s" items="${documentItems}">
 				<li id="result${s.index}">
 					<h3 class="title">
-						<a href="${doc.urlLink}" class="link">
-							${f:h(doc.contentTitle)} </a>
+						<a class="link" href="${doc.urlLink}" data-uri="${doc.urlLink}" data-id="${doc.docId}">${f:h(doc.contentTitle)}</a>
 					</h3>
 					<div class="body">
 						<div class="description">${doc.contentDescription}</div>
@@ -42,9 +41,9 @@
 							<a href="#result${s.index}"><bean:message key="labels.search_result_more" /></a>
 						</div>
 						<div class="info">
-							<c:if test="${doc.tstamp!=null && doc.tstamp!=''}">
-								<bean:message key="labels.search_result_tstamp" />
-								<fmt:formatDate value="${fe:parseDate(doc.tstamp)}" type="BOTH" />
+							<c:if test="${doc.created!=null && doc.created!=''}">
+								<bean:message key="labels.search_result_created" />
+								<fmt:formatDate value="${fe:parseDate(doc.created)}" type="BOTH" />
 								<span class="br-phone"></span>
 								<span class="hidden-phone">-</span>
 							</c:if>
@@ -62,7 +61,7 @@
 								<span class="hidden-phone">-</span>
 							</c:if>
 							<c:if test="${favoriteSupport}">
-								<a href="#${doc.url}" class="favorite"><bean:message
+								<a href="#${doc.docId}" class="favorite"><bean:message
 										key="labels.search_result_favorite" /></a>
 								<span class="favorited"><bean:message
 										key="labels.search_result_favorited" /></span>

+ 19 - 20
src/main/webapp/js/search.js

@@ -21,32 +21,31 @@ $(function(){
 	});
 
 	$result.on('mousedown', 'a.link', function(e){
-		var url = $(this).attr('href'),
+		var docId = $(this).attr('data-id'),
 			rt = $('#rt').val(),
 			buf = [];
 		buf.push('go?rt=');
 		buf.push(rt);
-		buf.push('&u=');
-		buf.push(encodeURIComponent(url));
+		buf.push('&docId=');
+		buf.push(docId);
 		$(this).attr('href', buf.join(''));
 	});
 
 	$result.on('mouseover', 'a.link', function(e){
 		if($screenshot.size() > 0) {
-			var url = $(this).attr('href'),
+			var docId = $(this).attr('data-id'),
 				rt = $('#rt').val(),
 				queryId = $queryId.val(),
 				buf = [];
 			buf.push('go?rt=');
 			buf.push(rt);
-			buf.push('&u=');
-			buf.push(encodeURIComponent(url));
+			buf.push('&docId=');
+			buf.push(docId);
 
 			$screenshot.children().remove();
 			
 			var content = '<a href="' + buf.join('') + '"><img src="screenshot?queryId='
-				+ queryId + '&u=' + encodeURIComponent(url)
-				+ '"></a>'
+				+ queryId + '&docId=' + docId + '"></a>'
 			$screenshot.append(content);
 			$('img', $screenshot).error(function() {
 				$screenshot.children().remove();
@@ -60,7 +59,7 @@ $(function(){
 		if(values.length === 2 && $queryId.size() > 0){
 			var contextPath = $('#contextPath').val();
 			var actionUrl = contextPath + '/favorite';
-			var favoriteUrl = values[1];
+			var docId = values[1];
 			$.ajax({
 				dataType: 'json',
 				cache: false,
@@ -68,18 +67,18 @@ $(function(){
 				timeoutNumber: 10000,
 				url: actionUrl,
 				data: {
-					u: favoriteUrl,
+					docId: docId,
 					queryId: $queryId.val()
 					}
 			}).done(function ( data ) {
 				if(data.response.status === 0 
 					&& typeof data.response.result !== 'undefined'
-					&& data.response.result == 'ok'){
+					&& data.response.result === 'ok'){
 					var $favorited = $favorite.siblings('.favorited');
 					$favorite.fadeOut(1000, function(){$favorited.fadeIn(1000)});
 				}
 			}).fail(function ( data ) {
-				$favorite.attr('href', '#' + favoriteUrl);
+				$favorite.attr('href', '#' + docId);
 //alert(JSON.stringify(data));
 			});
 		}
@@ -95,23 +94,23 @@ $(function(){
 			type: 'post',
 			timeoutNumber: 10000,
 			url: contextPath + '/favorites',
-				data: {
-						queryId: $queryId.val()
-				}
+			data: {
+				queryId: $queryId.val()
+			}
 		}).done(function ( data ) {
 			if(data.response.status === 0 
 				&& typeof data.response.num !== 'undefined'
 				&& data.response.num > 0){
-				var urls = data.response.urls;
-				for(var i = 0; i < urls.length; i++) {
-					urls[i] = '#' + urls[i];
+				var docIds = data.response.docIds;
+				for(var i = 0; i < docIds.length; i++) {
+					docIds[i] = '#' + docIds[i];
 				}
 				$favorites.each(function(index) {
 					var $favorite = $(this);
 					var url = $favorite.attr('href');
 					var found = false;
-					for(var i = 0; i< urls.length; i++) {
-						if(url == urls[i]) {
+					for(var i = 0; i< docIds.length; i++) {
+						if(url == docIds[i]) {
 							found = true;
 							break;
 						}