Index: main/plugin-odf-web/pages/services/search/course/search-course_2.3.xsl =================================================================== --- main/plugin-odf-web/pages/services/search/course/search-course_2.3.xsl (revision 36748) +++ main/plugin-odf-web/pages/services/search/course/search-course_2.3.xsl (working copy) @@ -39,7 +39,10 @@ hit page - + + + + @@ -56,7 +59,14 @@ Index: main/plugin-odf-web/pages/services/search/search-bydomain_1.3.xsl =================================================================== --- main/plugin-odf-web/pages/services/search/search-bydomain_1.3.xsl (revision 36748) +++ main/plugin-odf-web/pages/services/search/search-bydomain_1.3.xsl (working copy) @@ -41,12 +41,24 @@ hit page - + + + + Index: main/plugin-odf-web/pages/services/search/search-criteria/search-criteria-multiple.xsl =================================================================== --- main/plugin-odf-web/pages/services/search/search-criteria/search-criteria-multiple.xsl (revision 36748) +++ main/plugin-odf-web/pages/services/search/search-criteria/search-criteria-multiple.xsl (working copy) @@ -70,6 +70,45 @@ + + + +
+
+
+ +
+
+
+
+
+ + + + +
+
+
+ +
+
+
+
+
+ + + + +
+
+
+ +
+
+
+
+
+ Index: main/plugin-odf-web/pages/services/search/search-criteria/search-criteria.xsl =================================================================== --- main/plugin-odf-web/pages/services/search/search-criteria/search-criteria.xsl (revision 36748) +++ main/plugin-odf-web/pages/services/search/search-criteria/search-criteria.xsl (working copy) @@ -128,6 +128,36 @@ + + +
+
+
+ +
+
+
+
+ + +
+
+
+ +
+
+
+
+ + +
+
+
+ +
+
+
+
Index: main/plugin-odf-web/pages/services/search/search-criteria/search-criteria_1.2.xsl =================================================================== --- main/plugin-odf-web/pages/services/search/search-criteria/search-criteria_1.2.xsl (revision 36748) +++ main/plugin-odf-web/pages/services/search/search-criteria/search-criteria_1.2.xsl (working copy) @@ -155,6 +155,15 @@ + + + + + + + + + @@ -206,6 +215,45 @@
+ + + +
+
+
+ +
+
+
+
+
+ + + + +
+
+
+ +
+
+
+
+
+ + + + +
+
+
+ +
+
+
+
+
+ Index: main/plugin-odf-web/pages/services/search/search-map.xsl =================================================================== --- main/plugin-odf-web/pages/services/search/search-map.xsl (revision 36748) +++ main/plugin-odf-web/pages/services/search/search-map.xsl (working copy) @@ -168,12 +168,7 @@ - - - / - - .html - + @@ -186,7 +181,10 @@ - + + + + Index: main/plugin-odf-web/pages/services/search/search.xsl =================================================================== --- main/plugin-odf-web/pages/services/search/search.xsl (revision 36748) +++ main/plugin-odf-web/pages/services/search/search.xsl (working copy) @@ -20,7 +20,9 @@ xmlns:math="java.lang.Math" xmlns:ametys="org.ametys.web.transformation.xslt.AmetysXSLTHelper" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" - extension-element-prefixes="math"> + xmlns:url-encode="java.net.URLEncoder" + xmlns:str="http://exslt.org/strings" + extension-element-prefixes="math str url-encode"> @@ -69,7 +71,10 @@
hit even - + + + +
hit even @@ -126,6 +131,15 @@ + + + + + + + + + @@ -218,6 +232,15 @@ + + + + + + + + + @@ -228,5 +251,29 @@ - + + + + + /.html + + + & + ? + + search-keywords= + + + , + + , + + + + + , + + + + \ No newline at end of file Index: main/plugin-odf-web/pages/services/search/search_1.2.xsl =================================================================== --- main/plugin-odf-web/pages/services/search/search_1.2.xsl (revision 36748) +++ main/plugin-odf-web/pages/services/search/search_1.2.xsl (working copy) @@ -20,7 +20,9 @@ xmlns:math="java.lang.Math" xmlns:ametys="org.ametys.web.transformation.xslt.AmetysXSLTHelper" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" - extension-element-prefixes="math"> + xmlns:url-encode="java.net.URLEncoder" + xmlns:str="http://exslt.org/strings" + extension-element-prefixes="math str url-encode"> @@ -111,7 +113,10 @@ hit page - + + + + @@ -138,7 +143,16 @@ @@ -262,6 +276,30 @@ - - + + + + + + /.html + + + & + ? + + search-keywords= + + + , + + , + + + + + , + + + + Index: main/plugin-odf-web/plugin.xml =================================================================== --- main/plugin-odf-web/plugin.xml (revision 36748) +++ main/plugin-odf-web/plugin.xml (working copy) @@ -392,6 +392,10 @@ + + + plugin.web:PLUGINS_WEB_SERVICE_FRONT_SEARCH_ADVANCED_SEARCH_DESC + PLUGINS_ODFWEB_SERVICE_SEARCH_FACET_DESC @@ -510,6 +514,10 @@ + + + plugin.web:PLUGINS_WEB_SERVICE_FRONT_SEARCH_ADVANCED_SEARCH_DESC + PLUGINS_ODFWEB_SERVICE_SEARCH_FACET_DESC Index: main/plugin-odf-web/resources/js/search/facet-checkboxes-multi.i18n.js =================================================================== --- main/plugin-odf-web/resources/js/search/facet-checkboxes-multi.i18n.js (revision 36748) +++ main/plugin-odf-web/resources/js/search/facet-checkboxes-multi.i18n.js (working copy) @@ -46,7 +46,7 @@ // Add a 'onchange' listener on each checkbox when DOM is ready // Used to update the facets when an item is selected or deselected; - // Same for the title and textfield input fields + // Same for the title, textfield and advanced search input fields AOMF.onReady = function() { $j.each(_formsData, function(formId, formData) { @@ -56,8 +56,11 @@ AOMF.handleFacetsVisibility($form); $form.find('.field .input .checkbox input').change(AOMF.onFieldChange); - $form.find('#' + 'search-title-' + formData.uniqueId).change(AOMF.onFieldChange); - $form.find('#' + 'search-textfield-' + formData.uniqueId).change(AOMF.onFieldChange); + $form.find('#search-title-' + formData.uniqueId).change(AOMF.onFieldChange); + $form.find('#search-textfield-' + formData.uniqueId).change(AOMF.onFieldChange); + $form.find('#search-all-words-' + formData.uniqueId).change(AOMF.onFieldChange); + $form.find('#search-exact-wording-' + formData.uniqueId).change(AOMF.onFieldChange); + $form.find('#search-no-words-' + formData.uniqueId).change(AOMF.onFieldChange); }); } $j(function() { Index: main/plugin-odf-web/sitemap.xmap =================================================================== --- main/plugin-odf-web/sitemap.xmap (revision 36748) +++ main/plugin-odf-web/sitemap.xmap (working copy) @@ -111,6 +111,7 @@ + @@ -130,6 +131,7 @@ + Index: main/plugin-odf-web/src/org/ametys/plugins/odfweb/generators/AbstractODFSearchGenerator.java =================================================================== --- main/plugin-odf-web/src/org/ametys/plugins/odfweb/generators/AbstractODFSearchGenerator.java (revision 36748) +++ main/plugin-odf-web/src/org/ametys/plugins/odfweb/generators/AbstractODFSearchGenerator.java (working copy) @@ -21,10 +21,12 @@ import java.util.ArrayList; import java.util.BitSet; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.regex.Matcher; import org.apache.avalon.framework.configuration.Configuration; @@ -47,9 +49,11 @@ import org.apache.lucene.queryParser.MultiFieldQueryParser; import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.queryParser.QueryParser; +import org.apache.lucene.queryParser.QueryParser.Operator; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.Searcher; @@ -59,7 +63,6 @@ import org.apache.lucene.store.FSDirectory; import org.apache.tika.io.IOUtils; import org.xml.sax.SAXException; - import org.ametys.cms.contenttype.ContentType; import org.ametys.cms.contenttype.ContentTypeExtensionPoint; import org.ametys.cms.contenttype.MetadataDefinition; @@ -119,6 +122,9 @@ /** The inputs as a Map fieldName -> metadataPath. */ protected Map _searchFields; + /** The search fields to be excluded from some operations. */ + protected Set _excludedFields; + @Override public void service(ServiceManager smanager) throws ServiceException { @@ -139,6 +145,7 @@ public void generate() throws IOException, SAXException, ProcessingException { _searchFields = getSearchFields(); + _excludedFields = getExcludedFields(); super.generate(); } @@ -159,9 +166,32 @@ searchFields.put(fieldName, metadataPath); } + + boolean advancedSearch = parameters.getParameterAsBoolean("advanced-search", false); + if (advancedSearch) + { + searchFields.put("all-words", "all-words"); + searchFields.put("exact-wording", "exact-wording"); + searchFields.put("no-words", "no-words"); + } return searchFields; } + + /** + * Get the search fields to exclude from some operations + * @return The excluded search fields + */ + protected Set getExcludedFields() + { + Set excluded = new HashSet(); + excluded.add("textfield"); + excluded.add("title"); + excluded.add("all-words"); + excluded.add("exact-wording"); + excluded.add("no-words"); + return excluded; + } @Override protected Query getQuery (Request request) throws ParseException, IllegalArgumentException @@ -274,9 +304,12 @@ // Keywords _addTextFieldQuery(query, params.get("textfield"), getBoost("title", serviceId)); + // Advanced search + _addAdvancedSearchQuery(query, params.get("all-words"), params.get("exact-wording"), params.get("no-words")); + for (String fieldName : _searchFields.keySet()) { - if (!fieldName.equals("title") && !fieldName.equals("textfield") && !fieldName.equals("catalog")) + if (!fieldName.equals("catalog") && !_excludedFields.contains(fieldName)) { String value = params.get(fieldName); if (StringUtils.isNotEmpty(value)) @@ -342,7 +375,14 @@ // Keywords String textfield = params.containsKey("textfield") ? params.get("textfield").get(0) : null; _addTextFieldQuery(query, textfield, getBoost("title", serviceId)); + + // Advanced search + String allWords = params.containsKey("all-words") ? params.get("all-words").get(0) : null; + String exactWording = params.containsKey("exact-wording") ? params.get("exact-wording").get(0) : null; + String noWords = params.containsKey("no-words") ? params.get("no-words").get(0) : null; + _addAdvancedSearchQuery(query, allWords, exactWording, noWords); + // WARNING !! // Setting base query for facet data (ie. the query part without the selected criteria) if (facetData != null) { @@ -352,7 +392,7 @@ for (String fieldName : _searchFields.keySet()) { - if (!fieldName.equals("title") && !fieldName.equals("textfield") && !fieldName.equals("catalog")) + if (!fieldName.equals("catalog") && !_excludedFields.contains(fieldName)) { // Real values are expected here (non-empty) List values = params.get(fieldName); @@ -420,7 +460,7 @@ for (String fieldName : _searchFields.keySet()) { - if (!fieldName.equals("title") && !fieldName.equals("textfield")) + if (!_excludedFields.contains(fieldName)) { String metadataPath = _searchFields.get(fieldName); @@ -428,11 +468,10 @@ if (StringUtils.isNotEmpty(metadataPath)) { criterion = getCriterion(fieldName, metadataPath); - } - - if (criterion != null) - { - criteria.put(fieldName, criterion); + if (criterion != null) + { + criteria.put(fieldName, criterion); + } } } } @@ -538,14 +577,12 @@ @Override protected void saxFormFields (Request request, String siteName, String lang) throws SAXException { - if (_searchFields.containsKey("title")) - { - XMLUtils.createElement(contentHandler, "title"); - } - - if (_searchFields.containsKey("textfield")) + for (String excludedField : _excludedFields) { - XMLUtils.createElement(contentHandler, "textfield"); + if (_searchFields.containsKey(excludedField)) + { + XMLUtils.createElement(contentHandler, excludedField); + } } } @@ -748,6 +785,54 @@ } } + private void _addAdvancedSearchQuery(BooleanQuery query, String allWords, String exactWording, String noWords) throws ParseException, IllegalArgumentException + { + if (!StringUtils.isBlank(allWords)) + { + Matcher m = _TEXTFIELD_PATTERN.matcher(allWords); + if (!m.matches()) + { + throw new IllegalArgumentException(allWords + " does not match the expected regular expression : " + _TEXTFIELD_PATTERN.pattern()); + } + + MultiFieldQueryParser parser = new MultiFieldQueryParser(LuceneConstants.LUCENE_VERSION, _fields, _analyser); + parser.setDefaultOperator(Operator.AND); + Query multiQuery = parser.parse(QueryParser.escape(allWords)); + query.add(multiQuery, BooleanClause.Occur.MUST); + } + + if (!StringUtils.isBlank(exactWording)) + { + Matcher m = _TEXTFIELD_PATTERN.matcher(exactWording); + if (!m.matches()) + { + throw new IllegalArgumentException(exactWording + " does not match the expected regular expression : " + _TEXTFIELD_PATTERN.pattern()); + } + + PhraseQuery phraseQuery = new PhraseQuery(); + String[] fields = exactWording.split(" "); + for (String field : fields) + { + phraseQuery.add(new Term(FieldNames.ALL_NOT_ANALYZED, StringUtils.strip(field, ",?!:()[].{}=").toLowerCase())); + } + query.add(phraseQuery, BooleanClause.Occur.MUST); + } + + if (!StringUtils.isBlank(noWords)) + { + Matcher m = _TEXTFIELD_PATTERN.matcher(noWords); + if (!m.matches()) + { + throw new IllegalArgumentException(noWords + " does not match the expected regular expression : " + _TEXTFIELD_PATTERN.pattern()); + } + + MultiFieldQueryParser parser = new MultiFieldQueryParser(LuceneConstants.LUCENE_VERSION, _fields, _analyser); + parser.setDefaultOperator(Operator.AND); + Query multiQuery = parser.parse(QueryParser.escape(noWords)); + query.add(multiQuery, BooleanClause.Occur.MUST_NOT); + } + } + /** * Get the service search configuration * @param serviceId The id of service