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

Issue 1197 add memberof operator #1291

Merged
merged 19 commits into from
May 8, 2020
Merged

Conversation

Chandrasekar-Rajasekar
Copy link
Collaborator

Resolves #1197

Description

Add the member of operation for only Element Collection attributes for root entity or toOne relationship entity.
filter[book]=awards=hasmember='Booker Prize'
filter[book]=publisher.phoneNumbers=hasmember='999-999-9999'

Enables to filter on collection attribute

License

I confirm that this contribution is made under an Apache 2.0 license and that I have the authority necessary to make this contribution on behalf of its copyright owner.

@@ -59,6 +59,12 @@ public static boolean toManyInPathExceptLastPathElement(EntityDictionary diction
.anyMatch(RelationshipType::isToMany);
}

public static boolean checkLastPathElementType(EntityDictionary dictionary, Path path, Class<?> clz) {
int pathLength = path.getPathElements().size();
PathElement lastPathElement = path.getPathElements().get(pathLength - 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a method in Path to get the last element.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check out in the same file:

    public static boolean toManyInPath(EntityDictionary dictionary, Path path) {
        return path.getPathElements().stream()
                .map(element -> dictionary.getRelationshipType(element.getType(), element.getFieldName()))
                .anyMatch(RelationshipType::isToMany);
    }

    public static boolean toManyInPathExceptLastPathElement(EntityDictionary dictionary, Path path) {
        int pathLength = path.getPathElements().size();
        return path.getPathElements().stream()
                .limit(pathLength - 1)
                .map(element -> dictionary.getRelationshipType(element.getType(), element.getFieldName()))
                .anyMatch(RelationshipType::isToMany);
    }

Let's add a method that looks like one of these (toManyInLastPathElement):

throw new InvalidPredicateException("HasMember can only take one argument");
}
Object val = getFieldValue(entity, field, requestScope);
String filterStr = CoerceUtil.coerce(values.get(0), String.class);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

coerce into val.getClass(). We probably want to support lists of numbers too.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Object val will be an instance of Collection. To get the parameterized type of collection, I think we need to iterate over the path and get paremeterizedType from entityDict. I am not sure how to get parameterized type from Collection object

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, we'll need to iterate over it.
Another alternative is to convert field into a Path object in Elide. That will do all the type conversion for you.

@@ -111,12 +112,19 @@ public FilterExpression parseGlobalExpression(String path, MultivaluedMap<String
throw new ParseException(String.format("Invalid predicate: %s", filterPredicate));
}

if ((filterPredicate.getOperator().equals(Operator.HASMEMBER)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check seems redundant with the validation logic below. Do we also need this check?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is for Global Filter. The validation logic below is for the typed filter. Previously we did not have toMany relationship check for global filter. So I decided to include only Iscollection check for global filter.

Without this check in global filter, we allow filter expr like book?filter=title=hasmember='1' to pass through and jpql rejects these statement.

@@ -170,6 +172,21 @@
return String.format("%s IS NOT EMPTY", columnAlias);
});

operatorGenerators.put(HASMEMBER, (columnAlias, params) -> {
Preconditions.checkArgument(params.size() == 1);
String x = String.format("%s MEMBER OF %s",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove the local variable here?

@@ -40,7 +40,7 @@

<properties>
<dataStoreHarness>com.yahoo.elide.datastores.hibernate3.HibernateDataStoreHarness</dataStoreHarness>
<excludeTags>emptyOnAttributeCollection</excludeTags>
<excludeTags>emptyOnAttributeCollection,memberOfOperation</excludeTags>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Elide 5 - we make tags for stores (rather than features).

result = getAsNode(String.format("/book?filter[book.awards][hasmember]=%s", filterString));
assertEquals(awardBook.size(), result.get("data").size());
for (JsonNode book : result.get("data")) {
assertTrue(awardBook.contains(book.get("id")));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a much cleaner way to do these comparisons with hamcrest Matchers. There are some good examples in PaginationIT.

@Chandrasekar-Rajasekar Chandrasekar-Rajasekar force-pushed the ISSUE-1197_add_memberof_operator branch from f9833ba to c9b9244 Compare April 27, 2020 23:27
@coveralls
Copy link
Collaborator

coveralls commented Apr 28, 2020

Coverage Status

Coverage increased (+0.03%) to 81.785% when pulling b31c5ac on ISSUE-1197_add_memberof_operator into 1ce3b3c on master.

@@ -59,6 +59,15 @@ public static boolean toManyInPathExceptLastPathElement(EntityDictionary diction
.anyMatch(RelationshipType::isToMany);
}

public static boolean checkLastPathElementType(EntityDictionary dictionary, Path path, Class<?> clz) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rename this method to something more specific about what it's checking?

How about isLastPathElementAssignableFrom

throw new InvalidPredicateException("HasMember can only take one argument");
}
Object val = getFieldValue(entity, field, requestScope);
String filterStr = CoerceUtil.coerce(values.get(0), String.class);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, we'll need to iterate over it.
Another alternative is to convert field into a Path object in Elide. That will do all the type conversion for you.

throw new InvalidPredicateException("HasMember can only take one argument");
}
Object val = getFieldValue(entity, fieldPath, requestScope);
String filterStr = CoerceUtil.coerce(values.get(0), String.class);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need to fix the coercion.


@Test
@Tag("excludeOnHibernate3")
void testExceptionOnMemberOfOperator() throws IOException {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also add error tests for book.authors and book.publisher?

@Chandrasekar-Rajasekar Chandrasekar-Rajasekar force-pushed the ISSUE-1197_add_memberof_operator branch from 7257fa0 to 43ced02 Compare May 7, 2020 19:22
@aklish aklish merged commit 21a8332 into master May 8, 2020
@aklish aklish deleted the ISSUE-1197_add_memberof_operator branch May 8, 2020 18:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add MEMBER OF Operation
3 participants