Create a custom search engine with Episteme - gsi-upm/Episteme GitHub Wiki
In this example we are going to create a search engine for job positions.
First of all we will define the following RDF data: "Person", "Skill", "Skill level". Here there is an example of each.
- File: person.rdf
- File: skill.rdf
- File: level.rdf
We want the following search fields:
- Skill and skill level
- Province or city
- Candidate type
- Will travel?
- Free text
We need to know the SPARQL queries to populate the autocomplete fields. Depending on the ontology we use the queries will be different. For this ontology are the following:
Query to obtain the list of skills:
SELECT ?o WHERE {
?s skos:prefLabel ?o .
?s skos:member "skill"
}
Query to obtain the list of levels:
SELECT ?o WHERE {
?s skos:prefLabel ?o .
?s skos:member "level"
}
Query to obtain the list of provinces:
PREFIX vcard: <http://www.w3.org/2006/vcard/ns#>
SELECT DISTINCT ?province WHERE {
?s rdf:type foaf:Person .
?s vcard:locality ?province
}
ORDER BY ?province ASC(?province)
Query to obtain the candidate types:
PREFIX cv: <http://kaste.lv/~captsolo/semweb/resume/cv.rdfs#>
SELECT DISTINCT ?type WHERE {
?a rdf:type foaf:Person .
?a cv:jobTitle ?type
}
ORDER BY ?type ASC(?type)
In this example all the fields matches the one in original Episteme with companies, exception the "will travel condition". For those wich are the same, we have to change the SPARQL queries in the javascript code (they must be url encoded).
For example, to load the list of skills, the new code will be: (only changing the 'query' variable)
function loadSkills(graph) {
templateSkills = []
var graphINIT = ''
var graphEND = ''
if (graph != '') {
graphINIT = '+++GRAPH+%3Chttp%3A%2F%2F' + graph + '%3E%7B%0D%0A'
graphEND = '+++%7D%0D%0A'
}
var query = endPoint + 'sparql/select?query=PREFIX+mao%3A+%3Chttp%3A%2F%2Fwww.w3.org%2Fns%2Fma-ont%23%3E%0D%0APREFIX+yt%3A+%3Chttp%3A%2F%2Fgdata.youtube.com%2Fschemas%2F2007%23%3E%0D%0APREFIX+rdfs%3A+%3Chttp%3A%2F%2Fwww.w3.org%2F2000%2F01%2Frdf-schema%23%3E%0D%0APREFIX+foaf%3A+%3Chttp%3A%2F%2Fxmlns.com%2Ffoaf%2F0.1%2F%3E%0D%0APREFIX+ldp%3A+%3Chttp%3A%2F%2Fwww.w3.org%2Fns%2Fldp%23%3E%0D%0APREFIX+lmftypes%3A+%3Chttp%3A%2F%2Fwww.newmedialab.at%2Flmf%2Ftypes%2F1.0%2F%3E%0D%0APREFIX+dc%3A+%3Chttp%3A%2F%2Fpurl.org%2Fdc%2Felements%2F1.1%2F%3E%0D%0APREFIX+skos%3A+%3Chttp%3A%2F%2Fwww.w3.org%2F2004%2F02%2Fskos%2Fcore%23%3E%0D%0APREFIX+rdf%3A+%3Chttp%3A%2F%2Fwww.w3.org%2F1999%2F02%2F22-rdf-syntax-ns%23%3E%0D%0APREFIX+owl%3A+%3Chttp%3A%2F%2Fwww.w3.org%2F2002%2F07%2Fowl%23%3E%0D%0APREFIX+sioc%3A+%3Chttp%3A%2F%2Frdfs.org%2Fsioc%2Fns%23%3E%0D%0APREFIX+dcat%3A+%3Chttp%3A%2F%2Fwww.w3.org%2Fns%2Fdcat%23%3E%0D%0APREFIX+dct%3A+%3Chttp%3A%2F%2Fpurl.org%2Fdc%2Fterms%2F%3E%0D%0APREFIX+context%3A+%3Chttp%3A%2F%2Fkrusti.gsi.dit.upm.es%3A8080%2FLMF-3.0.0%2Fcontext%2F%3E%0D%0APREFIX+xsd%3A+%3Chttp%3A%2F%2Fwww.w3.org%2F2001%2FXMLSchema%23%3E%0D%0APREFIX+local%3A+%3Chttp%3A%2F%2Fkrusti.gsi.dit.upm.es%3A8080%2FLMF-3.0.0%2Fresource%2F%3E%0D%0ASELECT+%3Fskillname+WHERE+%7B%0D%0A++%3Fs+skos%3AprefLabel+%3Fskillname+.%0D%0A++%3Fs+skos%3Amember+%22skill%22+++%0D%0A%7D&output=json';
$.getJSON(query, function (data) {
$.each(data.results.bindings, function (key, val) {
var skill = val.skillname.value;
//console.log(skill);
templateSkills.push(skill);
});
var unique = templateSkills.filter(function (itm, i, a) {
return i == a.indexOf(itm);
});
self.Skill(unique);
});
}
We have to create the new search field for "will travel" condition.
Editing index.html, near line 300, below other SOLR filters we add:
<span data-bind="if: $data.field() == 'Travel'">
<span class="filterName" data-bind="text: $root.lang().type"></span>
<input class="solrInput" data-bind="kendoComboBox: { data: $root.Type, value: $root.selectedType }" />
<a class="greenButton" data-bind="click: $root.addSolrFilter.bind($data, $data.values, $root.selectedType)">+</a>
<a class="filterInfo blueButton" data-bind="click: $root.addSolrFilter.bind($data, $data.values, $root.selectedType),attr: { 'title': $root.lang().typeHelp}">?</a>
</span>
So it looks like:
<!-- Filtros SOLR -->
<div data-bind="foreach: $data.solr">
<div class="filter" data-bind="visible: $data.name() != 'hidden'">
<span data-bind="if: $data.field() == 'Province'">
<span class="filterName" data-bind="text: $root.lang().province"></span>
<input class="solrInput" data-bind="kendoComboBox: { data: $root.Province, value: $root.selectedProvince }" />
<a class="greenButton" data-bind="click: $root.addSolrFilter.bind($data, $data.values, $root.selectedProvince)">+</a>
<a class="filterInfo blueButton" data-bind="click: $root.addSolrFilter.bind($data, $data.values, $root.selectedType),attr: { 'title': $root.lang().provinceHelp}">?</a>
</span>
<span data-bind="if: $data.field() == 'Type'">
<span class="filterName" data-bind="text: $root.lang().type"></span>
<input class="solrInput" data-bind="kendoComboBox: { data: $root.Type, value: $root.selectedType }" />
<a class="greenButton" data-bind="click: $root.addSolrFilter.bind($data, $data.values, $root.selectedType)">+</a>
<a class="filterInfo blueButton" data-bind="click: $root.addSolrFilter.bind($data, $data.values, $root.selectedType),attr: { 'title': $root.lang().typeHelp}">?</a>
</span>
<span data-bind="if: $data.field() == 'Description'">
<span class="filterName" data-bind="text: $root.lang().freeText"></span>
<input class="solrInput k-input" type="text" data-bind="value: $root.selectedDescription, valueUpdate: 'afterkeydown', attr: { 'placeholder': $root.lang().typePlaceHolder }">
<a class="greenButton" data-bind="click: $root.addSolrFilter.bind($data, $data.values, $root.selectedDescription)">+</a>
<a class="filterInfo blueButton" data-bind="click: $root.addSolrFilter.bind($data, $data.values, $root.selectedType),attr: { 'title': $root.lang().descriptionHelp}">?</a>
</span>
<!-- New SOLR filter: Traveling posibility-->
<span data-bind="if: $data.field() == 'Travel'">
<span class="filterName" data-bind="text: $root.lang().travel"></span>
<input class="solrInput" data-bind="kendoComboBox: { data: $root.Travel, value: $root.selectedTravel}" />
<a class="greenButton" data-bind="click: $root.addSolrFilter.bind($data, $data.values, $root.selectedTravel)">+</a>
<a class="filterInfo blueButton" data-bind="click: $root.addSolrFilter.bind($data, $data.values, $root.selectedType),attr: { 'title': $root.lang().travelHelp}">?</a>
</span>
We need to add the string variables we are going to use in the /js/dictionary.js file. For example:
In Spanish lang:
travel: "Con posibilidades de viajar",
travelHelp: "Marcar si se desean candidatos que tengan disponibilidad de viajar",
In English lang
travel: "Condition will travel",
travelHelp: "Check if desired candidates with travel condition",
We have to edit mvvm.js, around line 63, editing the "templateSelectedSearch" object, we add:
{ "name": "travel",
"field": "Travel",
"type": "comboBox",
"values": []
},
Looking like:
"solr": [{
"name": "hidden",
"field": "Provenance",
"type": "comboBox",
"values": ["gsi"]
}, {
"name": "province",
"field": "Province",
"type": "autoComplete",
"values": []
}, {
"name": "type",
"field": "Type",
"type": "comboBox",
"values": []
}, {
"name": "travel",
"field": "Travel",
"type": "comboBox",
"values": []
}, {
"name": "freeText",
"field": "Description",
"type": "inputText",
"values": []
}
And we also have to define the posible values the comboBox can take, we add the following lines after line 216 aprox.
/* added travel */
self.Travel = ko.observableArray(["true","false"]);
self.selectedTravel = ko.observable("");
/* end added */
With this lines we say that this new field can take the "true" and "false" values. If we would like to retrieve those values from the database executing an SPARQL query, we can follow the example given with the rest of fields, for example with the provinces list:
self.Province = ko.observableArray(templateProvinces);
self.selectedProvince = ko.observable("");
It loads an array variable called "templateProvinces" that we have to generate first, this is done when we call:
function loadProvinces()
So we should have to create a variable called "travelingData", a function named "loadTravelingData()" that fullfill the variable with the corresponging danta, and then call the function somewhere in the code, like in line 236:
//initial functions
loadSkills('gsi');
loadProvinces('gsi');
loadTypes('gsi');