1
0
mirror of https://github.com/stevenferrer/multi-select-facet.git synced 2025-11-23 21:54:45 +02:00

dynamic facet configurations

This commit is contained in:
Steven Ferrer
2020-06-13 23:21:28 +08:00
parent a1fd241063
commit 207be3ace3
13 changed files with 475 additions and 306 deletions

View File

@@ -1,8 +1,8 @@
# Multi-Select Facet Example
An example of multi-select facet using [Solr](https://lucene.apache.org/solr), [Vue](https://vuejs.org) and [Go](http://go.dev/).
This example uses [Buefy](http://buefy.org/) components and [solr-go](https://github.com/stevenferrer/solr-go) for interacting with [Solr](https://lucene.apache.org/solr).
## Running the example
Use docker-compose maybe?
## Contributing
Please feel free to improve this by [sending a PR](https://github.com/stevenferrer/multi-select-facet/pulls) or [opening an issue](https://github.com/stevenferrer/multi-select-facet/issues).

View File

@@ -1,276 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"github.com/stevenferrer/solr-go"
)
type facet struct {
Name string `json:"name"`
Buckets []bucket `json:"buckets"`
}
type bucket struct {
Val string `json:"val"`
SkuCount int `json:"skuCount"`
ProductCount int `json:"productCount"`
}
type product struct {
ID string `json:"id"`
Name string `json:"name"`
Category string `json:"category"`
ProductType string `json:"productType"`
Brand string `json:"brand"`
}
type facetConfig struct {
field, facet string
vals []string
}
type searchHandler struct {
solrClient solr.Client
}
func (h *searchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
categories := strings.Split(r.URL.Query().Get("categories"), ",")
productTypes := strings.Split(r.URL.Query().Get("productTypes"), ",")
brands := strings.Split(r.URL.Query().Get("brands"), ",")
colorFamilies := strings.Split(r.URL.Query().Get("colorFamilies"), ",")
simCardSlots := strings.Split(r.URL.Query().Get("simCardSlots"), ",")
operatingSystems := strings.Split(r.URL.Query().Get("operatingSystems"), ",")
storageCapacities := strings.Split(r.URL.Query().Get("storageCapacities"), ",")
facetsMap := M{}
catsFacetCfg := facetConfig{
facet: "categories",
field: "category",
vals: categories,
}
productTypsFacetCfg := facetConfig{
facet: "productTypes",
field: "productType",
vals: productTypes,
}
brandsFacetCfg := facetConfig{
facet: "brands",
field: "brand",
vals: brands,
}
filters := []string{}
for _, facetCfg := range []facetConfig{catsFacetCfg, productTypsFacetCfg, brandsFacetCfg} {
tagVals := []string{}
for _, val := range facetCfg.vals {
if val == "" {
continue
}
tagVals = append(tagVals, facetCfg.field+":"+val)
}
if len(tagVals) == 0 {
filters = append(filters, fmt.Sprintf("{!tag=top}%s:*", facetCfg.field))
} else {
filters = append(filters, fmt.Sprintf("{!tag=top}%s", strings.Join(tagVals, " OR ")))
}
facetsMap[facetCfg.facet] = M{
"facet": M{
"productCount": "uniqueBlock(_root_)",
},
"field": facetCfg.field,
"limit": -1,
"type": "terms",
}
}
colorFamiliesFctCfg := facetConfig{
facet: "colorFamilies",
field: "colorFamily_s",
vals: colorFamilies,
}
simCardSlotsFctCfg := facetConfig{
facet: "simCardSlots",
field: "simCardSlots_s",
vals: simCardSlots,
}
operatingSystemsFctCfg := facetConfig{
facet: "operatingSystems",
field: "operatingSystem_s",
vals: operatingSystems,
}
storageCapacitiesFctCfg := facetConfig{
facet: "storageCapacities",
field: "storageCapacity_s",
vals: storageCapacities,
}
childFqs := []string{}
for _, fctCfg := range []facetConfig{colorFamiliesFctCfg, simCardSlotsFctCfg, operatingSystemsFctCfg, storageCapacitiesFctCfg} {
tagVals := []string{}
for _, val := range fctCfg.vals {
if val == "" {
continue
}
tagVals = append(tagVals, fctCfg.field+":"+val)
}
if len(tagVals) == 0 {
childFqs = append(childFqs, fmt.Sprintf("{!tag=%s}%s:*", fctCfg.field, fctCfg.field))
} else {
childFqs = append(childFqs, fmt.Sprintf("{!tag=%s}%s", fctCfg.field, strings.Join(tagVals, " OR ")))
}
facetsMap[fctCfg.facet] = M{
"domain": M{
"excludeTags": "top",
"filter": []string{
fmt.Sprintf("{!filters param=$child.fq excludeTags=%s v=$child.query}", fctCfg.field),
"{!child of=$parent.fq filters=$fq v=$parent.fq}",
},
},
"type": "terms",
"field": fctCfg.field,
"limit": -1,
"facet": M{
"productCount": "uniqueBlock(_root_)",
},
}
}
query := M{
"query": "{!parent tag=top filters=$child.fq which=$parent.fq score=total v=$child.query}",
"queries": M{
"parent.fq": "docType:product",
"child.query": "docType:sku",
"child.fq": childFqs,
},
"filter": filters,
"fields": "*",
"facet": facetsMap,
}
// b, err := json.MarshalIndent(query, "", " ")
// if err != nil {
// log.Fatal(err)
// }
// fmt.Println(string(b))
queryRespone, err := h.solrClient.Query().Query(r.Context(), collection, query)
if err != nil {
http.Error(w, err.Error(), 500)
}
var facets = []facet{}
for k, v := range queryRespone.Facets {
if k == "count" {
continue
}
vv, ok := v.(map[string]interface{})
if !ok {
http.Error(w, "buckets is not map[string]interface{}", 500)
}
bucks, ok := vv["buckets"].([]interface{})
if !ok {
http.Error(w, "buckets not found", 500)
}
buckets := []bucket{}
for _, bk := range bucks {
buck, ok := bk.(map[string]interface{})
if !ok {
http.Error(w, "buck not map[string]interface{}", 500)
}
productCount, ok := buck["productCount"].(float64)
if !ok {
http.Error(w, "product count not found", 500)
}
skuCount, ok := buck["count"].(float64)
if !ok {
http.Error(w, "sku count not found", 500)
}
val, ok := buck["val"].(string)
if !ok {
http.Error(w, "val not found", 500)
}
buckets = append(buckets, bucket{
Val: val,
SkuCount: int(skuCount),
ProductCount: int(productCount),
})
}
facets = append(facets, facet{
Name: k,
Buckets: buckets,
})
}
var products = []product{}
for _, doc := range queryRespone.Response.Docs {
id, ok := doc["id"].(string)
if !ok {
http.Error(w, "id not found", 500)
}
name, ok := doc["name"].([]interface{})
if !ok {
http.Error(w, "name not found", 500)
}
category, ok := doc["category"].([]interface{})
if !ok {
http.Error(w, "category not found", 500)
}
brand, ok := doc["brand"].([]interface{})
if !ok {
http.Error(w, "brand not found", 500)
}
productType, ok := doc["productType"].(string)
if !ok {
http.Error(w, "productType not found", 500)
}
products = append(products, product{
ID: id,
Name: name[0].(string),
Category: category[0].(string),
Brand: brand[0].(string),
ProductType: productType,
})
}
resp := M{
"products": products,
"facets": facets,
"queryResponse": queryRespone,
}
w.Header().Add("content-type", "application/json")
err = json.NewEncoder(w).Encode(resp)
if err != nil {
http.Error(w, http.StatusText(500), 500)
}
}

View File

@@ -17,16 +17,20 @@ import (
solrschema "github.com/stevenferrer/solr-go/schema"
)
type M = map[string]interface{}
// Any is a convenience type for interface{}
type Any = interface{}
// Map is a convenience type for map[string]interface{}
type Map = map[string]Any
const (
collection = "multi-select-demo"
dataPath = "phones.json"
dataPath = "products.json"
)
func main() {
initSchema := flag.Bool("init-schema", false, "initialize solr schema")
index := flag.Bool("index", false, "index products")
collection := flag.String("collection", "multi-select-demo", "specify the name of collection")
initSchema := flag.Bool("initialize-schema", false, "initialize solr schema")
index := flag.Bool("index-data", false, "index the data")
flag.Parse()
solrClient := solr.NewClient("localhost", 8983)
@@ -34,7 +38,7 @@ func main() {
ctx := context.Background()
if *initSchema {
log.Print("initializing solr schema...")
err := initSolrSchema(ctx, solrClient.Schema())
err := initSolrSchema(ctx, *collection, solrClient.Schema())
if err != nil {
log.Fatal(err)
}
@@ -42,7 +46,7 @@ func main() {
if *index {
log.Println("indexing products...")
err := indexProducts(ctx, solrClient.Index())
err := indexProducts(ctx, *collection, solrClient.Index())
if err != nil {
log.Fatal(err)
}
@@ -63,6 +67,7 @@ func main() {
// search handler
r.Method(http.MethodGet, "/search", &searchHandler{
collection: *collection,
solrClient: solrClient,
})
@@ -74,7 +79,8 @@ func main() {
}
}
func initSolrSchema(ctx context.Context, schemaClient solrschema.Client) error {
func initSolrSchema(ctx context.Context, collection string,
schemaClient solrschema.Client) error {
// define the fields
fields := []solrschema.Field{
{
@@ -116,31 +122,29 @@ func initSolrSchema(ctx context.Context, schemaClient solrschema.Client) error {
}
}
// define copy field
err := schemaClient.AddCopyField(ctx, collection, solrschema.CopyField{
Source: "*",
Dest: "_text_",
})
if err != nil {
return errors.Wrap(err, "add copy field")
}
return nil
}
func indexProducts(ctx context.Context, indexClient solrindex.JSONClient) error {
func indexProducts(ctx context.Context, collection string,
indexClient solrindex.JSONClient) error {
b, err := ioutil.ReadFile(dataPath)
if err != nil {
return err
}
var docs []M
var docs []Map
err = json.Unmarshal(b, &docs)
if err != nil {
return err
}
err = indexClient.AddMultiple(ctx, collection, docs)
err = indexClient.AddDocs(ctx, collection, docs)
if err != nil {
return err
}
// commit updates
err = indexClient.Commit(ctx, collection)
if err != nil {
return err
}

View File

@@ -186,5 +186,63 @@
"storageCapacity_s": "128GB"
}
]
},
{
"id": "7",
"name": "Amazon Kindle Paperwhite - 10th Gen",
"brand": "Amazon",
"category": "Electronic Devices",
"productType": "E-reader",
"docType": "product",
"skus": [
{
"id": "70",
"docType": "sku",
"colorFamily_s": "Black",
"storageCapacity_s": "8GB",
"operatingSystem_s": "Linux"
},
{
"id": "71",
"docType": "sku",
"colorFamily_s": "Black",
"storageCapacity_s": "32GB",
"operatingSystem_s": "Linux"
}
]
},
{
"id": "8",
"name": "Kobo Clara HD 6\" Carta E Ink Touchscreen E-Reader",
"brand": "Kobo",
"category": "Electronic Devices",
"productType": "E-reader",
"docType": "product",
"skus": [
{
"id": "80",
"docType": "sku",
"colorFamily_s": "Black",
"storageCapacity_s": "8GB",
"operatingSystem_s": "Linux"
}
]
},
{
"id": "9",
"name": "Likebook Mars E-Reader, 7.8' Carta Touch Screen",
"brand": "Likebook Mars",
"category": "Electronic Devices",
"productType": "E-reader",
"docType": "product",
"skus": [
{
"id": "90",
"docType": "sku",
"colorFamily_s": "Black",
"storageCapacity_s": "16GB",
"operatingSystem_s": "Android"
}
]
}
]

66
cmd/api/sample-query.json Normal file
View File

@@ -0,0 +1,66 @@
{
"query": "{!parent tag=top filters=$skuFilters which=$product score=total v=$sku}",
"queries": {
"product": "docType:product",
"sku": "docType:sku",
"skuFilters": [
"{!tag=colorFamily_s}colorFamily_s:Black",
"{!tag=operatingSystem_s}operatingSystem_s:*",
"{!tag=storageCapacity_s}storageCapacity_s:*"
]
},
"filter": ["{!tag=top}productType:*", "{!tag=top}brand:Amazon"],
"facet": {
"Brand": {
"facet": { "productCount": "uniqueBlock(_root_)" },
"field": "brand",
"limit": -1,
"type": "terms"
},
"Color Family": {
"domain": {
"excludeTags": "top",
"filter": [
"{!filters param=$skuFilters excludeTags=colorFamily_s v=$sku}",
"{!child of=$product filters=$fq v=$product}"
]
},
"facet": { "productCount": "uniqueBlock(_root_)" },
"field": "colorFamily_s",
"limit": -1,
"type": "terms"
},
"Operating System": {
"domain": {
"excludeTags": "top",
"filter": [
"{!filters param=$skuFilters excludeTags=operatingSystem_s v=$sku}",
"{!child of=$product filters=$fq v=$product}"
]
},
"facet": { "productCount": "uniqueBlock(_root_)" },
"field": "operatingSystem_s",
"limit": -1,
"type": "terms"
},
"Product Type": {
"facet": { "productCount": "uniqueBlock(_root_)" },
"field": "productType",
"limit": -1,
"type": "terms"
},
"Storage Capacity": {
"domain": {
"excludeTags": "top",
"filter": [
"{!filters param=$skuFilters excludeTags=storageCapacity_s v=$sku}",
"{!child of=$product filters=$fq v=$product}"
]
},
"facet": { "productCount": "uniqueBlock(_root_)" },
"field": "storageCapacity_s",
"limit": -1,
"type": "terms"
}
}
}

265
cmd/api/search_handler.go Normal file
View File

@@ -0,0 +1,265 @@
package main
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
"github.com/stevenferrer/solr-go"
"github.com/stevenferrer/solr-go/query"
)
// 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) {
facetsMap := Map{}
productFilters, skuFilters := []string{}, []string{}
for _, fctCfg := range facetConfigs {
tagVals := []string{}
filterVals := strings.Split(r.URL.Query().Get(fctCfg.urlParam), ",")
for _, val := range filterVals {
if val == "" {
continue
}
tagVals = append(tagVals, fctCfg.field+":"+val)
}
if !fctCfg.isChild {
// product filters
if len(tagVals) > 0 {
productFilters = append(productFilters, fmt.Sprintf("{!tag=top}%s", strings.Join(tagVals, " OR ")))
} else {
productFilters = append(productFilters, fmt.Sprintf("{!tag=top}%s:*", fctCfg.field))
}
facetsMap[fctCfg.facet] = Map{
"facet": Map{
"productCount": "uniqueBlock(_root_)",
},
"field": fctCfg.field,
"limit": -1,
"type": "terms",
}
continue
}
// sku filters
if len(tagVals) > 0 {
skuFilters = append(skuFilters, fmt.Sprintf("{!tag=%s}%s", fctCfg.field, strings.Join(tagVals, " OR ")))
} else {
skuFilters = append(skuFilters, fmt.Sprintf("{!tag=%s}%s:*", fctCfg.field, fctCfg.field))
}
facetsMap[fctCfg.facet] = Map{
"domain": Map{
"excludeTags": "top",
"filter": []string{
fmt.Sprintf("{!filters param=$skuFilters excludeTags=%s v=$sku}", fctCfg.field),
"{!child of=$product filters=$filter v=$product}",
},
},
"type": "terms",
"field": fctCfg.field,
"limit": -1,
"facet": Map{
"productCount": "uniqueBlock(_root_)",
},
}
}
query := Map{
"query": "{!parent tag=top filters=$skuFilters which=$product score=total v=$sku}",
"queries": Map{
"product": "docType:product",
"sku": "docType:sku",
"skuFilters": skuFilters,
},
"filter": productFilters,
"facet": facetsMap,
}
queryResp, err := h.solrClient.Query().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 := buildResp(queryResp)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
resp["query"] = query
w.Header().Add("content-type", "application/json")
err = json.NewEncoder(w).Encode(resp)
if err != nil {
http.Error(w, err.Error(), 500)
}
}
func buildResp(queryResp *query.Response) (Map, error) {
var facets = []Map{}
for name, v := range queryResp.Facets {
if name == "count" {
continue
}
vv, ok := v.(Map)
if !ok {
return nil, errors.New("v is not M")
}
bucks, ok := vv["buckets"].([]Any)
if !ok {
return nil, errors.New("vv is not []Any")
}
buckets := []Map{}
for _, bk := range bucks {
buck, ok := bk.(Map)
if !ok {
return nil, errors.New("bucket is not M")
}
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, map[string]interface{}{
"val": val,
"skuCount": int(skuCount),
"productCount": int(productCount),
})
}
var param string
for _, fctCfg := range facetConfigs {
if fctCfg.facet == name {
param = fctCfg.urlParam
}
}
facets = append(facets, Map{
"name": name,
"param": param,
"buckets": buckets,
})
}
var products = []Map{}
for _, doc := range queryResp.Response.Docs {
id, ok := doc["id"].(string)
if !ok {
return nil, errors.New("id not found or is not string")
}
name, ok := doc["name"].([]Any)
if !ok {
return nil, errors.New("name not found or is not []Any")
}
category, ok := doc["category"].([]Any)
if !ok {
return nil, errors.New("category not found or is not []Any")
}
brand, ok := doc["brand"].([]Any)
if !ok {
return nil, errors.New("brand not found or is not []Any")
}
productType, ok := doc["productType"].(string)
if !ok {
return nil, errors.New("productType not found or is not string")
}
products = append(products, Map{
"id": id,
"name": name[0].(string),
"category": category[0].(string),
"brand": brand[0].(string),
"productType": productType,
})
}
return Map{
"products": products,
"facets": facets,
}, nil
}

View File

@@ -0,0 +1,28 @@
{
"query": "{!parent tag=top filters=$skuFilters which=docType:product v=docType:sku}",
"queries": {
"skuFilters": ["{!tag=colorFamily_s}colorFamily_s:Black"]
},
"filter": ["{!tag=top}brand:Amazon"],
"facet": {
"Brand": {
"facet": { "productCount": "uniqueBlock(_root_)" },
"field": "brand",
"limit": -1,
"type": "terms"
},
"Color Family": {
"domain": {
"excludeTags": "top",
"filter": [
"{!filters param=$skuFilters excludeTags=colorFamily_s v=$sku}",
"{!child of=docType:product filters=$filter v=docType:product}"
]
},
"facet": { "productCount": "uniqueBlock(_root_)" },
"field": "colorFamily_s",
"limit": -1,
"type": "terms"
}
}
}

View File

@@ -1,4 +1,4 @@
module github.com/stevenferrer/multi-select-facet/api
module github.com/stevenferrer/multi-select-facet
go 1.14
@@ -7,6 +7,6 @@ require (
github.com/go-chi/chi v4.1.2+incompatible
github.com/go-chi/cors v1.1.1
github.com/pkg/errors v0.9.1
github.com/stevenferrer/solr-go v0.0.4
github.com/stevenferrer/solr-go v0.0.6
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 // indirect
)

View File

@@ -13,9 +13,12 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stevenferrer/solr-go v0.0.4 h1:XsUtbAdlX9txVUNWNPwdLoXb7lWBEGGcD4ZLs33ACdE=
github.com/stevenferrer/solr-go v0.0.4/go.mod h1:gatY+DJUzNG3NdONghbJa2JJw6ykdECbiYXtiBy7ykY=
github.com/stevenferrer/solr-go v0.0.6 h1:pFludjKdCmQJxJTwmszwRfAvoqDRG/gcYzf3u+rG5wE=
github.com/stevenferrer/solr-go v0.0.6/go.mod h1:ePa8+6kV1baPpoywPbmcR06wQ0500EGuMntYGxli5Xk=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
@@ -27,3 +30,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -30,7 +30,7 @@ async function search(filters) {
const url = new URL("http://localhost:8081/search");
filters.forEach((filter) => {
url.searchParams.append(filter.name, filter.selected.join(","));
url.searchParams.append(filter.param, filter.selected.join(","));
});
let response = await fetch(url, {
@@ -49,7 +49,7 @@ async function search(filters) {
facets: facets
.map((facet) => {
let selected = [];
let filter = filters.find((filter) => filter.name === facet.name);
let filter = filters.find((filter) => filter.param === facet.param);
if (filter) {
selected = filter.selected;
}
@@ -96,8 +96,8 @@ export default {
const { facets: oldFacet } = this;
const filters = oldFacet
.filter(({ selected }) => selected.length > 0)
.map(({ name, selected }) => {
return { name, selected };
.map(({ name, param, selected }) => {
return { name, param, selected };
});
const { products, facets } = await search(filters);

View File

@@ -1,7 +1,7 @@
<template>
<div>
<div v-for="(facet, i) in facets" :key="i">
<h1 class="title">{{ facet.name }}</h1>
<h1 class="is-size-5">{{ facet.name }}</h1>
<div class="fields">
<div v-for="(bucket, j) in facet.buckets.slice(0, 5)" :key="j">
<b-checkbox

View File

@@ -10,8 +10,9 @@
<div class="product-title">
<span class="is-size-6">{{ data.name }}</span
><br />
<span class="has-text-grey">{{ data.brand }}</span>
</div>
<span class="has-text-grey">{{ data.brand }}</span>
<br />
<span class="is-size-6 has-text-success">PHP 1,501.30 </span>
<br />
@@ -24,6 +25,24 @@
</div>
</template>
<style scoped>
.card {
overflow: hidden;
position: relative;
border-radius: 0.5em;
}
.product-title {
display: -webkit-box;
height: 2em;
-webkit-line-clamp: 3;
line-height: 1em;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
<script>
export default {
props: {