Customizing Search Queries

cancel
Showing results for 
Search instead for 
Did you mean: 

Customizing Search Queries

ddraper
Intermediate II
2 4 5,859

Update 14/11/2016

From version 1.0.96 of Aikau this post is out-of-date in that the issue was raised as a feature enhancement and a solution was implemented to support adding additional query parameters through configuration of the additionalQueryParameters on the AlfSearchList widget.

Introduction

Now that we have this great new Community platform it should be easier for me to stay on top of Aikau and Share customization related questions. In particular I'm going to try and set aside some time each day to work through any interesting use cases that are raised in the ECM space.

This is the first question that I'm going to provide an example for. The requirement is to be able to customize the search results page so that only documents are shown. There are actually a number of different ways in which this problem can be solved using Aikau and I wanted to work through a few of them.

The various solutions fall into two different categories:

  1. Update the query before it is sent to the Repository
  2. Filter out any unwanted items from the response

Use renderFilter?

The second option could be achieved by creating an extension module to change the configuration of the search result widgets to only display when the "type" attribute of the result is "document". This would be done using the renderFilter configuration and would mostly work with a couple of caveats:

  1. If items were filtered out then the displayed search result count would not match the number of results displayed
  2. If only non-documents were found and all of them were filtered out then the user would not see the "No results found" message.
  3. If only non-documents made up the first page of results, then it would not be possible to trigger the infinite scrolling events to load the next page of data.
  4. It would be necessary to apply the configuration to widgets in each view (the search page has two views out-of-the-box).

So it looks like the renderFilter configuration approach is not the best solution in this case, but definitely one to be aware of when solving other problems. However, the configuration would look something like this:

...
"renderFilter": [
  {
    "property": "type",
    "values": ["document"]
  }
]
...‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Some Background on Search

So in order to avoid the problems that would be introduced with the renderFilter approach we want to customize the search query before it is made. At this point it would probably be helpful to explain a little bit about how the search page (and Aikau in general) works.

The AlfSearchList widget is responsible for managing search state. It tracks search requests and manages the scope and facet filters that the user wishes to apply. It also manages what page of data is being displayed as well as the required sorting information and views that can display the data.

However it does not actually make the REST API call to perform the search, nor is it responsible for rendering the results. The REST API call is actually made by the SearchService which subscribes to a search request topic that the AlfSearchList publishes.

Therefore we have two choices - we can customize the AlfSearchList so that the request payload is updated before it is published, or we can customize the SearchService so that the payload is updated when it is received.

Configure AlfSearchList?

The ideal solution here would be if we were able to re-configure the AlfSearchList to force it to include additional data in the search payload. Unfortunately at the time of writing (when the latest Aikau release is 1.0.85) this capability does not exist. If you encounter this situation when trying to customize Share then ideally we would like you to raise an issue on the Aikau GitHub project, like this.....  or better yet - provide an update via a pull request

Customize AlfSearchList?

Since there isn't a configuration option available to us at present, then next option is to consider extending AlfSearchList with out own custom widget. The AlfList (which is an ancestor of AlfSearchList) provides the updateLoadDataPayload function for exactly this purpose - but sadly (due to the history of the development of AlfSearchList) this function is not called. If you were to encounter a problem like this then it would also be a good candidate for an issue!

Customize SearchService

So this leaves (at the time of writing at least) the best option as being to extend the SearchService. You can define an extending module to do this like this:

define(["dojo/_base/declare",
        "alfresco/services/SearchService"],
       function(declare,SearchService) {

  return declare([SearchService], {
    onSearchRequest: function(payload) {
      payload.term = payload.term + ""
      payload.datatype = "cm:content";
      this.inherited(arguments); // This calls the superclass function
    }
  });
});‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

This shows the onSearchRequest function being extended to update the payload before the superclass function is called. In the example shown above we are showing a couple of options:

  1. Appending to the search term
  2. Adding a query parameter to set the datatype to be "cm:content"

(Note: Any payload attribute unknown to the SearchService is treated as a query parameter!)

Creating An Extension Module

The thing we need to do is to create an extension module that can be deployed into Share to apply our changes. This module needs to do 3 things:

  1. Define an AMD package for our custom service
  2. Customize the search model to swap out the original service for our custom service
  3. Provide the custom service

Creating an extension module for a "full" Aikau page is incredibly simple using the "Developer View" and is described in detail in this previous blog post.

We will need to update the extension module to add in a new AMD package and this is described in this previous blog post.

In order to customize the search page model we will need to edit the "faceted-search.get.js" file that is generated for us to look like this:

var services = model.jsonModel.services;
for (var i=0; i<services.length; i++)
{
   if (services[i] === "alfresco/services/SearchService")
   {
      services[i] = "blogs/CustomSearchService";
   }
   else if (services[i].name === "alfresco/services/SearchService")
   {
      services[i].name = "blogs/CustomSearchService";
   }
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

We iterate over the services (which can be defined either as a simple string or as an object) and when we find the default search service we swap it out for our custom service.

The End Result

Once you've re-packaged your extension module as a JAR file you can drop it into the "share/WEB-INF/lib" folder (or include it in an AMP file, or port the code into an SDK project to build an AMP) and restart Share. All subsequent searches on the search page will only return items that are "cm:content".

The problem is that a lot more things are "cm:content" than you might realise. The question author only wanted to show documents, but "cm:content" does not mean "document". If you have a special type defined that you're using then this would work, actually though what you're probably going to want to do is to append some extra stuff the search term. This sort of thing in fact...

AND -TYPE:"cm:thumbnail" AND -TYPE:"cm:failedThumbnail" AND -TYPE:"cm:rating" AND -TYPE:"fm:post" AND -ASPECT:"sys:hidden" AND -cm:creator:system'‍‍

Note: In fact this query still doesn't completely filter everything out, but you get the general idea of what is required

Download and try it out

I've made the code available on this GitHub repository for people to review in more detail and try out. There is even a ready made JAR file that you can download and test with.

If you've found this post useful or interesting then it would be great if you could like or share it from the top of the page!

4 Comments