diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..7dcea80c --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,45 @@ +# This config was automatically generated from your source code +# Stacks detected: cicd:jenkins:.,deps:java:.,tool:gradle: +version: 2.1 +jobs: + test-java: + docker: + - image: cimg/openjdk:17.0 + steps: + - checkout + - run: + name: Calculate cache key + command: |- + find . -name 'pom.xml' -o -name 'gradlew*' -o -name '*.gradle*' | \ + sort | xargs cat > /tmp/CIRCLECI_CACHE_KEY + - restore_cache: + key: cache-{{ checksum "/tmp/CIRCLECI_CACHE_KEY" }} + - run: + command: ./gradlew check + - store_test_results: + path: build/test-results + - save_cache: + key: cache-{{ checksum "/tmp/CIRCLECI_CACHE_KEY" }} + paths: + - ~/.gradle/caches + - store_artifacts: + path: build/reports + deploy: + # This is an example deploy job, not actually used by the workflow + docker: + - image: cimg/base:stable + steps: + # Replace this with steps to deploy to users + - run: + name: deploy + command: '#e.g. ./deploy.sh' + - run: + name: found jenkins config + command: ':' +workflows: + build-and-test: + jobs: + - test-java + # - deploy: + # requires: + # - test-java diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index 79ee123c..a55e7a17 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,5 +1,5 @@ - \ No newline at end of file diff --git a/README.md b/README.md index a6503aae..0ce38963 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,34 @@ --- -**_Get up and running with a fully featured, SEO friendly, keyword searchable, faceted search engine with an in-built, example search page to test it all out._** +> **_Get up and running with a fully featured, SEO friendly, keyword searchable, faceted search engine with an in-built, example search page to test it all out._** --- +``` +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# __ # +# .-----.---.-.-----.| | # +# | _ | _ | || | # +# | __|___._|__|__||__| # +# |__| ... .-.. # +# # +# ~ ~ ~ * ~ ~ ~ # +# # +# # +# SOLR/PANL # +# # +# ---------- # +# # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +``` + +`Build and test 'main' branch:` [![CircleCI](https://dl.circleci.com/status-badge/img/circleci/3Y1eqqe4mcvtSZuzJnQ3tJ/DyFKUm7c7AoLa1wHmRjAnf/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/circleci/3Y1eqqe4mcvtSZuzJnQ3tJ/DyFKUm7c7AoLa1wHmRjAnf/tree/main) + +`Latest release tag:` ![GitHub Tag](https://img.shields.io/github/v/tag/synapticloop/panl) + +`Latest release:` ![GitHub Release](https://img.shields.io/github/v/release/synapticloop/panl) + ## Download the Panl Server Release: [https://github.com/synapticloop/panl/releases](https://github.com/synapticloop/panl/releases) @@ -17,7 +41,7 @@ - Online book (HTML): [https://synapticloop.github.io/panl/](https://synapticloop.github.io/panl/) - Offline book (PDF): [Getting Started With Synapticloop Panl.pdf](https://github.com/synapticloop/panl/blob/main/src/dist/book/Getting%20Started%20With%20Synapticloop%20Panl.pdf) -Both of the book links above refer to Solr Panl integration 9 with help for earlier versions of Solr. +Both of the book links above refer to Solr Panl integration 9 with instructions for setting up and running earlier versions of Solr. ## Why? @@ -100,7 +124,7 @@ _The image is a screenshot of the in-built Panl Results Viewer Web App available ## The Panl Results Explainer Web App -The In-Built Panl Results Explainer +The In-Built Panl Results Explainer _**Image**: The features and functionality of the Panl results explainer_ @@ -356,10 +380,12 @@ with the release files named `solr-panl-9-x.x.x` - Update Mechanical Pencils - Added in hierarchy for the Pencil Model example - Dynamic range functionality - pulling actual values for the facet + - Suppress facet values for ranges, so that the user may only select from the range UI, and the individual range facet values do not appear + - Documentation update + - New documentation for additional features and functionality - Fixed general spelling and grammar errors - - Added in documentation for new features and functionality - Updated mechanical pencils introductory dataset explanations - Added in documentation for Fields diff --git a/src/dist/sample/data/mechanical-pencils.json b/src/dist/sample/data/mechanical-pencils.json index 6b52d6a3..fb2f08bc 100644 --- a/src/dist/sample/data/mechanical-pencils.json +++ b/src/dist/sample/data/mechanical-pencils.json @@ -141,7 +141,7 @@ "variants": [ "Light Blue" ], "colours": [ "Blue" ], "id": 7, - "description": "", + "description": "Whilst the design has probably not been updated in 50 years, it has an appealing classic retro look about it. I can understand wanting to keep the classic design of the clip and clip mechanism, however, times have changed and technology is better, and the clip needs to be better. This pencil looks like it is a no-nonsense, workhorse of a pencil that gets out of the way to let you do your work. At home on a drafting board for technical drawings, or just to pull out for jotting notes or doing a quick sketch.", "images": [ "400-alvin-tech-da-blue-body-with-red-cap.jpg", "400-alvin-tech-da-blue-body-with-green-cap.jpg", @@ -179,7 +179,7 @@ "variants": [ "Silver", "Black", "White" ], "colours": [ "Silver", "Black", "White" ], "id": 8, - "description": "", + "description": "This pencil is an ‘update’ to the original, full-metal, classic Bic Criterium 2603 model, it had the same hexagonal shape and size, but the body was aluminium not plastic. The grip was also subtly different from the current design. It is a shame that they ‘updated’ the body to be plastic. The original Criterium pencil with grip styling — was manufactured by Conté. Generally, Bic products are well made, if you get one with a proper mechanism, or invest a small amount of time in fixing one that isn’t working, then this is a great entry-level pencil.", "images": [ "400-bic-criterium-silver.jpg", "400-bic-criterium-white.jpg", @@ -217,7 +217,7 @@ "variants": [ "Black with red cap", "Black with blue cap", "Black with black cap" ], "colours": [ "Black" ], "id": 9, - "description": "", + "description": "This is the pencil for people who know about pencils. Understated and those who know will appreciate the craftsmanship that went into this and will be duly impressed. Not as common as the Rotring or Staedtler, but far more impressive to own.\n\nIf you like something light and want to make a very understated statement, this is the pencil for you.\n\nIf you like these pencils, the choice comes down to textured vs non-textured grip and the colour of the cap.\n\nThis is a pencil to own if you want to pay homage to the creator of the mechanical pencil.", "images": [ "400-caran-d-ache-fixpencil-22--smooth--black-body-with-red-cap.jpg", "400-caran-d-ache-fixpencil-22--smooth--black-body-with-blue-cap.jpg", @@ -255,7 +255,7 @@ "variants": [ "Black with red cap", "Black with blue cap", "Black with black cap" ], "colours": [ "Black" ], "id": 10, - "description": "", + "description": "This is the pencil for people who know about pencils. Understated and those who know will appreciate the craftsmanship that went into this and will be duly impressed. Not as common as the Rotring or Staedtler, but far more impressive to own.\n\nIf you like something light and want to make a very understated statement, this is the pencil for you.\n\nIf you like these pencils, the choice comes down to textured vs non-textured grip and the colour of the cap.\n\nThis is a pencil to own if you want to pay homage to the creator of the mechanical pencil.", "images": [ "400-caran-d-ache-fixpencil-22--textured--black-body-with-red-cap.jpg", "400-caran-d-ache-fixpencil-22--textured--black-body-with-blue-cap.jpg", @@ -2052,7 +2052,7 @@ "variants": [ "Black", "Brass" ], "colours": [ "Black", "Brass" ], "id": 57, - "description": "", + "description": "This pencil leaves you with a feeling of comfort and delight. It is just so very well presented and prepared. If you want the full experience, buy one of these — although they are pricy, especially including shipping and taxes.\n\nYou will not be disappointed.", "images": [ "400-ystudio-classic-revolve-black.jpg", "400-ystudio-classic-revolve-brass.jpg" diff --git a/src/dist/sample/panl/book-store/author-alphabetical.panl.properties b/src/dist/sample/panl/book-store/author-alphabetical.panl.properties index 57c474fb..2bb28efb 100644 --- a/src/dist/sample/panl/book-store/author-alphabetical.panl.properties +++ b/src/dist/sample/panl/book-store/author-alphabetical.panl.properties @@ -365,6 +365,10 @@ solr.highlight=false # panl.range.max.wildcard. # (Optional) If the maximum value acts as a wildcard (and therefore # will also include values which are greater than this value) +# panl.range.suppress. +# (Optional) By default, panl will return a range and the individual +# values for a facet. Setting this to true will only return the range +# values. # # ~ ~ ~ * ~ ~ ~ # diff --git a/src/dist/sample/panl/book-store/book-store.panl.properties b/src/dist/sample/panl/book-store/book-store.panl.properties index 7340fa85..72b7a19b 100644 --- a/src/dist/sample/panl/book-store/book-store.panl.properties +++ b/src/dist/sample/panl/book-store/book-store.panl.properties @@ -365,6 +365,10 @@ solr.highlight=false # panl.range.max.wildcard. # (Optional) If the maximum value acts as a wildcard (and therefore # will also include values which are greater than this value) +# panl.range.suppress. +# (Optional) By default, panl will return a range and the individual +# values for a facet. Setting this to true will only return the range +# values. # # ~ ~ ~ * ~ ~ ~ # diff --git a/src/dist/sample/panl/mechanical-pencils-or/mechanical-pencils-or.panl.properties b/src/dist/sample/panl/mechanical-pencils-or/mechanical-pencils-or.panl.properties index c9728975..d3fe3d33 100644 --- a/src/dist/sample/panl/mechanical-pencils-or/mechanical-pencils-or.panl.properties +++ b/src/dist/sample/panl/mechanical-pencils-or/mechanical-pencils-or.panl.properties @@ -372,6 +372,10 @@ solr.highlight=false # panl.range.max.wildcard. # (Optional) If the maximum value acts as a wildcard (and therefore # will also include values which are greater than this value) +# panl.range.suppress. +# (Optional) By default, panl will return a range and the individual +# values for a facet. Setting this to true will only return the range +# values. # # ~ ~ ~ * ~ ~ ~ # @@ -477,6 +481,8 @@ panl.range.min.value.w=from light panl.range.max.value.w=heavy pencils panl.range.min.wildcard.w=true panl.range.max.wildcard.w=true +panl.range.suppress.w=true + # @@ -525,6 +531,7 @@ panl.type.I=solr.BoolField # panl.field.v=variants panl.name.v=Variants +panl.multivalue.v=true panl.type.v=solr.StrField # @@ -573,6 +580,7 @@ panl.bool.9.false=No in-built sharpener # panl.facet.W=colours panl.name.W=Colours +panl.multivalue.W=true panl.type.W=solr.StrField diff --git a/src/dist/sample/panl/mechanical-pencils/mechanical-pencils.panl.properties b/src/dist/sample/panl/mechanical-pencils/mechanical-pencils.panl.properties index 01c2e7d1..18d0f3d2 100644 --- a/src/dist/sample/panl/mechanical-pencils/mechanical-pencils.panl.properties +++ b/src/dist/sample/panl/mechanical-pencils/mechanical-pencils.panl.properties @@ -382,6 +382,10 @@ solr.highlight=false # panl.range.max.wildcard. # (Optional) If the maximum value acts as a wildcard (and therefore # will also include values which are greater than this value) +# panl.range.suppress. +# (Optional) By default, panl will return a range and the individual +# values for a facet. Setting this to true will only return the range +# values. # # ~ ~ ~ * ~ ~ ~ # @@ -537,6 +541,7 @@ panl.type.I=solr.BoolField # panl.field.v=variants panl.name.v=Variants +panl.multivalue.v=true panl.type.v=solr.StrField # @@ -585,6 +590,7 @@ panl.bool.9.false=No in-built sharpener # panl.facet.W=colours panl.name.W=Colours +panl.multivalue.W=true panl.type.W=solr.StrField diff --git a/src/dist/sample/panl/simple-date/simple-date.panl.properties b/src/dist/sample/panl/simple-date/simple-date.panl.properties index 8082014f..fe24a70b 100644 --- a/src/dist/sample/panl/simple-date/simple-date.panl.properties +++ b/src/dist/sample/panl/simple-date/simple-date.panl.properties @@ -364,6 +364,10 @@ solr.highlight=false # panl.range.max.wildcard. # (Optional) If the maximum value acts as a wildcard (and therefore # will also include values which are greater than this value) +# panl.range.suppress. +# (Optional) By default, panl will return a range and the individual +# values for a facet. Setting this to true will only return the range +# values. # # ~ ~ ~ * ~ ~ ~ # diff --git a/src/main/java/com/synapticloop/panl/Main.java b/src/main/java/com/synapticloop/panl/Main.java index cc3ab743..fe48c98d 100644 --- a/src/main/java/com/synapticloop/panl/Main.java +++ b/src/main/java/com/synapticloop/panl/Main.java @@ -44,7 +44,7 @@ import java.util.stream.Collectors; /** - *

