shopping24 tech blog

s is for shopping

November 12, 2015 / by Jörg Rathlev / Software engineer / @

A utility library for working with Solr's NamedList

We’ve published a small open-source library that makes working with Solr’s NamedList a bit easier.

The class NamedList is a core class in Solr. Unfortunately, working with instances of NamedList is not the most pleasant experience. Two of the main annoyances we observed are:

  • NamedList is a generic class. In practice, however, it is often used to hold heterogenous objects, and many methods that return named lists only return a NamedList<Object>. Thus, a lot of type casting is required.
  • Working with nested lists involves a lot of null-checks if some of the elements are optional.

To alleviate those issues, we have written a utility class that provides a set of helper methods for working with named lists. There are navigate methods that help with navigating into nested named lists, and getter methods that help with accessing content objects in typesafe ways without requiring a type cast.

All of the methods are null-safe. If the named list passed into a method is null, the method will simply return null. This is a deliberate design decision to make working with nested named lists easier: you only have to check if the final element you retrieved using the utility methods is null.

There is also a wrapper class, ConvenientNamedList, that allows you to navigate nested named lists and access their elements in a fluent API style. Note that using this class has a small additional overhead because it creates a new ConvenientNamedList wrapper instance when you get a nested NamedList.

Usage

The library is published on Maven Central. If you use Maven, simply add the following dependency:

<dependency>
    <groupId>com.s24.search.solr</groupId>
    <artifactId>solr-util</artifactId>
    <version>1.1</version>
</dependency>

There are no external dependencies except SolrJ, and JUnit for unit tests.

The library is licensed under the Apache License, Version 2.0. Source code is available on Github.

Examples

In the first example, which is a slightly edited example from our production code, the utility methods are used to first retrieve a list of NamedLists from the response (which is also a NamedList). Then, some elements are retrieved from each list. Note that no type casts are required in this code:

List<NamedList<?>> entities = NamedLists.getList(response, "entities");
for (NamedList<?> object : entities) {
   String entityType = NamedLists.getString(object, "entityType");
   String entityName = NamedLists.getString(object, "entityName");
   ...
}

The second example was previously implemented using plain SolrJ and then refactored. This is the old version, copied nearly verbatim from our production code:

private String firstSuggestion(SolrQueryResponse response) {
   NamedList spellcheck = (NamedList) response.getValues().get("spellcheck");
   if (spellcheck != null) {
      NamedList termsWithSuggestions = (NamedList) spellcheck.get("suggestions");
      if (termsWithSuggestions != null && termsWithSuggestions.size() > 0) {
         NamedList firstTerm = (NamedList) termsWithSuggestions.getVal(0);
         if (firstTerm != null) {
            Set<String> suggestions = (Set<String>) firstTerm.get("suggestion");
            if (suggestions != null) {
               return suggestions.stream().findFirst().orElse(null);
            }
         }
      }
   }
   return null;
}

This is the refactored version, using the ConvenientNamedList:

private String firstSuggestion(SolrQueryResponse response) {
   Set<String> suggestions = new ConvenientNamedList(response.getValues())
         .getNested("spellcheck", "suggestions")
         .getNested(0)
         .getSet("suggestion");
   if (suggestions != null) {
      return suggestions.stream().findFirst().orElse(null);
   }
   return null;
}

Compared to the previous implementation, this code has a small runtime overhead for creating the wrapper objects, but it is much more readable and easier to maintain.

Conclusion

We think that using NestedList becomes a lot easier with some simple helper methods like the ones provided by our library. We hope that this library is useful for you as well!