diff --git a/defaults/yacy.init b/defaults/yacy.init index 92367510b..26bae7040 100644 --- a/defaults/yacy.init +++ b/defaults/yacy.init @@ -932,10 +932,12 @@ search.result.show.ranking = false # search navigators: comma-separated list of default values for search navigation. -# can be temporary different if search string is given with differen navigation values +# By default navigators keys are sorted by descending counts. To sort by ascending displayed labels, add the :label suffix (example : hosts:label). +# The sort direction can also be specified with the :asc or :desc suffixes (example : hosts:label:desc) +# can be temporary different if search string is given with different navigation values # assigning no value(s) means that no navigation is shown search.navigation=location,hosts,authors,namespace,topics,filetype,protocol,language -#search.navigation=location,hosts,authors,namespace,topics,filetype,protocol,language,collections,date,year,year:dates_in_content_dts:Event +#search.navigation=location,hosts:label,authors,namespace,topics,filetype,protocol,language,collections,date,year,year:dates_in_content_dts:Event # max number of items displayed in search navigators search.navigation.maxcount=100 diff --git a/htroot/ConfigSearchPage_p.html b/htroot/ConfigSearchPage_p.html index 05178410e..4d591002a 100644 --- a/htroot/ConfigSearchPage_p.html +++ b/htroot/ConfigSearchPage_p.html @@ -139,16 +139,36 @@ #{search.navigation.plugin}# - + + + #{/search.navigation.plugin}# diff --git a/htroot/ConfigSearchPage_p.java b/htroot/ConfigSearchPage_p.java index 159cb6d1c..d3d0716b8 100644 --- a/htroot/ConfigSearchPage_p.java +++ b/htroot/ConfigSearchPage_p.java @@ -30,8 +30,10 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.sql.Date; +import java.util.HashSet; import java.util.Map; import java.util.Properties; +import java.util.Set; import net.yacy.cora.date.GenericFormatter; import net.yacy.cora.protocol.RequestHeader; @@ -42,7 +44,11 @@ import net.yacy.search.Switchboard; import net.yacy.search.SwitchboardConstants; import net.yacy.search.navigator.Navigator; import net.yacy.search.navigator.NavigatorPlugins; +import net.yacy.search.navigator.NavigatorSort; +import net.yacy.search.navigator.NavigatorSortDirection; +import net.yacy.search.navigator.NavigatorSortType; import net.yacy.search.query.QueryParams; +import net.yacy.search.query.SearchEventCache; import net.yacy.server.serverObjects; import net.yacy.server.serverSwitch; @@ -56,6 +62,10 @@ public class ConfigSearchPage_p { /* Check this is a valid transaction */ TransactionManager.checkPostTransaction(header, post); + final int initialNavMaxCOunt = sb.getConfigInt( + SwitchboardConstants.SEARCH_NAVIGATION_MAXCOUNT, QueryParams.FACETS_STANDARD_MAXCOUNT_DEFAULT); + final String initialNavConf = sb.getConfig("search.navigation", ""); + if (post.containsKey("searchpage_set")) { final String newGreeting = post.get(SwitchboardConstants.GREETING, ""); // store this call as api call @@ -96,24 +106,33 @@ public class ConfigSearchPage_p { sb.setConfig("search.result.show.snapshots", post.getBoolean("search.result.show.snapshots")); // construct navigation String - String nav = ""; - if (post.getBoolean("search.navigation.location")) nav += "location,"; - // if (post.getBoolean("search.navigation.filetype")) nav += "filetype,"; - if (post.getBoolean("search.navigation.protocol")) nav += "protocol,"; - // if (post.getBoolean("search.navigation.hosts")) nav += "hosts,"; - // if (post.getBoolean("search.navigation.language")) nav += "language,"; - // if (post.getBoolean("search.navigation.authors")) nav += "authors,"; - // if (post.getBoolean("search.navigation.collections")) nav += "collections,"; - // if (post.getBoolean("search.navigation.namespace")) nav += "namespace,"; - if (post.getBoolean("search.navigation.topics")) nav += "topics,"; - if (post.getBoolean("search.navigation.date")) nav += "date,"; + Set navConfigs = new HashSet<>(); + if (post.getBoolean("search.navigation.location")) { + navConfigs.add("location"); + } + if (post.getBoolean("search.navigation.protocol")) { + navConfigs.add("protocol"); + } + if (post.getBoolean("search.navigation.topics")) { + navConfigs.add("topics"); + } + if (post.getBoolean("search.navigation.date")) { + navConfigs.add("date"); + } // append active navigator plugins - String[] navplugins = post.getAll("search.navigation.active"); - for (String navname:navplugins) { - nav += navname + ","; + String[] activeNavNames = post.getAll("search.navigation.active"); + for (final String navName : activeNavNames) { + String navConfig = navName; + final String navSortConfig = post.get("search.navigation." + navName + ".navSort"); + final NavigatorSort defaultSort = NavigatorPlugins.getDefaultSort(navName); + if (NavigatorPlugins.parseNavSortConfig( + navName + NavigatorPlugins.NAV_PROPS_CONFIG_SEPARATOR + navSortConfig, + defaultSort) != defaultSort) { + navConfig += NavigatorPlugins.NAV_PROPS_CONFIG_SEPARATOR + navSortConfig; + } + navConfigs.add(navConfig); } - if (nav.endsWith(",")) nav = nav.substring(0, nav.length() - 1); - sb.setConfig("search.navigation", nav); + sb.setConfig("search.navigation", navConfigs); // maxcount nav entries, default int navmaxcnt = post.getInt(SwitchboardConstants.SEARCH_NAVIGATION_MAXCOUNT, QueryParams.FACETS_STANDARD_MAXCOUNT_DEFAULT); if (navmaxcnt > 5) { @@ -131,16 +150,15 @@ public class ConfigSearchPage_p { if (post.containsKey("add.nav")) { // button: add navigator plugin to ative list String navname = post.get("search.navigation.navname"); if (navname != null && !navname.isEmpty()) { - String naviconf = sb.getConfig("search.navigation", ""); - naviconf += "," + navname; - sb.setConfig("search.navigation", naviconf); + final Set navConfigs = sb.getConfigSet("search.navigation"); + navConfigs.add(navname); + sb.setConfig("search.navigation", navConfigs); } } else if (post.containsKey("del.nav")) { // button: delete navigator plugin from active list - String navname = post.get("del.nav"); - String naviconf = sb.getConfig("search.navigation", ""); - naviconf = naviconf.replace(navname, ""); - naviconf = naviconf.replace(",,", ","); - sb.setConfig("search.navigation", naviconf); + final String navToDelete = post.get("del.nav"); + final Set navConfigs = sb.getConfigSet("search.navigation"); + navConfigs.removeIf(navConfig -> NavigatorPlugins.getNavName(navConfig).equals(navToDelete)); + sb.setConfig("search.navigation", navConfigs); } if (post.containsKey("searchpage_default")) { @@ -203,6 +221,12 @@ public class ConfigSearchPage_p { config.getProperty(SwitchboardConstants.SEARCH_NAVIGATION_DATES_MAXCOUNT, String.valueOf(QueryParams.FACETS_DATE_MAXCOUNT_DEFAULT))); } + + if(!initialNavConf.equals(sb.getConfig("search.navigation", "")) || initialNavMaxCOunt != sb.getConfigInt( + SwitchboardConstants.SEARCH_NAVIGATION_MAXCOUNT, QueryParams.FACETS_STANDARD_MAXCOUNT_DEFAULT)) { + /* Clean up search events cache when necessary */ + SearchEventCache.cleanupEvents(true); + } } /* Acquire a transaction token for the next POST form submission */ @@ -252,24 +276,54 @@ public class ConfigSearchPage_p { prop.put("search.result.show.snapshots", sb.getConfigBool("search.result.show.snapshots", false) ? 1 : 0); prop.put("search.result.show.ranking", sb.getConfigBool(SwitchboardConstants.SEARCH_RESULT_SHOW_RANKING, SwitchboardConstants.SEARCH_RESULT_SHOW_RANKING_DEFAULT) ? 1 : 0); - prop.put("search.navigation.location", sb.getConfig("search.navigation", "").indexOf("location",0) >= 0 ? 1 : 0); - // prop.put("search.navigation.filetype", sb.getConfig("search.navigation", "").indexOf("filetype",0) >= 0 ? 1 : 0); - prop.put("search.navigation.protocol", sb.getConfig("search.navigation", "").indexOf("protocol",0) >= 0 ? 1 : 0); - // prop.put("search.navigation.hosts", sb.getConfig("search.navigation", "").indexOf("hosts",0) >= 0 ? 1 : 0); - // prop.put("search.navigation.language", sb.getConfig("search.navigation", "").indexOf("language",0) >= 0 ? 1 : 0); - // prop.put("search.navigation.authors", sb.getConfig("search.navigation", "").indexOf("authors",0) >= 0 ? 1 : 0); - // prop.put("search.navigation.collections", sb.getConfig("search.navigation", "").indexOf("collections",0) >= 0 ? 1 : 0); - // prop.put("search.navigation.namespace", sb.getConfig("search.navigation", "").indexOf("namespace",0) >= 0 ? 1 : 0); - prop.put("search.navigation.topics", sb.getConfig("search.navigation", "").indexOf("topics",0) >= 0 ? 1 : 0); - prop.put("search.navigation.date", sb.getConfig("search.navigation", "").indexOf("date",0) >= 0 ? 1 : 0); + final Set navConfigs = sb.getConfigSet("search.navigation"); + boolean locationNavEnabled = false; + boolean protocolNavEnabled = false; + boolean topicsNavEnabled = false; + boolean dateNavEnabled = false; + for(final String navConfig : navConfigs) { + final String navName = NavigatorPlugins.getNavName(navConfig); + if("location".equals(navName)) { + locationNavEnabled = true; + } else if("protocol".equals(navName)) { + protocolNavEnabled = true; + } else if("topics".equals(navName)) { + topicsNavEnabled = true; + } else if("date".equals(navName)) { + dateNavEnabled = true; + } + } + + prop.put("search.navigation.location", locationNavEnabled); + prop.put("search.navigation.protocol", protocolNavEnabled); + prop.put("search.navigation.topics", topicsNavEnabled); + prop.put("search.navigation.date", dateNavEnabled); // list active navigator plugins - String naviconf = sb.getConfig("search.navigation", ""); - Map navplugins = NavigatorPlugins.initFromCfgString(naviconf); + Map navplugins = NavigatorPlugins.initFromCfgStrings(navConfigs); int i = 0; - for (String navname:navplugins.keySet()) { + for (final String navname : navplugins.keySet()) { Navigator nav = navplugins.get(navname); prop.put("search.navigation.plugin_" + i + "_name", navname); prop.put("search.navigation.plugin_" + i + "_displayname", nav.getDisplayName()); + final int navSort; + if(nav.getSort() == null) { + navSort = 0; + } else { + if(nav.getSort().getSortType() == NavigatorSortType.COUNT) { + if(nav.getSort().getSortDir() == NavigatorSortDirection.DESC) { + navSort = 0; + } else { + navSort = 1; + } + } else { + if(nav.getSort().getSortDir() == NavigatorSortDirection.DESC) { + navSort = 2; + } else { + navSort = 3; + } + } + } + prop.put("search.navigation.plugin_" + i + "_navSort", navSort); i++; } prop.put("search.navigation.plugin", i); diff --git a/htroot/IndexControlRWIs_p.java b/htroot/IndexControlRWIs_p.java index 25343a6a9..5ded66e67 100644 --- a/htroot/IndexControlRWIs_p.java +++ b/htroot/IndexControlRWIs_p.java @@ -30,6 +30,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.regex.PatternSyntaxException; @@ -63,8 +64,8 @@ import net.yacy.peers.DHTSelection; import net.yacy.peers.Protocol; import net.yacy.peers.Seed; import net.yacy.repository.Blacklist; -import net.yacy.repository.BlacklistHostAndPath; import net.yacy.repository.Blacklist.BlacklistType; +import net.yacy.repository.BlacklistHostAndPath; import net.yacy.search.Switchboard; import net.yacy.search.SwitchboardConstants; import net.yacy.search.index.Segment; @@ -680,7 +681,7 @@ public class IndexControlRWIs_p { sb.getRanking(), "",//userAgent 0.0d, 0.0d, 0.0d, - new String[0]); + new HashSet<>()); final SearchEvent theSearch = SearchEventCache.getEvent(query, sb.peers, sb.tables, null, false, sb.loader, Integer.MAX_VALUE, Long.MAX_VALUE); if (theSearch.rwiProcess != null && theSearch.rwiProcess.isAlive()) try {theSearch.rwiProcess.join();} catch (final InterruptedException e) {} if (theSearch.local_rwi_available.get() == 0) { diff --git a/htroot/yacy/search.java b/htroot/yacy/search.java index de10b8849..612ebb13d 100644 --- a/htroot/yacy/search.java +++ b/htroot/yacy/search.java @@ -254,7 +254,7 @@ public final class search { 0.0d, 0.0d, 0.0d, - new String[0] + new HashSet<>() ); theQuery.setStrictContentDom(strictContentDom); Network.log.info("INIT HASH SEARCH (abstracts only): " + QueryParams.anonymizedQueryHashes(theQuery.getQueryGoal().getIncludeHashes()) + " - " + theQuery.itemsPerPage() + " links"); @@ -319,7 +319,7 @@ public final class search { 0.0d, 0.0d, 0.0d, - new String[0] + new HashSet<>() ); theQuery.setStrictContentDom(strictContentDom); Network.log.info("INIT HASH SEARCH (query-" + abstracts + "): " + QueryParams.anonymizedQueryHashes(theQuery.getQueryGoal().getIncludeHashes()) + " - " + theQuery.itemsPerPage() + " links"); diff --git a/htroot/yacysearch.java b/htroot/yacysearch.java index 909c96652..64248e3f5 100644 --- a/htroot/yacysearch.java +++ b/htroot/yacysearch.java @@ -700,7 +700,7 @@ public class yacysearch { ranking, header.get(HeaderFramework.USER_AGENT, ""), lat, lon, rad, - sb.getConfigArray("search.navigation", "")); + sb.getConfigSet("search.navigation")); theQuery.setStrictContentDom(strictContentDom); theQuery.setMaxSuggestions(meanMax); theQuery.setStandardFacetsMaxCount(sb.getConfigInt(SwitchboardConstants.SEARCH_NAVIGATION_MAXCOUNT, diff --git a/htroot/yacysearchtrailer.html b/htroot/yacysearchtrailer.html index e49ebb9e5..bd1ca1ec9 100644 --- a/htroot/yacysearchtrailer.html +++ b/htroot/yacysearchtrailer.html @@ -189,7 +189,15 @@ function toggleVisibility(name, count) { #{navs}# @@ -198,7 +206,12 @@ function toggleVisibility(name, count) { #{nav-vocabulary}# diff --git a/htroot/yacysearchtrailer.java b/htroot/yacysearchtrailer.java index ae5db2ed1..119b8b259 100644 --- a/htroot/yacysearchtrailer.java +++ b/htroot/yacysearchtrailer.java @@ -44,6 +44,8 @@ import net.yacy.search.EventTracker; import net.yacy.search.Switchboard; import net.yacy.search.SwitchboardConstants; import net.yacy.search.navigator.Navigator; +import net.yacy.search.navigator.NavigatorSortDirection; +import net.yacy.search.navigator.NavigatorSortType; import net.yacy.search.query.QueryParams; import net.yacy.search.query.SearchEvent; import net.yacy.search.query.SearchEventCache; @@ -343,8 +345,28 @@ public class yacysearchtrailer { prop.put("navs_" + ni + "_displayname", navi.getDisplayName()); prop.put("navs_" + ni + "_name", naviname); prop.put("navs_" + ni + "_count", navi.size()); + + final int navSort; + if(navi.getSort() == null) { + navSort = 0; + } else { + if(navi.getSort().getSortType() == NavigatorSortType.COUNT) { + if(navi.getSort().getSortDir() == NavigatorSortDirection.DESC) { + navSort = 0; + } else { + navSort = 1; + } + } else { + if(navi.getSort().getSortDir() == NavigatorSortDirection.DESC) { + navSort = 2; + } else { + navSort = 3; + } + } + } + prop.put("navs_" + ni + "_navSort", navSort); - navigatorIterator = navi.keys(false); + navigatorIterator = navi.navigatorKeys(); int i = 0, pos = 0, neg = 0; String nav, rawNav; while (i < theSearch.getQuery().getStandardFacetsMaxCount() && navigatorIterator.hasNext()) { diff --git a/source/net/yacy/cora/federate/FederateSearchManager.java b/source/net/yacy/cora/federate/FederateSearchManager.java index 132ecdd85..31204b663 100644 --- a/source/net/yacy/cora/federate/FederateSearchManager.java +++ b/source/net/yacy/cora/federate/FederateSearchManager.java @@ -222,7 +222,7 @@ public class FederateSearchManager { this.switchboard.getRanking(), "",//userAgent 0.0d, 0.0d, 0.0d, - new String[0]); + new HashSet<>()); return query(query); } diff --git a/source/net/yacy/peers/Protocol.java b/source/net/yacy/peers/Protocol.java index 537bda1de..cdf1ba15d 100644 --- a/source/net/yacy/peers/Protocol.java +++ b/source/net/yacy/peers/Protocol.java @@ -1296,7 +1296,7 @@ public final class Protocol { // evaluate facets if(useSolrFacets) { - for (String field: event.query.facetfields) { + for (String field: event.query.facetfields.keySet()) { FacetField facet = rsp[0].getFacetField(field); ReversibleScoreMap result = new ClusteredScoreMap(UTF8.insensitiveUTF8Comparator); List values = facet == null ? null : facet.getValues(); diff --git a/source/net/yacy/search/navigator/FileTypeNavigator.java b/source/net/yacy/search/navigator/FileTypeNavigator.java index bf89438e0..576da21c1 100644 --- a/source/net/yacy/search/navigator/FileTypeNavigator.java +++ b/source/net/yacy/search/navigator/FileTypeNavigator.java @@ -38,8 +38,8 @@ import net.yacy.search.schema.CollectionSchema; */ public class FileTypeNavigator extends StringNavigator implements Navigator { - public FileTypeNavigator(String title, CollectionSchema field) { - super(title, field); + public FileTypeNavigator(final String title, final CollectionSchema field, final NavigatorSort sort) { + super(title, field, sort); } @Override diff --git a/source/net/yacy/search/navigator/HostNavigator.java b/source/net/yacy/search/navigator/HostNavigator.java index ab950799c..7b3dd6e86 100644 --- a/source/net/yacy/search/navigator/HostNavigator.java +++ b/source/net/yacy/search/navigator/HostNavigator.java @@ -34,9 +34,9 @@ import net.yacy.search.schema.CollectionSchema; * www.host.org and host.org as same url */ public class HostNavigator extends StringNavigator implements Navigator { - - public HostNavigator(String title, CollectionSchema field) { - super(title, field); + + public HostNavigator(final String title, final CollectionSchema field, final NavigatorSort sort) { + super(title, field, sort); } @Override diff --git a/source/net/yacy/search/navigator/LanguageNavigator.java b/source/net/yacy/search/navigator/LanguageNavigator.java index 74d1b4152..1d12c2758 100644 --- a/source/net/yacy/search/navigator/LanguageNavigator.java +++ b/source/net/yacy/search/navigator/LanguageNavigator.java @@ -39,13 +39,11 @@ public class LanguageNavigator extends StringNavigator implements Navigator { * Default constructor, using the default YaCy Solr field language_s. * * @param title the navigator display name + * @param sort the sort properties to apply when iterating over keys with the + * {@link #navigatorKeys()} function */ - public LanguageNavigator(String title) { - super(title, CollectionSchema.language_s); - } - - public LanguageNavigator(String title, CollectionSchema field) { - super(title, field); + public LanguageNavigator(final String title, final NavigatorSort sort) { + super(title, CollectionSchema.language_s, sort); } /** diff --git a/source/net/yacy/search/navigator/NameSpaceNavigator.java b/source/net/yacy/search/navigator/NameSpaceNavigator.java index 20eca84f8..632c9121f 100644 --- a/source/net/yacy/search/navigator/NameSpaceNavigator.java +++ b/source/net/yacy/search/navigator/NameSpaceNavigator.java @@ -32,8 +32,8 @@ import net.yacy.kelondro.data.meta.URIMetadataNode; */ public class NameSpaceNavigator extends StringNavigator implements Navigator { - public NameSpaceNavigator(String title) { - super(title, null); + public NameSpaceNavigator(final String title, final NavigatorSort sort) { + super(title, null, sort); } @Override diff --git a/source/net/yacy/search/navigator/Navigator.java b/source/net/yacy/search/navigator/Navigator.java index ec8dc4feb..5a2b00d12 100644 --- a/source/net/yacy/search/navigator/Navigator.java +++ b/source/net/yacy/search/navigator/Navigator.java @@ -22,6 +22,7 @@ */ package net.yacy.search.navigator; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -105,4 +106,16 @@ public interface Navigator extends ScoreMap { * @param listener an eventual object which wants to listen to successful updates on this navigator score map */ public void setUpdatesListener(final ScoreMapUpdatesListener listener); + + /** + * Creates and returns a sorted view of this navigator keys, sorted by the navigator order (for example by descending counts, or by ascending display names) + * + * @return an iterator accessing the navigator keys + */ + public Iterator navigatorKeys(); + + /** + * @return the sort properties of the navigator + */ + public NavigatorSort getSort(); } diff --git a/source/net/yacy/search/navigator/NavigatorPlugins.java b/source/net/yacy/search/navigator/NavigatorPlugins.java index ca5b1aeec..350c78e3d 100644 --- a/source/net/yacy/search/navigator/NavigatorPlugins.java +++ b/source/net/yacy/search/navigator/NavigatorPlugins.java @@ -22,18 +22,30 @@ */ package net.yacy.search.navigator; +import static net.yacy.search.query.SearchEvent.log; + +import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.TreeMap; + import net.yacy.crawler.CrawlSwitchboard; -import static net.yacy.search.query.SearchEvent.log; import net.yacy.search.schema.CollectionSchema; /** * Class to create and manipulate search navigator plugin list */ public class NavigatorPlugins { - + + /** + * Separator for specific properties in each navigator configuration + */ + public static final String NAV_PROPS_CONFIG_SEPARATOR = ":"; + /** * List of available navigators * @return Map key=navigatorCfgname, value=std.DisplayName @@ -51,24 +63,120 @@ public class NavigatorPlugins { defaultnavplugins.put("keywords", "Keywords"); return defaultnavplugins; } + + /** + * @param navConfig a navigator configuration String + * @return the name identifying the navigator, or null when navConfig is null + */ + public static final String getNavName(final String navConfig) { + String name = navConfig; + if(navConfig != null) { + final int navConfigPopsIndex = navConfig.indexOf(NAV_PROPS_CONFIG_SEPARATOR); + if(navConfigPopsIndex >= 0) { + name = navConfig.substring(0, navConfigPopsIndex); + } + } + return name; + } + + /** + * @param navName a navigator name + * @return the default sort properties to apply for the given navigator + */ + public static final NavigatorSort getDefaultSort(final String navName) { + if("year".equals(navName)) { + return NavigatorSort.LABEL_DESC; + } + return NavigatorSort.COUNT_DESC; + } + + /** + *

