submitted by Gwen Harold Autencio
-filtered/advanced search

additional changes to patch:
-applied formatting
-removed unused methods and imports


git-svn-id: https://svn.apache.org/repos/asf/archiva/trunk@696137 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Maria Odea B. Ching 2008-09-17 02:51:40 +00:00
parent dbc8a0f7d9
commit d0771be563
9 changed files with 671 additions and 112 deletions

View File

@ -69,4 +69,21 @@ public interface CrossRepositorySearch
* @return the results.
*/
public SearchResults searchForChecksum( String principal, List<String> selectedRepos, String checksum, SearchResultLimits limits );
/**
* Search for a specific artifact matching the given field values. The search is performed on the bytecode
* index/indices.
*
* @param principal
* @param selectedRepos repository to be searched
* @param groupId groupId to be matched
* @param artifactId artifactId to be matched
* @param version version to be matched
* @param className Java class or package name to be matched
* @param limits the limits to apply to the search results
* @return
*/
public SearchResults executeFilteredSearch( String principal, List<String> selectedRepos, String groupId,
String artifactId, String version, String className,
SearchResultLimits limits );
}

View File

@ -24,6 +24,7 @@ import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
@ -32,16 +33,20 @@ import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.MultiSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryWrapperFilter;
import org.apache.lucene.search.Searchable;
import org.apache.lucene.search.TermQuery;
import org.apache.maven.archiva.configuration.ArchivaConfiguration;
import org.apache.maven.archiva.configuration.ConfigurationNames;
import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
import org.apache.maven.archiva.indexer.ArtifactKeys;
import org.apache.maven.archiva.indexer.RepositoryContentIndex;
import org.apache.maven.archiva.indexer.RepositoryContentIndexFactory;
import org.apache.maven.archiva.indexer.RepositoryIndexException;
import org.apache.maven.archiva.indexer.RepositoryIndexSearchException;
import org.apache.maven.archiva.indexer.bytecode.BytecodeHandlers;
import org.apache.maven.archiva.indexer.bytecode.BytecodeKeys;
import org.apache.maven.archiva.indexer.filecontent.FileContentHandlers;
import org.apache.maven.archiva.indexer.hashcodes.HashcodesHandlers;
import org.apache.maven.archiva.indexer.hashcodes.HashcodesKeys;
@ -57,7 +62,7 @@ import org.slf4j.LoggerFactory;
/**
* DefaultCrossRepositorySearch
*
*
* @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a>
* @version $Id$
* @plexus.component role="org.apache.maven.archiva.indexer.search.CrossRepositorySearch" role-hint="default"
@ -66,12 +71,12 @@ public class DefaultCrossRepositorySearch
implements CrossRepositorySearch, RegistryListener, Initializable
{
private Logger log = LoggerFactory.getLogger( DefaultCrossRepositorySearch.class );
/**
* @plexus.requirement role-hint="lucene"
*/
private RepositoryContentIndexFactory indexFactory;
/**
* @plexus.requirement
*/
@ -79,14 +84,61 @@ public class DefaultCrossRepositorySearch
private List<ManagedRepositoryConfiguration> localIndexedRepositories = new ArrayList<ManagedRepositoryConfiguration>();
public SearchResults searchForChecksum( String principal, List<String> selectedRepos, String checksum, SearchResultLimits limits )
public SearchResults executeFilteredSearch( String principal, List<String> selectedRepos, String groupId,
String artifactId, String version, String className,
SearchResultLimits limits )
{
List<RepositoryContentIndex> indexes = getBytecodeIndexes( principal, selectedRepos );
SearchResults results = new SearchResults();
BooleanQuery booleanQuery = new BooleanQuery();
if ( groupId != null && groupId.length() > 0 )
{
parseAndAdd( booleanQuery, ArtifactKeys.GROUPID, groupId, "\\.|-" );
}
if ( artifactId != null && artifactId.length() > 0 )
{
parseAndAdd( booleanQuery, ArtifactKeys.ARTIFACTID, artifactId, "\\.|-" );
}
if ( version != null && version.length() > 0 )
{
parseAndAdd( booleanQuery, ArtifactKeys.VERSION, version, "\\.|-" );
}
if ( className != null && className.length() > 0 )
{
try
{
QueryParser parser =
new MultiFieldQueryParser( new String[] { BytecodeKeys.CLASSES, BytecodeKeys.FILES,
BytecodeKeys.METHODS }, new BytecodeHandlers().getAnalyzer() );
booleanQuery.add( parser.parse( className ), BooleanClause.Occur.MUST );
}
catch ( ParseException e )
{
}
}
LuceneQuery query = new LuceneQuery( booleanQuery );
results = searchAll( query, limits, indexes, null );
results.getRepositories().add( this.localIndexedRepositories );
return results;
}
public SearchResults searchForChecksum( String principal, List<String> selectedRepos, String checksum,
SearchResultLimits limits )
{
List<RepositoryContentIndex> indexes = getHashcodeIndexes( principal, selectedRepos );
try
{
QueryParser parser = new MultiFieldQueryParser( new String[]{HashcodesKeys.MD5, HashcodesKeys.SHA1},
new HashcodesHandlers().getAnalyzer() );
new HashcodesHandlers().getAnalyzer() );
LuceneQuery query = new LuceneQuery( parser.parse( checksum ) );
SearchResults results = searchAll( query, limits, indexes, null );
results.getRepositories().addAll( this.localIndexedRepositories );
@ -124,23 +176,22 @@ public class DefaultCrossRepositorySearch
return new SearchResults();
}
public SearchResults searchForTerm( String principal, List<String> selectedRepos, String term, SearchResultLimits limits )
{
return searchForTerm( principal, selectedRepos, term, limits, null );
}
public SearchResults searchForTerm( String principal, List<String> selectedRepos, String term,
SearchResultLimits limits, List<String> previousSearchTerms )
{
List<RepositoryContentIndex> indexes = getFileContentIndexes( principal, selectedRepos );
try
{
QueryParser parser = new FileContentHandlers().getQueryParser();
LuceneQuery query = null;
SearchResults results = null;
if( previousSearchTerms == null || previousSearchTerms.isEmpty() )
if ( previousSearchTerms == null || previousSearchTerms.isEmpty() )
{
query = new LuceneQuery( parser.parse( term ) );
results = searchAll( query, limits, indexes, null );
@ -149,17 +200,17 @@ public class DefaultCrossRepositorySearch
{
// AND the previous search terms
BooleanQuery booleanQuery = new BooleanQuery();
for( String previousSearchTerm : previousSearchTerms )
for ( String previousSearchTerm : previousSearchTerms )
{
booleanQuery.add( parser.parse( previousSearchTerm ), BooleanClause.Occur.MUST );
}
query = new LuceneQuery( booleanQuery );
query = new LuceneQuery( booleanQuery );
Filter filter = new QueryWrapperFilter( parser.parse( term ) );
results = searchAll( query, limits, indexes, filter );
}
}
results.getRepositories().addAll( this.localIndexedRepositories );
return results;
}
catch ( ParseException e )
@ -168,9 +219,9 @@ public class DefaultCrossRepositorySearch
}
// empty results.
return new SearchResults();
return new SearchResults();
}
private SearchResults searchAll( LuceneQuery luceneQuery, SearchResultLimits limits, List<RepositoryContentIndex> indexes, Filter filter )
{
org.apache.lucene.search.Query specificQuery = luceneQuery.getLuceneQuery();
@ -203,7 +254,7 @@ public class DefaultCrossRepositorySearch
// Perform the search.
Hits hits = null;
if( filter != null )
if ( filter != null )
{
hits = searcher.search( specificQuery, filter );
}
@ -355,7 +406,7 @@ public class DefaultCrossRepositorySearch
return ret;
}
private boolean indexExists( RepositoryContentIndex index )
{
try
@ -402,6 +453,26 @@ public class DefaultCrossRepositorySearch
}
}
private void parseAndAdd( BooleanQuery query, String key, String value, String delimiter )
{
if ( value != null && value.length() > 0 )
{
String[] terms = value.split( delimiter );
for ( int i = 0; i < terms.length; i++ )
{
Term valueTerm = new Term( key, terms[i] );
Query valueQuery = new TermQuery( valueTerm );
query.add( valueQuery, BooleanClause.Occur.MUST );
}
}
else
{
Term valueTerm = new Term( key, value );
Query valueQuery = new TermQuery( valueTerm );
query.add( valueQuery, BooleanClause.Occur.MUST );
}
}
public void initialize()
throws InitializationException
{

View File

@ -242,6 +242,51 @@ public class DefaultCrossRepositorySearchTest
"org.apache.maven.continuum.web.action.BuildDefinitionAction.isBuildFresh", null, true );
}
public void testExecuteFilteredSearch()
throws Exception
{
CrossRepositorySearch search = lookupCrossRepositorySearch();
String expectedRepos[] = new String[] { TEST_DEFAULT_REPO_ID };
String expectedResults[] = new String[] { "org1", "org2", "org3", "org4", "org5", "org6", "org7", "org8" };
String secondExpectedResults[] = new String[] { "continuum-webapp" };
String thirdExpectedResults[] = new String[] { "archiva-common" };
// search for groupId
assertFilteredSearchResults( expectedRepos, expectedResults, search, "org", null, null, null, 30 );
// search for groupId and artifactId
assertFilteredSearchResults( expectedRepos, secondExpectedResults, search, "org.apache.maven",
"continuum-webapp", null, null, 30 );
// search for groupId , artifactId and version
assertFilteredSearchResults( expectedRepos, thirdExpectedResults, search, "org.apache.maven.archiva",
"archiva-common", "1.0", null, 30 );
}
private void assertFilteredSearchResults ( String expectedRepos[], String expectedResults[], CrossRepositorySearch search,
String groupId, String artifactId, String version, String className , int rowCount )
{
SearchResultLimits limits = new SearchResultLimits( 0 );
limits.setPageSize( rowCount );
List<String> selectedRepos = new ArrayList<String>();
selectedRepos.addAll( Arrays.asList( expectedRepos ) );
SearchResults results = null;
results = search.executeFilteredSearch( "guest" , selectedRepos, groupId, artifactId, version, className, limits );
assertNotNull( "Search Results should not be null.", results );
assertEquals( "Repository Hits", expectedRepos.length, results.getRepositories().size() );
assertEquals( expectedRepos.length, 1);
assertEquals( TEST_DEFAULT_REPO_ID , selectedRepos.get( 0 ) );
assertEquals( "Search Result Hits", expectedResults.length, results.getHits().size() );
}
private void assertSearchResults( String expectedRepos[], String expectedResults[], CrossRepositorySearch search,
String term, List<String> previousSearchTerms, boolean bytecode )
throws Exception
@ -251,11 +296,12 @@ public class DefaultCrossRepositorySearchTest
List<String> selectedRepos = new ArrayList<String>();
selectedRepos.addAll( Arrays.asList( expectedRepos ) );
SearchResults results = null;
if( previousSearchTerms == null )
{
if( bytecode )
{
if( bytecode )
{
results = search.searchForBytecode( "guest", selectedRepos, term, limits );
}
@ -268,16 +314,18 @@ public class DefaultCrossRepositorySearchTest
{
results = search.searchForTerm( "guest", selectedRepos, term, limits, previousSearchTerms );
}
assertNotNull( "Search Results should not be null.", results );
assertEquals( "Repository Hits", expectedRepos.length, results.getRepositories().size() );
// TODO: test the repository ids returned.
assertEquals( "Search Result Hits", expectedResults.length, results.getHits().size() );
// TODO: test the order of hits.
// TODO: test the value of the hits.
}
protected ManagedRepositoryConfiguration createRepository( String id, String name, File location )
{
ManagedRepositoryConfiguration repo = new ManagedRepositoryConfiguration();

View File

@ -1,5 +1,5 @@
package org.apache.maven.archiva.web.action;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@ -23,10 +23,12 @@ import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.opensymphony.xwork.ActionContext;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.archiva.configuration.ArchivaConfiguration;
import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
import org.apache.maven.archiva.database.ArchivaDAO;
import org.apache.maven.archiva.database.Constraint;
import org.apache.maven.archiva.database.constraints.ArtifactsByChecksumConstraint;
@ -42,6 +44,9 @@ import org.apache.maven.archiva.security.PrincipalNotFoundException;
import org.apache.maven.archiva.security.UserRepositories;
import org.codehaus.plexus.xwork.action.PlexusActionSupport;
import com.opensymphony.xwork.ActionContext;
import com.opensymphony.xwork.Preparable;
/**
* Search all indexed fields by the given criteria.
*
@ -49,10 +54,16 @@ import org.codehaus.plexus.xwork.action.PlexusActionSupport;
*/
public class SearchAction
extends PlexusActionSupport
{
implements Preparable
{
/**
* Query string.
*/
private ArchivaConfiguration archivaConfiguration;
private Map<String, ManagedRepositoryConfiguration> managedRepositories;
private String q;
/**
@ -90,27 +101,132 @@ public class SearchAction
private int totalPages;
private boolean searchResultsOnly;
private boolean searchResultsOnly;
private String completeQueryString;
private static final String COMPLETE_QUERY_STRING_SEPARATOR = ";";
private static final String BYTECODE_KEYWORD = "bytecode:";
private List<String> managedRepositoryList;
private String groupId;
private String artifactId;
private String version;
private String className;
private int rowCount = 30;
private String repositoryId;
private boolean fromFilterSearch;
private boolean filterSearch = false;
private boolean fromResultsPage;
private int num;
public boolean isFromResultsPage()
{
return fromResultsPage;
}
public void setFromResultsPage( boolean fromResultsPage )
{
this.fromResultsPage = fromResultsPage;
}
public boolean isFromFilterSearch()
{
return fromFilterSearch;
}
public void setFromFilterSearch( boolean fromFilterSearch )
{
this.fromFilterSearch = fromFilterSearch;
}
public void prepare()
{
managedRepositoryList = new ArrayList<String>();
managedRepositoryList = getObservableRepos();
if ( managedRepositoryList.size() > 0 )
{
managedRepositoryList.add( "all" );
}
}
// advanced search MRM-90 -- filtered search
public String filteredSearch()
throws MalformedURLException, RepositoryIndexException, RepositoryIndexSearchException
{
fromFilterSearch = true;
if ( CollectionUtils.isEmpty( managedRepositoryList ) )
{
return GlobalResults.ACCESS_TO_NO_REPOS;
}
SearchResultLimits limits = new SearchResultLimits( currentPage );
limits.setPageSize( rowCount );
List<String> selectedRepos = new ArrayList<String>();
if ( repositoryId.equals( "all" ) )
{
selectedRepos = getObservableRepos();
}
else
{
selectedRepos.add( repositoryId );
}
if ( CollectionUtils.isEmpty( selectedRepos ) )
{
return GlobalResults.ACCESS_TO_NO_REPOS;
}
results =
crossRepoSearch.executeFilteredSearch( getPrincipal(), selectedRepos, groupId, artifactId, version,
className, limits );
if ( results.isEmpty() )
{
addActionError( "No results found" );
return INPUT;
}
totalPages = results.getTotalHits() / limits.getPageSize();
if ( ( results.getTotalHits() % limits.getPageSize() ) != 0 )
{
totalPages = totalPages + 1;
}
return SUCCESS;
}
public String quickSearch()
throws MalformedURLException, RepositoryIndexException, RepositoryIndexSearchException
{
/* TODO: give action message if indexing is in progress.
* This should be based off a count of 'unprocessed' artifacts.
* This (yet to be written) routine could tell the user that X (unprocessed) artifacts are not yet
* This (yet to be written) routine could tell the user that X (unprocessed) artifacts are not yet
* present in the full text search.
*/
assert q != null && q.length() != 0;
fromFilterSearch = false;
SearchResultLimits limits = new SearchResultLimits( currentPage );
List<String> selectedRepos = getObservableRepos();
if ( CollectionUtils.isEmpty( selectedRepos ) )
{
@ -118,13 +234,13 @@ public class SearchAction
}
if( isBytecodeSearch( q ) )
{
{
results = crossRepoSearch.searchForBytecode( getPrincipal(), selectedRepos, removeKeyword( q ), limits );
}
else
{
if( searchResultsOnly && !completeQueryString.equals( "" ) )
{
{
results = crossRepoSearch.searchForTerm( getPrincipal(), selectedRepos, q, limits, parseCompleteQueryString() );
}
else
@ -133,33 +249,33 @@ public class SearchAction
results = crossRepoSearch.searchForTerm( getPrincipal(), selectedRepos, q, limits );
}
}
if ( results.isEmpty() )
{
addActionError( "No results found" );
return INPUT;
}
totalPages = results.getTotalHits() / limits.getPageSize();
if( (results.getTotalHits() % limits.getPageSize()) != 0 )
{
totalPages = totalPages + 1;
}
// TODO: filter / combine the artifacts by version? (is that even possible with non-artifact hits?)
/* I don't think that we should, as I expect us to utilize the 'score' system in lucene in
/* I don't think that we should, as I expect us to utilize the 'score' system in lucene in
* the future to return relevant links better.
* I expect the lucene scoring system to take multiple hits on different areas of a single document
* to result in a higher score.
* to result in a higher score.
* - Joakim
*/
if( !isEqualToPreviousSearchTerm( q ) )
{
buildCompleteQueryString( q );
}
return SUCCESS;
}
@ -185,10 +301,10 @@ public class SearchAction
if ( databaseResults.size() == 1 )
{
// 1 hit? return it's information directly!
// 1 hit? return it's information directly!
return ARTIFACT;
}
return RESULTS;
}
@ -197,12 +313,12 @@ public class SearchAction
{
return INPUT;
}
private String getPrincipal()
{
return archivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
}
private List<String> getObservableRepos()
{
try
@ -227,44 +343,44 @@ public class SearchAction
private void buildCompleteQueryString( String searchTerm )
{
if( searchTerm.indexOf( COMPLETE_QUERY_STRING_SEPARATOR ) != -1 )
if ( searchTerm.indexOf( COMPLETE_QUERY_STRING_SEPARATOR ) != -1 )
{
searchTerm = StringUtils.remove( searchTerm, COMPLETE_QUERY_STRING_SEPARATOR );
}
if( completeQueryString == null || "".equals( completeQueryString ) )
if ( completeQueryString == null || "".equals( completeQueryString ) )
{
completeQueryString = searchTerm;
}
else
{
{
completeQueryString = completeQueryString + COMPLETE_QUERY_STRING_SEPARATOR + searchTerm;
}
}
private List<String> parseCompleteQueryString()
{
List<String> parsedCompleteQueryString = new ArrayList<String>();
List<String> parsedCompleteQueryString = new ArrayList<String>();
String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR );
CollectionUtils.addAll( parsedCompleteQueryString, parsed );
return parsedCompleteQueryString;
}
private boolean isEqualToPreviousSearchTerm( String searchTerm )
{
if( !"".equals( completeQueryString ) )
if ( !"".equals( completeQueryString ) )
{
String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR );
if( StringUtils.equalsIgnoreCase( searchTerm, parsed[ parsed.length - 1 ] ) )
if ( StringUtils.equalsIgnoreCase( searchTerm, parsed[parsed.length - 1] ) )
{
return true;
}
}
return false;
}
public String getQ()
{
return q;
@ -284,12 +400,12 @@ public class SearchAction
{
return databaseResults;
}
public void setCurrentPage( int page )
{
this.currentPage = page;
}
public int getCurrentPage()
{
return currentPage;
@ -323,23 +439,123 @@ public class SearchAction
public void setCompleteQueryString( String completeQueryString )
{
this.completeQueryString = completeQueryString;
}
}
private boolean isBytecodeSearch( String queryString )
{
if( queryString.startsWith( BYTECODE_KEYWORD ) )
if ( queryString.startsWith( BYTECODE_KEYWORD ) )
{
return true;
return true;
}
return false;
}
private String removeKeyword( String queryString )
{
{
String qString = StringUtils.uncapitalize( queryString );
qString= StringUtils.remove( queryString, BYTECODE_KEYWORD );
qString = StringUtils.remove( queryString, BYTECODE_KEYWORD );
return qString;
}
public ArchivaConfiguration getArchivaConfiguration()
{
return archivaConfiguration;
}
public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
{
this.archivaConfiguration = archivaConfiguration;
}
public Map<String, ManagedRepositoryConfiguration> getManagedRepositories()
{
return getArchivaConfiguration().getConfiguration().getManagedRepositoriesAsMap();
}
public void setManagedRepositories( Map<String, ManagedRepositoryConfiguration> managedRepositories )
{
this.managedRepositories = managedRepositories;
}
public String getGroupId()
{
return groupId;
}
public void setGroupId( String groupId )
{
this.groupId = groupId;
}
public String getArtifactId()
{
return artifactId;
}
public void setArtifactId( String artifactId )
{
this.artifactId = artifactId;
}
public String getVersion()
{
return version;
}
public void setVersion( String version )
{
this.version = version;
}
public int getRowCount()
{
return rowCount;
}
public void setRowCount( int rowCount )
{
this.rowCount = rowCount;
}
public boolean isFilterSearch()
{
return filterSearch;
}
public void setFilterSearch( boolean filterSearch )
{
this.filterSearch = filterSearch;
}
public String getRepositoryId()
{
return repositoryId;
}
public void setRepositoryId( String repositoryId )
{
this.repositoryId = repositoryId;
}
public List<String> getManagedRepositoryList()
{
return managedRepositoryList;
}
public void setManagedRepositoryList( List<String> managedRepositoryList )
{
this.managedRepositoryList = managedRepositoryList;
}
public String getClassName()
{
return className;
}
public void setClassName( String className )
{
this.className = className;
}
}

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one
~ or more contributor license agreements. See the NOTICE file
~ distributed with this work for additional information
~ regarding copyright ownership. The ASF licenses this file
~ to you under the Apache License, Version 2.0 (the
~ "License"); you may not use this file except in compliance
~ with the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing,
~ software distributed under the License is distributed on an
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
~ KIND, either express or implied. See the License for the
~ specific language governing permissions and limitations
~ under the License.
-->
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<validators>
<field name="rowCount">
<field-validator type="int">
<message>Invalid entry</message>
</field-validator>
<field-validator type="int">
<param name="min">1</param>
<message>Row count must be larger than ${min}.</message>
</field-validator>
</field>
</validators>

View File

@ -143,6 +143,16 @@
<action name="index" class="searchAction" method="input">
<result name="input">/WEB-INF/jsp/quickSearch.jsp</result>
</action>
<action name="advancedSearch" class="searchAction" method="input">
<result name="input">/WEB-INF/jsp/advancedSearch.jsp</result>
</action>
<action name="filteredSearch" class="searchAction" method="filteredSearch">
<result name="input">/WEB-INF/jsp/advancedSearch.jsp</result>
<result>/WEB-INF/jsp/results.jsp</result>
<result name="error">/WEB-INF/jsp/quickSearch.jsp</result>
</action>
<action name="quickSearch" class="searchAction" method="quickSearch">
<result name="input">/WEB-INF/jsp/quickSearch.jsp</result>

View File

@ -0,0 +1,67 @@
<%--
~ Licensed to the Apache Software Foundation (ASF) under one
~ or more contributor license agreements. See the NOTICE file
~ distributed with this work for additional information
~ regarding copyright ownership. The ASF licenses this file
~ to you under the Apache License, Version 2.0 (the
~ "License"); you may not use this file except in compliance
~ with the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing,
~ software distributed under the License is distributed on an
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
~ KIND, either express or implied. See the License for the
~ specific language governing permissions and limitations
~ under the License.
--%>
<%@ taglib prefix="ww" uri="/webwork" %>
<html>
<head>
<title>Advanced Search</title>
<ww:head/>
</head>
<ww:if test="%{infoMessage != null}">
<p>${infoMessage}</p>
</ww:if>
<body>
<h1>Advanced Search</h1>
<div id="contentArea">
<div id="searchBox">
<ww:form method="get" action="filteredSearch" validate="true">
<ww:textfield label="Row Count" size="50" name="rowCount"/>
<ww:textfield label="Group Id" size="50" name="groupId"/>
<ww:textfield label="Artifact Id" size="50" name="artifactId"/>
<ww:textfield label="Version" size="50" name="version"/>
<ww:textfield label="Class / Package" size="50" name="className"/>
<ww:select name="repositoryId" label="Repository ID" list="managedRepositoryList"/>
<ww:hidden name="completeQueryString" value="${completeQueryString}"/>
<ww:hidden name="fromFilterSearch" value="${fromFilterSearch}"/>
<ww:submit label="Go!"/>
</ww:form>
<ww:url id="indexUrl" action="index"/>
<ww:a href="%{indexUrl}">
Quick Search Page
</ww:a>
</div>
<script type="text/javascript">
document.getElementById("filteredSearch_groupId").focus();
</script>
<ww:actionerror/>
</div>
</body>
</html>

View File

@ -44,6 +44,11 @@
document.getElementById("quickSearch_q").focus();
</script>
<ww:url id="filteredSearchUrl" action="advancedSearch"/>
<ww:a href="%{filteredSearchUrl}">
Advanced Search
</ww:a>
<p>
<ww:actionerror/>
</p>

View File

@ -30,7 +30,12 @@
<body>
<h1>Search</h1>
<c:if test="${fromFilterSearch == true}">
<h1>Advanced Search</h1>
</c:if>
<c:if test="${fromFilterSearch == false}">
<h1>Search</h1>
</c:if>
<c:url var="imgNextPageUrl" value="/images/icon_next_page.gif"/>
<c:url var="imgPrevPageUrl" value="/images/icon_prev_page.gif"/>
@ -39,20 +44,44 @@
<div id="contentArea">
<div id="searchBox">
<ww:form method="get" action="quickSearch" validate="true">
<ww:textfield label="Search for" size="50" name="q"/>
<ww:checkbox label="Search within results" name="searchResultsOnly"/>
<ww:hidden name="completeQueryString" value="${completeQueryString}"/>
<ww:submit label="Go!"/>
</ww:form>
<script type="text/javascript">
document.getElementById("quickSearch_q").focus();
</script>
<c:if test="${fromFilterSearch == true}">
<ww:form method="get" action="filteredSearch" validate="true">
<ww:textfield label="Row Count" size="50" name="rowCount"/>
<ww:textfield label="Group Id" size="50" name="groupId"/>
<ww:textfield label="Artifact Id" size="50" name="artifactId"/>
<ww:textfield label="Version" size="50" name="version"/>
<ww:textfield label="Class / Package" size="50" name="className"/>
<ww:select name="repositoryId" label="Repository ID" list="managedRepositoryList"/>
<ww:hidden name="completeQueryString" value="${completeQueryString}"/>
<ww:hidden name="fromFilterSearch" value="${fromFilterSearch}"/>
<ww:submit label="Go!"/>
</ww:form>
<ww:url id="indexUrl" action="index"/>
<ww:a href="%{indexUrl}">
Quick Search Page
</ww:a>
<script type="text/javascript">
document.getElementById("filteredSearch_groupId").focus();
</script>
</c:if>
<c:if test="${fromFilterSearch == false}">
<ww:form method="get" action="quickSearch" validate="true">
<ww:textfield label="Search for" size="50" name="q"/>
<ww:checkbox label="Search within results" name="searchResultsOnly"/>
<ww:hidden name="completeQueryString" value="${completeQueryString}"/>
<ww:submit label="Go!"/>
</ww:form>
<script type="text/javascript">
document.getElementById("quickSearch_q").focus();
</script>
</c:if>
<p>
<ww:actionerror/>
</p>
</div>
<h1>Results</h1>
@ -62,10 +91,10 @@
<%-- search was made from the indices --%>
<c:when test="${databaseResults == null}">
<c:set var="hitsNum">${fn:length(results.hits) + (currentPage * results.limits.pageSize)}</c:set>
<c:set var="hitsNum">${fn:length(results.hits) + (currentPage * results.limits.pageSize)}</c:set>
<c:choose>
<c:when test="${results.totalHits > results.limits.pageSize}">
<p>Hits: ${(hitsNum - results.limits.pageSize) + 1} to ${hitsNum} of ${results.totalHits}</p>
<p>Hits: ${(hitsNum - results.limits.pageSize) + 1} to ${hitsNum} of ${results.totalHits}</p>
</c:when>
<c:otherwise>
<p>Hits: 1 to ${hitsNum} of ${results.totalHits}</p>
@ -80,22 +109,55 @@
<%-- Pagination start --%>
<p>
<%-- Prev & Next icons --%>
<c:set var="prevPageUrl">
<ww:url action="quickSearch" namespace="/">
<ww:param name="q" value="%{'${q}'}"/>
<ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/>
<ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/>
<ww:param name="currentPage" value="%{'${currentPage - 1}'}"/>
</ww:url>
</c:set>
<c:set var="nextPageUrl">
<ww:url action="quickSearch" namespace="/">
<ww:param name="q" value="%{'${q}'}"/>
<ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/>
<ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/>
<ww:param name="currentPage" value="%{'${currentPage + 1}'}"/>
</ww:url>
</c:set>
<c:if test="${fromFilterSearch == false}">
<c:set var="prevPageUrl">
<ww:url action="quickSearch" namespace="/">
<ww:param name="q" value="%{'${q}'}"/>
<ww:param name="currentPage" value="%{'${currentPage - 1}'}"/>
</ww:url>
</c:set>
<c:set var="nextPageUrl">
<ww:url action="quickSearch" namespace="/">
<ww:param name="q" value="%{'${q}'}"/>
<ww:param name="currentPage" value="%{'${currentPage + 1}'}"/>
</ww:url>
</c:set>
</c:if>
<c:if test="${fromFilterSearch == true}">
<c:set var="prevPageUrl">
<ww:url action="filteredSearch" namespace="/">
<%-- <ww:param name="q" value="%{'${q}'}"/> --%>
<ww:param name="rowCount" value="%{'${rowCount}'}"/>
<ww:param name="groupId" value="%{'${groupId}'}"/>
<ww:param name="artifactId" value="%{'${artifactId}'}"/>
<ww:param name="version" value="%{'${version}'}"/>
<ww:param name="className" value="%{'${className}'}"/>
<ww:param name="repositoryId" value="%{'${repositoryId}'}"/>
<ww:param name="filterSearch" value="%{'${filterSearch}'}"/>
<ww:param name="fromResultsPage" value="true"/>
<ww:param name="currentPage" value="%{'${currentPage - 1}'}"/>
<ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/>
<ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/>
</ww:url>
</c:set>
<c:set var="nextPageUrl">
<ww:url action="filteredSearch" namespace="/">
<%-- <ww:param name="q" value="%{'${q}'}"/> --%>
<ww:param name="rowCount" value="%{'${rowCount}'}"/>
<ww:param name="groupId" value="%{'${groupId}'}"/>
<ww:param name="artifactId" value="%{'${artifactId}'}"/>
<ww:param name="version" value="%{'${version}'}"/>
<ww:param name="className" value="%{'${className}'}"/>
<ww:param name="repositoryId" value="%{'${repositoryId}'}"/>
<ww:param name="filterSearch" value="%{'${filterSearch}'}"/>
<ww:param name="fromResultsPage" value="true"/>
<ww:param name="currentPage" value="%{'${currentPage + 1}'}"/>
<ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/>
<ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/>
</ww:url>
</c:set>
</c:if>
<c:choose>
<c:when test="${currentPage == 0}">
@ -133,25 +195,54 @@
</c:choose>
<c:forEach var="i" begin="${beginVal}" end="${endVal}">
<c:choose>
<c:when test="${i != currentPage}">
<c:set var="specificPageUrl">
<ww:url action="quickSearch" namespace="/">
<ww:param name="q" value="%{'${q}'}"/>
<ww:param name="currentPage" value="%{'${i}'}"/>
<ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/>
<ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/>
</ww:url>
</c:set>
<a href="${specificPageUrl}">${i + 1}</a>
</c:when>
<c:otherwise>
<b>${i + 1}</b>
</c:otherwise>
</c:choose>
<c:if test="${fromFilterSearch == false}">
<c:choose>
<c:when test="${i != currentPage}">
<c:set var="specificPageUrl">
<ww:url action="quickSearch" namespace="/">
<ww:param name="q" value="%{'${q}'}"/>
<ww:param name="currentPage" value="%{'${i}'}"/>
<ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/>
<ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/>
</ww:url>
</c:set>
<a href="${specificPageUrl}">${i + 1}</a>
</c:when>
<c:otherwise>
<b>${i + 1}</b>
</c:otherwise>
</c:choose>
</c:if>
<c:if test="${fromFilterSearch == true}">
<c:choose>
<c:when test="${i != currentPage}">
<c:set var="specificPageUrl">
<ww:url action="filteredSearch" namespace="/">
<%-- <ww:param name="q" value="%{'${q}'}"/> --%>
<ww:param name="rowCount" value="%{'${rowCount}'}"/>
<ww:param name="groupId" value="%{'${groupId}'}"/>
<ww:param name="artifactId" value="%{'${artifactId}'}"/>
<ww:param name="version" value="%{'${version}'}"/>
<ww:param name="className" value="%{'${className}'}"/>
<ww:param name="repositoryId" value="%{'${repositoryId}'}"/>
<ww:param name="filterSearch" value="%{'${filterSearch}'}"/>
<ww:param name="fromResultsPage" value="true"/>
<ww:param name="currentPage" value="%{'${i}'}"/>
<ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/>
<ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/>
</ww:url>
</c:set>
<a href="${specificPageUrl}">${i + 1}</a>
</c:when>
<c:otherwise>
<b>${i + 1}</b>
</c:otherwise>
</c:choose>
</c:if>
</c:forEach>
<c:choose>
<c:choose>
<c:when test="${currentPage == (totalPages - 1)}">
<img src="${imgNextPageDisabledUrl}"/>
</c:when>