Skip to content

Search sessions and history

Overview

The Modern UI supports search sessions, the ability to establish a session that persists with the search user across multiple queries. This capability allows Funnelback to record the search and click history for a user, as well as provide a result shopping basket for the user to retain a set of selected search results.

The user session is established through HTTP cookies. History and result selection are saved in a database. There is one database for a given Funnelback installation, but the data is saved on a per-collection basis, meaning that each collection will have separate search and click history, and separate results selection for a given user.

When sessions are enabled, users are automatically assigned a unique ID which is transmitted with every search query.

Caveats

  • This feature rely on the URL of search results being valid URLs, and identical as their URL in the index. If the URL is rewritten in any way (using a hook script for example) you will need to ensure that the search results URLs passed to the session API calls are the indexed URLs, not the rewritten ones.

Enabling and configuring search sessions

Sessions are enabled with the ui.modern.session=true parameter. Once enabled, Funnelback will immediately start recording searches and clicks, and allow users to select results.

Enabling this parameter will also record unique user IDs in the query and click logs.

Configuration options

Various parameters can be used to configure the session features. Please consult their individual documentation for more details:

Cookies and identifiers

Enabling sessions will provide each user with a unique ID, generated and stored server-side. A J2EE session cookie is then sent back and forth between the user and the server.

It might be desirable in some cases to expose the user ID directly in the cookie, or to enable the user ID generation without enabling the full set of session features. To do so, use the ui.modern.session.set_user_id_cookie setting. When enabled, the user ID will be set in a cookie named user-id, even if ui.modern.session is set to false, and the user ID will also be written in the query and click logs.

Performance impacts

To record search history Funnelback needs to contact the session database for every query and this impacts the query response time. The time spent contacting and querying the database will depend on the server configuration, the type of database used and the size of the database. For example, on a standard Linux server with the built-in database engine and a fresh database the overhead is around 20ms per query.

For this reason it's preferable to not enable the session features until you decide to make use of them in your search results page. If you need unique user IDs only, without the search & click history and results selection, consider using ui.modern.session.set_userid_cookie as described in the previous section. This will cause the Modern UI to generate user IDs, but will not cause it to connect to the database to retrieve the user session data.

Using an external database

Funnelback ships with an embedded H2 database to store the user sessions. If you want to use an external database you will need to configure a JNDI datasource in Jetty and initialise a new schema with the required tables.

The following instructions give an overview of the process however some steps will be specific to the external database system type. The example given below is specific to PostgreSQL. Please contact support@funnelback.com for further assistance.

The first step is to configure a new datasource in Jetty:

  • Obtain the relevant JDBC driver JAR for your database. It can be stored anywhere on the Funnelback server. In this example we'll use $SEARCH_HOME/web/postgresql-9.4-1201.jdbc4.jar
  • Update the Jetty service file to add this new JAR to the classpath:
    • Edit $SEARCH_HOME/services/jetty-webserver.service
    • Add a line wrapper.java.classpath.jdbc=/path/to/search_home/web/postgresql-9.4-1201.jdbc4.jar
  • Create a $SEARCH_HOME/web/conf/customiseJettyServers.groovy as outlined in Configuring Funnelback's embedded web server with the content below:
  • Restart Jetty
    import org.eclipse.jetty.server.Server;
    import org.eclipse.jetty.util.log.*;
    import org.eclipse.jetty.plus.jndi.*;
    // The following import will be specific to your JDBC database driver.
    // Note that it's often preferable to use a connection pool rather than using
    // the driver directly
    import org.postgresql.ds.PGSimpleDataSource;

    def Map<String, Server> customise(Map<String, Server> servers) {

       // Create data source
       def ds = new PGSimpleDataSource()
       ds.user = "user"
       ds.password = "password"
       ds.databaseName = "databaseName"
       ds.serverName = "serverName"
       ds.portNumber = 1234

       // Register the datasource and Hibernate dialect
       // to the JNDI context. The list of available dialects can be found
       // in the Hibernate documentation, e.g. http://docs.jboss.org/hibernate/orm/3.5/javadocs/org/hibernate/dialect/package-summary
       new EnvEntry("jdbc/search-session", ds)
       new EnvEntry("jdbc/dialect/search-session", "org.hibernate.dialect.PostgreSQLDialect")

       // A logger might be useful for debugging. The messages will appear in the Jetty log ($SEARCH_HOME/web/logs/jetty.log)
       def logger = Log.getLogger("com.funnelback.jetty.customise")
       logger.info("Configured JNDI datasource")

       return servers;
    }

You also need to create a new database with the relevant credentials, and initialise its schema with the Funnelback tables. Those steps are specific to the database system type, but Funnelback provides a script to initialise the required tables in $SEARCH_HOME/bin/setup/update-session-db.groovy. This script needs to be run with Groovy. For example:

cd $SEARCH_HOME
tools/groovy/bin/groovy bin/setup/update-session-db.groovy --help

