Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Standardize and improve search functionality #477

Merged
merged 1 commit into from
Mar 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,14 @@ public static Specification<ApplicationEntity> find(
return (final Root<ApplicationEntity> root, final CriteriaQuery<?> cq, final CriteriaBuilder cb) -> {
final List<Predicate> predicates = new ArrayList<>();
if (StringUtils.isNotBlank(name)) {
predicates.add(cb.equal(root.get(ApplicationEntity_.name), name));
predicates.add(
JpaSpecificationUtils.getStringLikeOrEqualPredicate(cb, root.get(ApplicationEntity_.name), name)
);
}
if (StringUtils.isNotBlank(user)) {
predicates.add(cb.equal(root.get(ApplicationEntity_.user), user));
predicates.add(
JpaSpecificationUtils.getStringLikeOrEqualPredicate(cb, root.get(ApplicationEntity_.user), user)
);
}
if (statuses != null && !statuses.isEmpty()) {
final List<Predicate> orPredicates =
Expand All @@ -86,7 +90,9 @@ public static Specification<ApplicationEntity> find(
);
}
if (StringUtils.isNotBlank(type)) {
predicates.add(cb.equal(root.get(ApplicationEntity_.type), type));
predicates.add(
JpaSpecificationUtils.getStringLikeOrEqualPredicate(cb, root.get(ApplicationEntity_.type), type)
);
}
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ public static Specification<ClusterEntity> find(
return (final Root<ClusterEntity> root, final CriteriaQuery<?> cq, final CriteriaBuilder cb) -> {
final List<Predicate> predicates = new ArrayList<>();
if (StringUtils.isNotBlank(name)) {
predicates.add(cb.like(root.get(ClusterEntity_.name), name));
predicates.add(
JpaSpecificationUtils.getStringLikeOrEqualPredicate(cb, root.get(ClusterEntity_.name), name)
);
}
if (minUpdateTime != null) {
predicates.add(cb.greaterThanOrEqualTo(root.get(ClusterEntity_.updated), minUpdateTime));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,14 @@ public static Specification<CommandEntity> find(
return (final Root<CommandEntity> root, final CriteriaQuery<?> cq, final CriteriaBuilder cb) -> {
final List<Predicate> predicates = new ArrayList<>();
if (StringUtils.isNotBlank(name)) {
predicates.add(cb.equal(root.get(CommandEntity_.name), name));
predicates.add(
JpaSpecificationUtils.getStringLikeOrEqualPredicate(cb, root.get(CommandEntity_.name), name)
);
}
if (StringUtils.isNotBlank(user)) {
predicates.add(cb.equal(root.get(CommandEntity_.user), user));
predicates.add(
JpaSpecificationUtils.getStringLikeOrEqualPredicate(cb, root.get(CommandEntity_.user), user)
);
}
if (statuses != null && !statuses.isEmpty()) {
final List<Predicate> orPredicates =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,13 @@ public static Predicate getFindPredicate(
) {
final List<Predicate> predicates = new ArrayList<>();
if (StringUtils.isNotBlank(id)) {
predicates.add(cb.like(root.get(JobEntity_.id), id));
predicates.add(JpaSpecificationUtils.getStringLikeOrEqualPredicate(cb, root.get(JobEntity_.id), id));
}
if (StringUtils.isNotBlank(name)) {
predicates.add(cb.like(root.get(JobEntity_.name), name));
predicates.add(JpaSpecificationUtils.getStringLikeOrEqualPredicate(cb, root.get(JobEntity_.name), name));
}
if (StringUtils.isNotBlank(user)) {
predicates.add(cb.equal(root.get(JobEntity_.user), user));
predicates.add(JpaSpecificationUtils.getStringLikeOrEqualPredicate(cb, root.get(JobEntity_.user), user));
}
if (statuses != null && !statuses.isEmpty()) {
final List<Predicate> orPredicates =
Expand All @@ -107,13 +107,17 @@ public static Predicate getFindPredicate(
predicates.add(cb.equal(root.get(JobEntity_.cluster), cluster));
}
if (StringUtils.isNotBlank(clusterName)) {
predicates.add(cb.equal(root.get(JobEntity_.clusterName), clusterName));
predicates.add(
JpaSpecificationUtils.getStringLikeOrEqualPredicate(cb, root.get(JobEntity_.clusterName), clusterName)
);
}
if (command != null) {
predicates.add(cb.equal(root.get(JobEntity_.command), command));
}
if (StringUtils.isNotBlank(commandName)) {
predicates.add(cb.equal(root.get(JobEntity_.commandName), commandName));
predicates.add(
JpaSpecificationUtils.getStringLikeOrEqualPredicate(cb, root.get(JobEntity_.commandName), commandName)
);
}
if (minStarted != null) {
predicates.add(cb.greaterThanOrEqualTo(root.get(JobEntity_.started), minStarted));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
import com.netflix.genie.core.jpa.entities.CommonFieldsEntity;
import org.apache.commons.lang3.StringUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import javax.validation.constraints.NotNull;
import java.util.Set;

Expand All @@ -36,6 +39,26 @@ public final class JpaSpecificationUtils {
protected JpaSpecificationUtils() {
}

/**
* Create either an equals or like predicate based on the presence of the '%' character in the search value.
*
* @param cb The criteria builder to use for predicate creation
* @param expression The expression of the field the predicate is acting on
* @param value The value to compare the field to
* @return A LIKE predicate if the value contains a '%' otherwise an EQUAL predicate
*/
public static Predicate getStringLikeOrEqualPredicate(
@NotNull final CriteriaBuilder cb,
@NotNull final Expression<String> expression,
@NotNull final String value
) {
if (StringUtils.contains(value, PERCENT)) {
return cb.like(expression, value);
} else {
return cb.equal(expression, value);
}
}

/**
* Get the sorted like statement for tags used in specification queries.
*
Expand All @@ -45,15 +68,15 @@ protected JpaSpecificationUtils() {
public static String getTagLikeString(@NotNull final Set<String> tags) {
final StringBuilder builder = new StringBuilder();
tags.stream()
.filter(StringUtils::isNotBlank)
.sorted(String.CASE_INSENSITIVE_ORDER)
.forEach(
tag -> builder
.append(PERCENT)
.append(CommonFieldsEntity.TAG_DELIMITER)
.append(tag)
.append(CommonFieldsEntity.TAG_DELIMITER)
);
.filter(StringUtils::isNotBlank)
.sorted(String.CASE_INSENSITIVE_ORDER)
.forEach(
tag -> builder
.append(PERCENT)
.append(CommonFieldsEntity.TAG_DELIMITER)
.append(tag)
.append(CommonFieldsEntity.TAG_DELIMITER)
);
return builder.append(PERCENT).toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
*/
package com.netflix.genie.core.jpa.specifications;

import com.netflix.genie.test.categories.UnitTest;
import com.netflix.genie.common.dto.ApplicationStatus;
import com.netflix.genie.core.jpa.entities.ApplicationEntity;
import com.netflix.genie.core.jpa.entities.ApplicationEntity_;
import com.netflix.genie.test.categories.UnitTest;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
Expand Down Expand Up @@ -78,32 +78,38 @@ public void setup() {

final Path<String> namePath = (Path<String>) Mockito.mock(Path.class);
final Predicate equalNamePredicate = Mockito.mock(Predicate.class);
final Predicate likeNamePredicate = Mockito.mock(Predicate.class);
Mockito.when(this.root.get(ApplicationEntity_.name)).thenReturn(namePath);
Mockito.when(this.cb.equal(namePath, NAME)).thenReturn(equalNamePredicate);
Mockito.when(this.cb.like(namePath, NAME)).thenReturn(likeNamePredicate);

final Path<String> userNamePath = (Path<String>) Mockito.mock(Path.class);
final Predicate equalUserNamePredicate = Mockito.mock(Predicate.class);
final Predicate likeUserNamePredicate = Mockito.mock(Predicate.class);
Mockito.when(this.root.get(ApplicationEntity_.user)).thenReturn(userNamePath);
Mockito.when(this.cb.equal(userNamePath, USER_NAME)).thenReturn(equalUserNamePredicate);
Mockito.when(this.cb.like(userNamePath, USER_NAME)).thenReturn(likeUserNamePredicate);

final Path<ApplicationStatus> statusPath = (Path<ApplicationStatus>) Mockito.mock(Path.class);
final Predicate equalStatusPredicate = Mockito.mock(Predicate.class);
Mockito.when(this.root.get(ApplicationEntity_.status)).thenReturn(statusPath);
Mockito.when(this.cb.equal(Mockito.eq(statusPath), Mockito.any(ApplicationStatus.class)))
.thenReturn(equalStatusPredicate);
.thenReturn(equalStatusPredicate);

final Path<String> tagPath = (Path<String>) Mockito.mock(Path.class);
final Predicate likeTagPredicate = Mockito.mock(Predicate.class);
Mockito.when(this.root.get(ApplicationEntity_.tags)).thenReturn(tagPath);
Mockito.when(this.cb.like(Mockito.eq(tagPath), Mockito.any(String.class)))
.thenReturn(likeTagPredicate);
.thenReturn(likeTagPredicate);

this.tagLikeStatement = JpaSpecificationUtils.getTagLikeString(TAGS);

final Path<String> typePath = (Path<String>) Mockito.mock(Path.class);
final Predicate typePredicate = Mockito.mock(Predicate.class);
final Predicate equalTypePredicate = Mockito.mock(Predicate.class);
final Predicate likeTypePredicate = Mockito.mock(Predicate.class);
Mockito.when(this.root.get(ApplicationEntity_.type)).thenReturn(typePath);
Mockito.when(this.cb.equal(typePath, TYPE)).thenReturn(typePredicate);
Mockito.when(this.cb.equal(typePath, TYPE)).thenReturn(equalTypePredicate);
Mockito.when(this.cb.like(typePath, TYPE)).thenReturn(likeTypePredicate);
}

/**
Expand All @@ -123,6 +129,27 @@ public void testFindAll() {
Mockito.verify(this.cb, Mockito.times(1)).equal(this.root.get(ApplicationEntity_.type), TYPE);
}

/**
* Test the find specification.
*/
@Test
public void testFindAllLike() {
final String newName = NAME + "%";
final String newUser = USER_NAME + "%";
final String newType = TYPE + "%";
final Specification<ApplicationEntity> spec
= JpaApplicationSpecs.find(newName, newUser, STATUSES, TAGS, newType);

spec.toPredicate(this.root, this.cq, this.cb);
Mockito.verify(this.cb, Mockito.times(1)).like(this.root.get(ApplicationEntity_.name), newName);
Mockito.verify(this.cb, Mockito.times(1)).like(this.root.get(ApplicationEntity_.user), newUser);
for (final ApplicationStatus status : STATUSES) {
Mockito.verify(this.cb, Mockito.times(1)).equal(this.root.get(ApplicationEntity_.status), status);
}
Mockito.verify(this.cb, Mockito.times(1)).like(this.root.get(ApplicationEntity_.tags), this.tagLikeStatement);
Mockito.verify(this.cb, Mockito.times(1)).like(this.root.get(ApplicationEntity_.type), newType);
}

/**
* Test the find specification.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,10 @@ public void setup() {

final Path<String> clusterNamePath = (Path<String>) Mockito.mock(Path.class);
final Predicate likeNamePredicate = Mockito.mock(Predicate.class);
final Predicate equalNamePredicate = Mockito.mock(Predicate.class);
Mockito.when(this.root.get(ClusterEntity_.name)).thenReturn(clusterNamePath);
Mockito.when(this.cb.like(clusterNamePath, NAME))
.thenReturn(likeNamePredicate);
Mockito.when(this.cb.like(clusterNamePath, NAME)).thenReturn(likeNamePredicate);
Mockito.when(this.cb.equal(clusterNamePath, NAME)).thenReturn(equalNamePredicate);

final Path<Date> minUpdatePath = (Path<Date>) Mockito.mock(Path.class);
final Predicate greaterThanOrEqualToPredicate = Mockito.mock(Predicate.class);
Expand Down Expand Up @@ -161,7 +162,36 @@ public void testFindAll() {

spec.toPredicate(this.root, this.cq, this.cb);
Mockito.verify(this.cb, Mockito.times(1))
.like(this.root.get(ClusterEntity_.name), NAME);
.equal(this.root.get(ClusterEntity_.name), NAME);
Mockito.verify(this.cb, Mockito.times(1))
.greaterThanOrEqualTo(this.root.get(ClusterEntity_.updated), MIN_UPDATE_TIME);
Mockito.verify(this.cb, Mockito.times(1)).lessThan(this.root.get(ClusterEntity_.updated), MAX_UPDATE_TIME);
Mockito.verify(this.cb, Mockito.times(1))
.like(this.root.get(ClusterEntity_.tags), this.tagLikeStatement);
for (final ClusterStatus status : STATUSES) {
Mockito.verify(this.cb, Mockito.times(1))
.equal(this.root.get(ClusterEntity_.status), status);
}
}

/**
* Test the find specification.
*/
@Test
public void testFindAllLike() {
final String newName = NAME + "%";
final Specification<ClusterEntity> spec = JpaClusterSpecs
.find(
newName,
STATUSES,
TAGS,
MIN_UPDATE_TIME,
MAX_UPDATE_TIME
);

spec.toPredicate(this.root, this.cq, this.cb);
Mockito.verify(this.cb, Mockito.times(1))
.like(this.root.get(ClusterEntity_.name), newName);
Mockito.verify(this.cb, Mockito.times(1))
.greaterThanOrEqualTo(this.root.get(ClusterEntity_.updated), MIN_UPDATE_TIME);
Mockito.verify(this.cb, Mockito.times(1)).lessThan(this.root.get(ClusterEntity_.updated), MAX_UPDATE_TIME);
Expand Down Expand Up @@ -190,6 +220,8 @@ public void testFindNoName() {
spec.toPredicate(this.root, this.cq, this.cb);
Mockito.verify(this.cb, Mockito.never())
.like(this.root.get(ClusterEntity_.name), NAME);
Mockito.verify(this.cb, Mockito.never())
.equal(this.root.get(ClusterEntity_.name), NAME);
Mockito.verify(this.cb, Mockito.times(1))
.greaterThanOrEqualTo(this.root.get(ClusterEntity_.updated), MIN_UPDATE_TIME);
Mockito.verify(this.cb, Mockito.times(1)).lessThan(this.root.get(ClusterEntity_.updated), MAX_UPDATE_TIME);
Expand Down Expand Up @@ -217,7 +249,7 @@ public void testFindNoStatuses() {

spec.toPredicate(this.root, this.cq, this.cb);
Mockito.verify(this.cb, Mockito.times(1))
.like(this.root.get(ClusterEntity_.name), NAME);
.equal(this.root.get(ClusterEntity_.name), NAME);
Mockito.verify(this.cb, Mockito.times(1))
.greaterThanOrEqualTo(this.root.get(ClusterEntity_.updated), MIN_UPDATE_TIME);
Mockito.verify(this.cb, Mockito.times(1)).lessThan(
Expand Down Expand Up @@ -246,7 +278,7 @@ public void testFindEmptyStatuses() {

spec.toPredicate(this.root, this.cq, this.cb);
Mockito.verify(this.cb, Mockito.times(1))
.like(this.root.get(ClusterEntity_.name), NAME);
.equal(this.root.get(ClusterEntity_.name), NAME);
Mockito.verify(this.cb, Mockito.times(1))
.greaterThanOrEqualTo(this.root.get(ClusterEntity_.updated), MIN_UPDATE_TIME);
Mockito.verify(this.cb, Mockito.times(1))
Expand Down Expand Up @@ -275,7 +307,7 @@ public void testFindNoTags() {

spec.toPredicate(this.root, this.cq, this.cb);
Mockito.verify(this.cb, Mockito.times(1))
.like(this.root.get(ClusterEntity_.name), NAME);
.equal(this.root.get(ClusterEntity_.name), NAME);
Mockito.verify(this.cb, Mockito.times(1))
.greaterThanOrEqualTo(this.root.get(ClusterEntity_.updated), MIN_UPDATE_TIME);
Mockito.verify(this.cb, Mockito.times(1))
Expand Down Expand Up @@ -304,7 +336,7 @@ public void testFindNoMinTime() {

spec.toPredicate(this.root, this.cq, this.cb);
Mockito.verify(this.cb, Mockito.times(1))
.like(this.root.get(ClusterEntity_.name), NAME);
.equal(this.root.get(ClusterEntity_.name), NAME);
Mockito.verify(this.cb, Mockito.never())
.greaterThanOrEqualTo(this.root.get(ClusterEntity_.updated), MIN_UPDATE_TIME);
Mockito.verify(this.cb, Mockito.times(1))
Expand Down Expand Up @@ -333,7 +365,7 @@ public void testFindNoMax() {

spec.toPredicate(this.root, this.cq, this.cb);
Mockito.verify(this.cb, Mockito.times(1))
.like(this.root.get(ClusterEntity_.name), NAME);
.equal(this.root.get(ClusterEntity_.name), NAME);
Mockito.verify(this.cb, Mockito.times(1))
.greaterThanOrEqualTo(this.root.get(ClusterEntity_.updated), MIN_UPDATE_TIME);
Mockito.verify(this.cb, Mockito.never())
Expand Down Expand Up @@ -363,7 +395,7 @@ public void testFindEmptyTag() {

spec.toPredicate(this.root, this.cq, this.cb);
Mockito.verify(this.cb, Mockito.times(1))
.like(this.root.get(ClusterEntity_.name), NAME);
.equal(this.root.get(ClusterEntity_.name), NAME);
Mockito.verify(this.cb, Mockito.times(1))
.greaterThanOrEqualTo(this.root.get(ClusterEntity_.updated), MIN_UPDATE_TIME);
Mockito.verify(this.cb, Mockito.never())
Expand Down
Loading