Skip to content

Commit 1bb8a3d

Browse files
committed
#935 Added option for tagging/excluding filters
Initial changes - still need to add code to handle multiple fields in fq.
1 parent 5e4ab73 commit 1bb8a3d

File tree

5 files changed

+110
-1
lines changed

5 files changed

+110
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Master branch [![Build Status](https://travis-ci.com/AtlasOfLivingAustralia/bioc
55

66
Occurrence & mapping webservices.
77

8-
Theses services are documented here https://api.ala.org.au/apps/biocache
8+
These services are documented here https://api.ala.org.au/apps/biocache
99

1010
## Versions
1111

src/main/java/au/org/ala/biocache/dao/SearchDAOImpl.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,20 @@ private SearchResultDTO processSolrResponse(SearchRequestDTO params, QueryRespon
10971097
SolrDocumentList sdl = qr.getResults();
10981098
// Iterator it = qr.getResults().iterator() // Use for download
10991099
List<FacetField> facets = qr.getFacetFields();
1100+
NamedList<List<PivotField>> facetPivot = qr.getFacetPivot();
1101+
1102+
// Add the (fake) facet pivot fields to the facets, as a workaround for tagging/excluding bug in solrj
1103+
// @see au.org.ala.biocache.util.QueryFormatUtils.applyFilterTagging comment for more details
1104+
if (params.getIncludeUnfilteredFacetValues() && facetPivot != null) {
1105+
for (Map.Entry<String, List<PivotField>> entry : facetPivot) {
1106+
FacetField pivotFacet = new FacetField(entry.getKey());
1107+
for (PivotField pivot : entry.getValue()) {
1108+
pivotFacet.add(pivot.getValue().toString(), pivot.getCount());
1109+
}
1110+
facets.add(pivotFacet);
1111+
}
1112+
}
1113+
11001114
List<FacetField> facetDates = qr.getFacetDates();
11011115
Map<String, Integer> facetQueries = qr.getFacetQuery();
11021116
if (facetDates != null) {
@@ -1365,6 +1379,10 @@ public SolrQuery initSolrQuery(SpatialSearchRequestDTO searchParams, boolean sub
13651379
}
13661380
}
13671381

1382+
for (String facet : searchParams.getFacetPivots()) {
1383+
solrQuery.addFacetPivotField(facet);
1384+
}
1385+
13681386
solrQuery.setFacetMinCount(1);
13691387
solrQuery.setFacetLimit(searchParams.getFlimit());
13701388
//include this so that the default fsort is still obeyed.

src/main/java/au/org/ala/biocache/dto/SearchRequestDTO.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public class SearchRequestDTO {
5555
* Initialised with the default facets to use
5656
*/
5757
protected String[] facets = new String[0]; //FacetThemes.getAllFacetsLimited();
58+
protected String[] facetPivots = new String[0];
5859
protected Integer facetsMax = 30; //FacetThemes.getFacetsMax();
5960
/** To disable facets */
6061
protected Boolean facet = true; //FacetThemes.getFacetDefault();
@@ -78,6 +79,11 @@ public class SearchRequestDTO {
7879
private String displayString;
7980

8081
protected Boolean includeMultivalues = false;
82+
/** Flag to activate filter/tagging: facet results will include values and counts for
83+
* unfiltered fields - see SOLR "tagging and excluding filters".
84+
* See https://solr.apache.org/guide/8_4/faceting.html#tagging-and-excluding-filters
85+
*/
86+
protected Boolean includeUnfilteredFacetValues = false;
8187

8288
/** The query context to be used for the search. This will be used to generate extra query filters based on the search technology */
8389
protected String qc = "";
@@ -389,6 +395,28 @@ public void setFacets(String[] facets) {
389395
this.facets = list.toArray(new String[0]);
390396
}
391397

398+
public String[] getFacetPivots() {
399+
return facetPivots;
400+
}
401+
402+
public void setFacetPivots(String[] facets) {
403+
QueryFormatUtils.assertNoSensitiveValues(SearchRequestDTO.class, "facets", facets);
404+
405+
if (facets != null && facets.length == 1 && facets[0].contains(",")) facets = facets[0].split(",");
406+
407+
//remove empty facets
408+
List<String> list = new ArrayList<String>();
409+
if (facets != null) {
410+
for (String f : facets) {
411+
//limit facets terms
412+
if (StringUtils.isNotEmpty(f) && list.size() < facetsMax) {
413+
list.add(f);
414+
}
415+
}
416+
}
417+
this.facetPivots = list.toArray(new String[0]);
418+
}
419+
392420
public Integer getFlimit() {
393421
return flimit;
394422
}
@@ -482,6 +510,14 @@ public void setIncludeMultivalues(Boolean includeMultivalues) {
482510
this.includeMultivalues = includeMultivalues;
483511
}
484512

513+
public Boolean getIncludeUnfilteredFacetValues() {
514+
return includeUnfilteredFacetValues;
515+
}
516+
517+
public void setIncludeUnfilteredFacetValues(Boolean includeUnfilteredFacetValues) {
518+
this.includeUnfilteredFacetValues = includeUnfilteredFacetValues;
519+
}
520+
485521
public String[] getFormattedFq() {
486522
return formattedFq;
487523
}

src/main/java/au/org/ala/biocache/dto/SpatialSearchRequestParams.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ public class SpatialSearchRequestParams {
8585
@Parameter(name="includeMultivalues", description = "Include multi values")
8686
protected Boolean includeMultivalues = false;
8787

88+
// @Parameter(name="includeUnfilteredFacetValues", description = "Include facet values for all available options, when filtering on the same field")
89+
protected Boolean includeUnfilteredFacetValues = false;
90+
8891
@Parameter(name="qc", description = "The query context to be used for the search. " +
8992
"This will be used to generate extra query filters.")
9093
protected String qc = "";

src/main/java/au/org/ala/biocache/util/QueryFormatUtils.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ public Map[] formatSearchQuery(SpatialSearchRequestDTO searchParams, boolean for
139139
//reset formattedFq in case of searchParams reuse
140140
searchParams.setFormattedFq(null);
141141

142+
// Apply filter tagging and excluding filters if flag is set
143+
if (searchParams.getIncludeUnfilteredFacetValues()) {
144+
applyFilterTagging(searchParams);
145+
}
146+
142147
//format fqs for facets that need ranges substituted
143148
if (searchParams.getFq() != null) {
144149
for (int i = 0; i < searchParams.getFq().length; i++) {
@@ -184,16 +189,62 @@ public Map[] formatSearchQuery(SpatialSearchRequestDTO searchParams, boolean for
184189

185190
//add spatial query term for wkt or lat/lon/radius parameters. DisplayString is already added by formatGeneral
186191
String spatialQuery = buildSpatialQueryString(searchParams);
192+
187193
if (StringUtils.isNotEmpty(spatialQuery)) {
188194
addFormattedFq(new String[] { spatialQuery }, searchParams);
189195
}
196+
190197
updateQualityProfileContext(searchParams);
191198
}
192199

193200
updateQueryContext(searchParams);
194201
return fqMaps;
195202
}
196203

204+
/**
205+
* Apply facet tagging and filter exclusions to the search request.
206+
*
207+
* Note: due to bug/feature in SOLRJ, the filtered facets are added to
208+
* the facet pivot list instead of the facet list, otherwise SOLRJ will
209+
* ignore the facets with counts greater than totalRecords count, when generating
210+
* the facetResults.
211+
*
212+
* @author "Nick dos Remedios <[email protected]>"
213+
* @date 2025-01-09
214+
*
215+
* @param searchParams
216+
*/
217+
private void applyFilterTagging(SpatialSearchRequestDTO searchParams) {
218+
List<String> facetList = new ArrayList<String>();
219+
List<String> facetPivotList = new ArrayList<String>();
220+
List<String> fqList = new ArrayList<String>();
221+
222+
for (String f : searchParams.getFacets()) {
223+
if (searchParams.getFq() != null && Arrays.stream(searchParams.getFq()).anyMatch(fq -> fq.contains(f) && !fq.contains("*"))) {
224+
String prefix = "{!ex=" + f + "}";
225+
facetPivotList.add(prefix + f);
226+
} else {
227+
facetList.add(f);
228+
}
229+
}
230+
231+
if (searchParams.getFq() != null) {
232+
for (String fq : searchParams.getFq()) {
233+
String fqField = org.apache.commons.lang3.StringUtils.substringBefore(fq, ":");
234+
if (Arrays.asList(searchParams.getFacets()).contains(fqField) && !fq.contains("*")) {
235+
String prefix = "{!tag=" + fqField + "}";
236+
fqList.add(prefix + fq);
237+
} else {
238+
fqList.add(fq);
239+
}
240+
}
241+
}
242+
243+
searchParams.setFacetPivots(facetPivotList.toArray(new String[0]));
244+
searchParams.setFacets(facetList.toArray(new String[0]));
245+
searchParams.setFq(fqList.toArray(new String[0]));
246+
}
247+
197248
/**
198249
* To retrieve the key from a fq
199250
*
@@ -248,6 +299,7 @@ public void addFqs(String [] fqs, SpatialSearchRequestDTO searchParams) {
248299
}
249300
}
250301
}
302+
251303
private void addFormattedFq(String [] fqs, SearchRequestDTO searchParams) {
252304
if (fqs != null && searchParams != null) {
253305
String[] currentFqs = searchParams.getFormattedFq();

0 commit comments

Comments
 (0)