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

Upgrade solr-go to version 0.2

This commit is contained in:
Steven Ferrer
2021-02-10 09:52:07 +08:00
parent fe3169f05f
commit 973d09768b
12 changed files with 193 additions and 218 deletions

2
.gitignore vendored
View File

@@ -13,3 +13,5 @@
# Dependency directories (remove the comment below to include it) # Dependency directories (remove the comment below to include it)
# vendor/ # vendor/
api

View File

@@ -1,11 +1,11 @@
DOCKER ?= docker PODMAN ?= podman
SOLR_INST ?="solr-multi-select-facet-demo" SOLR ?="multi-select-facet-demo"
COLLECTION ?= "multi-select-facet-demo"
.PHONY: solr .PHONY: start-solr
solr: stop-solr start-solr: stop-solr
$(DOCKER) run -d -p 8983:8983 --name $(SOLR_INST) solr:latest solr-precreate $(COLLECTION) $(PODMAN) run -d -p 8983:8983 --name $(SOLR) solr:8 solr -c -f
$(PODMAN) exec -it $(SOLR) bash -c 'sleep 5; wait-for-solr.sh --max-attempts 10 --wait-seconds 5'
.PHONY: stop-solr .PHONY: stop-solr
stop-solr: stop-solr:
$(DOCKER) rm -f $(SOLR_INST) || true $(PODMAN) rm -f $(SOLR) || true

View File

