diff --git a/htroot/ConfigSearchPage_p.java b/htroot/ConfigSearchPage_p.java
index 9ae5b5249..b86b5879b 100644
--- a/htroot/ConfigSearchPage_p.java
+++ b/htroot/ConfigSearchPage_p.java
@@ -72,6 +72,7 @@ public class ConfigSearchPage_p {
sb.setConfig("search.video", post.getBoolean("search.video"));
sb.setConfig("search.app", post.getBoolean("search.app"));
+ sb.setConfig("search.result.show.keywords", post.getBoolean("search.result.show.keywords"));
sb.setConfig("search.result.show.date", post.getBoolean("search.result.show.date"));
sb.setConfig("search.result.show.size", post.getBoolean("search.result.show.size"));
sb.setConfig("search.result.show.metadata", post.getBoolean("search.result.show.metadata"));
@@ -156,6 +157,7 @@ public class ConfigSearchPage_p {
sb.setConfig("search.audio", config.getProperty("search.audio","false"));
sb.setConfig("search.video", config.getProperty("search.video","false"));
sb.setConfig("search.app", config.getProperty("search.app","false"));
+ sb.setConfig("search.result.show.keywords", config.getProperty("search.result.show.keywords","false"));
sb.setConfig("search.result.show.date", config.getProperty("search.result.show.date","true"));
sb.setConfig("search.result.show.size", config.getProperty("search.result.show.size","false"));
sb.setConfig("search.result.show.metadata", config.getProperty("search.result.show.metadata","false"));
@@ -184,6 +186,7 @@ public class ConfigSearchPage_p {
prop.put("search.video", sb.getConfigBool("search.video", false) ? 1 : 0);
prop.put("search.app", sb.getConfigBool("search.app", false) ? 1 : 0);
+ prop.put("search.result.show.keywords", sb.getConfigBool("search.result.show.keywords", false) ? 1 : 0);
prop.put("search.result.show.date", sb.getConfigBool("search.result.show.date", false) ? 1 : 0);
prop.put("search.result.show.size", sb.getConfigBool("search.result.show.size", false) ? 1 : 0);
prop.put("search.result.show.metadata", sb.getConfigBool("search.result.show.metadata", false) ? 1 : 0);
diff --git a/htroot/index.html b/htroot/index.html
index 7da5b4f54..ad1cf55f7 100644
--- a/htroot/index.html
+++ b/htroot/index.html
@@ -161,6 +161,8 @@
from:<date1> to:<date2>
only pages with a date between <date1> and <date2> in content
#(/datesincontent)#
+ keyword:<phrase>
+ only pages with keyword anotation containing <phrase>
/http
only resources from http or https servers
/ftp
diff --git a/htroot/yacysearchitem.html b/htroot/yacysearchitem.html
index 5130c3734..d44347bfb 100644
--- a/htroot/yacysearchitem.html
+++ b/htroot/yacysearchitem.html
@@ -25,6 +25,7 @@
#[description]#
#[urlname]#
+ #(showKeywords)#::Tags: #[subject]#
#(/showKeywords)#
#(showDate)#::#[date]##(/showDate)#
#(showEvent)#::on #[date]##(/showEvent)#
diff --git a/htroot/yacysearchitem.java b/htroot/yacysearchitem.java
index 2f03a82f3..7cbdcea58 100644
--- a/htroot/yacysearchitem.java
+++ b/htroot/yacysearchitem.java
@@ -205,6 +205,7 @@ public class yacysearchitem {
prop.put("content_showEvent", showEvent ? 1 : 0);
Collection snapshotPaths = sb.getConfigBool("search.result.show.snapshots", true) ? Transactions.findPaths(result.url(), null, State.ANY) : null;
if (fileType == FileType.HTML) { // html template specific settings
+ prop.put("content_showKeywords", (sb.getConfigBool("search.result.show.keywords", false) && !result.dc_subject().isEmpty()) ? 1 : 0);
prop.put("content_showDate", sb.getConfigBool("search.result.show.date", true) && !showEvent ? 1 : 0);
prop.put("content_showSize", sb.getConfigBool("search.result.show.size", true) ? 1 : 0);
prop.put("content_showMetadata", sb.getConfigBool("search.result.show.metadata", true) ? 1 : 0);
@@ -219,6 +220,7 @@ public class yacysearchitem {
prop.put("content_showRanking", sb.getConfigBool("search.result.show.ranking", false) ? 1 : 0);
if (showEvent) prop.put("content_showEvent_date", GenericFormatter.RFC1123_SHORT_FORMATTER.format(events[0]));
+ prop.putHTML("content_showKeywords_subject", result.dc_subject());
prop.put("content_showDate_date", GenericFormatter.RFC1123_SHORT_FORMATTER.format(result.moddate()));
prop.putHTML("content_showSize_sizename", RSSMessage.sizename(result.filesize()));
prop.put("content_showMetadata_urlhash", urlhash);
diff --git a/source/net/yacy/search/navigator/StringNavigator.java b/source/net/yacy/search/navigator/StringNavigator.java
index c0d5c78eb..c42dc7104 100644
--- a/source/net/yacy/search/navigator/StringNavigator.java
+++ b/source/net/yacy/search/navigator/StringNavigator.java
@@ -68,6 +68,9 @@ public class StringNavigator extends ConcurrentScoreMap implements Navi
mod = "author:" + key;
}
break;
+ case keywords:
+ mod = "keyword:" + key;
+ break;
case url_protocol_s:
mod = "/" + key;
break;
diff --git a/source/net/yacy/search/query/QueryModifier.java b/source/net/yacy/search/query/QueryModifier.java
index c2194b565..4e3dffdd6 100644
--- a/source/net/yacy/search/query/QueryModifier.java
+++ b/source/net/yacy/search/query/QueryModifier.java
@@ -40,7 +40,7 @@ import net.yacy.server.serverObjects;
public class QueryModifier {
private final StringBuilder modifier;
- public String sitehost, sitehash, filetype, protocol, language, author, collection, on, from, to;
+ public String sitehost, sitehash, filetype, protocol, language, author, keyword, collection, on, from, to;
public int timezoneOffset;
public QueryModifier(final int timezoneOffset) {
@@ -51,6 +51,7 @@ public class QueryModifier {
this.protocol = null;
this.language = null;
this.author = null;
+ this.keyword = null;
this.collection = null;
this.on = null;
this.from = null;
@@ -145,7 +146,17 @@ public class QueryModifier {
add("author:" + author);
}
}
-
+
+ // parse keyword
+ final int keywordi = querystring.indexOf("keyword:", 0);
+ if (keywordi >= 0) {
+ // TODO: should we handle quoted keywords (to allow space) and comma separated list ?
+ int ftb = querystring.indexOf(' ', keywordi);
+ this.keyword = querystring.substring(keywordi + 8, ftb == -1 ? querystring.length() : ftb);
+ querystring = querystring.replace("keyword:" + this.keyword, "").replace(" ", " ").trim();
+ add("keyword:" + this.keyword);
+ }
+
// parse collection
int collectioni = querystring.indexOf("collection:", 0);
while (collectioni >= 0) { // due to possible collision with "on:" modifier make sure no "collection:" remains
@@ -281,7 +292,11 @@ public class QueryModifier {
if (this.author != null && this.author.length() > 0 && fq.indexOf(CollectionSchema.author_sxt.getSolrFieldName()) < 0) {
fq.append(" AND ").append(CollectionSchema.author_sxt.getSolrFieldName()).append(":\"").append(this.author).append('\"');
}
-
+
+ if (this.keyword != null && this.keyword.length() > 0 && fq.indexOf(CollectionSchema.keywords.getSolrFieldName()) < 0) {
+ fq.append(" AND ").append(CollectionSchema.keywords.getSolrFieldName()).append(":\"").append(this.keyword).append('\"');
+ }
+
if (this.collection != null && this.collection.length() > 0 && fq.indexOf(CollectionSchema.collection_sxt.getSolrFieldName()) < 0) {
fq.append(" AND ").append(QueryModifier.parseCollectionExpression(this.collection));
}
diff --git a/source/net/yacy/search/query/QueryParams.java b/source/net/yacy/search/query/QueryParams.java
index ed8455385..378d284b8 100644
--- a/source/net/yacy/search/query/QueryParams.java
+++ b/source/net/yacy/search/query/QueryParams.java
@@ -527,6 +527,11 @@ public final class QueryParams {
fqs.add(CollectionSchema.author_sxt.getSolrFieldName() + ":\"" + this.modifier.author + '\"');
}
+ // add keyword filter
+ if (this.modifier.keyword != null && this.modifier.keyword.length() > 0 && this.solrSchema.contains(CollectionSchema.keywords)) {
+ fqs.add(CollectionSchema.keywords.getSolrFieldName() + ":\"" + this.modifier.keyword + '\"');
+ }
+
// add collection facets
if (this.modifier.collection != null && this.modifier.collection.length() > 0 && this.solrSchema.contains(CollectionSchema.collection_sxt)) {
fqs.add(QueryModifier.parseCollectionExpression(this.modifier.collection));
diff --git a/source/net/yacy/search/query/SearchEvent.java b/source/net/yacy/search/query/SearchEvent.java
index e3aa44de6..24129ef3a 100644
--- a/source/net/yacy/search/query/SearchEvent.java
+++ b/source/net/yacy/search/query/SearchEvent.java
@@ -964,6 +964,14 @@ public final class SearchEvent {
continue pollloop;
}
}
+
+ if (this.query.modifier.keyword != null) {
+ if (iEntry.dc_subject().indexOf(this.query.modifier.keyword) < 0) {
+ if (log.isFine()) log.fine ("dropped Node: keyword");
+ continue pollloop;
+ }
+ }
+
// finally extend the double-check and insert result to stack
this.urlhashes.putUnique(iEntry.hash());
rankingtryloop: while (true) {