[OLINGO-568] improved search in technical service

Signed-off-by: Christian Amend <christian.amend@sap.com>
This commit is contained in:
Klaus Straubinger 2015-11-20 16:43:25 +01:00 committed by Christian Amend
parent 69ef9f5194
commit 8674a1f299
4 changed files with 94 additions and 63 deletions

View File

@ -19,10 +19,11 @@
package org.apache.olingo.fit.tecsvc.client;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.net.URI;
import java.util.List;
import org.apache.olingo.client.api.communication.ODataClientErrorException;
import org.apache.olingo.client.api.communication.request.retrieve.ODataEntitySetRequest;
@ -83,7 +84,7 @@ public class SystemQueryOptionITCase extends AbstractParamTecSvcITCase {
for (int i = 0; i < 5; i++) {
ClientEntity entity = response.getBody().getEntities().get(i);
assertEquals(new Integer(i + 1).toString(), entity.getProperty(PROPERTY_INT16).getValue().toString());
assertShortOrInt(i + 1, entity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
}
}
@ -102,7 +103,7 @@ public class SystemQueryOptionITCase extends AbstractParamTecSvcITCase {
for (int i = 0; i < 10; i++) {
ClientEntity entity = response.getBody().getEntities().get(i);
assertEquals(new Integer(i + 6).toString(), entity.getProperty(PROPERTY_INT16).getValue().toString());
assertShortOrInt(i + 6, entity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
}
}
@ -118,7 +119,7 @@ public class SystemQueryOptionITCase extends AbstractParamTecSvcITCase {
ODataRetrieveResponse<ClientEntitySet> response = request.execute();
saveCookieHeader(response);
assertEquals(0, response.getBody().getEntities().size());
assertTrue(response.getBody().getEntities().isEmpty());
}
@Test
@ -132,15 +133,16 @@ public class SystemQueryOptionITCase extends AbstractParamTecSvcITCase {
ODataRetrieveResponse<ClientEntitySet> response = request.execute();
saveCookieHeader(response);
assertEquals(0, response.getBody().getEntities().size());
assertTrue(response.getBody().getEntities().isEmpty());
}
@Test
public void filterWithTopSkipOrderByAndServerSidePaging() {
public void searchAndFilterWithTopSkipOrderByAndServerSidePaging() {
ODataEntitySetRequest<ClientEntitySet> request = getClient().getRetrieveRequestFactory()
.getEntitySetRequest(getClient().newURIBuilder(SERVICE_URI)
.appendEntitySetSegment(ES_SERVER_SIDE_PAGING)
.filter("PropertyInt16 le 105") // 1, 2, ... , 105
.search("\"Number:\" AND NOT \"106\"") // 1, 2, ..., 105, 107, ...
.filter("PropertyInt16 le 106") // 1, 2, ..., 105
.orderBy("PropertyInt16 desc") // 105, 104, ..., 2, 1
.count(true) // 105
.skip(3) // 102, 101, ..., 2, 1
@ -155,14 +157,14 @@ public class SystemQueryOptionITCase extends AbstractParamTecSvcITCase {
int id = 102;
// Check first 10 entities
// Check first 10 entities.
for (int i = 0; i < 10; i++) {
ClientEntity entity = response.getBody().getEntities().get(i);
assertEquals(new Integer(id).toString(), entity.getProperty(PROPERTY_INT16).getValue().toString());
assertShortOrInt(id, entity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
id--;
}
// Get 3 * 10 = 30 Entities and check the key
// Get 3 * 10 = 30 Entities and check the key.
for (int j = 0; j < 3; j++) {
request = getClient().getRetrieveRequestFactory().getEntitySetRequest(response.getBody().getNext());
setCookieHeader(request);
@ -172,12 +174,12 @@ public class SystemQueryOptionITCase extends AbstractParamTecSvcITCase {
assertEquals(10, response.getBody().getEntities().size());
for (int i = 0; i < 10; i++) {
ClientEntity entity = response.getBody().getEntities().get(i);
assertEquals(new Integer(id).toString(), entity.getProperty(PROPERTY_INT16).getValue().toString());
assertShortOrInt(id, entity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
id--;
}
}
// Get the last 3 items
// Get the last 3 items.
request = getClient().getRetrieveRequestFactory().getEntitySetRequest(response.getBody().getNext());
setCookieHeader(request);
response = request.execute();
@ -186,12 +188,12 @@ public class SystemQueryOptionITCase extends AbstractParamTecSvcITCase {
assertEquals(3, response.getBody().getEntities().size());
for (int i = 0; i < 3; i++) {
ClientEntity entity = response.getBody().getEntities().get(i);
assertEquals(new Integer(id).toString(), entity.getProperty(PROPERTY_INT16).getValue().toString());
assertShortOrInt(id, entity.getProperty(PROPERTY_INT16).getPrimitiveValue().toValue());
id--;
}
// Make sure that the body no not contain a next link
assertEquals(null, response.getBody().getNext());
assertNull(response.getBody().getNext());
}
@Test
@ -299,7 +301,7 @@ public class SystemQueryOptionITCase extends AbstractParamTecSvcITCase {
assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), e.getStatusLine().getStatusCode());
}
}
@Test
public void basicSearch() {
ODataEntitySetRequest<ClientEntitySet> request = getClient().getRetrieveRequestFactory()
@ -308,9 +310,9 @@ public class SystemQueryOptionITCase extends AbstractParamTecSvcITCase {
.search("Second")
.build());
setCookieHeader(request);
ODataRetrieveResponse<ClientEntitySet> response = request.execute();
List<ClientEntity> entities = response.getBody().getEntities();
assertEquals(1, entities.size());
final ODataRetrieveResponse<ClientEntitySet> response = request.execute();
saveCookieHeader(response);
assertEquals(1, response.getBody().getEntities().size());
}
@Test
@ -321,9 +323,9 @@ public class SystemQueryOptionITCase extends AbstractParamTecSvcITCase {
.search("Second AND positive")
.build());
setCookieHeader(request);
ODataRetrieveResponse<ClientEntitySet> response = request.execute();
List<ClientEntity> entities = response.getBody().getEntities();
assertEquals(0, entities.size());
final ODataRetrieveResponse<ClientEntitySet> response = request.execute();
saveCookieHeader(response);
assertTrue(response.getBody().getEntities().isEmpty());
}
@Test
@ -335,7 +337,7 @@ public class SystemQueryOptionITCase extends AbstractParamTecSvcITCase {
.build());
setCookieHeader(request);
ODataRetrieveResponse<ClientEntitySet> response = request.execute();
List<ClientEntity> entities = response.getBody().getEntities();
assertEquals(2, entities.size());
saveCookieHeader(response);
assertEquals(2, response.getBody().getEntities().size());
}
}

View File

@ -471,23 +471,22 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
edmEntitySet.getEntityType();
EntityCollection entitySetInitial = readEntityCollection(uriInfo);
if (entitySetInitial == null) {
entitySetInitial = new EntityCollection();
}
// Modifying the original entitySet means modifying the "database", so we have to make a shallow
// copy of the entity set (new EntitySet, but exactly the same data)
// copy of the entity set (new EntitySet, but exactly the same data).
EntityCollection entitySet = new EntityCollection();
entitySet.getEntities().addAll(entitySetInitial.getEntities());
// Apply system query options
// Apply system query options.
SearchHandler.applySearchSystemQueryOption(uriInfo.getSearchOption(), entitySet);
FilterHandler.applyFilterSystemQuery(uriInfo.getFilterOption(), entitySet, uriInfo, serviceMetadata.getEdm());
CountHandler.applyCountSystemQueryOption(uriInfo.getCountOption(), entitySet);
OrderByHandler.applyOrderByOption(uriInfo.getOrderByOption(), entitySet, uriInfo, serviceMetadata.getEdm());
SkipHandler.applySkipSystemQueryHandler(uriInfo.getSkipOption(), entitySet);
TopHandler.applyTopSystemQueryOption(uriInfo.getTopOption(), entitySet);
SearchHandler.applySearchSystemQueryOption(uriInfo.getSearchOption(), entitySet);
final Integer pageSize = odata.createPreferences(request.getHeaders(HttpHeader.PREFER)).getMaxPageSize();
final Integer serverPageSize = ServerSidePagingHandler.applyServerSidePaging(uriInfo.getSkipTokenOption(),

View File

@ -47,8 +47,8 @@ import org.apache.olingo.server.api.ODataRequest;
import org.apache.olingo.server.api.ODataResponse;
import org.apache.olingo.server.api.ServiceMetadata;
import org.apache.olingo.server.api.deserializer.FixedFormatDeserializer;
import org.apache.olingo.server.api.prefer.PreferencesApplied;
import org.apache.olingo.server.api.prefer.Preferences.Return;
import org.apache.olingo.server.api.prefer.PreferencesApplied;
import org.apache.olingo.server.api.processor.ComplexCollectionProcessor;
import org.apache.olingo.server.api.processor.ComplexProcessor;
import org.apache.olingo.server.api.processor.CountComplexCollectionProcessor;
@ -226,6 +226,9 @@ public class TechnicalPrimitiveComplexProcessor extends TechnicalProcessor
((UriResourceFunction) resourceParts.get(0)).getParameters(), resource), path) :
getPropertyData(entity, path);
// TODO: implement filter on collection properties (on a shallow copy of the values)
// FilterHandler.applyFilterSystemQuery(uriInfo.getFilterOption(), property, uriInfo, serviceMetadata.getEdm());
if (property == null && representationType != RepresentationType.COUNT) {
if (representationType == RepresentationType.VALUE) {
response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());

View File

@ -18,6 +18,13 @@
*/
package org.apache.olingo.server.tecsvc.processor.queryoptions.options;
import java.util.Calendar;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Locale;
import javax.xml.bind.DatatypeConverter;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection;
import org.apache.olingo.commons.api.data.Property;
@ -29,64 +36,83 @@ import org.apache.olingo.server.api.uri.queryoption.search.SearchBinaryOperatorK
import org.apache.olingo.server.api.uri.queryoption.search.SearchExpression;
import org.apache.olingo.server.api.uri.queryoption.search.SearchTerm;
import javax.xml.bind.DatatypeConverter;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Locale;
public class SearchHandler {
private static SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
public static void applySearchSystemQueryOption(final SearchOption searchOption, final EntityCollection entitySet)
public static void applySearchSystemQueryOption(final SearchOption searchOption, EntityCollection entitySet)
throws ODataApplicationException {
if (searchOption != null) {
SearchExpression se = searchOption.getSearchExpression();
Iterator<Entity> it = entitySet.getEntities().iterator();
while(it.hasNext()) {
while (it.hasNext()) {
boolean keep = false;
Entity entity = it.next();
ListIterator<Property> properties = entity.getProperties().listIterator();
while(properties.hasNext() && !keep) {
while (properties.hasNext() && !keep) {
keep = isTrue(se, properties.next());
}
if(!keep) {
if (!keep) {
it.remove();
}
}
}
}
private static boolean isTrue(SearchTerm term, Property property) {
if(property.isPrimitive() && !property.isNull()) {
String propertyString = asString(property);
return propertyString != null && propertyString.contains(term.getSearchTerm());
private static boolean isTrue(final SearchTerm term, final Property property) {
if (property.isNull()) {
return false;
} else if (property.isPrimitive()) {
if (property.isCollection()) {
for (final Object primitive : property.asCollection()) {
final String propertyString = asString(primitive);
if (propertyString != null && propertyString.contains(term.getSearchTerm())) {
return true;
}
}
return false;
} else {
final String propertyString = asString(property.asPrimitive());
return propertyString != null && propertyString.contains(term.getSearchTerm());
}
} else if (property.isComplex()) {
if (property.isCollection()) {
for (final Object member : property.asCollection()) {
if (isTrue(term, (Property) member)) {
return true;
}
}
return false;
} else {
for (final Property innerProperty : property.asComplex().getValue()) {
if (isTrue(term, innerProperty)) {
return true;
}
}
return false;
}
} else {
return false;
}
return false;
}
private static String asString(Property property) {
// TODO: mibo(151117): improve 'string' conversion
Object primitive = property.asPrimitive();
if(primitive instanceof Calendar) {
return SIMPLE_DATE_FORMAT.format(((Calendar) primitive).getTime());
} else if(primitive instanceof Date) {
return SIMPLE_DATE_FORMAT.format((Date) primitive);
} else if(primitive instanceof byte[]) {
private static String asString(final Object primitive) {
// TODO: improve 'string' conversion; maybe consider only String properties
if (primitive instanceof String) {
return (String) primitive;
} else if (primitive instanceof Calendar) {
return DatatypeConverter.printDateTime((Calendar) primitive);
} else if (primitive instanceof byte[]) {
return DatatypeConverter.printBase64Binary((byte[]) primitive);
} else {
return primitive.toString();
}
return primitive.toString();
}
private static boolean isTrue(SearchBinary binary, Property property) throws ODataApplicationException {
private static boolean isTrue(final SearchBinary binary, final Property property) throws ODataApplicationException {
SearchExpression left = binary.getLeftOperand();
SearchExpression right = binary.getRightOperand();
if(binary.getOperator() == SearchBinaryOperatorKind.AND) {
if (binary.getOperator() == SearchBinaryOperatorKind.AND) {
return isTrue(left, property) && isTrue(right, property);
} else if(binary.getOperator() == SearchBinaryOperatorKind.OR) {
} else if (binary.getOperator() == SearchBinaryOperatorKind.OR) {
return isTrue(left, property) || isTrue(right, property);
} else {
throw new ODataApplicationException("Found unknown SearchBinaryOperatorKind: " + binary.getOperator(),
@ -94,12 +120,13 @@ public class SearchHandler {
}
}
private static boolean isTrue(SearchExpression searchExpression, Property property) throws ODataApplicationException {
if(searchExpression.isSearchBinary()) {
private static boolean isTrue(final SearchExpression searchExpression, final Property property)
throws ODataApplicationException {
if (searchExpression.isSearchBinary()) {
return isTrue(searchExpression.asSearchBinary(), property);
} else if(searchExpression.isSearchTerm()) {
} else if (searchExpression.isSearchTerm()) {
return isTrue(searchExpression.asSearchTerm(), property);
} else if(searchExpression.isSearchUnary()) {
} else if (searchExpression.isSearchUnary()) {
return !isTrue(searchExpression.asSearchUnary().getOperand(), property);
}
throw new ODataApplicationException("Found unknown SearchExpression: " + searchExpression,