You've already forked multi-select-facet
mirror of
https://github.com/stevenferrer/multi-select-facet.git
synced 2025-11-23 21:54:45 +02:00
273 lines
6.3 KiB
Go
273 lines
6.3 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/stevenferrer/solr-go"
|
|
)
|
|
|
|
// searchHandler is the search handler
|
|
type searchHandler struct {
|
|
collection string
|
|
solrClient solr.Client
|
|
}
|
|
|
|
// facetConfig is a facet configuration
|
|
type facetConfig struct {
|
|
// field is the field name
|
|
field,
|
|
// facet is the facet name
|
|
facet,
|
|
// param is the url query param to be used for retrieving filter values
|
|
urlParam string
|
|
// isChild set to true to indicate that this is a child facet
|
|
isChild bool
|
|
}
|
|
|
|
var facetConfigs = []facetConfig{
|
|
// {
|
|
// facet: "Category",
|
|
// field: "category",
|
|
// urlParam: "categories",
|
|
// },
|
|
{
|
|
facet: "Product Type",
|
|
field: "productType",
|
|
urlParam: "productTypes",
|
|
},
|
|
{
|
|
facet: "Brand",
|
|
field: "brand",
|
|
urlParam: "brands",
|
|
},
|
|
{
|
|
facet: "Color Family",
|
|
field: "colorFamily_s",
|
|
urlParam: "colorFamilies",
|
|
isChild: true,
|
|
},
|
|
// {
|
|
// facet: "Sim Card Slots",
|
|
// field: "simCardSlots_s",
|
|
// urlParam: "simCardSlots",
|
|
// isChild: true,
|
|
// },
|
|
{
|
|
facet: "Operating System",
|
|
field: "operatingSystem_s",
|
|
urlParam: "operatingSystems",
|
|
isChild: true,
|
|
},
|
|
{
|
|
facet: "Storage Capacity",
|
|
field: "storageCapacity_s",
|
|
urlParam: "storageCapacities",
|
|
isChild: true,
|
|
},
|
|
}
|
|
|
|
func (h *searchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
facets := []solr.Faceter{}
|
|
productFilters, skuFilters := []string{}, []string{}
|
|
for _, fc := range facetConfigs {
|
|
filterVals := strings.Split(r.URL.Query().Get(fc.urlParam), ",")
|
|
tagVals := []string{}
|
|
for _, val := range filterVals {
|
|
if val == "" {
|
|
continue
|
|
}
|
|
tagVals = append(tagVals, fc.field+":"+val)
|
|
}
|
|
|
|
if !fc.isChild {
|
|
// product filters
|
|
if len(tagVals) > 0 {
|
|
productFilters = append(productFilters, solr.NewStandardQueryParser().
|
|
Tag("top").Query("'"+strings.Join(tagVals, " OR ")+"'").BuildParser())
|
|
} else {
|
|
productFilters = append(productFilters, solr.NewStandardQueryParser().
|
|
Tag("top").Query(fc.field+":*").BuildParser())
|
|
}
|
|
|
|
termsFacet := solr.NewTermsFacet(fc.facet).Field(fc.field).
|
|
Limit(-1).AddToFacet("productCount", "uniqueBlock(_root_)")
|
|
facets = append(facets, termsFacet)
|
|
|
|
continue
|
|
}
|
|
|
|
// sku filters
|
|
if len(tagVals) > 0 {
|
|
skuFilters = append(skuFilters, solr.NewStandardQueryParser().Tag(fc.field).
|
|
Query("'"+strings.Join(tagVals, " OR ")+"'").BuildParser())
|
|
} else {
|
|
skuFilters = append(skuFilters, solr.NewStandardQueryParser().Tag(fc.field).
|
|
Query(fc.field+":*").BuildParser())
|
|
}
|
|
|
|
termsFacet := solr.NewTermsFacet(fc.facet).
|
|
Field(fc.field).Limit(-1).
|
|
AddToDomain("excludeTags", "top").
|
|
AddToDomain("filter", []string{
|
|
solr.NewFiltersQueryParser().Param("$skuFilters").
|
|
ExcludeTags(fc.field).Query("$sku").BuildParser(),
|
|
solr.NewChildrenQueryParser().Of("$product").Filters("$filter").
|
|
Query("$product").BuildParser(),
|
|
}).
|
|
AddToFacet("productCount", "uniqueBlock(_root_)")
|
|
|
|
facets = append(facets, termsFacet)
|
|
}
|
|
|
|
queryStr := r.URL.Query().Get("q")
|
|
if len(queryStr) == 0 {
|
|
queryStr = "*"
|
|
} else {
|
|
queryStr = fmt.Sprintf("%q", queryStr)
|
|
}
|
|
|
|
productFilters = append(productFilters, solr.NewStandardQueryParser().
|
|
Tag("top").Query("_text_:"+queryStr).BuildParser())
|
|
|
|
queryParser := solr.NewParentQueryParser().Tag("top").Filters("$skuFilters").
|
|
Which("$product").Score("total").Query("$sku")
|
|
query := solr.NewQuery(queryParser.BuildParser()).
|
|
Filters(productFilters...).
|
|
Facets(facets...).
|
|
Queries(solr.M{
|
|
"product": "docType:product",
|
|
"sku": "docType:sku",
|
|
"skuFilters": skuFilters,
|
|
})
|
|
|
|
queryResp, err := h.solrClient.Query(r.Context(), h.collection, query)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), 500)
|
|
return
|
|
}
|
|
|
|
// extract products and facets from query response
|
|
resp, err := buildResponse(queryResp)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), 500)
|
|
return
|
|
}
|
|
|
|
debug := r.URL.Query().Get("debug") == "true"
|
|
if debug {
|
|
resp["query"] = query
|
|
}
|
|
|
|
err = json.NewEncoder(w).Encode(resp)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), 500)
|
|
}
|
|
}
|
|
|
|
func buildResponse(response *solr.QueryResponse) (solr.M, error) {
|
|
var facets = []solr.M{}
|
|
for name, v := range response.Facets {
|
|
if name == "count" {
|
|
continue
|
|
}
|
|
|
|
vv, ok := v.(map[string]interface{})
|
|
if !ok {
|
|
return nil, errors.New("v is not map[string]interface{}")
|
|
}
|
|
|
|
bucks, ok := vv["buckets"].([]interface{})
|
|
if !ok {
|
|
return nil, errors.New("vv is not []interface{}")
|
|
}
|
|
|
|
buckets := []solr.M{}
|
|
for _, bk := range bucks {
|
|
buck, ok := bk.(map[string]interface{})
|
|
if !ok {
|
|
return nil, errors.New("bucket is not map[string]interface{}")
|
|
}
|
|
|
|
productCount, ok := buck["productCount"].(float64)
|
|
if !ok {
|
|
return nil, errors.New("productCount is not float64")
|
|
}
|
|
|
|
skuCount, ok := buck["count"].(float64)
|
|
if !ok {
|
|
return nil, errors.New("count is not float64")
|
|
}
|
|
|
|
val, ok := buck["val"].(string)
|
|
if !ok {
|
|
return nil, errors.New("val is not string")
|
|
}
|
|
|
|
buckets = append(buckets, solr.M{
|
|
"val": val,
|
|
"skuCount": int(skuCount),
|
|
"productCount": int(productCount),
|
|
})
|
|
|
|
}
|
|
|
|
var param string
|
|
for _, fc := range facetConfigs {
|
|
if fc.facet == name {
|
|
param = fc.urlParam
|
|
}
|
|
}
|
|
|
|
facets = append(facets, solr.M{
|
|
"name": name,
|
|
"param": param,
|
|
"buckets": buckets,
|
|
})
|
|
}
|
|
|
|
var products = []solr.M{}
|
|
for _, doc := range response.Response.Documents {
|
|
id, ok := doc["id"].(string)
|
|
if !ok {
|
|
return nil, errors.New("id not found or is not string")
|
|
}
|
|
|
|
name, ok := doc["name"].([]interface{})
|
|
if !ok {
|
|
return nil, errors.New("name not found or is not []interface{}")
|
|
}
|
|
|
|
category, ok := doc["category"].([]interface{})
|
|
if !ok {
|
|
return nil, errors.New("category not found or is not []interface{}")
|
|
}
|
|
|
|
brand, ok := doc["brand"].([]interface{})
|
|
if !ok {
|
|
return nil, errors.New("brand not found or is not []interface{}")
|
|
}
|
|
|
|
productType, ok := doc["productType"].(string)
|
|
if !ok {
|
|
return nil, errors.New("productType not found or is not string")
|
|
}
|
|
|
|
products = append(products, solr.M{
|
|
"id": id,
|
|
"name": name[0].(string),
|
|
"category": category[0].(string),
|
|
"brand": brand[0].(string),
|
|
"productType": productType,
|
|
})
|
|
}
|
|
|
|
return solr.M{
|
|
"products": products,
|
|
"facets": facets,
|
|
}, nil
|
|
}
|