diff --git a/config/alfresco/messages/webclient.properties b/config/alfresco/messages/webclient.properties index 5438f502c4..184886fdfb 100644 --- a/config/alfresco/messages/webclient.properties +++ b/config/alfresco/messages/webclient.properties @@ -162,7 +162,7 @@ none=None no_selected_items=No selected items. search_select_item=Search for and select an item. search_select_items=Search for and select items. -search_minimum=Please enter a minimum of {0} characters to perform a search. +search_minimum=Not enough information was entered to perform a search, at least one value must be entered or a location selected to search within. Text fields require a minimum of {0} characters. filter=Filter choose_icon=Choose icon security=Security diff --git a/source/java/org/alfresco/web/bean/AdvancedSearchBean.java b/source/java/org/alfresco/web/bean/AdvancedSearchBean.java index 7907fcefdd..82931b0cdc 100644 --- a/source/java/org/alfresco/web/bean/AdvancedSearchBean.java +++ b/source/java/org/alfresco/web/bean/AdvancedSearchBean.java @@ -614,148 +614,145 @@ public class AdvancedSearchBean */ public String search() { - if (this.text != null && this.text.length() != 0) + // construct the Search Context and set on the navigation bean + // then simply navigating to the browse screen will cause it pickup the Search Context + SearchContext search = new SearchContext(); + + search.setText(this.text); + + if (this.mode.equals(MODE_ALL)) { - // construct the Search Context and set on the navigation bean - // then simply navigating to the browse screen will cause it pickup the Search Context - SearchContext search = new SearchContext(); - - search.setText(this.text); - - if (this.mode.equals(MODE_ALL)) - { - search.setMode(SearchContext.SEARCH_ALL); - } - else if (this.mode.equals(MODE_FILES_TEXT)) - { - search.setMode(SearchContext.SEARCH_FILE_NAMES_CONTENTS); - } - else if (this.mode.equals(MODE_FILES)) - { - search.setMode(SearchContext.SEARCH_FILE_NAMES); - } - else if (this.mode.equals(MODE_FOLDERS)) - { - search.setMode(SearchContext.SEARCH_SPACE_NAMES); - } - - // additional attributes search - if (this.description != null && this.description.length() != 0) - { - search.addAttributeQuery(ContentModel.PROP_DESCRIPTION, this.description); - } - if (this.title != null && this.title.length() != 0) - { - search.addAttributeQuery(ContentModel.PROP_TITLE, this.title); - } - if (this.author != null && this.author.length() != 0) - { - search.addAttributeQuery(ContentModel.PROP_AUTHOR, this.author); - } - if (this.contentFormat != null && this.contentFormat.length() != 0) - { - search.setMimeType(this.contentFormat); - } - if (this.createdDateChecked == true) - { - SimpleDateFormat df = CachingDateFormat.getDateFormat(); - String strCreatedDate = df.format(this.createdDateFrom); - String strCreatedDateTo = df.format(this.createdDateTo); - search.addRangeQuery(ContentModel.PROP_CREATED, strCreatedDate, strCreatedDateTo, true); - } - if (this.modifiedDateChecked == true) - { - SimpleDateFormat df = CachingDateFormat.getDateFormat(); - String strModifiedDate = df.format(this.modifiedDateFrom); - String strModifiedDateTo = df.format(this.modifiedDateTo); - search.addRangeQuery(ContentModel.PROP_MODIFIED, strModifiedDate, strModifiedDateTo, true); - } - - // walk each of the custom properties add add them as additional attributes - for (String qname : this.customProperties.keySet()) - { - Object value = this.customProperties.get(qname); - DataTypeDefinition typeDef = getCustomPropertyLookup().get(qname); - if (typeDef != null) - { - QName typeName = typeDef.getName(); - if (DataTypeDefinition.DATE.equals(typeName) || DataTypeDefinition.DATETIME.equals(typeName)) - { - // only apply date to search if the user has checked the enable checkbox - if (value != null && Boolean.valueOf(value.toString()) == true) - { - SimpleDateFormat df = CachingDateFormat.getDateFormat(); - String strDateFrom = df.format(this.customProperties.get( - UISearchCustomProperties.PREFIX_DATE_FROM + qname)); - String strDateTo = df.format(this.customProperties.get( - UISearchCustomProperties.PREFIX_DATE_TO + qname)); - search.addRangeQuery(QName.createQName(qname), strDateFrom, strDateTo, true); - } - } - else if (DataTypeDefinition.BOOLEAN.equals(typeName)) - { - if (((Boolean)value) == true) - { - search.addFixedValueQuery(QName.createQName(qname), value.toString()); - } - } - else if (DataTypeDefinition.NODE_REF.equals(typeName) || DataTypeDefinition.CATEGORY.equals(typeName)) - { - if (value != null) - { - search.addFixedValueQuery(QName.createQName(qname), value.toString()); - } - } - else if (DataTypeDefinition.INT.equals(typeName) || DataTypeDefinition.LONG.equals(typeName) || - DataTypeDefinition.FLOAT.equals(typeName) || DataTypeDefinition.DOUBLE.equals(typeName)) - { - String strVal = value.toString(); - if (strVal != null && strVal.length() != 0) - { - search.addFixedValueQuery(QName.createQName(qname), strVal); - } - } - else - { - // by default use toString() value - this is for text fields and unknown types - String strVal = value.toString(); - if (strVal != null && strVal.length() != 0) - { - search.addAttributeQuery(QName.createQName(qname), strVal); - } - } - } - } - - // location path search - if (this.lookin.equals(LOOKIN_OTHER) && this.location != null) - { - search.setLocation(SearchContext.getPathFromSpaceRef(this.location, this.locationChildren)); - } - - // category path search - if (this.categories.size() != 0) - { - String[] paths = new String[this.categories.size()]; - for (int i=0; iemptyList(); @@ -588,9 +591,7 @@ public class BrowseBean implements IContextListener return; } - // get the searcher object and perform the search of the root node - String query = searchContext.buildQuery(); - + // perform the search against the repo UserTransaction tx = null; ResultSet results = null; try diff --git a/source/java/org/alfresco/web/bean/SearchContext.java b/source/java/org/alfresco/web/bean/SearchContext.java index ab5b622893..357782371a 100644 --- a/source/java/org/alfresco/web/bean/SearchContext.java +++ b/source/java/org/alfresco/web/bean/SearchContext.java @@ -117,82 +117,91 @@ public final class SearchContext implements Serializable /** * Build the search query string based on the current search context members. * + * @param minimum small possible textual string used for a match + * this does not effect fixed values searches (e.g. boolean, int values) or date ranges + * * @return prepared search query string */ - public String buildQuery() + public String buildQuery(int minimum) { String query; + boolean validQuery = false; // the QName for the well known "name" attribute String nameAttr = Repository.escapeQName(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, ELEMENT_NAME)); // match against content text String text = this.text.trim(); - String fullTextQuery; - String nameAttrQuery; + String fullTextQuery = null; + String nameAttrQuery = null; - if (text.indexOf(' ') == -1) + if (text.length() >= minimum) { - // simple single word text search - if (text.charAt(0) != '*') + if (text.indexOf(' ') == -1) { - // escape characters and append the wildcard character - String safeText = QueryParser.escape(text); - fullTextQuery = " TEXT:" + safeText + '*'; - nameAttrQuery = " @" + nameAttr + ":" + safeText + '*'; - } - else - { - // found a leading wildcard - prepend it again after escaping the other characters - String safeText = QueryParser.escape(text.substring(1)); - fullTextQuery = " TEXT:*" + safeText + '*'; - nameAttrQuery = " @" + nameAttr + ":*" + safeText + '*'; - } - } - else - { - // multiple word search - if (text.charAt(0) == '"' && text.charAt(text.length() - 1) == '"') - { - // as quoted phrase - String quotedSafeText = '"' + QueryParser.escape(text.substring(1, text.length() - 1)) + '"'; - fullTextQuery = " TEXT:" + quotedSafeText; - nameAttrQuery = " @" + nameAttr + ":" + quotedSafeText; - } - else - { - // as individual search terms - StringTokenizer t = new StringTokenizer(text, " "); - StringBuilder fullTextBuf = new StringBuilder(64); - StringBuilder nameAttrBuf = new StringBuilder(64); - fullTextBuf.append('('); - nameAttrBuf.append('('); - while (t.hasMoreTokens()) + // simple single word text search + if (text.charAt(0) != '*') { - String term = t.nextToken(); - if (term.charAt(0) != '*') - { - String safeTerm = QueryParser.escape(term); - fullTextBuf.append("TEXT:").append(safeTerm).append('*'); - nameAttrBuf.append("@").append(nameAttr).append(":").append(safeTerm).append('*'); - } - else - { - String safeTerm = QueryParser.escape(term.substring(1)); - fullTextBuf.append("TEXT:*").append(safeTerm).append('*'); - nameAttrBuf.append("@").append(nameAttr).append(":*").append(safeTerm).append('*'); - } - if (t.hasMoreTokens()) - { - fullTextBuf.append(" OR "); - nameAttrBuf.append(" OR "); - } + // escape characters and append the wildcard character + String safeText = QueryParser.escape(text); + fullTextQuery = " TEXT:" + safeText + '*'; + nameAttrQuery = " @" + nameAttr + ":" + safeText + '*'; + } + else + { + // found a leading wildcard - prepend it again after escaping the other characters + String safeText = QueryParser.escape(text.substring(1)); + fullTextQuery = " TEXT:*" + safeText + '*'; + nameAttrQuery = " @" + nameAttr + ":*" + safeText + '*'; } - fullTextBuf.append(')'); - nameAttrBuf.append(')'); - fullTextQuery = fullTextBuf.toString(); - nameAttrQuery = nameAttrBuf.toString(); } + else + { + // multiple word search + if (text.charAt(0) == '"' && text.charAt(text.length() - 1) == '"') + { + // as quoted phrase + String quotedSafeText = '"' + QueryParser.escape(text.substring(1, text.length() - 1)) + '"'; + fullTextQuery = " TEXT:" + quotedSafeText; + nameAttrQuery = " @" + nameAttr + ":" + quotedSafeText; + } + else + { + // as individual search terms + StringTokenizer t = new StringTokenizer(text, " "); + StringBuilder fullTextBuf = new StringBuilder(64); + StringBuilder nameAttrBuf = new StringBuilder(64); + fullTextBuf.append('('); + nameAttrBuf.append('('); + while (t.hasMoreTokens()) + { + String term = t.nextToken(); + if (term.charAt(0) != '*') + { + String safeTerm = QueryParser.escape(term); + fullTextBuf.append("TEXT:").append(safeTerm).append('*'); + nameAttrBuf.append("@").append(nameAttr).append(":").append(safeTerm).append('*'); + } + else + { + String safeTerm = QueryParser.escape(term.substring(1)); + fullTextBuf.append("TEXT:*").append(safeTerm).append('*'); + nameAttrBuf.append("@").append(nameAttr).append(":*").append(safeTerm).append('*'); + } + if (t.hasMoreTokens()) + { + fullTextBuf.append(" OR "); + nameAttrBuf.append(" OR "); + } + } + fullTextBuf.append(')'); + nameAttrBuf.append(')'); + fullTextQuery = fullTextBuf.toString(); + nameAttrQuery = nameAttrBuf.toString(); + } + } + + validQuery = true; } // match a specific PATH for space location or categories @@ -224,10 +233,19 @@ public final class SearchContext implements Serializable attributeQuery = new StringBuilder(queryAttributes.size() << 6); for (QName qname : queryAttributes.keySet()) { - String escapedName = Repository.escapeQName(qname); - String value = QueryParser.escape(queryAttributes.get(qname)); - attributeQuery.append(" +@").append(escapedName) - .append(":").append(value).append('*'); + String value = queryAttributes.get(qname).trim(); + if (value.length() >= minimum) + { + String escapedName = Repository.escapeQName(qname); + attributeQuery.append(" +@").append(escapedName) + .append(":").append(QueryParser.escape(value)).append('*'); + } + } + + // handle the case where we did not add any attributes due to minimum length restrictions + if (attributeQuery.length() == 0) + { + attributeQuery = null; } } @@ -283,38 +301,64 @@ public final class SearchContext implements Serializable String fileTypeQuery; if (contentType != null) { - fileTypeQuery = " +TYPE:\"" + contentType + "\" "; + fileTypeQuery = " TYPE:\"" + contentType + "\" "; } else { // default to cm:content - fileTypeQuery = " +TYPE:\"{" + NamespaceService.CONTENT_MODEL_1_0_URI + "}content\" "; + fileTypeQuery = " TYPE:\"{" + NamespaceService.CONTENT_MODEL_1_0_URI + "}content\" "; } // match against FOLDER type - String folderTypeQuery = " +TYPE:\"{" + NamespaceService.CONTENT_MODEL_1_0_URI + "}folder\" "; + String folderTypeQuery = " TYPE:\"{" + NamespaceService.CONTENT_MODEL_1_0_URI + "}folder\" "; - switch (mode) + if (text.length() >= minimum) { - case SearchContext.SEARCH_ALL: - query = '(' + fileTypeQuery + " AND " + '(' + nameAttrQuery + fullTextQuery + ')' + ')' + " OR " + - '(' + folderTypeQuery + " AND " + nameAttrQuery + ')'; - break; - - case SearchContext.SEARCH_FILE_NAMES: - query = fileTypeQuery + " AND " + nameAttrQuery; - break; - - case SearchContext.SEARCH_FILE_NAMES_CONTENTS: - query = fileTypeQuery + " AND " + '(' + nameAttrQuery + fullTextQuery + ')'; - break; - - case SearchContext.SEARCH_SPACE_NAMES: - query = folderTypeQuery + " AND " + nameAttrQuery; - break; - - default: - throw new IllegalStateException("Unknown search mode specified: " + mode); + // text query for name and/or full text specified + switch (mode) + { + case SearchContext.SEARCH_ALL: + query = '(' + fileTypeQuery + " AND " + '(' + nameAttrQuery + fullTextQuery + ')' + ')' + " OR " + + '(' + folderTypeQuery + " AND " + nameAttrQuery + ')'; + break; + + case SearchContext.SEARCH_FILE_NAMES: + query = fileTypeQuery + " AND " + nameAttrQuery; + break; + + case SearchContext.SEARCH_FILE_NAMES_CONTENTS: + query = fileTypeQuery + " AND " + '(' + nameAttrQuery + fullTextQuery + ')'; + break; + + case SearchContext.SEARCH_SPACE_NAMES: + query = folderTypeQuery + " AND " + nameAttrQuery; + break; + + default: + throw new IllegalStateException("Unknown search mode specified: " + mode); + } + } + else + { + // no text query specified - must be an attribute/value query only + switch (mode) + { + case SearchContext.SEARCH_ALL: + query = '(' + fileTypeQuery + " OR " + folderTypeQuery + ')'; + break; + + case SearchContext.SEARCH_FILE_NAMES: + case SearchContext.SEARCH_FILE_NAMES_CONTENTS: + query = fileTypeQuery; + break; + + case SearchContext.SEARCH_SPACE_NAMES: + query = folderTypeQuery; + break; + + default: + throw new IllegalStateException("Unknown search mode specified: " + mode); + } } // match entire query against any additional attributes specified @@ -329,6 +373,14 @@ public final class SearchContext implements Serializable query = "(" + pathQuery + ") AND (" + query + ')'; } + // check that we have a query worth executing - if we have no attributes, paths or text/name search + // then we'll only have a search against files/type TYPE which does nothing by itself! + validQuery = validQuery | (attributeQuery != null) | (pathQuery != null); + if (validQuery == false) + { + query = null; + } + if (logger.isDebugEnabled()) logger.debug("Query: " + query); @@ -589,7 +641,7 @@ public final class SearchContext implements Serializable { root.addElement(ELEMENT_CONTENT_TYPE).addText(this.contentType); } - if (this.mimeType != null) + if (this.mimeType != null && this.mimeType.length() != 0) { root.addElement(ELEMENT_MIMETYPE).addText(this.mimeType); } diff --git a/source/web/jsp/dialog/advanced-search.jsp b/source/web/jsp/dialog/advanced-search.jsp index d621deca16..d3704146d7 100644 --- a/source/web/jsp/dialog/advanced-search.jsp +++ b/source/web/jsp/dialog/advanced-search.jsp @@ -33,19 +33,6 @@ function pageLoaded() { document.getElementById("advsearch:search-text").focus(); - checkButtonState(); - } - - function checkButtonState() - { - if (document.getElementById("advsearch:search-text").value.length == 0 ) - { - document.getElementById("advsearch:search-button").disabled = true; - } - else - { - document.getElementById("advsearch:search-button").disabled = false; - } } @@ -147,8 +134,7 @@ :  -  * +