@@ -7,32 +7,25 @@ Blog post: [Multi-Select Facet with Solr, Vue and Go](https://sf9v.github.io/pos
## Running the example ## Running the example
1. Run Solr in `docker`. 1. Run Solr using [podman](https://podman.io/).
```console ```console
$ make solr $ make start-solr
``` ```
Or using [podman](https://podman.io/). Or using `docker`.
```console ```console
$ DOCKER=podman make solr $ PODMAN=docker make start-solr
``` ```
The above commands starts Solr container and pre-creates `multi-select-facet-demo` collection.
Wait for a few seconds until Solr has completed loading.
2. Run the API 2. Run the API
```console ```console
// cd to api
$ cd cmd/api
// build the api // build the api
$ go build -v $ go build -v -o api
// start the api // start the api with the initialization options
$ ./api -init-schema -index-data -init-suggester $ ./api -create-collection -init-schema -index-data -init-suggester
``` ```
3. Run the web app (open a new terminal tab) 3. Run the web app (open a new terminal tab)
@@ -51,7 +44,7 @@ $ yarn serve // or npm run serve
## Contributing ## Contributing
Please feel free to improve this by [sending a PR](https://github.com/sf9v/multi-select-facet/pulls) or [opening an issue](https://github.com/sf9v/multi-select-facet/issues). Feel free to improve this project by [make a pull-request](https://github.com/sf9v/multi-select-facet/pulls) or [opening an issue](https://github.com/sf9v/multi-select-facet/issues).
## License ## License

7
go.mod
View File

@@ -1,11 +1,10 @@
module github.com/sf9v/multi-select-facet module github.com/sf9v/multi-select-facet
go 1.14 go 1.15
require ( require (
github.com/go-chi/chi v4.1.2+incompatible github.com/go-chi/chi v1.5.1
github.com/go-chi/cors v1.1.1 github.com/go-chi/cors v1.1.1
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/sf9v/solr-go v0.1.3 github.com/sf9v/solr-go v0.2.0
golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect
) )

28
go.sum
View File

@@ -1,33 +1,21 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= github.com/go-chi/chi v1.5.1 h1:kfTK3Cxd/dkMu/rKs5ZceWYp+t5CtiE7vmaTv3LjC6w=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec=
github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/go-chi/cors v1.1.1 h1:eHuqxsIw89iXcWnWUN8R72JMibABJTN/4IOYI5WERvw= github.com/go-chi/cors v1.1.1 h1:eHuqxsIw89iXcWnWUN8R72JMibABJTN/4IOYI5WERvw=
github.com/go-chi/cors v1.1.1/go.mod h1:K2Yje0VW/SJzxiyMYu6iPQYa7hMjQX2i/F491VChg1I= github.com/go-chi/cors v1.1.1/go.mod h1:K2Yje0VW/SJzxiyMYu6iPQYa7hMjQX2i/F491VChg1I=
github.com/jarcoal/httpmock v1.0.7 h1:d1a2VFpSdm5gtjhCPWsQHSnx8+5V3ms5431YwvmkuNk=
github.com/jarcoal/httpmock v1.0.7/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sf9v/solr-go v0.1.3 h1:ldgScD/jhZG5haSAOqjuMMXZAImz6dNkNT9XBUAIDDU= github.com/sf9v/solr-go v0.2.0 h1:fCzDh7knvWtDPAhCdGLpB1GliB7sw1BjMa82HAxHO94=
github.com/sf9v/solr-go v0.1.3/go.mod h1:JhdkNLuAOmKTj3TcZ6URW+SCQjoxEKkwLcI6EIEQkHE= github.com/sf9v/solr-go v0.2.0/go.mod h1:cSkq8rJSD20L8LkUSLBvzw5H8DGEu8W0OggweUpuMfo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -2,11 +2,10 @@ package main
import ( import (
"context" "context"
"encoding/json"
"flag" "flag"
"io/ioutil"
"log" "log"
"net/http" "net/http"
"os"
"github.com/go-chi/chi" "github.com/go-chi/chi"
"github.com/go-chi/chi/middleware" "github.com/go-chi/chi/middleware"
@@ -14,32 +13,35 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
solr "github.com/sf9v/solr-go" solr "github.com/sf9v/solr-go"
solrconfig "github.com/sf9v/solr-go/config"
solrindex "github.com/sf9v/solr-go/index"
solrschema "github.com/sf9v/solr-go/schema"
) )
// 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 ( const (
dataPath = "products.json" dataPath = "data/products.json"
coll = "multi-select-facet-demo" defaultCollection = "multi-select-facet-demo"
) )
func main() { func main() {
collection := flag.String("collection", coll, "specify the name of collection") collection := flag.String("collection", defaultCollection, "specify the name of collection")
createCollection := flag.Bool("create-collection", false, "create solr collection")
initSchema := flag.Bool("init-schema", false, "initialize solr schema") initSchema := flag.Bool("init-schema", false, "initialize solr schema")
initSuggester := flag.Bool("init-suggester", false, "initialize suggester component") initSuggester := flag.Bool("init-suggester", false, "initialize suggester component")
indexData := flag.Bool("index-data", false, "index the products data") indexData := flag.Bool("index-data", false, "index the products data")
flag.Parse() flag.Parse()
solrClient := solr.NewClient("localhost", 8983) baseURL := "http://localhost:8983"
solrClient := solr.NewJSONClient(baseURL)
ctx := context.Background() ctx := context.Background()
if *createCollection {
log.Println("creating collection...")
err := solrClient.CreateCollection(ctx, solr.NewCollectionParams().
Name(*collection).NumShards(1).ReplicationFactor(1))
if err != nil {
log.Fatal(err)
}
}
if *initSchema { if *initSchema {
log.Print("initializing solr schema...") log.Print("initializing solr schema...")
err := initSolrSchema(ctx, *collection, solrClient) err := initSolrSchema(ctx, *collection, solrClient)
@@ -50,7 +52,7 @@ func main() {
if *indexData { if *indexData {
log.Println("indexing products...") log.Println("indexing products...")
err := indexProducts(ctx, *collection, solrClient.Index()) err := indexProducts(ctx, *collection, solrClient)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@@ -58,7 +60,7 @@ func main() {
if *initSuggester { if *initSuggester {
log.Println("initializing suggester component...") log.Println("initializing suggester component...")
err := initSuggestConfig(ctx, *collection, solrClient.Config()) err := initSuggestConfig(ctx, *collection, solrClient)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@@ -98,52 +100,52 @@ func main() {
func initSolrSchema(ctx context.Context, collection string, solrClient solr.Client) (err error) { func initSolrSchema(ctx context.Context, collection string, solrClient solr.Client) (err error) {
// auto-suggest field type // Auto-suggest field type
fieldTypes := []solrschema.FieldType{ fieldTypes := []solr.FieldType{
// // approach #1 // approach #1
// // see: https://blog.griddynamics.com/implementing-autocomplete-with-solr/ // Refer to https://blog.griddynamics.com/implementing-autocomplete-with-solr/
// { {
// Name: "text_suggest", Name: "text_suggest_1",
// Class: "solr.TextField", Class: "solr.TextField",
// PositionIncrementGap: "100", PositionIncrementGap: "100",
// IndexAnalyzer: &solrschema.Analyzer{ IndexAnalyzer: &solr.Analyzer{
// Tokenizer: &solrschema.Tokenizer{ Tokenizer: &solr.Tokenizer{
// Class: "solr.StandardTokenizerFactory", Class: "solr.StandardTokenizerFactory",
// }, },
// Filters: []solrschema.Filter{ Filters: []solr.Filter{
// { {
// Class: "solr.LowerCaseFilterFactory", Class: "solr.LowerCaseFilterFactory",
// }, },
// { {
// Class: "solr.EdgeNGramFilterFactory", Class: "solr.EdgeNGramFilterFactory",
// MinGramSize: 1, MinGramSize: 1,
// MaxGramSize: 100, MaxGramSize: 100,
// }, },
// }, },
// }, },
// QueryAnalyzer: &solrschema.Analyzer{ QueryAnalyzer: &solr.Analyzer{
// Tokenizer: &solrschema.Tokenizer{ Tokenizer: &solr.Tokenizer{
// Class: "solr.KeywordTokenizerFactory", Class: "solr.KeywordTokenizerFactory",
// }, },
// Filters: []solrschema.Filter{ Filters: []solr.Filter{
// { {
// Class: "solr.LowerCaseFilterFactory", Class: "solr.LowerCaseFilterFactory",
// }, },
// }, },
// }, },
// }, },
// approach #2 // approach #2
// see: https://blog.griddynamics.com/implement-autocomplete-search-for-large-e-commerce-catalogs/ // Refer to https://blog.griddynamics.com/implement-autocomplete-search-for-large-e-commerce-catalogs/
{ {
Name: "text_suggest", Name: "text_suggest",
Class: "solr.TextField", Class: "solr.TextField",
PositionIncrementGap: "100", PositionIncrementGap: "100",
IndexAnalyzer: &solrschema.Analyzer{ IndexAnalyzer: &solr.Analyzer{
Tokenizer: &solrschema.Tokenizer{ Tokenizer: &solr.Tokenizer{
Class: "solr.WhitespaceTokenizerFactory", Class: "solr.WhitespaceTokenizerFactory",
}, },
Filters: []solrschema.Filter{ Filters: []solr.Filter{
{ {
Class: "solr.LowerCaseFilterFactory", Class: "solr.LowerCaseFilterFactory",
}, },
@@ -157,11 +159,11 @@ func initSolrSchema(ctx context.Context, collection string, solrClient solr.Clie
}, },
}, },
}, },
QueryAnalyzer: &solrschema.Analyzer{ QueryAnalyzer: &solr.Analyzer{
Tokenizer: &solrschema.Tokenizer{ Tokenizer: &solr.Tokenizer{
Class: "solr.WhitespaceTokenizerFactory", Class: "solr.WhitespaceTokenizerFactory",
}, },
Filters: []solrschema.Filter{ Filters: []solr.Filter{
{ {
Class: "solr.LowerCaseFilterFactory", Class: "solr.LowerCaseFilterFactory",
}, },
@@ -178,14 +180,14 @@ func initSolrSchema(ctx context.Context, collection string, solrClient solr.Clie
} }
for _, fieldType := range fieldTypes { for _, fieldType := range fieldTypes {
err = solrClient.Schema().AddFieldType(ctx, collection, fieldType) err = solrClient.AddFieldTypes(ctx, collection, fieldType)
if err != nil { if err != nil {
return errors.Wrap(err, "add field type") return errors.Wrap(err, "add field type")
} }
} }
// define the fields // define the fields
fields := []solrschema.Field{ fields := []solr.Field{
{ {
Name: "docType", Name: "docType",
Type: "string", Type: "string",
@@ -214,14 +216,12 @@ func initSolrSchema(ctx context.Context, collection string, solrClient solr.Clie
}, },
} }
for _, field := range fields { err = solrClient.AddFields(ctx, collection, fields...)
err = solrClient.Schema().AddField(ctx, collection, field)
if err != nil { if err != nil {
return errors.Wrap(err, "add field") return errors.Wrap(err, "add fields")
}
} }
copyFields := []solrschema.CopyField{ copyFields := []solr.CopyField{
{ {
Source: "name", Source: "name",
Dest: "suggest", Dest: "suggest",
@@ -249,48 +249,42 @@ func initSolrSchema(ctx context.Context, collection string, solrClient solr.Clie
}, },
} }
for _, copyField := range copyFields { err = solrClient.AddCopyFields(ctx, collection, copyFields...)
err = solrClient.Schema().AddCopyField(ctx, collection, copyField)
if err != nil { if err != nil {
return errors.Wrap(err, "add copy field") return errors.Wrap(err, "add copy fields")
}
} }
return nil return nil
} }
func initSuggestConfig(ctx context.Context, collection string, configClient solrconfig.Client) error { func initSuggestConfig(ctx context.Context, collection string, solrClient solr.Client) error {
// suggester configs // suggester configs
addSuggestComponent := solrconfig.NewComponentCommand( suggestComponent := solr.NewComponent(solr.SearchComponent).
solrconfig.AddSearchComponent, Map{ Name("suggest").Class("solr.SuggestComponent").
"name": "suggest", Config(solr.M{
"class": "solr.SuggestComponent", "suggester": solr.M{
"suggester": Map{
"name": "default", "name": "default",
"lookupImpl": "AnalyzingInfixLookupFactory", "lookupImpl": "AnalyzingInfixLookupFactory",
"dictionaryImpl": "DocumentDictionaryFactory", "dictionaryImpl": "DocumentDictionaryFactory",
"field": "suggest", "field": "suggest",
"suggestAnalyzerFieldType": "text_suggest", "suggestAnalyzerFieldType": "text_suggest",
}, },
}, })
)
addSuggestHandler := solrconfig.NewComponentCommand( suggestHandler := solr.NewComponent(solr.RequestHandler).
solrconfig.AddRequestHandler, Map{ Name("/suggest").Class("solr.SearchHandler").
"name": "/suggest", Config(solr.M{
"class": "solr.SearchHandler",
"startup": "lazy", "startup": "lazy",
"defaults": Map{ "defaults": solr.M{
"suggest": true, "suggest": true,
"suggest.count": 10, "suggest.count": 10,
"suggest.dictionary": "default", "suggest.dictionary": "default",
}, },
"components": []string{"suggest"}, "components": []string{"suggest"},
}, })
)
err := configClient.SendCommands(ctx, collection, err := solrClient.AddComponents(ctx, collection,
addSuggestComponent, addSuggestHandler) suggestComponent, suggestHandler)
if err != nil { if err != nil {
return errors.Wrap(err, "add suggester configs") return errors.Wrap(err, "add suggester configs")
} }
@@ -299,30 +293,19 @@ func initSuggestConfig(ctx context.Context, collection string, configClient solr
} }
func indexProducts(ctx context.Context, collection string, func indexProducts(ctx context.Context, collection string,
indexClient solrindex.Client) error { solrClient solr.Client) error {
b, err := ioutil.ReadFile(dataPath) f, err := os.OpenFile(dataPath, os.O_RDONLY, 0644)
if err != nil { if err != nil {
return err return err
} }
var products []Map _, err = solrClient.Update(ctx, collection, solr.JSON, f)
err = json.Unmarshal(b, &products)
if err != nil {
return err
}
docs := solrindex.NewDocs()
for _, product := range products {
docs.AddDoc(product)
}
err = indexClient.AddDocuments(ctx, collection, docs)
if err != nil { if err != nil {
return err return err
} }
// commit updates // commit updates
err = indexClient.Commit(ctx, collection) err = solrClient.Commit(ctx, collection)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -8,7 +8,6 @@ import (
"strings" "strings"
"github.com/sf9v/solr-go" "github.com/sf9v/solr-go"
"github.com/sf9v/solr-go/query"
) )
// searchHandler is the search handler // searchHandler is the search handler
@@ -72,7 +71,7 @@ var facetConfigs = []facetConfig{
} }
func (h *searchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *searchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
facetsMap := Map{} facets := []solr.Faceter{}
productFilters, skuFilters := []string{}, []string{} productFilters, skuFilters := []string{}, []string{}
for _, fctCfg := range facetConfigs { for _, fctCfg := range facetConfigs {
tagVals := []string{} tagVals := []string{}
@@ -87,45 +86,52 @@ func (h *searchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !fctCfg.isChild { if !fctCfg.isChild {
// product filters // product filters
if len(tagVals) > 0 { if len(tagVals) > 0 {
productFilters = append(productFilters, fmt.Sprintf("{!tag=top}%s", strings.Join(tagVals, " OR "))) filterQuery := solr.NewStandardQueryParser().Tag("top").
Query("'" + strings.Join(tagVals, " OR ") + "'").
BuildParser()
productFilters = append(productFilters, filterQuery)
} else { } else {
productFilters = append(productFilters, fmt.Sprintf("{!tag=top}%s:*", fctCfg.field)) filterQuery := solr.NewStandardQueryParser().Tag("top").
Query(fctCfg.field + ":*").BuildParser()
productFilters = append(productFilters, filterQuery)
} }
facetsMap[fctCfg.facet] = Map{ termsFacet := solr.NewTermsFacet(fctCfg.facet).
"facet": Map{ Field(fctCfg.field).Limit(-1).
"productCount": "uniqueBlock(_root_)", AddToFacet("productCount", "uniqueBlock(_root_)")
}, facets = append(facets, termsFacet)
"field": fctCfg.field,
"limit": -1,
"type": "terms",
}
continue continue
} }
// sku filters // sku filters
if len(tagVals) > 0 { if len(tagVals) > 0 {
skuFilters = append(skuFilters, fmt.Sprintf("{!tag=%s}%s", fctCfg.field, strings.Join(tagVals, " OR "))) filterQuery := solr.NewStandardQueryParser().Tag(fctCfg.field).
Query("'" + strings.Join(tagVals, " OR ") + "'").BuildParser()
skuFilters = append(skuFilters, filterQuery)
} else { } else {
skuFilters = append(skuFilters, fmt.Sprintf("{!tag=%s}%s:*", fctCfg.field, fctCfg.field)) filterQuery := solr.NewStandardQueryParser().Tag(fctCfg.field).
Query(fctCfg.field + ":*").BuildParser()
skuFilters = append(skuFilters, filterQuery)
} }
facetsMap[fctCfg.facet] = Map{ facet := solr.NewTermsFacet(fctCfg.facet).
"domain": Map{ Field(fctCfg.field).Limit(-1).
"excludeTags": "top", AddToDomain("excludeTags", "top").
"filter": []string{ AddToDomain("filter", []string{
fmt.Sprintf("{!filters param=$skuFilters excludeTags=%s v=$sku}", fctCfg.field), solr.NewFiltersQueryParser().
"{!child of=$product filters=$filter v=$product}", Param("$skuFilters").
}, ExcludeTags(fctCfg.field).
}, Query("$sku").BuildParser(),
"type": "terms", solr.NewChildrenQueryParser().
"field": fctCfg.field, Of("$product").
"limit": -1, Filters("$filter").
"facet": Map{ Query("$product").
"productCount": "uniqueBlock(_root_)", BuildParser(),
}, }).
} AddToFacet("productCount", "uniqueBlock(_root_)")
facets = append(facets, facet)
} }
q := r.URL.Query().Get("q") q := r.URL.Query().Get("q")
@@ -135,20 +141,27 @@ func (h *searchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
q = fmt.Sprintf("%q", q) q = fmt.Sprintf("%q", q)
} }
productFilters = append(productFilters, fmt.Sprintf("{!tag=top}_text_:%s", q)) productFilters = append(productFilters, solr.NewStandardQueryParser().
Tag("top").Query("_text_:"+q).BuildParser())
query := Map{ query := solr.NewQuery().
"query": "{!parent tag=top filters=$skuFilters which=$product score=total v=$sku}", QueryParser(
"queries": Map{ solr.NewParentQueryParser().
Tag("top").
Filters("$skuFilters").
Which("$product").
Score("total").
Query("$sku"),
).
Queries(solr.M{
"product": "docType:product", "product": "docType:product",
"sku": "docType:sku", "sku": "docType:sku",
"skuFilters": skuFilters, "skuFilters": skuFilters,
}, }).
"filter": productFilters, Filters(productFilters...).
"facet": facetsMap, Facets(facets...)
}
queryResp, err := h.solrClient.Query().Query(r.Context(), h.collection, query) queryResp, err := h.solrClient.Query(r.Context(), h.collection, query)
if err != nil { if err != nil {
http.Error(w, err.Error(), 500) http.Error(w, err.Error(), 500)
return return
@@ -166,33 +179,32 @@ func (h *searchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
resp["query"] = query resp["query"] = query
} }
w.Header().Add("content-type", "application/json")
err = json.NewEncoder(w).Encode(resp) err = json.NewEncoder(w).Encode(resp)
if err != nil { if err != nil {
http.Error(w, err.Error(), 500) http.Error(w, err.Error(), 500)
} }
} }
func buildResp(queryResp *query.Response) (Map, error) { func buildResp(queryResp *solr.QueryResponse) (solr.M, error) {
var facets = []Map{} var facets = []solr.M{}
for name, v := range queryResp.Facets { for name, v := range queryResp.Facets {
if name == "count" { if name == "count" {
continue continue
} }
vv, ok := v.(Map) vv, ok := v.(map[string]interface{})
if !ok { if !ok {
return nil, errors.New("v is not M") return nil, errors.New("v is not M")
} }
bucks, ok := vv["buckets"].([]Any) bucks, ok := vv["buckets"].([]interface{})
if !ok { if !ok {
return nil, errors.New("vv is not []Any") return nil, errors.New("vv is not []Any")
} }
buckets := []Map{} buckets := []solr.M{}
for _, bk := range bucks { for _, bk := range bucks {
buck, ok := bk.(Map) buck, ok := bk.(map[string]interface{})
if !ok { if !ok {
return nil, errors.New("bucket is not M") return nil, errors.New("bucket is not M")
} }
@@ -212,7 +224,7 @@ func buildResp(queryResp *query.Response) (Map, error) {
return nil, errors.New("val is not string") return nil, errors.New("val is not string")
} }
buckets = append(buckets, Map{ buckets = append(buckets, solr.M{
"val": val, "val": val,
"skuCount": int(skuCount), "skuCount": int(skuCount),
"productCount": int(productCount), "productCount": int(productCount),
@@ -227,31 +239,31 @@ func buildResp(queryResp *query.Response) (Map, error) {
} }
} }
facets = append(facets, Map{ facets = append(facets, solr.M{
"name": name, "name": name,
"param": param, "param": param,
"buckets": buckets, "buckets": buckets,
}) })
} }
var products = []Map{} var products = []solr.M{}
for _, doc := range queryResp.Response.Docs { for _, doc := range queryResp.Response.Documents {
id, ok := doc["id"].(string) id, ok := doc["id"].(string)
if !ok { if !ok {
return nil, errors.New("id not found or is not string") return nil, errors.New("id not found or is not string")
} }
name, ok := doc["name"].([]Any) name, ok := doc["name"].([]interface{})
if !ok { if !ok {
return nil, errors.New("name not found or is not []Any") return nil, errors.New("name not found or is not []Any")
} }
category, ok := doc["category"].([]Any) category, ok := doc["category"].([]interface{})
if !ok { if !ok {
return nil, errors.New("category not found or is not []Any") return nil, errors.New("category not found or is not []Any")
} }
brand, ok := doc["brand"].([]Any) brand, ok := doc["brand"].([]interface{})
if !ok { if !ok {
return nil, errors.New("brand not found or is not []Any") return nil, errors.New("brand not found or is not []Any")
} }
@@ -261,7 +273,7 @@ func buildResp(queryResp *query.Response) (Map, error) {
return nil, errors.New("productType not found or is not string") return nil, errors.New("productType not found or is not string")
} }
products = append(products, Map{ products = append(products, solr.M{
"id": id, "id": id,
"name": name[0].(string), "name": name[0].(string),
"category": category[0].(string), "category": category[0].(string),
@@ -270,7 +282,7 @@ func buildResp(queryResp *query.Response) (Map, error) {
}) })
} }
return Map{ return solr.M{
"products": products, "products": products,
"facets": facets, "facets": facets,
}, nil }, nil

View File

@@ -5,7 +5,6 @@ import (
"net/http" "net/http"
"github.com/sf9v/solr-go" "github.com/sf9v/solr-go"
"github.com/sf9v/solr-go/suggester"
) )
type suggestHandler struct { type suggestHandler struct {
@@ -13,6 +12,10 @@ type suggestHandler struct {
solrClient solr.Client solrClient solr.Client
} }
type suggestion struct {
Term string `json:"term"`
}
func (h *suggestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *suggestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query().Get("q") q := r.URL.Query().Get("q")
@@ -21,8 +24,9 @@ func (h *suggestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
dict := "default" dict := "default"
suggestResp, err := h.solrClient.Suggester().Suggest(r.Context(), h.collection, suggestParams := solr.NewSuggesterParams("suggest").
suggester.Params{Query: q, Dictionaries: []string{dict}}) Build().Query(q).Dictionaries(dict)
suggestResp, err := h.solrClient.Suggest(r.Context(), h.collection, suggestParams)
if err != nil { if err != nil {
http.Error(w, err.Error(), 500) http.Error(w, err.Error(), 500)
return return
@@ -31,23 +35,17 @@ func (h *suggestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
suggest := *suggestResp.Suggest suggest := *suggestResp.Suggest
termBody := suggest[dict][q] termBody := suggest[dict][q]
suggestions := []struct { suggestions := []suggestion{}
Term string `json:"term"`
}{}
for _, suggest := range termBody.Suggestions { for _, suggest := range termBody.Suggestions {
suggestions = append(suggestions, struct { suggestions = append(suggestions, suggestion{
Term string `json:"term"` Term: suggest.Term,
}{Term: suggest.Term}) })
} }
resp := Map{ err = json.NewEncoder(w).Encode(solr.M{
"numFound": termBody.NumFound, "numFound": termBody.NumFound,
"suggestions": suggestions, "suggestions": suggestions,
} })
w.Header().Add("content-type", "application/json")
err = json.NewEncoder(w).Encode(resp)
if err != nil { if err != nil {
http.Error(w, err.Error(), 500) http.Error(w, err.Error(), 500)
} }