From e8c8ea446cde6a5d1e9585a3509f0bc697e4a683 Mon Sep 17 00:00:00 2001 From: Patrick Schmitz Date: Tue, 6 Jul 2010 15:56:46 +0000 Subject: [PATCH] CSPACE-749, CSPACE-1643, etc. Added keyword search support to vocab and authorities. Added test for same to Person service. Modified the query builder to put the keyword search where clause into parens so this can be safely combined with other searches (like matching the inAuthority). --- .../services/common/query/IQueryManager.java | 2 + .../query/nuxeo/QueryManagerNuxeoImpl.java | 4 +- .../common/vocabulary/AuthorityResource.java | 8 +- .../client/PersonAuthorityClient.java | 10 ++- .../services/client/PersonAuthorityProxy.java | 3 +- .../test/PersonAuthoritySearchTest.java | 85 ++++++++++++++++--- 6 files changed, 94 insertions(+), 18 deletions(-) diff --git a/services/common/src/main/java/org/collectionspace/services/common/query/IQueryManager.java b/services/common/src/main/java/org/collectionspace/services/common/query/IQueryManager.java index 3ac1e2742..e7c1cabea 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/query/IQueryManager.java +++ b/services/common/src/main/java/org/collectionspace/services/common/query/IQueryManager.java @@ -28,6 +28,8 @@ package org.collectionspace.services.common.query; public interface IQueryManager { + final static String SEARCH_GROUP_OPEN = "("; + final static String SEARCH_GROUP_CLOSE = ")"; final static String SEARCH_TERM_SEPARATOR = " "; final static String SEARCH_LIKE = " LIKE "; final static String SEARCH_TYPE_KEYWORDS = "keywords"; diff --git a/services/common/src/main/java/org/collectionspace/services/common/query/nuxeo/QueryManagerNuxeoImpl.java b/services/common/src/main/java/org/collectionspace/services/common/query/nuxeo/QueryManagerNuxeoImpl.java index 972a47638..d6bd14f20 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/query/nuxeo/QueryManagerNuxeoImpl.java +++ b/services/common/src/main/java/org/collectionspace/services/common/query/nuxeo/QueryManagerNuxeoImpl.java @@ -83,7 +83,7 @@ public class QueryManagerNuxeoImpl implements IQueryManager { */ public String createWhereClauseFromKeywords(String keywords) { String result = null; - StringBuffer whereClause = new StringBuffer(); + StringBuffer whereClause = new StringBuffer(SEARCH_GROUP_OPEN); StringTokenizer stringTokenizer = new StringTokenizer(keywords); while (stringTokenizer.hasMoreElements() == true) { whereClause.append(ECM_FULLTEXT_LIKE + "'" + @@ -98,7 +98,7 @@ public class QueryManagerNuxeoImpl implements IQueryManager { } } - result = whereClause.toString(); + result = whereClause.append(SEARCH_GROUP_CLOSE).toString(); if (logger.isDebugEnabled()) { logger.debug("Final built WHERE clause is: " + result); } diff --git a/services/common/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityResource.java b/services/common/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityResource.java index 312f24823..9bda83231 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityResource.java +++ b/services/common/src/main/java/org/collectionspace/services/common/vocabulary/AuthorityResource.java @@ -60,6 +60,7 @@ import org.collectionspace.services.common.document.DocumentWrapper; import org.collectionspace.services.common.repository.RepositoryClient; import org.collectionspace.services.common.security.UnauthorizedException; import org.collectionspace.services.common.query.IQueryManager; +import org.collectionspace.services.common.query.QueryManager; import org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl; import org.jboss.resteasy.plugins.providers.multipart.MultipartInput; import org.jboss.resteasy.plugins.providers.multipart.MultipartOutput; @@ -684,6 +685,7 @@ AbstractMultiPartCollectionSpaceResourceImpl { public AuthItemCommonList getAuthorityItemList( @PathParam("csid") String parentcsid, @QueryParam(IQueryManager.SEARCH_TYPE_PARTIALTERM) String partialTerm, + @QueryParam(IQueryManager.SEARCH_TYPE_KEYWORDS_KW) String keywords, @Context UriInfo ui) { try { MultivaluedMap queryParams = ui.getQueryParameters(); @@ -704,6 +706,9 @@ AbstractMultiPartCollectionSpaceResourceImpl { + IQueryManager.SEARCH_LIKE + "'%" + partialTerm + "%'"; myFilter.appendWhereClause(ptClause, IQueryManager.SEARCH_QUALIFIER_AND); + } else if (keywords != null) { + String kwdClause = QueryManager.createWhereClauseFromKeywords(keywords); + myFilter.appendWhereClause(kwdClause, IQueryManager.SEARCH_QUALIFIER_AND); } if (logger.isDebugEnabled()) { logger.debug("getAuthorityItemList filtered WHERE clause: " @@ -741,6 +746,7 @@ AbstractMultiPartCollectionSpaceResourceImpl { public AuthItemCommonList getAuthorityItemListByAuthName( @PathParam("specifier") String specifier, @QueryParam(IQueryManager.SEARCH_TYPE_PARTIALTERM) String partialTerm, + @QueryParam(IQueryManager.SEARCH_TYPE_KEYWORDS_KW) String keywords, @Context UriInfo ui) { try { MultivaluedMap queryParams = ui.getQueryParameters(); @@ -751,7 +757,7 @@ AbstractMultiPartCollectionSpaceResourceImpl { // Need to get an Authority by name ServiceContext ctx = createServiceContext(queryParams); String parentcsid = getRepositoryClient(ctx).findDocCSID(ctx, whereClause); - return getAuthorityItemList(parentcsid, partialTerm, ui); + return getAuthorityItemList(parentcsid, partialTerm, keywords, ui); } catch (UnauthorizedException ue) { Response response = Response.status( Response.Status.UNAUTHORIZED).entity("Index failed reason " + ue.getErrorReason()).type("text/plain").build(); diff --git a/services/person/client/src/main/java/org/collectionspace/services/client/PersonAuthorityClient.java b/services/person/client/src/main/java/org/collectionspace/services/client/PersonAuthorityClient.java index d01f1213c..695667c20 100644 --- a/services/person/client/src/main/java/org/collectionspace/services/client/PersonAuthorityClient.java +++ b/services/person/client/src/main/java/org/collectionspace/services/client/PersonAuthorityClient.java @@ -194,15 +194,19 @@ public class PersonAuthorityClient extends AbstractServiceClientImpl { } /** - * Read item list, filtering by partial term match. + * Read item list, filtering by partial term match, or keywords. Only one of + * partialTerm or keywords should be specified. If both are specified, keywords + * will be ignored. * * @param vcsid the vcsid * @param partialTerm A partial term on which to match, * which will filter list results to return only matched resources. + * @param keywords A set of keywords on which to match, + * which will filter list results to return only matched resources. * @return the client response */ - public ClientResponse readItemList(String vcsid, String partialTerm) { - return personAuthorityProxy.readItemList(vcsid, partialTerm); + public ClientResponse readItemList(String vcsid, String partialTerm, String keywords) { + return personAuthorityProxy.readItemList(vcsid, partialTerm, keywords); } /** diff --git a/services/person/client/src/main/java/org/collectionspace/services/client/PersonAuthorityProxy.java b/services/person/client/src/main/java/org/collectionspace/services/client/PersonAuthorityProxy.java index 56faaf186..ad3a0eaf0 100644 --- a/services/person/client/src/main/java/org/collectionspace/services/client/PersonAuthorityProxy.java +++ b/services/person/client/src/main/java/org/collectionspace/services/client/PersonAuthorityProxy.java @@ -69,7 +69,8 @@ public interface PersonAuthorityProxy extends CollectionSpaceProxy { @Path("/{csid}/items/") ClientResponsereadItemList( @PathParam("csid") String parentcsid, - @QueryParam (IQueryManager.SEARCH_TYPE_PARTIALTERM) String partialTerm); + @QueryParam (IQueryManager.SEARCH_TYPE_PARTIALTERM) String partialTerm, + @QueryParam(IQueryManager.SEARCH_TYPE_KEYWORDS_KW) String keywords); /** * @param parentcsid diff --git a/services/person/client/src/test/java/org/collectionspace/services/client/test/PersonAuthoritySearchTest.java b/services/person/client/src/test/java/org/collectionspace/services/client/test/PersonAuthoritySearchTest.java index a6558eafc..a8928158a 100644 --- a/services/person/client/src/test/java/org/collectionspace/services/client/test/PersonAuthoritySearchTest.java +++ b/services/person/client/src/test/java/org/collectionspace/services/client/test/PersonAuthoritySearchTest.java @@ -72,6 +72,10 @@ public class PersonAuthoritySearchTest extends BaseServiceTest { // // shortId final String TEST_SHORT_ID = "lechWalesa"; + + final String TEST_KWD_BIRTH_PLACE = "Gdansk"; // Probably wrong on facts + + final String TEST_KWD_NO_MATCH = "Foobar"; // Non-existent partial term name (first letters of each of the words // in a pangram for the English alphabet). @@ -143,6 +147,14 @@ public class PersonAuthoritySearchTest extends BaseServiceTest { } } + private String getKwdTerm() { + return TEST_KWD_BIRTH_PLACE; + } + + private String getKwdTermNonExistent() { + return TEST_KWD_NO_MATCH; + } + @BeforeClass public void setup() { try { @@ -177,7 +189,7 @@ public class PersonAuthoritySearchTest extends BaseServiceTest { if (logger.isDebugEnabled()) { logger.debug("Attempting match on partial term '" + partialTerm + "' ..."); } - numMatchesFound = readItemListByPartialTerm(knownResourceId, partialTerm); + numMatchesFound = readItemListWithFilters(testName, knownResourceId, partialTerm, null); if (logger.isDebugEnabled()) { logger.debug("Found " + numMatchesFound + " match(es), expected " + NUM_MATCHES_EXPECTED + " match(es)."); @@ -203,7 +215,7 @@ public class PersonAuthoritySearchTest extends BaseServiceTest { logger.debug("Attempting match on partial term '" + partialTerm + "' ..."); } numMatchesFound = - readItemListByPartialTerm(knownResourceId, partialTerm); + readItemListWithFilters(testName, knownResourceId, partialTerm, null); if (logger.isDebugEnabled()) { logger.debug("Found " + numMatchesFound + " match(es), expected " + NUM_MATCHES_EXPECTED + " match(es)."); @@ -229,7 +241,7 @@ public class PersonAuthoritySearchTest extends BaseServiceTest { logger.debug("Attempting match on partial term '" + partialTerm + "' ..."); } numMatchesFound = - readItemListByPartialTerm(knownResourceId, partialTerm); + readItemListWithFilters(testName, knownResourceId, partialTerm, null); if (logger.isDebugEnabled()) { logger.debug("Found " + numMatchesFound + " match(es), expected " + NUM_MATCHES_EXPECTED + " match(es)."); @@ -252,7 +264,7 @@ public class PersonAuthoritySearchTest extends BaseServiceTest { if (logger.isDebugEnabled()) { logger.debug("Attempting match on partial term '" + partialTerm + "' ..."); } - numMatchesFound = readItemListByPartialTerm(knownResourceId, partialTerm); + numMatchesFound = readItemListWithFilters(testName, knownResourceId, partialTerm, null); // Zero matches are expected on a non-existent term. if (logger.isDebugEnabled()) { logger.debug("Found " + numMatchesFound + " match(es), expected " + @@ -288,6 +300,28 @@ public class PersonAuthoritySearchTest extends BaseServiceTest { Assert.assertEquals(numMatchesFound, NUM_MATCHES_EXPECTED); } */ + /** + * Reads an item list by partial term. + */ + @Test(dataProvider="testName", dataProviderClass=AbstractServiceTestImpl.class, + groups = {"readListByKwdTerm"}, dependsOnGroups = {"readListByPartialTerm"}) + public void keywordTermMatch(String testName) { + if (logger.isDebugEnabled()) { + logger.debug(testBanner(testName, CLASS_NAME)); + } + int numMatchesFound = 0; + String kwdTerm = getKwdTerm(); + if (logger.isDebugEnabled()) { + logger.debug("Attempting match on kwd term '" + kwdTerm + "' ..."); + } + numMatchesFound = readItemListWithFilters(testName, knownResourceId, null, kwdTerm); + if (logger.isDebugEnabled()) { + logger.debug("Found " + numMatchesFound + " match(es), expected " + + NUM_MATCHES_EXPECTED + " match(es)."); + } + Assert.assertEquals(numMatchesFound, NUM_MATCHES_EXPECTED); + } + // Failure outcomes @@ -307,7 +341,7 @@ public class PersonAuthoritySearchTest extends BaseServiceTest { if (logger.isDebugEnabled()) { logger.debug("Attempting match on partial term '" + partialTerm + "' ..."); } - numMatchesFound = readItemListByPartialTerm(knownResourceId, partialTerm); + numMatchesFound = readItemListWithFilters(testName, knownResourceId, partialTerm, null); // Zero matches are expected on a non-existent term. if (logger.isDebugEnabled()) { logger.debug("Found " + numMatchesFound + " match(es), expected " + @@ -317,16 +351,44 @@ public class PersonAuthoritySearchTest extends BaseServiceTest { } /** - * Reads an item list by partial term, given an authority and a term. - * + * Reads an item list by partial term, with a partial term that is not + * expected to be matched by any term in any resource. + */ + @Test(dataProvider="testName", dataProviderClass=AbstractServiceTestImpl.class, + groups = {"readListByKwdTerm"}, dependsOnMethods = {"keywordTermMatch"}) + public void keywordTermMatchOnNonexistentTerm(String testName) { + if (logger.isDebugEnabled()) { + logger.debug(testBanner(testName, CLASS_NAME)); + } + int numMatchesFound = 0; + int ZERO_MATCHES_EXPECTED = 0; + String kwdTerm = getKwdTermNonExistent(); + if (logger.isDebugEnabled()) { + logger.debug("Attempting match on kwd term '" + kwdTerm + "' ..."); + } + numMatchesFound = readItemListWithFilters(testName, knownResourceId, null, kwdTerm); + // Zero matches are expected on a non-existent term. + if (logger.isDebugEnabled()) { + logger.debug("Found " + numMatchesFound + " match(es), expected " + + ZERO_MATCHES_EXPECTED + " match(es)."); + } + Assert.assertEquals(numMatchesFound, ZERO_MATCHES_EXPECTED); + } + + /** + * Reads an item list by partial term or keywords, given an authority and a term. + * Only one of partialTerm or keywords should be specified. + * If both are specified, keywords will be ignored. + * + * @param testName Calling test name * @param authorityCsid The CSID of the authority within which partial term matching * will be performed. * @param partialTerm A partial term to match item resources. + * @param partialTerm A keyword list to match item resources. * @return The number of item resources matched by the partial term. */ - private int readItemListByPartialTerm(String authorityCsid, String partialTerm) { - - String testName = "readItemListByPartialTerm"; + private int readItemListWithFilters(String testName, + String authorityCsid, String partialTerm, String keywords) { // Perform setup. int expectedStatusCode = Response.Status.OK.getStatusCode(); @@ -337,7 +399,7 @@ public class PersonAuthoritySearchTest extends BaseServiceTest { PersonAuthorityClient client = new PersonAuthorityClient(); ClientResponse res = null; if (authorityCsid != null) { - res = client.readItemList(authorityCsid, partialTerm); + res = client.readItemList(authorityCsid, partialTerm, keywords); } else { Assert.fail("readItemListByPartialTerm passed null csid!"); } @@ -502,6 +564,7 @@ public class PersonAuthoritySearchTest extends BaseServiceTest { partialTermPersonMap.put(PersonJAXBSchema.DISPLAY_NAME, TEST_PARTIAL_TERM_DISPLAY_NAME); partialTermPersonMap.put(PersonJAXBSchema.FORE_NAME, TEST_PARTIAL_TERM_FORE_NAME); partialTermPersonMap.put(PersonJAXBSchema.SUR_NAME, TEST_PARTIAL_TERM_SUR_NAME); + partialTermPersonMap.put(PersonJAXBSchema.BIRTH_PLACE, TEST_KWD_BIRTH_PLACE); partialTermPersonMap.put(PersonJAXBSchema.GENDER, "male"); MultipartOutput multipart = PersonAuthorityClientUtils.createPersonInstance(authorityCsid, authRefName, partialTermPersonMap, -- 2.47.3