Skip to content

Commit

Permalink
#960 Multi level sorting for facets
Browse files Browse the repository at this point in the history
First level: sort by document count, descending (as before)
Second level: sort by label, alphabetically (new)

ATTENTION: Feature requires a re-index of ElasticSearch because a field
has changed

Procedure:
(1) Checkout code
(2) Start Imeji
(3) Login as Admin
(4) Reindex (Admin>Tools>Reindex)
(5) From then Imeji can be used as usual
  • Loading branch information
MPDLTam committed Aug 24, 2018
1 parent 093416d commit edca0ec
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 40 deletions.
Expand Up @@ -15,12 +15,14 @@
import de.mpg.imeji.logic.util.ObjectHelper;

/**
* Inner class to store collection informations
* Class to store collection informations with items in ElasticSearch
*
* @author saquet
*
*/
public class CollectionFields {

private static final String TITLE_ID_SEPARATOR = " ";
private final List<String> authors;
private final List<String> organizations;
private final String titleWithId;
Expand All @@ -30,7 +32,7 @@ public CollectionFields(CollectionImeji c) {
c.getPersons().stream().map(p -> p.getCompleteName()).collect(Collectors.toList());
this.organizations = c.getPersons().stream().flatMap(p -> p.getOrganizations().stream())
.map(o -> o.getName()).collect(Collectors.toList());
this.titleWithId = ObjectHelper.getId(URI.create(c.getId().toString())) + " " + c.getTitle();
this.titleWithId = this.titleWithIdOfCollection(c.getTitle(), ObjectHelper.getId(URI.create(c.getId().toString())));
}

public CollectionFields(GetField authorsField, GetField organizationsField, GetField id,
Expand All @@ -42,9 +44,47 @@ public CollectionFields(GetField authorsField, GetField organizationsField, GetF
? organizationsField.getValues().stream().map(Object::toString).collect(Collectors.toList())
: new ArrayList<>();
this.titleWithId =
ObjectHelper.getId(URI.create(id.getValue().toString())) + " " + title.getValue();
this.titleWithIdOfCollection((String) title.getValue(), ObjectHelper.getId(URI.create(id.getValue().toString()))) ;
}


/**
* Constructs a compound String that contains both the title and the ID of a collection
* @param collectionTitle
* @param id
* @return
*/
private String titleWithIdOfCollection(String collectionTitle, String id) {
return collectionTitle + TITLE_ID_SEPARATOR + id;
}

/**
* Deconstructs a compound String that contains both the title and the ID of a collection
* Returns the title
* @return
*/
public static String getTitle(String titleWithIdOfCollection) {

// ID is at the end of the String
int subStringTo = titleWithIdOfCollection.lastIndexOf(TITLE_ID_SEPARATOR);
String label = titleWithIdOfCollection.substring(0, subStringTo);
return label;
}

/**
* Deconstructs a compound String that contains both the title and the ID of a collection
* Return the string
* @return
*/
public static String getID(String titleWithIdOfCollection) {

// ID is at the end of the String
int subStringFrom = titleWithIdOfCollection.lastIndexOf(TITLE_ID_SEPARATOR);
String id = titleWithIdOfCollection.substring(subStringFrom +1);
return id;
}


public XContentBuilder toXContentBuilder() throws IOException {
return XContentFactory.jsonBuilder().startObject()
.field(ElasticFields.AUTHORS_OF_COLLECTION.field(), authors)
Expand Down
Expand Up @@ -28,6 +28,7 @@
@ManagedBean(name = "FacetSelectorBean")
@ViewScoped
public class FacetSelectorBean extends SuperBean {

private static final long serialVersionUID = 4953953758406265116L;
private static final Logger LOGGER = Logger.getLogger(FacetSelectorBean.class);
private List<FacetSelectorEntry> entries = new ArrayList<>();
Expand All @@ -52,13 +53,13 @@ private void init() {
*/
public String init(SearchResult result) {
if (result != null) {
entries = result.getFacets().stream()
this.entries = result.getFacets().stream()
.filter(f -> !f.getName().equals(Facet.ITEMS) && !f.getName().equals(Facet.SUBCOLLECTIONS)
&& !f.getName().equals(Facet.COLLECTION_ITEMS))
.map(r -> new FacetSelectorEntry(r, facetQuery, result.getNumberOfRecords()))
.map(r -> new FacetSelectorEntry(r, facetQuery, result.getNumberOfRecords(), this.getLocale()))
.sorted(
(f1, f2) -> Integer.compare(f1.getFacet().getPosition(), f2.getFacet().getPosition()))
.collect(Collectors.toList());
.collect(Collectors.toList());
setAddQuery();
setRemoveQuery();
setSelectedEntries();
Expand All @@ -71,6 +72,8 @@ public List<FacetSelectorEntryValue> getSelectedValues() {
.collect(Collectors.toList());
}



/**
* Set the addQuery to all values of all entries
*/
Expand Down
Expand Up @@ -2,7 +2,9 @@

import java.io.IOException;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;

import javax.faces.context.FacesContext;
Expand All @@ -26,6 +28,7 @@

/**
* Entry of the {@link FacetSelectorBean}
* GUI representation of a {@link Facet}, containing {@link FacetResults} that were retrieved from ElasticSearch
*
* @author saquet
*
Expand All @@ -40,54 +43,84 @@ public class FacetSelectorEntry implements Serializable {
private String to;
private boolean showMore = false;
private final long count;
private Locale locale;


public FacetSelectorEntry(FacetResult facetResult, SearchQuery facetsQuery, int countAll) {
/**
* Constructor
* @param facetResult retrieved from ElasticSearch
* @param facetsQuery
* @param countAll
*/
public FacetSelectorEntry(FacetResult facetResult, SearchQuery facetsQuery, int countAll, Locale locale) {

this.locale = locale;
this.facetsQuery = facetsQuery;

FacetService facetService = new FacetService();
this.facetsQuery = facetsQuery;
this.facet = facetService.retrieveByIndexFromCache(facetResult.getIndex());

this.values = facetResult.getValues().stream()
.map(v -> new FacetSelectorEntryValue(v, facet, facetsQuery)).collect(Collectors.toList());
addAnyLicense(facetResult, facet, facetsQuery, countAll);
.map(facetResultValue -> new FacetSelectorEntryValue(facetResultValue, facet, facetsQuery, locale)).collect(Collectors.toList());


addAnyLicenseToLicenceFacette(facetResult, facet, facetsQuery, countAll);
cleanFileTypeFacet(facet);
sortValues();

this.count = values.stream().collect(Collectors.summingLong(v -> v.getCount()));

if (count > 0) {
this.from = values.get(0).getMin();
this.to = values.get(0).getMax();
}
}

/**
* Using the facetsresults,calculate the facet for any licenses
* Sorts the list of {@link FacetSelectorEntryValue}
* first level: sort by document count
* second level: sort by label, alphabetically
*/
public void sortValues() {
Collections.sort(this.values);
}

/**
* Using the facetsresults, calculate the facet for any licenses
*
* @param facetResult
* @param facet
* @param facetsQuery
* @param countAll
*/
private void addAnyLicense(FacetResult facetResult, Facet facet, SearchQuery facetsQuery,
private void addAnyLicenseToLicenceFacette(FacetResult facetResult, Facet facet, SearchQuery facetsQuery,
int countAll) {
if (facet.getIndex().equals(SearchFields.license.getIndex())) {
long countNone = facetResult.getValues().stream()
.filter(r -> r.getLabel().equals(ImejiLicenses.NO_LICENSE)).map(r -> r.getCount())
.findFirst().orElse((long) countAll);
long countAny = countAll - countNone;
if (countAny > 0) {
FacetResultValue v = new FacetResultValue("Any", countAll - countNone);
values.add(new FacetSelectorEntryValue(v, facet, facetsQuery));
values = values.stream().sorted((v1, v2) -> Long.compare(v2.getCount(), v1.getCount()))
.collect(Collectors.toList());
}
}

if (facet.getIndex().equals(SearchFields.license.getIndex())) {
long countNone = facetResult.getValues().stream()
.filter(r -> r.getLabel().equals(ImejiLicenses.NO_LICENSE)).map(r -> r.getCount())
.findFirst().orElse((long) countAll);
long countAny = countAll - countNone;
if (countAny > 0) {
FacetResultValue v = new FacetResultValue("Any", countAll - countNone);
values.add(new FacetSelectorEntryValue(v, facet, facetsQuery, this.locale));
values = values.stream().sorted((v1, v2) -> Long.compare(v2.getCount(), v1.getCount()))
.collect(Collectors.toList());
}
}
}


private void cleanFileTypeFacet(Facet facet) {
if (facet.getIndex().equals(SearchFields.filetype.getIndex())) {
values = values.stream().filter(v -> v.getCount() > 0)
.sorted((v1, v2) -> Long.compare(v2.getCount(), v1.getCount()))
.collect(Collectors.toList());

if (facet.getIndex().equals(SearchFields.filetype.getIndex())) {
values = values.stream().filter(v -> v.getCount() > 0)
.sorted((v1, v2) -> Long.compare(v2.getCount(), v1.getCount()))
.collect(Collectors.toList());
}
}


public void search(HistoryPage page, String q) throws IOException {
String fq = buildFromToQuery();
if (fq != null) {
Expand All @@ -104,7 +137,7 @@ private String buildFromToQuery() {
resultValue.setMax(to);
resultValue.setMin(from);
FacetSelectorEntryValue selectorValueNew = new FacetSelectorEntryValue(resultValue, facet,
new SearchFactory(facetsQuery).clone().remove(selectorValue.getEntryQuery()).build());
new SearchFactory(facetsQuery).clone().remove(selectorValue.getEntryQuery()).build(), this.locale);
return SearchQueryParser.transform2URL(new SearchFactory(facetsQuery)
.remove(selectorValue.getEntryQuery()).and(selectorValueNew.getEntryQuery()).build());
} catch (UnprocessableError e) {
Expand Down
Expand Up @@ -3,7 +3,9 @@
import static de.mpg.imeji.logic.search.model.SearchLogicalRelation.LOGICAL_RELATIONS.AND;

import java.io.Serializable;
import java.text.Collator;
import java.util.List;
import java.util.Locale;

import org.apache.log4j.Logger;

Expand All @@ -12,6 +14,7 @@
import de.mpg.imeji.logic.model.SearchFields;
import de.mpg.imeji.logic.model.SearchMetadataFields;
import de.mpg.imeji.logic.model.StatementType;
import de.mpg.imeji.logic.search.elasticsearch.script.misc.CollectionFields;
import de.mpg.imeji.logic.search.facet.model.Facet;
import de.mpg.imeji.logic.search.facet.model.FacetResultValue;
import de.mpg.imeji.logic.search.factory.SearchFactory;
Expand All @@ -27,7 +30,7 @@
* @author saquet
*
*/
public class FacetSelectorEntryValue implements Serializable {
public class FacetSelectorEntryValue implements Serializable, Comparable<FacetSelectorEntryValue> {
private static final long serialVersionUID = -5562614379983226471L;
private static final Logger LOGGER = Logger.getLogger(FacetSelectorEntryValue.class);
private String label;
Expand All @@ -40,18 +43,22 @@ public class FacetSelectorEntryValue implements Serializable {
private boolean selected = false;
private String max;
private String min;
private Locale locale;


public FacetSelectorEntryValue(FacetResultValue resultValue, Facet facet,
SearchQuery facetsQuery) {
this.index = facet.getIndex();
this.type = facet.getType();
this.count = resultValue.getCount();
public FacetSelectorEntryValue(FacetResultValue facetResultValue, Facet facet,
SearchQuery facetsQuery, Locale locale) {

this.index = facet.getIndex();
this.type = facet.getType();
this.count = facetResultValue.getCount();
this.locale = locale;

if (facet.getType().equals(StatementType.DATE.name())
|| facet.getType().equals(StatementType.NUMBER.name())) {
initRangeEntry(resultValue, facet, facetsQuery);
initRangeEntry(facetResultValue, facet, facetsQuery);
} else {
initEntry(resultValue, facet, facetsQuery);
initEntry(facetResultValue, facet, facetsQuery);
}
}

Expand Down Expand Up @@ -79,8 +86,8 @@ private void initRangeEntry(FacetResultValue resultValue, Facet facet, SearchQue
* @return
*/
private String readLabel(FacetResultValue resultValue, Facet facet) {
if (facet.getIndex().equals(SearchFields.collection.getIndex())) {
return resultValue.getLabel().split(" ", 2)[1];
if (facet.getIndex().equals(SearchFields.collection.getIndex())) {
return CollectionFields.getTitle(resultValue.getLabel());
}
if (facet.getIndex().equals(SearchFields.license.getIndex())
&& resultValue.getLabel().equals(ImejiLicenses.NO_LICENSE)) {
Expand All @@ -89,6 +96,7 @@ private String readLabel(FacetResultValue resultValue, Facet facet) {
return resultValue.getLabel();
}


/**
* Read the value of the facet according to the query (and not from the facet result value)
*
Expand Down Expand Up @@ -116,7 +124,7 @@ private String readQueryValue(Facet facet, SearchQuery facetsQuery) {
*/
private String toQueryValue(FacetResultValue resultValue, Facet facet, SearchQuery facetsQuery) {
if (facet.getIndex().equals(SearchFields.collection.getIndex())) {
return resultValue.getLabel().split(" ", 2)[0];
return CollectionFields.getID(resultValue.getLabel());
}
if (facet.getIndex().equals(SearchFields.license.getIndex())) {
if ("Any".equalsIgnoreCase(resultValue.getLabel())) {
Expand Down Expand Up @@ -286,5 +294,45 @@ public String getMax() {
public String getMin() {
return min;
}

/**
* Compare FacetSelectorEntryValues for construction a sort order
* First level: sort by document count, descending
* Second level: sort by label, alphabetically
*/
@Override
public int compareTo(FacetSelectorEntryValue otherFacetSelectorEntryValue) {

// first level: sort by count, descending
if(this.count < otherFacetSelectorEntryValue.count) return 1;
else if(this.count > otherFacetSelectorEntryValue.count) return -1;
else{
// second level: sort by label, alphabetically
return sortAlphabetically(this.label, otherFacetSelectorEntryValue.label);
}
}


private int sortAlphabetically(String myLabel, String otherLabel) {

if(this.locale.getLanguage().compareTo(Locale.GERMAN.getLanguage()) == 0) {
return sortAlphabeticallyGerman(myLabel, otherLabel);
}
else {
return sortAlphabeticallyDefault(myLabel, otherLabel);
}
}

private int sortAlphabeticallyGerman(String myLabel, String otherLabel) {

Collator collator = Collator.getInstance(Locale.GERMAN);
collator.setStrength(Collator.SECONDARY);// a == A, a < Ä
return collator.compare(myLabel, otherLabel);
}

private int sortAlphabeticallyDefault(String myLabel, String otherLabel) {

Collator collator = Collator.getInstance(this.locale);
return collator.compare(myLabel, otherLabel);
}
}

0 comments on commit edca0ec

Please sign in to comment.