This is the main entry point for the PANL server/generator.

+ *

This is the main class for the PANL server/generator.

*/ public class Main { private static final Logger LOGGER = LoggerFactory.getLogger(Main.class); @@ -78,7 +78,7 @@ public class Main { private final String[] args; /** - *

Instantiate the Main

+ *

Instantiate the main class.

* * @param args The command line arguments */ @@ -141,11 +141,12 @@ private void parseAndExecuteCommandLine() throws PanlServerException, CommandLin * * @throws PanlServerException If there was an error starting the server * @throws CommandLineOptionException If there was an error with the command - * line options + * line options */ private void parseAndExecuteServerCommands() throws PanlServerException, CommandLineOptionException { this.propertiesFileLocation = OPTIONS_MAP.getOrDefault(CMD_OPTION_PROPERTIES, DEFAULT_PANL_PROPERTIES); String portNumberString = OPTIONS_MAP.getOrDefault(CMD_OPTION_PORT, DEFAULT_PORT_NUMBER); + try { this.portNumber = Integer.parseInt(portNumberString); } catch (NumberFormatException e) { @@ -237,7 +238,9 @@ private void usageAndException(String message) throws CommandLineOptionException } /** - *

Main starting point for the application.

+ *

Main starting point for the application, parsing the command line + * options and executing the required component. If there was an error when + * parsing the options then it will print out an error message and exit.

* * @param args The arguments to parse * diff --git a/src/main/java/com/synapticloop/panl/server/handler/CollectionRequestHandler.java b/src/main/java/com/synapticloop/panl/server/handler/CollectionRequestHandler.java index bc05a68b..ef7c6b89 100644 --- a/src/main/java/com/synapticloop/panl/server/handler/CollectionRequestHandler.java +++ b/src/main/java/com/synapticloop/panl/server/handler/CollectionRequestHandler.java @@ -339,17 +339,23 @@ private String parseResponse( panlObject.put(JSON_KEY_AVAILABLE, availableProcessor.processToObject(panlTokenMap, response)); + // now we are going to add the dynamic range if they exist JSONObject statsObject = solrJsonObject.optJSONObject("stats"); if(null != statsObject) { JSONObject statsFieldObjects = statsObject.optJSONObject("stats_fields"); - if (null != statsFieldObjects) { - JSONArray jsonArray = panlObject.getJSONObject(JSON_KEY_AVAILABLE).getJSONArray(Processor.JSON_KEY_RANGE_FACETS); - for (Object object : jsonArray) { - JSONObject rangeObject = (JSONObject) object; - String facetName = rangeObject.getString("facet_name"); - if(null != statsFieldObjects.optJSONObject(facetName, null)) { - rangeObject.put(JSON_KEY_DYNAMIC_MIN, statsFieldObjects.getJSONObject(facetName).getInt("min")); - rangeObject.put(JSON_KEY_DYNAMIC_MAX, statsFieldObjects.getJSONObject(facetName).getInt("max")); + if(null != statsFieldObjects) { + Iterator keys = statsFieldObjects.keys(); + while(keys.hasNext()) { + String key = keys.next(); + JSONObject valueObject = statsFieldObjects.getJSONObject(key); + // now that we have the value, go through the range facets and get the right one. + JSONArray jsonArray = panlObject.getJSONObject(JSON_KEY_AVAILABLE).getJSONArray(Processor.JSON_KEY_RANGE_FACETS); + for(Object object : jsonArray) { + JSONObject rangeObject = (JSONObject) object; + if(rangeObject.getString("facet_name").equals(key)) { + rangeObject.put(JSON_KEY_DYNAMIC_MIN, valueObject.optInt("min", -1)); + rangeObject.put(JSON_KEY_DYNAMIC_MAX, valueObject.optInt("max", -1)); + } } } } @@ -357,6 +363,20 @@ private String parseResponse( solrJsonObject.remove("stats"); + // now we need to go through the range facets and remove any that are + // suppressed + + JSONArray removedRanges = new JSONArray(); + for (Object jsonObject : panlObject.getJSONObject(JSON_KEY_AVAILABLE).getJSONArray(Processor.JSON_KEY_FACETS)) { + JSONObject facetObject = (JSONObject) jsonObject; + String lpseCode = facetObject.getString(Processor.JSON_KEY_PANL_CODE); + if(!collectionProperties.getIsSuppressedRangeFacet(lpseCode)) { + removedRanges.put(facetObject); + } + } + + panlObject.getJSONObject(JSON_KEY_AVAILABLE).put(Processor.JSON_KEY_FACETS, removedRanges); + panlObject.put(JSON_KEY_ACTIVE, activeProcessor.processToObject(panlTokenMap)); diff --git a/src/main/java/com/synapticloop/panl/server/handler/fielderiser/field/BaseField.java b/src/main/java/com/synapticloop/panl/server/handler/fielderiser/field/BaseField.java index 24bbc6a1..d68b2c7f 100644 --- a/src/main/java/com/synapticloop/panl/server/handler/fielderiser/field/BaseField.java +++ b/src/main/java/com/synapticloop/panl/server/handler/fielderiser/field/BaseField.java @@ -758,7 +758,7 @@ protected JSONObject getAdditionURIObject(CollectionProperties collectionPropert if (baseField.getLpseCode().equals(additionLpseCode)) { additionObject.put(JSON_KEY_BEFORE, lpseUri.toString() + baseField.getResetUriPath(panlTokenMap, collectionProperties)); -// additionObject.put(JSON_KEY_BEFORE, baseField.getResetUriPath(panlTokenMap, collectionProperties)); + lpseUri.setLength(0); lpseCode.append(baseField.getResetLpseCode(panlTokenMap, collectionProperties)); lpseCode.append(baseField.getLpseCode()); diff --git a/src/main/java/com/synapticloop/panl/server/handler/fielderiser/field/facet/PanlOrFacetField.java b/src/main/java/com/synapticloop/panl/server/handler/fielderiser/field/facet/PanlOrFacetField.java index 1b2e92c4..f3d471a1 100644 --- a/src/main/java/com/synapticloop/panl/server/handler/fielderiser/field/facet/PanlOrFacetField.java +++ b/src/main/java/com/synapticloop/panl/server/handler/fielderiser/field/facet/PanlOrFacetField.java @@ -81,7 +81,7 @@ public PanlOrFacetField(String lpseCode, String propertyKey, Properties properti } @Override protected void applyToQueryInternal(SolrQuery solrQuery, List lpseTokenList) { - // if there is only one... + // if there is only one... no need to do anything different if (lpseTokenList.size() == 1) { OrFacetLpseToken facetLpseToken = (OrFacetLpseToken) lpseTokenList.get(0); @@ -94,7 +94,7 @@ public PanlOrFacetField(String lpseCode, String propertyKey, Properties properti StringBuilder stringBuilder = new StringBuilder(); boolean isFirst = true; - // at this point, we are going through the or filters + // at this point, we are going through the OR filters for (LpseToken lpseToken : lpseTokenList) { OrFacetLpseToken orFacetLpseToken = (OrFacetLpseToken) lpseToken; if (isFirst) { @@ -199,6 +199,7 @@ public PanlOrFacetField(String lpseCode, String propertyKey, Properties properti return (true); } } + return (false); } diff --git a/src/main/java/com/synapticloop/panl/server/handler/fielderiser/field/facet/PanlRangeFacetField.java b/src/main/java/com/synapticloop/panl/server/handler/fielderiser/field/facet/PanlRangeFacetField.java index 046d5b34..64c9492b 100644 --- a/src/main/java/com/synapticloop/panl/server/handler/fielderiser/field/facet/PanlRangeFacetField.java +++ b/src/main/java/com/synapticloop/panl/server/handler/fielderiser/field/facet/PanlRangeFacetField.java @@ -57,6 +57,7 @@ public class PanlRangeFacetField extends PanlFacetField { public static final String PROPERTY_KEY_PANL_RANGE_MAX = "panl.range.max."; public static final String PROPERTY_KEY_PANL_RANGE_MIN_WILDCARD = "panl.range.min.wildcard."; public static final String PROPERTY_KEY_PANL_RANGE_MAX_WILDCARD = "panl.range.max.wildcard."; + public static final String PROPERTY_KEY_PANL_RANGE_SUPPRESS = "panl.range.suppress."; public static final String PROPERTY_KEY_PANL_RANGE_PREFIX = "panl.range.prefix."; public static final String PROPERTY_KEY_PANL_RANGE_SUFFIX = "panl.range.suffix."; public static final String PROPERTY_KEY_PANL_RANGE_MIN_VALUE = "panl.range.min.value."; @@ -90,6 +91,8 @@ public class PanlRangeFacetField extends PanlFacetField { private final boolean hasMinRangeWildcard; private final boolean hasMaxRangeWildcard; + private boolean rangeSuppress = false; + public PanlRangeFacetField(String lpseCode, String propertyKey, Properties properties, String solrCollection, String panlCollectionUri, int lpseLength) throws PanlServerException { super(lpseCode, propertyKey, properties, solrCollection, panlCollectionUri, lpseLength); @@ -124,6 +127,7 @@ public PanlRangeFacetField(String lpseCode, String propertyKey, Properties prope this.rangeMaxValueReplacement = properties.getProperty(PROPERTY_KEY_PANL_RANGE_MAX_VALUE + lpseCode, null); this.hasMinRangeWildcard = properties.getProperty(PROPERTY_KEY_PANL_RANGE_MIN_WILDCARD + lpseCode, "false").equals("true"); this.hasMaxRangeWildcard = properties.getProperty(PROPERTY_KEY_PANL_RANGE_MAX_WILDCARD + lpseCode, "false").equals("true"); + this.rangeSuppress = properties.getProperty(PROPERTY_KEY_PANL_RANGE_SUPPRESS + lpseCode, "false").equals("true"); } @Override @@ -350,6 +354,7 @@ public boolean appendAvailableRangeValues( rangeFacetObject.put(JSON_KEY_VALUE_TO, rangeFacetLpseToken.getToValue()); } } + // addition URIs are a little bit different... JSONObject additionURIObject = getRangeAdditionURIObject(collectionProperties, panlTokenMap); rangeFacetObject.put(JSON_KEY_URIS, additionURIObject); @@ -386,7 +391,7 @@ private JSONObject getRangeAdditionURIObject( // depends on whether there is an infix // at this point we want to also do the min value replacement, if it // exists - if (null != rangeMaxValueReplacement) { + if (null != rangeMinValueReplacement) { additionObject.put(JSON_KEY_BEFORE_MIN_VALUE, lpseUri.toString() + URLEncoder.encode(rangeMinValueReplacement, StandardCharsets.UTF_8)); } @@ -420,22 +425,26 @@ private JSONObject getRangeAdditionURIObject( lpseUri.setLength(0); lpseCodeUri.append(baseField.getLpseCode()); - if (hasRangeInfix) { - lpseUri.append(URLEncoder.encode(getRangeSuffix(), StandardCharsets.UTF_8)); - } else { - lpseUri.append(URLEncoder.encode(getValueSuffix(), StandardCharsets.UTF_8)); - } + if (hasRangeInfix) { + lpseUri.append(URLEncoder.encode(getRangeSuffix(), StandardCharsets.UTF_8)); + } else { + lpseUri.append(URLEncoder.encode(getValueSuffix(), StandardCharsets.UTF_8)); + } + + if (null != rangeMaxValueReplacement) { + lpseUriAfterMax.append(URLEncoder.encode(rangeMaxValueReplacement, StandardCharsets.UTF_8)) + .append(FORWARD_SLASH); + } - if (null != rangeMaxValueReplacement) { - lpseUriAfterMax.append(URLEncoder.encode(rangeMaxValueReplacement, StandardCharsets.UTF_8)) - .append(FORWARD_SLASH); - } lpseUri.append(FORWARD_SLASH); } } additionObject.put(JSON_KEY_AFTER, lpseUri.toString() + lpseCodeUri.toString() + FORWARD_SLASH); - additionObject.put(JSON_KEY_AFTER_MAX_VALUE, lpseUriAfterMax.toString() + lpseCodeUri.toString() + FORWARD_SLASH); + + if (null != rangeMaxValueReplacement) { + additionObject.put(JSON_KEY_AFTER_MAX_VALUE, lpseUriAfterMax.toString() + lpseCodeUri.toString() + FORWARD_SLASH); + } return (additionObject); } @@ -474,11 +483,20 @@ public FromToBean getDecodedRangeValues(String value) { String fromString = ""; String toString = ""; + boolean hasFrom = false; + boolean hasTo = false; + + // if we have a range infix - then there is either a min/max value + // replacement, or there is a range prefix/suffix + if (hasRangeInfix) { // It is OK to decode the value as it is all in one String decodedValue = URLDecoder.decode(value, StandardCharsets.UTF_8); // then we need to split by the infix String[] fromToSplit = decodedValue.split(rangeValueInfix); + + // at this point, with a range infix, determine whether we have a min + // /max value replacement if (fromToSplit.length != 2) { return (null); } else { @@ -496,10 +514,11 @@ public FromToBean getDecodedRangeValues(String value) { } // at this point we have two values, the from and to - although they may - // have a min or max value replacement + // have a min or max value replacement, and we need to remove the ranges if (hasRangeInfix) { + // test the range min value replacement if (null != rangeMinValueReplacement) { if (fromString.equals(rangeMinValueReplacement)) { fromString = getMinRange(); @@ -782,4 +801,8 @@ public void addToRemoveObject(JSONObject removeObject, LpseToken lpseToken) { } } } + + public boolean getRangeSuppress() { + return rangeSuppress; + } } diff --git a/src/main/java/com/synapticloop/panl/server/handler/processor/AvailableProcessor.java b/src/main/java/com/synapticloop/panl/server/handler/processor/AvailableProcessor.java index 478e555f..c0607f47 100644 --- a/src/main/java/com/synapticloop/panl/server/handler/processor/AvailableProcessor.java +++ b/src/main/java/com/synapticloop/panl/server/handler/processor/AvailableProcessor.java @@ -160,6 +160,7 @@ public AvailableProcessor(CollectionProperties collectionProperties) { * @param panlTokenMap The Map of existing tokens that are already in the URI * * @return The addition URI + * private JSONObject getAdditionURIObject(BaseField lpseField, Map> panlTokenMap, boolean shouldRange) { String additionLpseCode = lpseField.getLpseCode(); diff --git a/src/main/java/com/synapticloop/panl/server/handler/properties/CollectionProperties.java b/src/main/java/com/synapticloop/panl/server/handler/properties/CollectionProperties.java index 381d8ed7..2dc50a0f 100644 --- a/src/main/java/com/synapticloop/panl/server/handler/properties/CollectionProperties.java +++ b/src/main/java/com/synapticloop/panl/server/handler/properties/CollectionProperties.java @@ -142,7 +142,8 @@ public class CollectionProperties { private final Map SOLR_NAME_TO_FIELD_MAP = new HashMap<>(); private final Map LPSE_CODE_TO_SORT_FIELD_MAP = new HashMap<>(); private final Map SOLR_NAME_TO_SORT_FIELD_MAP = new HashMap<>(); - private final Map LPSE_CODE_DATE_FACET_MAP = new HashMap<>(); + private final Map LPSE_CODE_DATE_RANGE_FACET_MAP = new HashMap<>(); + private final Map LPSE_CODE_RANGE_FACET_MAP = new HashMap<>(); private final Map LPSE_CODE_BOOLEAN_FACET_MAP = new HashMap<>(); private final Map> LPSE_CODE_WHEN_MAP = new HashMap<>(); @@ -423,7 +424,7 @@ private void parseFacetFields() throws PanlServerException { PanlFacetField facetField; if (TYPE_SOLR_DATE_POINT_FIELD.equals(solrFieldType)) { facetField = new PanlDateRangeFacetField(lpseCode, panlFieldKey, properties, solrCollection, panlCollectionUri, lpseLength); - LPSE_CODE_DATE_FACET_MAP.put(lpseCode, (PanlDateRangeFacetField) facetField); + LPSE_CODE_DATE_RANGE_FACET_MAP.put(lpseCode, (PanlDateRangeFacetField) facetField); } else if (TYPE_SOLR_BOOL_FIELD.equals(solrFieldType)) { facetField = new PanlBooleanFacetField(lpseCode, panlFieldKey, properties, solrCollection, panlCollectionUri, lpseLength); LPSE_CODE_BOOLEAN_FACET_MAP.put(lpseCode, (PanlBooleanFacetField) facetField); @@ -432,6 +433,7 @@ private void parseFacetFields() throws PanlServerException { PANL_CODE_OR_FIELDS.add(lpseCode); } else if (isRangeFacet) { facetField = new PanlRangeFacetField(lpseCode, panlFieldKey, properties, solrCollection, panlCollectionUri, lpseLength); + LPSE_CODE_RANGE_FACET_MAP.put(lpseCode, (PanlRangeFacetField) facetField); PANL_CODE_RANGE_FIELDS.add(lpseCode); } else { facetField = new PanlFacetField(lpseCode, panlFieldKey, properties, solrCollection, panlCollectionUri, lpseLength); @@ -927,9 +929,16 @@ public boolean getHighlight() { } public List getDateRangeFacetFields() { - return (List.of(LPSE_CODE_DATE_FACET_MAP.values().toArray(new PanlDateRangeFacetField[0]))); + return (List.of(LPSE_CODE_DATE_RANGE_FACET_MAP.values().toArray(new PanlDateRangeFacetField[0]))); } + public boolean getIsSuppressedRangeFacet(String lpseCode) { + if(LPSE_CODE_RANGE_FACET_MAP.containsKey(lpseCode)) { + return(LPSE_CODE_RANGE_FACET_MAP.get(lpseCode).getRangeSuppress()); + } + return(false); + } + public Set getLpseWhenCode(String lpseCode) { return (LPSE_CODE_WHEN_MAP.get(lpseCode)); } diff --git a/src/main/java/com/synapticloop/panl/server/handler/tokeniser/token/param/QueryLpseToken.java b/src/main/java/com/synapticloop/panl/server/handler/tokeniser/token/param/QueryLpseToken.java index 093365f9..9f155a81 100644 --- a/src/main/java/com/synapticloop/panl/server/handler/tokeniser/token/param/QueryLpseToken.java +++ b/src/main/java/com/synapticloop/panl/server/handler/tokeniser/token/param/QueryLpseToken.java @@ -29,6 +29,7 @@ import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; +import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.*; @@ -50,13 +51,16 @@ public QueryLpseToken( super(lpseCode, collectionProperties); if (null != valueTokeniser && valueTokeniser.hasMoreTokens()) { - this.value = valueTokeniser.nextToken(); + this.value = URLDecoder.decode( + valueTokeniser.nextToken(), + StandardCharsets.UTF_8); } for (NameValuePair nameValuePair : URLEncodedUtils.parse(queryFromUri, StandardCharsets.UTF_8)) { if (nameValuePair.getName().equals(collectionProperties.getFormQueryRespondTo())) { - this.value = nameValuePair.getValue(); - + this.value = URLDecoder.decode( + nameValuePair.getValue(), + StandardCharsets.UTF_8); isOverride = true; break; } diff --git a/src/main/resources/panl_collection_url.panl.properties.template b/src/main/resources/panl_collection_url.panl.properties.template index 600691eb..d62d6481 100644 --- a/src/main/resources/panl_collection_url.panl.properties.template +++ b/src/main/resources/panl_collection_url.panl.properties.template @@ -346,6 +346,7 @@ solr.highlight=false # panl.range.max.value.= # panl.range.min.wildcard.= # panl.range.max.wildcard.= +# panl.range.suppress.= # # Notes: # panl.range.facet. @@ -373,6 +374,10 @@ solr.highlight=false # panl.range.max.wildcard. # (Optional) If the maximum value acts as a wildcard (and therefore # will also include values which are greater than this value) +# panl.range.suppress. +# (Optional) By default, panl will return a range and the individual +# values for a facet. Setting this to true will only return the range +# values. # # ~ ~ ~ * ~ ~ ~ # diff --git a/src/main/resources/webapp/static/panl-explainer.js b/src/main/resources/webapp/static/panl-explainer.js index 40ebcde8..5c1bd337 100644 --- a/src/main/resources/webapp/static/panl-explainer.js +++ b/src/main/resources/webapp/static/panl-explainer.js @@ -10,7 +10,7 @@ $(document).ready(function() { currentCollectionName = collectionName; } - availableCollections.append(" - [ " + fieldSet + " ]"); + availableCollections.append(" - [ " + fieldSet + " ]"); } availableCollections.append("
") diff --git a/src/main/resources/webapp/static/panl-viewer.js b/src/main/resources/webapp/static/panl-viewer.js index 0fbdf839..7e7b74fd 100644 --- a/src/main/resources/webapp/static/panl-viewer.js +++ b/src/main/resources/webapp/static/panl-viewer.js @@ -481,6 +481,7 @@ function addAvailableFilters(availableObject, activeObject) { "\" id=\"range-anchor-" + facet.facet_name + "\">[Apply]"); + ranges.append("
(Actual dynamic range: " + facet.dynamic_min + " to " + facet.dynamic_max + ")
") slider.noUiSlider.on("update", function(values, handle, unencoded, tap, positions, noUiSlider) { @@ -498,6 +499,7 @@ function addAvailableFilters(availableObject, activeObject) { facet.uris.before + values[0] + (!facet.uris.has_infix ? "" : (facet.suffix !== undefined ? facet.suffix : "")); + var generatedHrefAfter = (facet.uris.has_infix ? "" : (facet.prefix !== undefined ? facet.prefix : "")) + values[1] + @@ -505,6 +507,7 @@ function addAvailableFilters(availableObject, activeObject) { var generatedHrefDuring = facet.uris.during; + // if(facet.uris.before_min_value !== undefined && values[0] === parseInt(facet.min)) { generatedHrefBefore = facet.uris.before_min_value; if(!facet.uris.has_infix && values[0] === parseInt(facet.min)) { diff --git a/src/test/java/com/synapticloop/panl/server/handler/processor/AvailableProcessorTest.java b/src/test/java/com/synapticloop/panl/server/handler/processor/AvailableProcessorTest.java index cbef7258..78ed1833 100644 --- a/src/test/java/com/synapticloop/panl/server/handler/processor/AvailableProcessorTest.java +++ b/src/test/java/com/synapticloop/panl/server/handler/processor/AvailableProcessorTest.java @@ -46,9 +46,9 @@ public class AvailableProcessorTest { } - JSONArray rageFacets = jsonObject.getJSONArray(Processor.JSON_KEY_RANGE_FACETS); - for(int i = 0; i < rageFacets.length(); i++) { - JSONObject facetObject = rageFacets.getJSONObject(i); + JSONArray rangeFacets = jsonObject.getJSONArray(Processor.JSON_KEY_RANGE_FACETS); + for(int i = 0; i < rangeFacets.length(); i++) { + JSONObject facetObject = rangeFacets.getJSONObject(i); JSONObject urisObject = facetObject.getJSONObject(Processor.JSON_KEY_URIS); assertEquals("/", urisObject.getString(Processor.JSON_KEY_BEFORE)); diff --git a/src/test/java/com/synapticloop/panl/server/handler/tokeniser/token/RangeFacetLpseTokenTest.java b/src/test/java/com/synapticloop/panl/server/handler/tokeniser/token/RangeFacetLpseTokenTest.java index d6daab49..d9936c96 100644 --- a/src/test/java/com/synapticloop/panl/server/handler/tokeniser/token/RangeFacetLpseTokenTest.java +++ b/src/test/java/com/synapticloop/panl/server/handler/tokeniser/token/RangeFacetLpseTokenTest.java @@ -29,7 +29,7 @@ private void testDefaults(String propertiesLocation, String uri) { @Test public void testPreMidSufRangeDecoding() { testDefaults("/range/prefix-infix-suffix.properties", "/weighing+from+10+to+50+grams/"); testDefaults("/range/prefix-infix-suffix.properties", "/from+light+to+heavy+pencils/"); - testDefaults("/range/prefix-infix-suffix.properties", "/10+grams+to+heavy+pencils/"); + testDefaults("/range/prefix-infix-suffix.properties", "/weighing+from+10+to+heavy+pencils/"); testDefaults("/range/prefix-infix-suffix.properties", "/from+light+to+50+grams/"); }