+ * Parse a navigator configuration entry and return the sort properties to + * apply. + *

+ *

+ * Supported formats : + *

    + *
  • "navName" : descending sort by count values (except for the year navigator, where the default is by descending displayed labels)
  • + *
  • "navName:count" : descending sort by count values
  • + *
  • "navName:label" : ascending sort by displayed labels
  • + *
  • "navName:count:asc" : ascending sort by count values
  • + *
  • "navName:count:desc" : descending sort by count values
  • + *
  • "navName:label:asc" : ascending sort by displayed labels
  • + *
  • "navName:label:desc" : descending sort by displayed labels
  • + *
+ *

+ * + * @param navConfig a navigator configuration String + * @return return the sort properties of the navigator + */ + public static final NavigatorSort parseNavSortConfig(final String navConfig) { + return parseNavSortConfig(navConfig, getDefaultSort(getNavName(navConfig))); + } + + /** + *

+ * Parse a navigator configuration entry and return the sort properties to + * apply. + *

+ *

+ * Supported formats : + *

    + *
  • "navName" : apply provided default sort/li> + *
  • "navName:count" : descending sort by count values
  • + *
  • "navName:label" : ascending sort by displayed labels
  • + *
  • "navName:count:asc" : ascending sort by count values
  • + *
  • "navName:count:desc" : descending sort by count values
  • + *
  • "navName:label:asc" : ascending sort by displayed labels
  • + *
  • "navName:label:desc" : descending sort by displayed labels
  • + *
+ *

+ * + * @param navConfig a navigator configuration String + * @param defaultSort the default sort properties to apply when the + * configuration String does not specify sort properties + * @return return the sort properties of the navigator + */ + public static final NavigatorSort parseNavSortConfig(final String navConfig, final NavigatorSort defaultSort) { + if (navConfig == null) { + return defaultSort; + } + final Set navProperties = new HashSet<>(); + Collections.addAll(navProperties, navConfig.split(NAV_PROPS_CONFIG_SEPARATOR)); + NavigatorSort sort = defaultSort; + if (navProperties.contains(NavigatorSortType.LABEL.toString().toLowerCase(Locale.ROOT))) { + sort = NavigatorSort.LABEL_ASC; // default label sort + if (navProperties.contains(NavigatorSortDirection.DESC.toString().toLowerCase(Locale.ROOT))) { + sort = NavigatorSort.LABEL_DESC; + } + } if (navProperties.contains(NavigatorSortType.COUNT.toString().toLowerCase(Locale.ROOT))) { + sort = NavigatorSort.COUNT_DESC; // default count sort + if (navProperties.contains(NavigatorSortDirection.ASC.toString().toLowerCase(Locale.ROOT))) { + sort = NavigatorSort.COUNT_ASC; + } + } + return sort; + } + /** - * Creates map of active search navigators from comma separated config string - * @param navcfg comma separated string of navigator names + * Creates map of active search navigators from navigator config strings + * @param navConfigs navigator configuration strings * @return map key=navigatorname, value=navigator.plugin reference */ - static public Map initFromCfgString(final String navcfg) { + public static Map initFromCfgStrings(final Set navConfigs) { - LinkedHashMap navigatorPlugins = new LinkedHashMap(); - if (navcfg == null || navcfg.isEmpty()) return navigatorPlugins; - String[] navnames = navcfg.split(","); - for (String navname : navnames) { - if (navname.contains("authors")) { - navigatorPlugins.put("authors", new StringNavigator("Authors", CollectionSchema.author_sxt)); - } - - if (navname.contains("collections")) { - RestrictedStringNavigator tmpnav = new RestrictedStringNavigator("Collection", CollectionSchema.collection_sxt); + final LinkedHashMap navigatorPlugins = new LinkedHashMap<>(); + if(navConfigs == null) { + return navigatorPlugins; + } + for (final String navConfig : navConfigs) { + final String navName = getNavName(navConfig); + if ("authors".equals(navName)) { + navigatorPlugins.put("authors", new StringNavigator("Authors", CollectionSchema.author_sxt, parseNavSortConfig(navConfig))); + } else if ("collections".equals(navName)) { + RestrictedStringNavigator tmpnav = new RestrictedStringNavigator("Collection", CollectionSchema.collection_sxt, parseNavSortConfig(navConfig)); // exclude default internal collection names tmpnav.addForbidden("dht"); tmpnav.addForbidden("robot_" + CrawlSwitchboard.CRAWL_PROFILE_AUTOCRAWL_DEEP); @@ -82,46 +190,47 @@ public class NavigatorPlugins { tmpnav.addForbidden("robot_" + CrawlSwitchboard.CRAWL_PROFILE_SNIPPET_GLOBAL_MEDIA); tmpnav.addForbidden("robot_" + CrawlSwitchboard.CRAWL_PROFILE_SURROGATE); navigatorPlugins.put("collections", tmpnav); - } - - if (navname.contains("filetype")) { - navigatorPlugins.put("filetype", new FileTypeNavigator("Filetype", CollectionSchema.url_file_ext_s)); - } - - if (navname.contains("hosts")) { - navigatorPlugins.put("hosts", new HostNavigator("Provider", CollectionSchema.host_s)); - } - - if (navname.contains("language")) { - navigatorPlugins.put("language", new LanguageNavigator("Language")); - } - - if (navname.contains("namespace")) { - navigatorPlugins.put("namespace", new NameSpaceNavigator("Wiki Name Space")); - } - - // YearNavigator with possible def of :fieldname:title in configstring - if (navname.contains("year")) { - if ((navname.indexOf(':')) > 0) { // example "year:dates_in_content_dts:Events" - String[] navfielddef = navname.split(":"); - try { - // year:fieldname:title - CollectionSchema field = CollectionSchema.valueOf(navfielddef[1]); - if (navfielddef.length > 2) { - navigatorPlugins.put(navfielddef[1], new YearNavigator(navfielddef[2], field)); - } else { - navigatorPlugins.put(navfielddef[1], new YearNavigator("Year-" + navfielddef[1], field)); - } - } catch (java.lang.IllegalArgumentException ex) { - log.severe("wrong navigator name in config: \"" + navname + "\" " + ex.getMessage()); - } - } else { // "year" only use default last_modified - navigatorPlugins.put("year", new YearNavigator("Year", CollectionSchema.last_modified)); + } else if ("filetype".equals(navName)) { + navigatorPlugins.put("filetype", new FileTypeNavigator("Filetype", CollectionSchema.url_file_ext_s, + parseNavSortConfig(navConfig))); + } else if("hosts".equals(navName)) { + navigatorPlugins.put("hosts", + new HostNavigator("Provider", CollectionSchema.host_s, parseNavSortConfig(navConfig))); + } else if ("language".equals(navName)) { + navigatorPlugins.put("language", new LanguageNavigator("Language", parseNavSortConfig(navConfig))); + } else if ("namespace".equals(navName)) { + navigatorPlugins.put("namespace", new NameSpaceNavigator("Wiki Name Space", parseNavSortConfig(navConfig))); + } else if ("year".equals(navName)) { + // YearNavigator with possible def of :fieldname:title in configstring + final LinkedHashSet navProperties = new LinkedHashSet<>(); + Collections.addAll(navProperties, navConfig.split(NAV_PROPS_CONFIG_SEPARATOR)); + + /* Remove sort related properties */ + for(final NavigatorSortType sortType : NavigatorSortType.values()) { + navProperties.remove(sortType.toString().toLowerCase(Locale.ROOT)); + } + for(final NavigatorSortDirection sortDir : NavigatorSortDirection.values()) { + navProperties.remove(sortDir.toString().toLowerCase(Locale.ROOT)); + } + final String[] navfielddef = navProperties.toArray(new String[navProperties.size()]); + + if (navfielddef.length > 1) { + try { + // year:fieldname:title + CollectionSchema field = CollectionSchema.valueOf(navfielddef[1]); + if (navfielddef.length > 2) { + navigatorPlugins.put(navfielddef[1], new YearNavigator(navfielddef[2], field, parseNavSortConfig(navConfig))); + } else { + navigatorPlugins.put(navfielddef[1], new YearNavigator("Year-" + navfielddef[1], field, parseNavSortConfig(navConfig))); + } + } catch (final java.lang.IllegalArgumentException ex) { + log.severe("wrong navigator name in config: \"" + navConfig + "\" " + ex.getMessage()); + } + } else { // "year" only use default last_modified + navigatorPlugins.put("year", new YearNavigator("Year", CollectionSchema.last_modified, parseNavSortConfig(navConfig))); } - } - - if (navname.contains("keywords")) { - navigatorPlugins.put("keywords", new TokenizedStringNavigator("Keywords", CollectionSchema.keywords)); + } else if ("keywords".equals(navName)) { + navigatorPlugins.put("keywords", new TokenizedStringNavigator("Keywords", CollectionSchema.keywords, parseNavSortConfig(navConfig))); } } return navigatorPlugins; diff --git a/source/net/yacy/search/navigator/NavigatorSort.java b/source/net/yacy/search/navigator/NavigatorSort.java new file mode 100644 index 000000000..2f86b1033 --- /dev/null +++ b/source/net/yacy/search/navigator/NavigatorSort.java @@ -0,0 +1,98 @@ +// NavigatorSort.java +// --------------------------- +// Copyright 2019 by luccioman; https://github.com/luccioman +// +// This is a part of YaCy, a peer-to-peer based web search engine +// +// LICENSE +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package net.yacy.search.navigator; + +/** + * Enumeration of navigator sort properties. + */ +public enum NavigatorSort { + + /** + * Descending sort on count values. + */ + COUNT_DESC(NavigatorSortType.COUNT, NavigatorSortDirection.DESC), + + /** + * Ascending sort on count values. + */ + COUNT_ASC(NavigatorSortType.COUNT, NavigatorSortDirection.ASC), + + /** + * Descending sort on displayed labels. + */ + LABEL_DESC(NavigatorSortType.LABEL, NavigatorSortDirection.DESC), + + /** + * Ascending sort on displayed labels. + */ + LABEL_ASC(NavigatorSortType.LABEL, NavigatorSortDirection.ASC); + + /** + * The type of sort to apply when iterating over a navigator keys with the + * {@link Navigator#navigatorKeys()} function + */ + private final NavigatorSortType sortType; + + /** + * The direction of the sort to apply when iterating over a navigator keys with + * the {@link Navigator#navigatorKeys()} function + */ + private final NavigatorSortDirection sortDir; + + /** + * @param sortType The type of sort to apply when iterating over a navigator + * keys with the {@link Navigator#navigatorKeys(boolean)} + * function + * @param sortDir The direction of the sort to apply when iterating over a + * navigator keys with the + * {@link Navigator#navigatorKeys(boolean)} function + */ + private NavigatorSort(final NavigatorSortType sortType, final NavigatorSortDirection sortDir) { + if (sortType == null) { + this.sortType = NavigatorSortType.COUNT; + } else { + this.sortType = sortType; + } + if (sortDir == null) { + this.sortDir = NavigatorSortDirection.DESC; + } else { + this.sortDir = sortDir; + } + } + + /** + * @return The type of sort to apply when iterating over a navigator keys with + * the {@link Navigator#navigatorKeys(boolean)} function + */ + public NavigatorSortType getSortType() { + return this.sortType; + } + + /** + * @return The direction of the sort to apply when iterating over a navigator + * keys with the {@link Navigator#navigatorKeys(boolean)} function + */ + public NavigatorSortDirection getSortDir() { + return this.sortDir; + } +} diff --git a/source/net/yacy/search/navigator/NavigatorSortDirection.java b/source/net/yacy/search/navigator/NavigatorSortDirection.java new file mode 100644 index 000000000..07a0bc843 --- /dev/null +++ b/source/net/yacy/search/navigator/NavigatorSortDirection.java @@ -0,0 +1,33 @@ +// NavigatorSortDirection.java +// --------------------------- +// Copyright 2019 by luccioman; https://github.com/luccioman +// +// This is a part of YaCy, a peer-to-peer based web search engine +// +// LICENSE +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package net.yacy.search.navigator; + +/** + * Enumeration of sort directions for navigators. + */ +public enum NavigatorSortDirection { + /** Ascending order */ + ASC, + /** Descending order */ + DESC +} diff --git a/source/net/yacy/search/navigator/NavigatorSortType.java b/source/net/yacy/search/navigator/NavigatorSortType.java new file mode 100644 index 000000000..f14e22709 --- /dev/null +++ b/source/net/yacy/search/navigator/NavigatorSortType.java @@ -0,0 +1,33 @@ +// NavigatorSortType.java +// --------------------------- +// Copyright 2019 by luccioman; https://github.com/luccioman +// +// This is a part of YaCy, a peer-to-peer based web search engine +// +// LICENSE +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package net.yacy.search.navigator; + +/** + * Enumeration of types of sort for navigators. + */ +public enum NavigatorSortType { + /** Sort by navigator displayed labels */ + LABEL, + /** Sort by navigator counts */ + COUNT +} diff --git a/source/net/yacy/search/navigator/RestrictedStringNavigator.java b/source/net/yacy/search/navigator/RestrictedStringNavigator.java index d6d44a3bc..bac5f31e0 100644 --- a/source/net/yacy/search/navigator/RestrictedStringNavigator.java +++ b/source/net/yacy/search/navigator/RestrictedStringNavigator.java @@ -37,8 +37,8 @@ public class RestrictedStringNavigator extends StringNavigator implements Naviga Set allowed; // complete list of keys, if empty all keys are allowed Set forbidden; // keys to exclude - public RestrictedStringNavigator(String title, CollectionSchema field) { - super(title, field); + public RestrictedStringNavigator(final String title, final CollectionSchema field, final NavigatorSort sort) { + super(title, field, sort); this.allowed = new HashSet(); this.forbidden = new HashSet(); } diff --git a/source/net/yacy/search/navigator/StringNavigator.java b/source/net/yacy/search/navigator/StringNavigator.java index c5c122251..9eea57d70 100644 --- a/source/net/yacy/search/navigator/StringNavigator.java +++ b/source/net/yacy/search/navigator/StringNavigator.java @@ -22,9 +22,14 @@ */ package net.yacy.search.navigator; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; import java.util.List; import java.util.Map; + import net.yacy.cora.sorting.ConcurrentScoreMap; import net.yacy.cora.sorting.ReversibleScoreMap; import net.yacy.kelondro.data.meta.URIMetadataNode; @@ -35,16 +40,41 @@ import net.yacy.search.schema.CollectionSchema; * Search navigator for simple string entries based on ScoreMap to count and * order the result list by counted occurence */ -public class StringNavigator extends ConcurrentScoreMap implements Navigator { +public class StringNavigator extends ConcurrentScoreMap implements Navigator { public String title; protected final CollectionSchema field; + + /** + * The sort properties to apply when iterating over keys with the + * {@link #navigatorKeys()} function + */ + private final NavigatorSort sort; + + /** + * Constructor applying a descending sort by counts as defaut. + * @param title the navigator title + * @param field the indexed field to count + */ + public StringNavigator(final String title, final CollectionSchema field) { + this(title, field, NavigatorSort.COUNT_DESC); + } - public StringNavigator(String title, CollectionSchema field) { - super(); - this.title = title; - this.field = field; - } + /** + * @param title the navigator title + * @param field the indexed field to count + * @param sort the sort properties to apply when iterating over keys with the + * {@link #navigatorKeys()} function + */ + public StringNavigator(final String title, final CollectionSchema field, final NavigatorSort sort) { + this.title = title; + this.field = field; + if(sort == null) { + this.sort = NavigatorSort.COUNT_DESC; + } else { + this.sort = sort; + } + } @Override public String getDisplayName() { @@ -153,4 +183,26 @@ public class StringNavigator extends ConcurrentScoreMap implements Navi } return ""; } + + @Override + public Iterator navigatorKeys() { + if(this.sort.getSortType() == NavigatorSortType.LABEL) { + final ArrayList keys = new ArrayList<>(this.map.keySet()); + + Comparator keyComparator = Comparator.comparing(this::getElementDisplayName); + if(this.sort.getSortDir() == NavigatorSortDirection.DESC) { + keyComparator = keyComparator.reversed(); + } + Collections.sort(keys, keyComparator); + + + return keys.iterator(); + } + return keys(this.sort.getSortDir() == NavigatorSortDirection.ASC); + } + + @Override + public NavigatorSort getSort() { + return this.sort; + } } diff --git a/source/net/yacy/search/navigator/TokenizedStringNavigator.java b/source/net/yacy/search/navigator/TokenizedStringNavigator.java index cd8325a01..4e25f9880 100644 --- a/source/net/yacy/search/navigator/TokenizedStringNavigator.java +++ b/source/net/yacy/search/navigator/TokenizedStringNavigator.java @@ -36,8 +36,8 @@ import net.yacy.search.schema.CollectionSchema; */ public class TokenizedStringNavigator extends StringNavigator implements Navigator { - public TokenizedStringNavigator(String title, CollectionSchema field) { - super(title, field); + public TokenizedStringNavigator(String title, CollectionSchema field, final NavigatorSort sort) { + super(title, field, sort); } /** diff --git a/source/net/yacy/search/navigator/YearNavigator.java b/source/net/yacy/search/navigator/YearNavigator.java index 30eef72e0..cb7975fd7 100644 --- a/source/net/yacy/search/navigator/YearNavigator.java +++ b/source/net/yacy/search/navigator/YearNavigator.java @@ -24,11 +24,9 @@ package net.yacy.search.navigator; import java.util.Calendar; import java.util.Collection; -import java.util.Collections; import java.util.Date; import java.util.Iterator; import java.util.Map; -import java.util.TreeSet; import net.yacy.cora.federate.solr.SolrType; import net.yacy.cora.sorting.ReversibleScoreMap; import net.yacy.kelondro.data.meta.URIMetadataNode; @@ -54,8 +52,8 @@ import net.yacy.search.schema.CollectionSchema; */ public class YearNavigator extends StringNavigator implements Navigator { - public YearNavigator(String title, CollectionSchema field) { - super(title, field); + public YearNavigator(final String title, final CollectionSchema field, final NavigatorSort sort) { + super(title, field, sort == null ? NavigatorSort.LABEL_DESC : sort); if (field.getType() != SolrType.date) throw new IllegalArgumentException("field is not of type Date"); } @@ -119,32 +117,6 @@ public class YearNavigator extends StringNavigator implements Navigator { } } - /** - * YearNavigator returns keys in asc or desc order instead of ordered by - * score - * - * @param up true = asc - * @return key alphabetically ordered - */ - @Override - public Iterator keys(boolean up) { - TreeSet years; - if (up) { - years = new TreeSet(); - } else { - years = new TreeSet(Collections.reverseOrder()); - } - - // make sure keys with high score are included (display may be limited in size) - // Iterator it = this.iterator(); - Iterator it = super.keys(false); - - while (it.hasNext()) { - years.add(it.next()); - } - return years.iterator(); - } - /** * For date_in_content_dts it return true if form:YEAR and to:YEAR is part diff --git a/source/net/yacy/search/query/QueryParams.java b/source/net/yacy/search/query/QueryParams.java index 325123dc9..770d769f8 100644 --- a/source/net/yacy/search/query/QueryParams.java +++ b/source/net/yacy/search/query/QueryParams.java @@ -34,10 +34,10 @@ import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.Iterator; -import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.SortedSet; import java.util.regex.Matcher; @@ -77,6 +77,8 @@ import net.yacy.kelondro.util.Bitfield; import net.yacy.kelondro.util.SetTools; import net.yacy.peers.Seed; import net.yacy.search.index.Segment; +import net.yacy.search.navigator.NavigatorPlugins; +import net.yacy.search.navigator.NavigatorSort; import net.yacy.search.ranking.RankingProfile; import net.yacy.search.schema.CollectionConfiguration; import net.yacy.search.schema.CollectionSchema; @@ -89,6 +91,18 @@ public final class QueryParams { /** The default maximum number of date elements in the date navigator */ public static final int FACETS_DATE_MAXCOUNT_DEFAULT = 640; + /** + * The Solr facet limit to apply when resorting or filtering is done in a YaCy + * search navigator. For example sort by ascending counts or by descending + * indexed terms are not supported with Solr 6.6 (but should be on Solr 7 JSON + * Facet API - see + * https://lucene.apache.org/solr/guide/7_6/json-facet-api.html). The limit + * defined here is set large enough so that resorting can be done by the YaCy + * Navigator itself. We don't set the facet to unlimited to prevent a too high + * memory usage. + */ + private static final int FACETS_MAXCOUNT_FOR_RESORT_ON_SEARCH_NAV = 100000; + public enum Searchdom { LOCAL, CLUSTER, GLOBAL; @@ -188,7 +202,9 @@ public final class QueryParams { public long searchtime, urlretrievaltime, snippetcomputationtime; // time to perform the search, to get all the urls, and to compute the snippets public final String userAgent; protected double lat, lon, radius; - public LinkedHashSet facetfields; + + /** Map from facet/navigator name to sort properties */ + public Map facetfields; private SolrQuery cachedQuery; private CollectionConfiguration solrSchema; public final int timezoneOffset; @@ -228,7 +244,7 @@ public final class QueryParams { final double lat, final double lon, final double radius, - final String[] search_navigation + final Set navConfigs ) { this.queryGoal = queryGoal; this.modifier = modifier; @@ -286,7 +302,7 @@ public final class QueryParams { this.snippetCacheStrategy = snippetCacheStrategy; this.clienthost = host; this.remotepeer = null; - this.starttime = Long.valueOf(System.currentTimeMillis()); + this.starttime = System.currentTimeMillis(); this.maxtime = 10000; this.indexSegment = indexSegment; this.userAgent = userAgent; @@ -296,23 +312,23 @@ public final class QueryParams { this.lat = Math.floor(lat * this.kmNormal) / this.kmNormal; this.lon = Math.floor(lon * this.kmNormal) / this.kmNormal; this.radius = Math.floor(radius * this.kmNormal + 1) / this.kmNormal; - this.facetfields = new LinkedHashSet(); + this.facetfields = new HashMap<>(); this.solrSchema = indexSegment.fulltext().getDefaultConfiguration(); - for (String navkey: search_navigation) { - CollectionSchema f = defaultfacetfields.get(navkey); + for (final String navConfig: navConfigs) { + CollectionSchema f = defaultfacetfields.get(NavigatorPlugins.getNavName(navConfig)); // handle special field, authors_sxt (add to facet w/o contains check, as authors_sxt is not enabled (is copyfield)) // dto. for coordinate_p_0_coordinate is not enabled but used for location facet (because coordinate_p not valid for facet field) - if (f != null && (solrSchema.contains(f) || f.name().equals("author_sxt") || f.name().equals("coordinate_p_0_coordinate") )) - this.facetfields.add(f.getSolrFieldName()); + if (f != null && (solrSchema.contains(f) || f == CollectionSchema.author_sxt || f == CollectionSchema.coordinate_p_0_coordinate)) + this.facetfields.put(f.getSolrFieldName(), NavigatorPlugins.parseNavSortConfig(navConfig)); } if (LibraryProvider.autotagging != null) for (Tagging v: LibraryProvider.autotagging.getVocabularies()) { if (v.isFacet()) { - this.facetfields.add(CollectionSchema.VOCABULARY_PREFIX + v.getName() + CollectionSchema.VOCABULARY_TERMS_SUFFIX); + this.facetfields.put(CollectionSchema.VOCABULARY_PREFIX + v.getName() + CollectionSchema.VOCABULARY_TERMS_SUFFIX, NavigatorSort.COUNT_DESC); } } for (String context: ProbabilisticClassifier.getContextNames()) { - this.facetfields.add(CollectionSchema.VOCABULARY_PREFIX + context + CollectionSchema.VOCABULARY_TERMS_SUFFIX); + this.facetfields.put(CollectionSchema.VOCABULARY_PREFIX + context + CollectionSchema.VOCABULARY_TERMS_SUFFIX, NavigatorSort.COUNT_DESC); } this.cachedQuery = null; this.standardFacetsMaxCount = FACETS_STANDARD_MAXCOUNT_DEFAULT; @@ -686,6 +702,61 @@ public final class QueryParams { return params; } + /** + * Fill the Solr parameters with the relevant values to apply the search + * navigator sort properties. + * + * @param params the Solr parameters to modify + * @param a Solr field name + * @param navSort navigator sort properties to apply + */ + private void fillSolrParamWithNavSort(final SolrQuery params, final String solrFieldName, + final NavigatorSort navSort) { + if (params != null && solrFieldName != null && navSort != null) { + switch (navSort) { + case COUNT_ASC: + params.setParam("f." + solrFieldName + ".facet.sort", FacetParams.FACET_SORT_COUNT); + /* + * Ascending count is not supported with Solr 6.6 (but should be on Solr 7 JSON + * Facet API https://lucene.apache.org/solr/guide/7_6/json-facet-api.html) So we + * use a here a high limit and ascending resorting will be done by YaCy + * Navigator + */ + params.setParam("f." + solrFieldName + ".facet.limit", + String.valueOf(FACETS_MAXCOUNT_FOR_RESORT_ON_SEARCH_NAV)); + break; + case LABEL_DESC: + /* + * Descending index order is not supported with Solr 6.6 (but should be on Solr + * 7 JSON Facet API + * https://lucene.apache.org/solr/guide/7_6/json-facet-api.html) So we use a + * here a high limit and descending resorting will be done by YaCy Navigator + */ + params.setParam("f." + solrFieldName + ".facet.sort", FacetParams.FACET_SORT_INDEX); + params.setParam("f." + solrFieldName + ".facet.limit", + String.valueOf(FACETS_MAXCOUNT_FOR_RESORT_ON_SEARCH_NAV)); + break; + case LABEL_ASC: + params.setParam("f." + solrFieldName + ".facet.sort", FacetParams.FACET_SORT_INDEX); + break; + default: + /* Nothing to add for COUNT_DESC which is the default for Solr */ + break; + } + + if (CollectionSchema.language_s.getSolrFieldName().equals(solrFieldName) + || CollectionSchema.url_file_ext_s.getSolrFieldName().equals(solrFieldName) + || CollectionSchema.collection_sxt.getSolrFieldName().equals(solrFieldName)) { + /* + * For these search navigators additional filtering or resorting is done in the navigator itself. + * So we use a here a high limit so that the navigator apply its rules without missing elements. + */ + params.setParam("f." + solrFieldName + ".facet.limit", + String.valueOf(FACETS_MAXCOUNT_FOR_RESORT_ON_SEARCH_NAV)); + } + } + } + private SolrQuery getBasicParams(final boolean getFacets, final List fqs) { final SolrQuery params = new SolrQuery(); params.setParam("defType", "edismax"); @@ -713,15 +784,19 @@ public final class QueryParams { params.setFacetLimit(this.standardFacetsMaxCount); params.setFacetSort(FacetParams.FACET_SORT_COUNT); params.setParam(FacetParams.FACET_METHOD, FacetParams.FACET_METHOD_enum); // fight the fieldcache - for (String field: this.facetfields) params.addFacetField("{!ex=" + field + "}" + field); // params.addFacetField("{!ex=" + field + "}" + field); - if (this.facetfields.contains(CollectionSchema.dates_in_content_dts.name())) { + for (final Entry entry : this.facetfields.entrySet()) { + params.addFacetField("{!ex=" + entry.getKey() + "}" + entry.getKey()); // params.addFacetField("{!ex=" + field + "}" + field); + fillSolrParamWithNavSort(params, entry.getKey(), entry.getValue()); + } + final NavigatorSort datesInContentSort = this.facetfields.get(CollectionSchema.dates_in_content_dts.name()); + if (datesInContentSort != null) { params.setParam(FacetParams.FACET_RANGE, CollectionSchema.dates_in_content_dts.name()); String start = new Date(System.currentTimeMillis() - 1000L * 60L * 60L * 24L * 3).toInstant().toString(); String end = new Date(System.currentTimeMillis() + 1000L * 60L * 60L * 24L * 3).toInstant().toString(); params.setParam("f." + CollectionSchema.dates_in_content_dts.getSolrFieldName() + ".facet.range.start", start); params.setParam("f." + CollectionSchema.dates_in_content_dts.getSolrFieldName() + ".facet.range.end", end); params.setParam("f." + CollectionSchema.dates_in_content_dts.getSolrFieldName() + ".facet.range.gap", "+1DAY"); - params.setParam("f." + CollectionSchema.dates_in_content_dts.getSolrFieldName() + ".facet.sort", "index"); + fillSolrParamWithNavSort(params, CollectionSchema.dates_in_content_dts.getSolrFieldName(), datesInContentSort); params.setParam("f." + CollectionSchema.dates_in_content_dts.getSolrFieldName() + ".facet.limit", Integer.toString(this.dateFacetMaxCount)); // the year constraint should cause that limitation already } //for (String k: params.getParameterNames()) {ArrayList al = new ArrayList<>(); for (String s: params.getParams(k)) al.add(s); System.out.println("Parameter: " + k + "=" + al.toString());} diff --git a/source/net/yacy/search/query/SearchEvent.java b/source/net/yacy/search/query/SearchEvent.java index 6ea109cc7..742c23ea0 100644 --- a/source/net/yacy/search/query/SearchEvent.java +++ b/source/net/yacy/search/query/SearchEvent.java @@ -352,15 +352,34 @@ public final class SearchEvent implements ScoreMapUpdatesListener { this.maxExpectedRemoteReferences = new AtomicInteger(0); this.expectedRemoteReferences = new AtomicInteger(0); this.excludeintext_image = Switchboard.getSwitchboard().getConfigBool("search.excludeintext.image", true); + // prepare configured search navigation - final String navcfg = Switchboard.getSwitchboard().getConfig("search.navigation", ""); - this.locationNavigator = navcfg.contains("location") ? new ConcurrentScoreMap(this) : null; - this.protocolNavigator = navcfg.contains("protocol") ? new ConcurrentScoreMap(this) : null; - this.dateNavigator = navcfg.contains("date") ? new ConcurrentScoreMap(this) : null; - this.topicNavigatorCount = navcfg.contains("topics") ? MAX_TOPWORDS : 0; + final Set navConfigs = Switchboard.getSwitchboard().getConfigSet("search.navigation"); + + boolean locationNavEnabled = false; + boolean protocolNavEnabled = false; + boolean topicsNavEnabled = false; + boolean dateNavEnabled = false; + for(final String navConfig : navConfigs) { + final String navName = NavigatorPlugins.getNavName(navConfig); + if("location".equals(navName)) { + locationNavEnabled = true; + } else if("protocol".equals(navName)) { + protocolNavEnabled = true; + } else if("topics".equals(navName)) { + topicsNavEnabled = true; + } else if("date".equals(navName)) { + dateNavEnabled = true; + } + } + + this.locationNavigator = locationNavEnabled ? new ConcurrentScoreMap<>(this) : null; + this.protocolNavigator = protocolNavEnabled ? new ConcurrentScoreMap<>(this) : null; + this.dateNavigator = dateNavEnabled ? new ConcurrentScoreMap<>(this) : null; + this.topicNavigatorCount = topicsNavEnabled ? MAX_TOPWORDS : 0; this.vocabularyNavigator = new TreeMap>(); // prepare configured search navigation (plugins) - this.navigatorPlugins = NavigatorPlugins.initFromCfgString(navcfg); + this.navigatorPlugins = NavigatorPlugins.initFromCfgStrings(navConfigs); if(this.navigatorPlugins != null) { for(final Navigator nav : this.navigatorPlugins.values()) { nav.setUpdatesListener(this); diff --git a/source/net/yacy/server/serverSwitch.java b/source/net/yacy/server/serverSwitch.java index 69fabc3e0..817f3d14d 100644 --- a/source/net/yacy/server/serverSwitch.java +++ b/source/net/yacy/server/serverSwitch.java @@ -446,8 +446,7 @@ public class serverSwitch { /** * get a configuration parameter set - * @param key - * @param dflt a default list + * @param key name of the configuration parameter * @return a set of strings which had been separated by comma in the setting */ public Set getConfigSet(final String key) {