diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml
index 630c62ff45..39bae9778e 100644
--- a/config/alfresco/core-services-context.xml
+++ b/config/alfresco/core-services-context.xml
@@ -1408,6 +1408,20 @@
+
+
+
+
+
+
+ search.suggesterService
+
+
+
+ org.alfresco.service.cmr.search.SuggesterService
+
+
+
diff --git a/config/alfresco/subsystems/Search/solr/solr-search-context.xml b/config/alfresco/subsystems/Search/solr/solr-search-context.xml
index 400d816552..1717ee07e7 100644
--- a/config/alfresco/subsystems/Search/solr/solr-search-context.xml
+++ b/config/alfresco/subsystems/Search/solr/solr-search-context.xml
@@ -299,5 +299,14 @@
-
+
+
+
+ ${solr.suggester.enabled}
+
+
+
+
+
+
diff --git a/config/alfresco/subsystems/Search/solr/solr-search.properties b/config/alfresco/subsystems/Search/solr/solr-search.properties
index 9c0fbae172..815fd4204f 100644
--- a/config/alfresco/subsystems/Search/solr/solr-search.properties
+++ b/config/alfresco/subsystems/Search/solr/solr-search.properties
@@ -4,3 +4,7 @@ solr.port.ssl=8443
solr.query.includeGroupsForRoleAdmin=false
solr.query.maximumResultsFromUnlimitedQuery=${system.acl.maxPermissionChecks}
solr.baseUrl=/solr
+#
+# Solr Suggester properties
+#
+solr.suggester.enabled=true
\ No newline at end of file
diff --git a/config/alfresco/subsystems/Search/solr4/solr-search-context.xml b/config/alfresco/subsystems/Search/solr4/solr-search-context.xml
index b90325f37f..90d2d832c4 100644
--- a/config/alfresco/subsystems/Search/solr4/solr-search-context.xml
+++ b/config/alfresco/subsystems/Search/solr4/solr-search-context.xml
@@ -298,6 +298,15 @@
-
-
+
+
+
+
+ ${solr.suggester.enabled}
+
+
+
+
+
+
diff --git a/config/alfresco/subsystems/Search/solr4/solr-search.properties b/config/alfresco/subsystems/Search/solr4/solr-search.properties
index 8526fabf6d..9c9a799611 100644
--- a/config/alfresco/subsystems/Search/solr4/solr-search.properties
+++ b/config/alfresco/subsystems/Search/solr4/solr-search.properties
@@ -4,3 +4,7 @@ solr.port.ssl=8446
solr.query.includeGroupsForRoleAdmin=false
solr.query.maximumResultsFromUnlimitedQuery=${system.acl.maxPermissionChecks}
solr.baseUrl=/solr4
+#
+# Solr Suggester properties
+#
+solr.suggester.enabled=true
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/search/impl/lucene/SolrSuggesterResult.java b/source/java/org/alfresco/repo/search/impl/lucene/SolrSuggesterResult.java
new file mode 100644
index 0000000000..ba904c4589
--- /dev/null
+++ b/source/java/org/alfresco/repo/search/impl/lucene/SolrSuggesterResult.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2005-2014 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+package org.alfresco.repo.search.impl.lucene;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.alfresco.service.cmr.search.SuggesterResult;
+import org.alfresco.util.Pair;
+import org.alfresco.util.ParameterCheck;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * @author Jamal Kaabi-Mofrad
+ * @since 5.0
+ */
+public class SolrSuggesterResult implements SuggesterResult
+{
+ private static final Log logger = LogFactory.getLog(SolrSuggesterResult.class);
+
+ private Long numberFound;
+ private List> suggestions = new ArrayList<>();
+
+ public SolrSuggesterResult()
+ {
+ }
+
+ public SolrSuggesterResult(JSONObject jsonObject)
+ {
+ try
+ {
+ processJson(jsonObject);
+ }
+ catch (Exception e)
+ {
+ logger.info(e.getMessage());
+ }
+ }
+
+ /**
+ * Parses the json returned from the suggester
+ *
+ * @param json the JSON object
+ * @throws JSONException
+ */
+ @SuppressWarnings("rawtypes")
+ protected void processJson(JSONObject json) throws JSONException
+ {
+ ParameterCheck.mandatory("json", json);
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Suggester JSON response: " + json);
+ }
+
+ JSONObject suggest = json.getJSONObject("suggest");
+ for (Iterator suggestIterator = suggest.keys(); suggestIterator.hasNext(); /**/)
+ {
+ String dictionary = (String) suggestIterator.next();
+
+ JSONObject dictionaryJsonObject = suggest.getJSONObject(dictionary);
+ for (Iterator dicIterator = dictionaryJsonObject.keys(); dicIterator.hasNext(); /**/)
+ {
+ String termStr = (String) dicIterator.next();
+
+ JSONObject termJsonObject = dictionaryJsonObject.getJSONObject(termStr);
+ // number found
+ this.numberFound = termJsonObject.getLong("numFound");
+
+ // the suggested terms
+ JSONArray suggestion = termJsonObject.getJSONArray("suggestions");
+ for (int i = 0, length = suggestion.length(); i < length; i++)
+ {
+ JSONObject data = suggestion.getJSONObject(i);
+ this.suggestions.add(new Pair(data.getString("term"), data.getInt("weight")));
+ }
+ }
+ }
+ }
+
+ @Override
+ public long getNumberFound()
+ {
+ return this.numberFound;
+ }
+
+ @Override
+ public List> getSuggestions()
+ {
+ return this.suggestions;
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder builder = new StringBuilder(250);
+ builder.append("SolrSuggesterResult [numberFound=").append(this.numberFound).append(", suggestions=")
+ .append(this.suggestions).append("]");
+ return builder.toString();
+ }
+}
diff --git a/source/java/org/alfresco/repo/search/impl/solr/SolrAdminHTTPClient.java b/source/java/org/alfresco/repo/search/impl/solr/SolrAdminHTTPClient.java
index 30e6557ff1..6b3a650368 100644
--- a/source/java/org/alfresco/repo/search/impl/solr/SolrAdminHTTPClient.java
+++ b/source/java/org/alfresco/repo/search/impl/solr/SolrAdminHTTPClient.java
@@ -106,19 +106,28 @@ public class SolrAdminHTTPClient
this.httpClientFactory = httpClientFactory;
}
- public JSONObject execute(HashMapargs)
- {
+ public JSONObject execute(HashMap args)
+ {
+ return execute(adminUrl, args);
+ }
+
+ public JSONObject execute(String relativeHandlerPath, HashMap args)
+ {
+ ParameterCheck.mandatory("relativeHandlerPath", relativeHandlerPath);
+ ParameterCheck.mandatory("args", args);
+
+ String path = getPath(relativeHandlerPath);
try
- {
+ {
URLCodec encoder = new URLCodec();
StringBuilder url = new StringBuilder();
-
- for(String key : args.keySet())
+
+ for (String key : args.keySet())
{
String value = args.get(key);
- if(url.length() == 0)
+ if (url.length() == 0)
{
- url.append(adminUrl);
+ url.append(path);
url.append("?");
url.append(encoder.encode(key, "UTF-8"));
url.append("=");
@@ -129,27 +138,27 @@ public class SolrAdminHTTPClient
url.append("&");
url.append(encoder.encode(key, "UTF-8"));
url.append("=");
- url.append(encoder.encode(value, "UTF-8"));
+ url.append(encoder.encode(value, "UTF-8"));
}
-
+
}
-
- //PostMethod post = new PostMethod(url.toString());
+
+ // PostMethod post = new PostMethod(url.toString());
GetMethod get = new GetMethod(url.toString());
-
+
try
{
httpClient.executeMethod(get);
- if(get.getStatusCode() == HttpStatus.SC_MOVED_PERMANENTLY || get.getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY)
+ if (get.getStatusCode() == HttpStatus.SC_MOVED_PERMANENTLY || get.getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY)
{
- Header locationHeader = get.getResponseHeader("location");
- if (locationHeader != null)
- {
- String redirectLocation = locationHeader.getValue();
- get.setURI(new URI(redirectLocation, true));
- httpClient.executeMethod(get);
- }
+ Header locationHeader = get.getResponseHeader("location");
+ if (locationHeader != null)
+ {
+ String redirectLocation = locationHeader.getValue();
+ get.setURI(new URI(redirectLocation, true));
+ httpClient.executeMethod(get);
+ }
}
if (get.getStatusCode() != HttpServletResponse.SC_OK)
@@ -185,4 +194,20 @@ public class SolrAdminHTTPClient
}
}
+ private String getPath(String path)
+ {
+ if (path.startsWith(baseUrl))
+ {
+ return path;
+ }
+ else if (path.startsWith("/"))
+ {
+ return baseUrl + path;
+ }
+ else
+ {
+ return baseUrl + '/' + path;
+ }
+ }
+
}
diff --git a/source/java/org/alfresco/repo/search/impl/solr/SolrSuggesterServiceImpl.java b/source/java/org/alfresco/repo/search/impl/solr/SolrSuggesterServiceImpl.java
new file mode 100644
index 0000000000..c5ef224992
--- /dev/null
+++ b/source/java/org/alfresco/repo/search/impl/solr/SolrSuggesterServiceImpl.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2005-2014 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+package org.alfresco.repo.search.impl.solr;
+
+import java.util.HashMap;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.repo.search.impl.lucene.SolrSuggesterResult;
+import org.alfresco.service.cmr.search.SuggesterResult;
+import org.alfresco.service.cmr.search.SuggesterService;
+import org.json.JSONObject;
+
+/**
+ * Solr Suggester Service Implementation.
+ *
+ * @author Jamal Kaabi-Mofrad
+ * @since 5.0
+ */
+public class SolrSuggesterServiceImpl implements SuggesterService
+{
+
+ public static final String SUGGESER_PATH = "/alfresco/suggest";
+
+ private boolean enabled;
+ private SolrAdminHTTPClient solrAdminHTTPClient;
+
+ public void setEnabled(boolean isEnabled)
+ {
+ this.enabled = isEnabled;
+ }
+
+ public void setSolrAdminHTTPClient(SolrAdminHTTPClient solrAdminHTTPClient)
+ {
+ this.solrAdminHTTPClient = solrAdminHTTPClient;
+ }
+
+ @Override
+ public boolean isEnabled()
+ {
+ return this.enabled;
+ }
+
+ @Override
+ public SuggesterResult getSuggestions(String term, int limit)
+ {
+ // if it is not enabled, return an empty result set
+ if (!enabled)
+ {
+ return new SolrSuggesterResult();
+ }
+ try
+ {
+ HashMap params = new HashMap<>(3);
+ params.put("q", term);
+ if (limit > 0)
+ {
+ params.put("suggest.count", Integer.toString(limit));
+ }
+ params.put("wt", "json");
+
+ JSONObject response = solrAdminHTTPClient.execute(SUGGESER_PATH, params);
+ return new SolrSuggesterResult(response);
+ }
+ catch (Exception e)
+ {
+ throw new AlfrescoRuntimeException("SolrSuggester failed.", e);
+ }
+ }
+
+}
diff --git a/source/java/org/alfresco/service/cmr/search/SuggesterResult.java b/source/java/org/alfresco/service/cmr/search/SuggesterResult.java
new file mode 100644
index 0000000000..2627caaeed
--- /dev/null
+++ b/source/java/org/alfresco/service/cmr/search/SuggesterResult.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2005-2014 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+package org.alfresco.service.cmr.search;
+
+import java.util.List;
+
+import org.alfresco.util.Pair;
+
+/**
+ * Term suggestions response object
+ *
+ * @author Jamal Kaabi-Mofrad
+ * @since 5.0
+ */
+public interface SuggesterResult
+{
+
+ /**
+ * Get the number of suggestions
+ *
+ * @return
+ */
+ long getNumberFound();
+
+ /**
+ * Get the list of suggestions as ("term", "weight") pairs. Never null.
+ *
+ * @return list of suggestions
+ */
+ List> getSuggestions();
+}
diff --git a/source/java/org/alfresco/service/cmr/search/SuggesterService.java b/source/java/org/alfresco/service/cmr/search/SuggesterService.java
new file mode 100644
index 0000000000..99d8bfc783
--- /dev/null
+++ b/source/java/org/alfresco/service/cmr/search/SuggesterService.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2005-2014 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+package org.alfresco.service.cmr.search;
+
+/**
+ * A service that returns term suggestions
+ *
+ * @author Jamal Kaabi-Mofrad
+ * @since 5.0
+ */
+public interface SuggesterService
+{
+
+ /**
+ * Whether the Suggester is enabled (refer to 'solr.suggester.enabled' repository property) or not
+ *
+ * @return true if the Suggester is enabled, false otherwise
+ */
+ public boolean isEnabled();
+
+ /**
+ * Get suggestions for the specified term
+ *
+ * @param term the term to use for the search
+ * @param limit the number of suggestions for Solr to return
+ * @return term suggestions result. Never null
+ */
+ public SuggesterResult getSuggestions(String term, int limit);
+}