By default this script will initialise the built-in H2 database. To target your external database you'll have to set the --driver and --url options, to specify the class of the JDBC driver, and the JDBC url to use to connect to the database. Additionally you need to ensure that your JDBC driver is on the classpath with the -cp option.

This script runs in "dry mode" by default. To actually apply the changes to the database, pass the --apply command line option. See the script help for further details.

A complete command line example is:

 cd $SEARCH_HOME
 tools/groovy/bin/groovy -cp $SEARCH_HOME/web/postgresql-9.4-1201.jdbc4.jar bin/setup/update-session-db.groovy --driver org.postgresql.Driver --url 'jdbc:postgresql://db-server?user=username&password=password' --apply

The Jetty documentation provides some examples of using a connection pool based on a JDBC url and driver class. They can be converted to the Groovy syntax above to be used in Funnelback.

User interface and API

Data model

When search sessions are enabled, a new top-level session node will be present in the Data Model. This node contains:

  • The ID of the user
  • The list of previous queries
  • The list of previously clicked results
  • The selection of results (cart) made by the user

These nodes can be manipulated with FreeMarker like the other nodes of the data model.

REST API

A REST API is available to manipulate the results selection, as well as clearing the search and click history. The cart manipulation is especially useful when you don't want the user to have to submit a new query every time a result is added or removed from the cart. With this API, the cart operations can be implemented client side in Javascript using Ajax to call the API.

Clearing the search and click history

  • To clear the search history, call /s/search-history.json?collection=... with a HTTP DELETE method.
  • To clear the click history, call /s/click-history.json?collection=... with a HTTP DELETE method.

This will clear the history for the current user.

Cart manipulation

The REST endpoint to manipulate the cart is /s/cart.json?collection=.... The HTTP method used will decide the type of operation:

  • GET will return the cart, as a JSON list.
  • DELETE, when called with no parameters, will clear the entire cart.
  • DELETE, when called with a url parameter (/s/cart.json?collection=...&url=...), will remove the URL from the cart.
  • POST will add a new item to the cart. An url parameter must be present, with the URL of the result to add to the cart.

All method calls will return the content of the cart for the current user, in the form of a JSON list.

Purging old sessions

The database will grow over time as carts and history events get stored. To purge the session database and only retain the N last day of sessions, a GET call to /maintenance/purge-sessions.json?daysToKeep=N can be made. It's recommended to make this call at regular intervals to keep the database size under control.

Note: To prevent arbitrary external users to purge the sessions database, this call can only be made from localhost (e.g. http://localhost/s/maintenance/purge-sessions.json)

Processing and rendering the cart

The result cart can be processed and rendered by implementing a custom Groovy action, and a custom FreeMarker template. Typical use cases includes exporting the cart in a PDF or printable format, or contacting a third party web service to submit the cart information to it.

Processing the cart with a custom Groovy script

The cart will be processed by redirecting the user to /s/cart-process?collection=...&profile=.... When this URL is called, Funnelback will invoke the cart-process.groovy script from the collection configuration folder. This script needs to implement the following interface:

 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import com.funnelback.publicui.search.model.collection.Collection;
 import com.funnelback.publicui.search.model.transaction.session.CartResult;
 import com.funnelback.publicui.search.model.transaction.session.SearchUser;
 import com.funnelback.publicui.search.web.controllers.session.CustomCartProcessor;
 import org.springframework.web.servlet.ModelAndView;
 def class MyCartProcessor extends CustomCartProcessor {
   /**
    * Processes the cart
    *
    * @param collection Collection where processing takes place
    * @param user Current user from the session
    * @param cart Results cart, for the given collection and user
    * @return A {@link ModelAndView} containing the data and the view to use
    */
   @Override
   public ModelAndView process(Collection collection, SearchUser user, List<CartResult> cart) {
     ...
   }
 }

The following parameters are passed in:

collection

A collection object, identical to the one used in User interface hook scripts

user

The current user, with its ID in user.id

cart

The cart content, as a list of results identical to the search data model results in transaction.response.resultPacket.resultsThe script must return a value of type ModelAndView. This is a composite class containing a data model and the name of a FreeMarker template to use to render the cart. For example, if you wanted to place the current date in the data model, and render the cart using the application-form.ftl template, you could use the following code:

 def model = [:]
 model["date"] = new Date()
 return new ModelAndView("application-form", model);

Note that the Modern UI will automatically place the collection, current user and cart content in the data model so you don't need to add them by yourself.

Implementation of this Groovy script is optional. If it's not present, the Modern UI will directly render the cart using the default FreeMarker template name: cart-process.ftl

Rendering the cart with FreeMarker

Once the cart has been processed, the configured FreeMarker template is called with the following entries in the data model:

collection

A collection object, identical to the one used in User interface hook scripts

user

The current user, with its ID in user.id

cart

The cart content, as a list of results identical to the search data model results in transaction.response.resultPacket.resultsOther items can be added to the data model by customising the cart processing, as explained in the previous section.

The FreeMarker template is then rendered, identically to a regular search query.

See also

top

Funnelback logo
v15.12.0