The address book will scale better if there is a search capability.  This is a practical necessity if there are hundreds or thousands of entries. That requires some JavaScript to call back to the Callimachus server to perform the search.  You could do that with a named query that takes a parameter, or you could issue against the SPARQL endpoint directly. We will show you how to do the latter since you already know how to set up a named query.

Modify the Named Query View Template

  1. Create an <input type="text"> tag somewhere on the view template.
HTML Code HTML Rendered

<form id="search" class="input-group">
    <input id="searchInput" type="text" class="form-control" 
        placeholder="Search..." />
    <span class="input-group-btn">
        <button type="submit" class="btn btn-default">
            <span class="glyphicon glyphicon-search" />
        </button>
    </span>
</form>

Write JavaScript Against SPARQL Endpoint

Now that there is an HTML element that JavaScript can be bound to, we can write the JavaScript that uses that input and feeds it into the SPARQL query.

Step Instructions
1.

Write a JavaScript function that retrieves and stores the value of the <input> element when a user submits the form. 


$("#search").bind("submit", function(event) {
        event.preventDefault();
        var term = $("#searchInput").val();
});
2.

Write a SPARQL query to return only instances of the Person class that contain the string searched by the user. It's often easiest to test these against the SPARQL endpoint directly before converting it into JavaScript.


SELECT ?name WHERE {
  ?person a <{your-path-to-person-class}/Person>
    ; rdfs:label ?name .
  FILTER regex(?name, '.*{your-search-term}.*', 'i')
}
3.

Encode the query as a string in JavaScript and pass in the value gathered in step 1 to replace {your-search-term} in step 2.


var query = "SELECT ?name WHERE {" +
    "?person a <{your-path-to-person-class}/Person>" +
    " ; rdfs:label ?name ." +
    "FILTER regex(?name, '.*" + term + ".*', 'i')" +
    "} LIMIT 10";
4.

Execute the query via an AJAX call to the SPARQL endpoint for your local Callimachus instance.


$.ajax({
    type: 'GET',
    url: '/sparql?query=' + encodeURIComponent(query),
    headers: {
        Accept: 'application/sparql-results+xml'
    },
    success: function(data, textStatus, jqXHR) {
        console.log(data);
    }
});

Now that we've got all the pieces we can put it together into a single cohesive JavaScript code block.


$("#search").bind("submit", function(event) {
   event.preventDefault();
   var term = $("#searchInput").val();
   var query = "SELECT ?name WHERE {" +
        "?person a </Development/Projects/Tutorial/finished-app/classes/Person>" +
        " ; rdfs:label ?name ." +
        "FILTER regex(?name, '.*" + term + ".*', 'i')" + 
        "} LIMIT 10";
        
    $.ajax({
        type: 'GET',
        url: '/sparql?query=' + encodeURIComponent(query),
        headers: {
            Accept: 'application/sparql-results+xml'
        },
        success: function(data, textStatus, jqXHR) {
           console.log(data);
        }
    });
});

Filter View Template Based on Results

Now we have data being returned based on a user's input, but all we're doing is logging it to the console. Not very useful! Now it's time to write the JavaScript (via jQuery) that will actually take action based on those results and execute our search filter.

Step Instructions
1.

Store the data you will be using for comparison in an array for future use. In this case we'll be using the person's name to filter. You could easily use another field if you altered the query, or use multiple fields to make a more robust searching solution.


var people = [];
$(data).find("literal").each(function() {
    people.push($(this).text());  
});
2.

Now you want to loop through the existing people in your address book and remove any rows that contains a person who is not present in your search results. jQuery makes this easy with :selectors, $.each(), and other similar functions.


$("tbody > tr > td:first-child").each(function() {
    var name = $(this).find("a").text();
    if ($.inArray(name, people) == -1) {
        $(this).closest('tr').hide();
    }
});

We can plug that into the function we created earlier to get our final JavaScript function which looks like this:


$("#search").bind("submit", function(event) {
	event.preventDefault();
	var term = $("#searchInput").val();
	var query = "SELECT ?name WHERE {" +
		"?person a </Development/Projects/Tutorial/finished-app/classes/Person>" +
		" ; rdfs:label ?name ." +
		"FILTER regex(?name, '.*" + term + ".*', 'i')" + 
		"} LIMIT 10";
	
	$.ajax({
		type: 'GET',
		url: '/sparql?query=' + encodeURIComponent(query),
		headers: {
			Accept: 'application/sparql-results+xml'
		},
		success: function(data, textStatus, jqXHR) {
			var people = [];
			$(data).find("literal").each(function() {
				people.push($(this).text());  
			});
			
			$("tbody > tr > td:first-child").each(function() {
				var name = $(this).find("a").text();
				if ($.inArray(name, people) == -1) {
					$(this).closest('tr').hide();
				}
			});
		}
	});
});

Now when a user types something into the search box and submits the form, our JavaScript retrieves that value, uses it to search through Callimachus, parses the results, and filters the table on our page based on those results. Pretty neat, huh? There's a few more things you'll notice on our page that make the user experience a bit more pleasant. For example, users need to be able to revert to the entire address book so they can keep searching. Here's how we decided to accomplish this:

HTML Code JavaScript Code HMTL Rendered

<span id="reset" class="close pull-right">Reset</span>

function reset() {
    $('tbody tr').each(function() {
        $(this).show();
    });
    $("#searchInput").val('');
}

$("#reset").click(reset);

$("#searchInput").keyup(function() {
    if ($(this).val() === "") {
        reset();
    } 
});

All of the code related to search has been spruced up by placing it inside a Bootstrap 3 Panel. This is by no means tied to the functionality and could easily be changed to fit whatever design or layout was desired.

A Note About SPARQL Endpoint Permissions

The use of the Callimachus SPARQL endpoint is limited by default to those users in the 'admin' and 'power' groups.  Administrators may change this behavior by giving 'Reader'  permissions to other users or groups by selecting 'Permissions' from the main menu when viewing the SPARQL endpoint.  'Reader' permission on the SPARQL endpoint allows HTTP GET (read) operations via that interface.

Administrators are cautioned not to provide members of the public with permissions to issue SPARQL queries.  Doing so provides substantial opportunities for denial of service (DoS) attacks.

Wrap Up & Review

This tutorial demonstrates a couple important points about Callimachus. One is that every Callimachus instance comes with a default SPARQL endpoint available at /sparql. This allows you to execute any valid SPARQL query across your entire database. These queries can be executed directly via the SPARQL interface or via AJAX and JavaScript, as we did here. The second important point is that jQuery comes by default in Callimachus so there's no need to explicitly include it on any given page. This offers the ability to quickly and easily write JavaScript to extend the functionality of a Callimachus page.