Skip to content

Commit

Permalink
faceted search support #413
Browse files Browse the repository at this point in the history
- basic facets counting values
- nested facets where earlier facets restrict the count of later ones
- sorting/filtering
- in-memory and jpa support
  • Loading branch information
remmeier committed Oct 28, 2018
1 parent 63a51bc commit 708e3c3
Show file tree
Hide file tree
Showing 62 changed files with 1,732 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

public class ReflectionFieldAccessor implements ResourceFieldAccessor {

private final Field privateField;
private Method getter;

private Method setter;
Expand Down Expand Up @@ -40,12 +41,17 @@ public ReflectionFieldAccessor(Class<?> resourceType, String fieldName, Class<?>
this.getter = ClassUtils.findGetter(resourceType, fieldName);
this.setter = ClassUtils.findSetter(resourceType, fieldName, fieldType);
this.field = ClassUtils.findClassField(resourceType, fieldName);
this.privateField = field;
if (field != null && !Modifier.isPublic(field.getModifiers())) {
this.field = null;
}

}

public Field getField() {
return privateField;
}

@Override
public Object getValue(Object resource) {
if (resource == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public static SimpleModule createJacksonModule(boolean serializeLinksAsObjects)
if (serializeLinksAsObjects) {
simpleModule.addSerializer(new LinksInformationSerializer());
}

return simpleModule;
}
}
38 changes: 5 additions & 33 deletions crnk-core/src/main/java/io/crnk/core/module/ModuleRegistry.java
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public DefaultInformationBuilder getInformationBuilder() {
}

public List<ResourceModificationFilter> getResourceModificationFilters() {
return prioritze(aggregatedModule.getResourceModificationFilters());
return Prioritizable.prioritze(aggregatedModule.getResourceModificationFilters());
}

public void setServerInfo(Map<String, String> serverInfo) {
Expand Down Expand Up @@ -234,7 +234,7 @@ protected void checkState(InitializedState minState, InitializedState maxState)
public ResourceInformationProvider getResourceInformationBuilder() {
if (resourceInformationProvider == null) {
resourceInformationProvider =
new CombinedResourceInformationProvider(prioritze(aggregatedModule.getResourceInformationProviders()));
new CombinedResourceInformationProvider(Prioritizable.prioritze(aggregatedModule.getResourceInformationProviders()));
InformationBuilder informationBuilder = new DefaultInformationBuilder(typeParser);
DefaultResourceInformationProviderContext context =
new DefaultResourceInformationProviderContext(resourceInformationProvider, informationBuilder, typeParser,
Expand Down Expand Up @@ -267,7 +267,7 @@ public ResourceLookup getResourceLookup() {

public List<HttpRequestProcessor> getHttpRequestProcessors() {
checkState(InitializedState.INITIALIZED, InitializedState.INITIALIZED);
return prioritze(aggregatedModule.getHttpRequestProcessors());
return Prioritizable.prioritze(aggregatedModule.getHttpRequestProcessors());
}

/**
Expand Down Expand Up @@ -527,15 +527,15 @@ private void applyRepositoryRegistration(Object repository) {
* @return {@link DocumentFilter} added by all modules
*/
public List<DocumentFilter> getFilters() {
return prioritze(aggregatedModule.getFilters());
return Prioritizable.prioritze(aggregatedModule.getFilters());
}


/**
* @return {@link RepositoryFilter} added by all modules
*/
public List<RepositoryFilter> getRepositoryFilters() {
return prioritze(aggregatedModule.getRepositoryFilters());
return Prioritizable.prioritze(aggregatedModule.getRepositoryFilters());
}

/**
Expand Down Expand Up @@ -985,35 +985,7 @@ public PropertiesProvider getPropertiesProvider() {
}
}

private static <T> List<T> prioritze(List<T> list) {
Map<Object, Integer> indexMap = new HashMap<>();
int index = 0;
for (T item : list) {
indexMap.put(item, index--);
}

ArrayList<T> results = new ArrayList<>(list);
Collections.sort(results, new Comparator<T>() {
@Override
public int compare(T o1, T o2) {
int p1 = getPriority(o1);
int p2 = getPriority(o2);
if (p1 == p2) {
p1 = indexMap.get(o1);
p2 = indexMap.get(o2);
}
return p1 - p2;
}

private int getPriority(T o1) {
if (o1 instanceof Prioritizable) {
return ((Prioritizable) o1).getPriority();
}
return 0;
}
});
return results;
}

public QuerySpecUrlMapper getUrlMapper() {
return urlMapper;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.crnk.core.queryspec;

import com.fasterxml.jackson.annotation.JsonIgnore;

import java.util.List;

public class AbstractPathSpec {
Expand All @@ -26,6 +28,7 @@ public void setPath(PathSpec path) {
this.path = path;
}

@JsonIgnore
public List<String> getAttributePath() {
return path != null ? path.getElements() : null;
}
Expand Down
19 changes: 17 additions & 2 deletions crnk-core/src/main/java/io/crnk/core/queryspec/FilterOperator.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
/**
* Filter operator used to compare attributes to values by {@link FilterSpec}.
*/
public abstract class FilterOperator {
public class FilterOperator {

/**
* Boolean and
Expand All @@ -23,6 +23,19 @@ public boolean matches(Object value1, Object value2) {

};

/**
* Selection of facet for nested filtering
*/
public static final FilterOperator SELECT = new FilterOperator("SELECT") {

@Override
public boolean matches(Object value1, Object value2) {
throw new UnsupportedOperationException(); // handle differently
}

};


/**
* Like operation. In case of in-memory filtering it makes use of "%" as
* wildcard and is case-insenstive.
Expand Down Expand Up @@ -218,7 +231,9 @@ public String getName() {
* @param value2 second value
* @return true if matches
*/
public abstract boolean matches(Object value1, Object value2);
public boolean matches(Object value1, Object value2){
throw new UnsupportedOperationException("not implemented");
}

/**
* Typically the type of a filter parameter and the type of an attribute match. But some operators like LIKE have a type oder
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package io.crnk.core.queryspec;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.crnk.core.engine.internal.utils.CompareUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
Expand All @@ -10,8 +15,12 @@

public class FilterSpec extends AbstractPathSpec implements Comparable<FilterSpec> {

@JsonSerialize(using = ToStringSerializer.class)
private FilterOperator operator;

private Object value;

@JsonInclude(JsonInclude.Include.NON_NULL)
private List<FilterSpec> expressions;

protected FilterSpec() {
Expand Down
14 changes: 12 additions & 2 deletions crnk-core/src/main/java/io/crnk/core/queryspec/PathSpec.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
package io.crnk.core.queryspec;

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.crnk.core.engine.internal.utils.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import io.crnk.core.engine.internal.utils.StringUtils;

@JsonSerialize(using = ToStringSerializer.class)
public class PathSpec {

private List<String> elements;

private PathSpec() {
}

/**
* used back Jackson ToStringSerializer
*/
protected PathSpec(String path){
elements = of(path).elements;
}

public static PathSpec of(String... elements) {
return elements != null ? of(Arrays.asList(elements)) : null;
}
Expand Down
20 changes: 19 additions & 1 deletion crnk-core/src/main/java/io/crnk/core/queryspec/QuerySpec.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;

public class QuerySpec {

Expand Down Expand Up @@ -234,6 +235,19 @@ public void setFilters(List<FilterSpec> filters) {
this.filters = filters;
}

public Optional<FilterSpec> findFilter(final PathSpec pathSpec) {
for (FilterSpec filterSpec : filters) {
if (filterSpec.getPath().equals(pathSpec)) {
return Optional.of(filterSpec);
}
}
return Optional.empty();
}

/**
* @deprecated use {@link #findFilter(PathSpec)}
*/
@Deprecated
public FilterSpec getFilter(final String name) {
for (FilterSpec filterSpec : filters) {
if (filterSpec.getAttributePath().contains(name)) {
Expand All @@ -244,6 +258,10 @@ public FilterSpec getFilter(final String name) {
return null;
}

/**
* @deprecated use {@link #findFilter(PathSpec)}
*/
@Deprecated
public FilterSpec getFilterOrThrow(final String name) {
FilterSpec filter = getFilter(name);
if (filter == null) {
Expand Down Expand Up @@ -377,7 +395,7 @@ public QuerySpec getOrCreateQuerySpec(Class<?> targetResourceClass, String targe
if (querySpec == null && targetResourceType != null) {
querySpec = getQuerySpec(targetResourceType);
}
if (targetResourceClass != null) {
if (querySpec == null && targetResourceClass != null) {
querySpec = getQuerySpec(targetResourceClass);
}
if (querySpec == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public DefaultQuerySpecUrlMapper() {
supportedOperators.add(FilterOperator.GE);
supportedOperators.add(FilterOperator.LT);
supportedOperators.add(FilterOperator.LE);
supportedOperators.add(FilterOperator.SELECT);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@ public void setDecoratedObject(ResourceRepositoryV2<T, I> wrappedRepository) {
this.decoratedObject = wrappedRepository;
super.setWrappedRepository(wrappedRepository);
}

public ResourceRepositoryV2<T, I> getDecoratedObject() {
return decoratedObject;
}
}
37 changes: 37 additions & 0 deletions crnk-core/src/main/java/io/crnk/core/utils/Prioritizable.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
package io.crnk.core.utils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Priority for registered objects like filters and request processors.
* <p>
Expand All @@ -12,4 +19,34 @@ public interface Prioritizable {
* e.g. 1 as first priority, 2 as second priority, etc.
*/
int getPriority();

static <T> List<T> prioritze(List<T> list) {
Map<Object, Integer> indexMap = new HashMap<>();
int index = 0;
for (T item : list) {
indexMap.put(item, index--);
}

ArrayList<T> results = new ArrayList<>(list);
Collections.sort(results, new Comparator<T>() {
@Override
public int compare(T o1, T o2) {
int p1 = getPriority(o1);
int p2 = getPriority(o2);
if (p1 == p2) {
p1 = indexMap.get(o1);
p2 = indexMap.get(o2);
}
return p1 - p2;
}

private int getPriority(T o1) {
if (o1 instanceof Prioritizable) {
return ((Prioritizable) o1).getPriority();
}
return 0;
}
});
return results;
}
}
8 changes: 8 additions & 0 deletions crnk-data/crnk-data-facet/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apply plugin: 'java'

dependencies {
compile project(':crnk-core')

testCompile project(':crnk-client')
testCompile project(':crnk-test')
}

0 comments on commit 708e3c3

Please sign in to comment.