JavaScript Facet-Filter

Heute mal wieder etwas mehr Theorie und Forschung und nicht nur Einrichtung und Administration von Servern.

Manchmal hat man einen Satz und Daten den filtern möchte und dann hätte man noch zusätzlich gerne Ansichten auf das Ergebnis von verschieden Facetten aus. Anders gesagt, man möchte wissen, welches Ergebnis zu welchen Filter gehört.

Ich hab hier einen Service in JavaScript geschrieben der genau so etwas macht und der auch mit AngularJS oder Node.js verwendet werden kann. Für kleine Datensätze erspart man sich damit Systeme wie ElasticSearch, die natürlich sehr viel besser und ausgereifter sind, als dieses kleine Beispiel, aber es gibt einen guten Einblick in das Thema. Auch sieht man daran, dass man alles gut parallelisieren kann.

Einmal kann man beim allgemeinen Filter-Durchlauf die zu filternde Menge aufteilen und jeden Teil einzeln in einem Worker/Thread filtern lassen und dann die Ergebnisse wieder zusammen führen. Danach kann man auch jedes Facet wiederum durch einen eigenen Worker/Thread bearbeiten lassen. Beides habe ich hier aber noch nicht umgesetzt.

Der Service:


/*
{
filter:[
{
field:"",
values:[]
op:"eq"
}
]
facetFields:[
{
field:"",
value:""
}
]
}
*/

function FacetsFilterService(){
this.filterArray=function(field, values, items, op){
var result = [];
for(var i=0;i<items.length;i++){
var item=items;
if(item[field]){
if((!op || op=="ep") && values.indexOf(item[field]) > -1){
result[result.length] = item;
}
else if(op=="gt"){
var found=false;
for(var v=0;v<values.length && !found;v++){
if(values[v] > item[field]){
found = true;
}
}
if(found){
result[result.length] = item;
}
}
else if(op=="gt"){
var found=false;
for(var v=0;v<values.length && !found;v++){
if(values[v] < item[field]){
found = true;
}
}
if(found){
result[result.length] = item;
}
}
else if(op=="bw"){

}
}
}
return result;
}

this.filter=function(request, items){
var fullResult = items;
//TODO this to a worker and split items
for(var i=0; i< request.filter.length; i++){
var filter = request.filter;
if(filter.values && filter.values.length>0){
fullResult = this.filterArray(filter.field, filter.values, fullResult);
}
}

var result = {
request : request,
full : fullResult,
facets: []
};

//TODO worker per facet-field
for(var j=0; j< request.facetFields.length;j++){
var facetField = request.facetFields[j];
result.facets[result.facets.length] = {
field:facetField.field,
value:facetField.value,
hits: this.filterArray(facetField.field, facetField.value, fullResult)
};
}

return result;
}

this.createFilterList = function(items, ignoreList){
var result=[];
for(var i=0; i< items.length; i++){
var item = items;
var keys = Object.keys(item);
for(var k=0;k<keys.length;k++){
var key=keys[k];
if(!ignoreList || !ignoreList.indexOf(key) == -1){
if(!result[key]){
result[key] = {
name:key,
values:[]
}
}

var value = item[key];
if(result[key].indexOf(value) == -1){
result[key].values.push(value);
}
}
}
}

var tmp=[];
for(var index in result){
tmp[tmp.length] = result[index];
}

return result;
}
}


Beispiel Daten:


var values=[
{
id:1,
type:"80486",
postfix:"SX",
mhz:25,
socket:"Socket 3"
},
{
id:2,
type:"80486",
postfix:"DX",
mhz:66,
socket:"Socket 3"
},
{
id:3,
type:"80486",
postfix:"DX",
mhz:100,
socket:"Socket 3"
},
{
id:4,
type:"P5",
postfix:"",
mhz:60,
socket:"Socket 5"
},
{
id:5,
type:"P54",
postfix:"C",
mhz:90,
socket:"Socket 5"
},
{
id:6,
type:"P54",
postfix:"C",
mhz:100,
socket:"Socket 5"
},
{
id:7,
type:"P54",
postfix:"C",
mhz:133,
socket:"Socket 5"
},
{
id:8,
type:"P55",
postfix:"C",
mhz:166,
socket:"Socket 7"
},
{
id:9,
type:"P55",
postfix:"C",
mhz:200,
socket:"Socket 7"
}
];



Und nun wollen wir alle CPUs mit 66MHz, 100MHz, 133MHz und 166MHz. Zusätzlich wollen wir aber noch wissen, wie sich diese Ergebnisse auf den Socket 3,4 und 5 verteilen:


var request={
filter:[
{
field:"mhz",
values:[66,100,133,166]
}
],
facetFields:[
{
field:"socket",
value:"Socket 3"
},
{
field:"socket",
value:"Socket 4"
},
{
field:"socket",
value:"Socket 5"
}
]
};


Das wenden wir nun auf die DAten an:


var service=new FacetsFilterService();
var result = service.filter(request, values);
console.log(result);


Und unser Ergebnis sieht wie erwartet aus:

* 486 mit 66MHz
* 486 und Pentium mit 100MHz
* Pentium mit 133MHz
* Pentium MMX mit 166MHz

bbcode-image


und der Pentium MMX 166MHz fällt bei den Facets wieder raus weil er zum Sockel 7 gehört, der aber nicht durch die Facets abgedeckt sind:

* Sockel 3: 66Mhz und 100Mhz
* Sockel 4: keine Ergebnisse
* Sockel 5: 100MHz und 133MHz

bbcode-image


Man kann die Facets natürlich auch ohne vorgeschalteten Filter nutzen und so damit Daten für die Anzeige aufbereiten.

Auch kann man dieses ohne Probleme auch auf PHP oder Java portieren und gerade bei Java kann man mit Threads sicher eine sehr performante Lösung damit bauen.
User annonyme 2017-03-18 12:41

write comment:
Six + = 7

Möchtest Du AdSense-Werbung erlauben und mir damit helfen die laufenden Kosten des Blogs tragen zu können?