Advanced Search

Provide users with alternate search interfaces through a dropdown in the search pane. These additional interfaces have their own saved searches and completely control the interaction of search.

Search extensions control how the search is executed and the results are displayed.

Tutorial

Web Plugin

Register the plugin and a component/template for the new search interface.

app.registerJavaScript("/org/visallo/examples/search_advanced/plugin.js", true);
app.registerJavaScript("/org/visallo/examples/search_advanced/example.js", false);
app.registerJavaScriptTemplate("/org/visallo/examples/search_advanced/template.hbs");

The search extension requires a search URL used for saved searches, but we can use the built-in ones by just defining a new route. To access the route in the front-end we need to also add a services object in worker.js.

app.registerWebWorkerJavaScript("/org/visallo/examples/search_advanced/worker.js");
// line hidden…

app.get("/org/visallo/examples/search_advanced/search", authenticator, csrfProtector, ReadPrivilegeFilter.class, ElementSearch.class);
app.post("/org/visallo/examples/search_advanced/search", authenticator, csrfProtector, ReadPrivilegeFilter.class, ElementSearch.class);

Defining a new services object extends what methods dataRequest can access.

define('data/web-worker/services/org-visallo-examples-search', [
    '../util/ajax'
], function(ajax) {
    'use strict';

    var api = {
            search: function(query, offset, size) {
                return ajax('GET', '/element/search', {
                    q: query,
                    filter: '[]',
                    offset: offset,
                    size: size
                })
            }
        };

    return api;
});

Register Extension

Register the search extension pointing to a Flight component. The savedSearchUrl points to the route created previously.

visallo.registry.registerExtension('org.visallo.search.advanced', {
    displayName: 'Example',
    componentPath: 'org/visallo/examples/search_advanced/example',
    savedSearchUrl: '/org/visallo/examples/search_advanced/search'
});

Search Component

Create the Flight component, it will be responsible for the UI, loading saved searches, executing searches, and displaying results.

define([
    'public/v1/api',
    'hbs!./template'
], function(
    api,
    template) {
    // 4 lines hidden…
    return api.defineComponent(SearchExample);

    function SearchExample() {

        // 81 lines hidden…

    }
});

Run Search and Display Results

Using the service created earlier, we can make a data request to run the search and get the result as a promise. Using the public API we access the List component for display.

api.connect().then(function(connected) {
    self.dataRequest = connected.dataRequest;
    return Promise.all([
        connected.components.List,
        connected.dataRequest('org-visallo-examples-search', 'search', query, 0, SIZE)
    ])
}).spread(function(List, result) {

The search results should be rendered in an element outside of the extension component. The search interface defines a container to use, and the CSS selector to that container is passed to all extension interfaces as the attribute resultsSelector.

this.resultsContainer = $(this.attr.resultsSelector);

The container HTML is structured as follows:

<div class="{{ resultsSelector }}" style="display:none">
    <div class="content">
        <!-- results content should be here -->
        <!-- or attach element list to "content" node -->
    </div>
</div>

To display the results, render the List component into the results containers' .content element, switch the display style on the container to show it, and enable infiniteScrolling.

var content = self.resultsContainer.css('display', 'block')
    .find('.content')
    .teardownAllComponents()
    .empty()

List.attachTo(content, {
    items: result.elements,
    usageContext: 'searchresults',
    nextOffset: result.nextOffset,
    infiniteScrolling: true,
    total: result.totalHits
})

Infinite Scroll

To finish making infinite scroll work, we listen for events on the results. Note, we have to listen using the container since events won't bubble up to the extension container as its not a descendant.

this.on(this.resultsContainer, 'infiniteScrollRequest', this.onInfiniteScrollRequest);

Then, when we get notified of scrolling, make another search request with the given offset, and trigger an update to the List element.

this.onInfiniteScrollRequest = function(event, data) {
    var self = this;

    this.dataRequest('org-visallo-examples-search', 'search', this.query, data.paging.offset, SIZE)
        .then(function(result) {
            self.trigger(
                event.target,
                'addInfiniteItems',
                {
                    success: true,
                    items: result.elements,
                    total: result.totalHits,
                    nextOffset: result.nextOffset
                }
            );
        })
};

Notify of Search Changes

All search extensions should notify via setCurrentSearchForSaving that the search has modified. This allows the extension to work with the Saved Searches component to save the current search. The url should match the savedSearchUrl defined in the extension.

this.onChange = function(event) {
    this.trigger('setCurrentSearchForSaving', {
        url: '/org/visallo/examples/search_advanced/search',
        parameters: {
            q: $(event.target).val()
        }
    })
}

results matching ""

    No results matching ""