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
Upgrade solr-go to version 0.2
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -13,3 +13,5 @@
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
api
|
||||
14
Makefile
14
Makefile
@@ -1,11 +1,11 @@
|
||||
DOCKER ?= docker
|
||||
SOLR_INST ?="solr-multi-select-facet-demo"
|
||||
COLLECTION ?= "multi-select-facet-demo"
|
||||
PODMAN ?= podman
|
||||
SOLR ?="multi-select-facet-demo"
|
||||
|
||||
.PHONY: solr
|
||||
solr: stop-solr
|
||||
$(DOCKER) run -d -p 8983:8983 --name $(SOLR_INST) solr:latest solr-precreate $(COLLECTION)
|
||||
.PHONY: start-solr
|
||||
start-solr: stop-solr
|
||||
$(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
|
||||
stop-solr:
|
||||
$(DOCKER) rm -f $(SOLR_INST) || true
|
||||
$(PODMAN) rm -f $(SOLR) || true
|
||||
|
||||
23
README.md
23
README.md
@@ -7,32 +7,25 @@ Blog post: [Multi-Select Facet with Solr, Vue and Go](https://sf9v.github.io/pos
|
||||
|
||||
## Running the example
|
||||
|
||||
1. Run Solr in `docker`.
|
||||
1. Run Solr using [podman](https://podman.io/).
|
||||
|
||||
```console
|
||||
$ make solr
|
||||
$ make start-solr
|
||||
```
|
||||
|
||||
Or using [podman](https://podman.io/).
|
||||
Or using `docker`.
|
||||
|
||||
```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
|
||||
```console
|
||||
// cd to api
|
||||
$ cd cmd/api
|
||||
|
||||
// build the api
|
||||
$ go build -v
|
||||
$ go build -v -o api
|
||||
|
||||
// start the api
|
||||
$ ./api -init-schema -index-data -init-suggester
|
||||
// start the api with the initialization options
|
||||
$ ./api -create-collection -init-schema -index-data -init-suggester
|
||||
```
|
||||
|
||||
3. Run the web app (open a new terminal tab)
|
||||
@@ -51,7 +44,7 @@ $ yarn serve // or npm run serve
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
|
||||
0
cmd/api/.gitignore → data/.gitignore
vendored
0
cmd/api/.gitignore → data/.gitignore
vendored
7
go.mod
7
go.mod
@@ -1,11 +1,10 @@
|
||||
module github.com/sf9v/multi-select-facet
|
||||
|
||||
go 1.14
|
||||
go 1.15
|
||||
|
||||
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/pkg/errors v0.9.1
|
||||
github.com/sf9v/solr-go v0.1.3
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect
|
||||
github.com/sf9v/solr-go v0.2.0
|
||||
)
|
||||
|
||||
28
go.sum
28
go.sum
@@ -1,33 +1,21 @@
|
||||
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/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
|
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
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/chi v1.5.1 h1:kfTK3Cxd/dkMu/rKs5ZceWYp+t5CtiE7vmaTv3LjC6w=
|
||||
github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
|
||||
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/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/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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/sf9v/solr-go v0.1.3 h1:ldgScD/jhZG5haSAOqjuMMXZAImz6dNkNT9XBUAIDDU=
|
||||
github.com/sf9v/solr-go v0.1.3/go.mod h1:JhdkNLuAOmKTj3TcZ6URW+SCQjoxEKkwLcI6EIEQkHE=
|
||||
github.com/sf9v/solr-go v0.2.0 h1:fCzDh7knvWtDPAhCdGLpB1GliB7sw1BjMa82HAxHO94=
|
||||
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/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
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/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=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
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/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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -2,11 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
@@ -14,32 +13,35 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
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 (
|
||||
dataPath = "products.json"
|
||||
coll = "multi-select-facet-demo"
|
||||
dataPath = "data/products.json"
|
||||
defaultCollection = "multi-select-facet-demo"
|
||||
)
|
||||
|
||||
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")
|
||||
initSuggester := flag.Bool("init-suggester", false, "initialize suggester component")
|
||||
indexData := flag.Bool("index-data", false, "index the products data")
|
||||
flag.Parse()
|
||||
|
||||
solrClient := solr.NewClient("localhost", 8983)
|
||||
baseURL := "http://localhost:8983"
|
||||
solrClient := solr.NewJSONClient(baseURL)
|
||||
|
||||
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 {
|
||||
log.Print("initializing solr schema...")
|
||||
err := initSolrSchema(ctx, *collection, solrClient)
|
||||
@@ -50,7 +52,7 @@ func main() {
|
||||
|
||||
if *indexData {
|
||||
log.Println("indexing products...")
|
||||
err := indexProducts(ctx, *collection, solrClient.Index())
|
||||
err := indexProducts(ctx, *collection, solrClient)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -58,7 +60,7 @@ func main() {
|
||||
|
||||
if *initSuggester {
|
||||
log.Println("initializing suggester component...")
|
||||
err := initSuggestConfig(ctx, *collection, solrClient.Config())
|
||||
err := initSuggestConfig(ctx, *collection, solrClient)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -98,52 +100,52 @@ func main() {
|
||||
|
||||
func initSolrSchema(ctx context.Context, collection string, solrClient solr.Client) (err error) {
|
||||
|
||||
// auto-suggest field type
|
||||
fieldTypes := []solrschema.FieldType{
|
||||
// // approach #1
|
||||
// // see: https://blog.griddynamics.com/implementing-autocomplete-with-solr/
|
||||
// {
|
||||
// Name: "text_suggest",
|
||||
// Class: "solr.TextField",
|
||||
// PositionIncrementGap: "100",
|
||||
// IndexAnalyzer: &solrschema.Analyzer{
|
||||
// Tokenizer: &solrschema.Tokenizer{
|
||||
// Class: "solr.StandardTokenizerFactory",
|
||||
// },
|
||||
// Filters: []solrschema.Filter{
|
||||
// {
|
||||
// Class: "solr.LowerCaseFilterFactory",
|
||||
// },
|
||||
// {
|
||||
// Class: "solr.EdgeNGramFilterFactory",
|
||||
// MinGramSize: 1,
|
||||
// MaxGramSize: 100,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// QueryAnalyzer: &solrschema.Analyzer{
|
||||
// Tokenizer: &solrschema.Tokenizer{
|
||||
// Class: "solr.KeywordTokenizerFactory",
|
||||
// },
|
||||
// Filters: []solrschema.Filter{
|
||||
// {
|
||||
// Class: "solr.LowerCaseFilterFactory",
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// Auto-suggest field type
|
||||
fieldTypes := []solr.FieldType{
|
||||
// approach #1
|
||||
// Refer to https://blog.griddynamics.com/implementing-autocomplete-with-solr/
|
||||
{
|
||||
Name: "text_suggest_1",
|
||||
Class: "solr.TextField",
|
||||
PositionIncrementGap: "100",
|
||||
IndexAnalyzer: &solr.Analyzer{
|
||||
Tokenizer: &solr.Tokenizer{
|
||||
Class: "solr.StandardTokenizerFactory",
|
||||
},
|
||||
Filters: []solr.Filter{
|
||||
{
|
||||
Class: "solr.LowerCaseFilterFactory",
|
||||
},
|
||||
{
|
||||
Class: "solr.EdgeNGramFilterFactory",
|
||||
MinGramSize: 1,
|
||||
MaxGramSize: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
QueryAnalyzer: &solr.Analyzer{
|
||||
Tokenizer: &solr.Tokenizer{
|
||||
Class: "solr.KeywordTokenizerFactory",
|
||||
},
|
||||
Filters: []solr.Filter{
|
||||
{
|
||||
Class: "solr.LowerCaseFilterFactory",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// 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",
|
||||
Class: "solr.TextField",
|
||||
PositionIncrementGap: "100",
|
||||
IndexAnalyzer: &solrschema.Analyzer{
|
||||
Tokenizer: &solrschema.Tokenizer{
|
||||
IndexAnalyzer: &solr.Analyzer{
|
||||
Tokenizer: &solr.Tokenizer{
|
||||
Class: "solr.WhitespaceTokenizerFactory",
|
||||
},
|
||||
Filters: []solrschema.Filter{
|
||||
Filters: []solr.Filter{
|
||||
{
|
||||
Class: "solr.LowerCaseFilterFactory",
|
||||
},
|
||||
@@ -157,11 +159,11 @@ func initSolrSchema(ctx context.Context, collection string, solrClient solr.Clie
|
||||
},
|
||||
},
|
||||
},
|
||||
QueryAnalyzer: &solrschema.Analyzer{
|
||||
Tokenizer: &solrschema.Tokenizer{
|
||||
QueryAnalyzer: &solr.Analyzer{
|
||||
Tokenizer: &solr.Tokenizer{
|
||||
Class: "solr.WhitespaceTokenizerFactory",
|
||||
},
|
||||
Filters: []solrschema.Filter{
|
||||
Filters: []solr.Filter{
|
||||
{
|
||||
Class: "solr.LowerCaseFilterFactory",
|
||||
},
|
||||
@@ -178,14 +180,14 @@ func initSolrSchema(ctx context.Context, collection string, solrClient solr.Clie
|
||||
}
|
||||
|
||||
for _, fieldType := range fieldTypes {
|
||||
err = solrClient.Schema().AddFieldType(ctx, collection, fieldType)
|
||||
err = solrClient.AddFieldTypes(ctx, collection, fieldType)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "add field type")
|
||||
}
|
||||
}
|
||||
|
||||
// define the fields
|
||||
fields := []solrschema.Field{
|
||||
fields := []solr.Field{
|
||||
{
|
||||
Name: "docType",
|
||||
Type: "string",
|
||||
@@ -214,14 +216,12 @@ func initSolrSchema(ctx context.Context, collection string, solrClient solr.Clie
|
||||
},
|
||||
}
|
||||
|
||||
for _, field := range fields {
|
||||
err = solrClient.Schema().AddField(ctx, collection, field)
|
||||
err = solrClient.AddFields(ctx, collection, fields...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "add field")
|
||||
}
|
||||
return errors.Wrap(err, "add fields")
|
||||
}
|
||||
|
||||
copyFields := []solrschema.CopyField{
|
||||
copyFields := []solr.CopyField{
|
||||
{
|
||||
Source: "name",
|
||||
Dest: "suggest",
|
||||
@@ -249,48 +249,42 @@ func initSolrSchema(ctx context.Context, collection string, solrClient solr.Clie
|
||||
},
|
||||
}
|
||||
|
||||
for _, copyField := range copyFields {
|
||||
err = solrClient.Schema().AddCopyField(ctx, collection, copyField)
|
||||
err = solrClient.AddCopyFields(ctx, collection, copyFields...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "add copy field")
|
||||
}
|
||||
return errors.Wrap(err, "add copy fields")
|
||||
}
|
||||
|
||||
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
|
||||
addSuggestComponent := solrconfig.NewComponentCommand(
|
||||
solrconfig.AddSearchComponent, Map{
|
||||
"name": "suggest",
|
||||
"class": "solr.SuggestComponent",
|
||||
"suggester": Map{
|
||||
suggestComponent := solr.NewComponent(solr.SearchComponent).
|
||||
Name("suggest").Class("solr.SuggestComponent").
|
||||
Config(solr.M{
|
||||
"suggester": solr.M{
|
||||
"name": "default",
|
||||
"lookupImpl": "AnalyzingInfixLookupFactory",
|
||||
"dictionaryImpl": "DocumentDictionaryFactory",
|
||||
"field": "suggest",
|
||||
"suggestAnalyzerFieldType": "text_suggest",
|
||||
},
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
addSuggestHandler := solrconfig.NewComponentCommand(
|
||||
solrconfig.AddRequestHandler, Map{
|
||||
"name": "/suggest",
|
||||
"class": "solr.SearchHandler",
|
||||
suggestHandler := solr.NewComponent(solr.RequestHandler).
|
||||
Name("/suggest").Class("solr.SearchHandler").
|
||||
Config(solr.M{
|
||||
"startup": "lazy",
|
||||
"defaults": Map{
|
||||
"defaults": solr.M{
|
||||
"suggest": true,
|
||||
"suggest.count": 10,
|
||||
"suggest.dictionary": "default",
|
||||
},
|
||||
"components": []string{"suggest"},
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
err := configClient.SendCommands(ctx, collection,
|
||||
addSuggestComponent, addSuggestHandler)
|
||||
err := solrClient.AddComponents(ctx, collection,
|
||||
suggestComponent, suggestHandler)
|
||||
if err != nil {
|
||||
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,
|
||||
indexClient solrindex.Client) error {
|
||||
b, err := ioutil.ReadFile(dataPath)
|
||||
solrClient solr.Client) error {
|
||||
f, err := os.OpenFile(dataPath, os.O_RDONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var products []Map
|
||||
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)
|
||||
_, err = solrClient.Update(ctx, collection, solr.JSON, f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// commit updates
|
||||
err = indexClient.Commit(ctx, collection)
|
||||
err = solrClient.Commit(ctx, collection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/sf9v/solr-go"
|
||||
"github.com/sf9v/solr-go/query"
|
||||
)
|
||||
|
||||
// searchHandler is the search handler
|
||||
@@ -72,7 +71,7 @@ var facetConfigs = []facetConfig{
|
||||
}
|
||||
|
||||
func (h *searchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
facetsMap := Map{}
|
||||
facets := []solr.Faceter{}
|
||||
productFilters, skuFilters := []string{}, []string{}
|
||||
for _, fctCfg := range facetConfigs {
|
||||
tagVals := []string{}
|
||||
@@ -87,45 +86,52 @@ func (h *searchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if !fctCfg.isChild {
|
||||
// product filters
|
||||
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 {
|
||||
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{
|
||||
"facet": Map{
|
||||
"productCount": "uniqueBlock(_root_)",
|
||||
},
|
||||
"field": fctCfg.field,
|
||||
"limit": -1,
|
||||
"type": "terms",
|
||||
}
|
||||
termsFacet := solr.NewTermsFacet(fctCfg.facet).
|
||||
Field(fctCfg.field).Limit(-1).
|
||||
AddToFacet("productCount", "uniqueBlock(_root_)")
|
||||
facets = append(facets, termsFacet)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// sku filters
|
||||
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 {
|
||||
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{
|
||||
"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_)",
|
||||
},
|
||||
}
|
||||
facet := solr.NewTermsFacet(fctCfg.facet).
|
||||
Field(fctCfg.field).Limit(-1).
|
||||
AddToDomain("excludeTags", "top").
|
||||
AddToDomain("filter", []string{
|
||||
solr.NewFiltersQueryParser().
|
||||
Param("$skuFilters").
|
||||
ExcludeTags(fctCfg.field).
|
||||
Query("$sku").BuildParser(),
|
||||
solr.NewChildrenQueryParser().
|
||||
Of("$product").
|
||||
Filters("$filter").
|
||||
Query("$product").
|
||||
BuildParser(),
|
||||
}).
|
||||
AddToFacet("productCount", "uniqueBlock(_root_)")
|
||||
|
||||
facets = append(facets, facet)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
productFilters = append(productFilters, fmt.Sprintf("{!tag=top}_text_:%s", q))
|
||||
productFilters = append(productFilters, solr.NewStandardQueryParser().
|
||||
Tag("top").Query("_text_:"+q).BuildParser())
|
||||
|
||||
query := Map{
|
||||
"query": "{!parent tag=top filters=$skuFilters which=$product score=total v=$sku}",
|
||||
"queries": Map{
|
||||
query := solr.NewQuery().
|
||||
QueryParser(
|
||||
solr.NewParentQueryParser().
|
||||
Tag("top").
|
||||
Filters("$skuFilters").
|
||||
Which("$product").
|
||||
Score("total").
|
||||
Query("$sku"),
|
||||
).
|
||||
Queries(solr.M{
|
||||
"product": "docType:product",
|
||||
"sku": "docType:sku",
|
||||
"skuFilters": skuFilters,
|
||||
},
|
||||
"filter": productFilters,
|
||||
"facet": facetsMap,
|
||||
}
|
||||
}).
|
||||
Filters(productFilters...).
|
||||
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 {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
@@ -166,33 +179,32 @@ func (h *searchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
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{}
|
||||
func buildResp(queryResp *solr.QueryResponse) (solr.M, error) {
|
||||
var facets = []solr.M{}
|
||||
for name, v := range queryResp.Facets {
|
||||
if name == "count" {
|
||||
continue
|
||||
}
|
||||
|
||||
vv, ok := v.(Map)
|
||||
vv, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, errors.New("v is not M")
|
||||
}
|
||||
|
||||
bucks, ok := vv["buckets"].([]Any)
|
||||
bucks, ok := vv["buckets"].([]interface{})
|
||||
if !ok {
|
||||
return nil, errors.New("vv is not []Any")
|
||||
}
|
||||
|
||||
buckets := []Map{}
|
||||
buckets := []solr.M{}
|
||||
for _, bk := range bucks {
|
||||
buck, ok := bk.(Map)
|
||||
buck, ok := bk.(map[string]interface{})
|
||||
if !ok {
|
||||
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")
|
||||
}
|
||||
|
||||
buckets = append(buckets, Map{
|
||||
buckets = append(buckets, solr.M{
|
||||
"val": val,
|
||||
"skuCount": int(skuCount),
|
||||
"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,
|
||||
"param": param,
|
||||
"buckets": buckets,
|
||||
})
|
||||
}
|
||||
|
||||
var products = []Map{}
|
||||
for _, doc := range queryResp.Response.Docs {
|
||||
var products = []solr.M{}
|
||||
for _, doc := range queryResp.Response.Documents {
|
||||
id, ok := doc["id"].(string)
|
||||
if !ok {
|
||||
return nil, errors.New("id not found or is not string")
|
||||
}
|
||||
|
||||
name, ok := doc["name"].([]Any)
|
||||
name, ok := doc["name"].([]interface{})
|
||||
if !ok {
|
||||
return nil, errors.New("name not found or is not []Any")
|
||||
}
|
||||
|
||||
category, ok := doc["category"].([]Any)
|
||||
category, ok := doc["category"].([]interface{})
|
||||
if !ok {
|
||||
return nil, errors.New("category not found or is not []Any")
|
||||
}
|
||||
|
||||
brand, ok := doc["brand"].([]Any)
|
||||
brand, ok := doc["brand"].([]interface{})
|
||||
if !ok {
|
||||
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")
|
||||
}
|
||||
|
||||
products = append(products, Map{
|
||||
products = append(products, solr.M{
|
||||
"id": id,
|
||||
"name": name[0].(string),
|
||||
"category": category[0].(string),
|
||||
@@ -270,7 +282,7 @@ func buildResp(queryResp *query.Response) (Map, error) {
|
||||
})
|
||||
}
|
||||
|
||||
return Map{
|
||||
return solr.M{
|
||||
"products": products,
|
||||
"facets": facets,
|
||||
}, nil
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/sf9v/solr-go"
|
||||
"github.com/sf9v/solr-go/suggester"
|
||||
)
|
||||
|
||||
type suggestHandler struct {
|
||||
@@ -13,6 +12,10 @@ type suggestHandler struct {
|
||||
solrClient solr.Client
|
||||
}
|
||||
|
||||
type suggestion struct {
|
||||
Term string `json:"term"`
|
||||
}
|
||||
|
||||
func (h *suggestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query().Get("q")
|
||||
|
||||
@@ -21,8 +24,9 @@ func (h *suggestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
dict := "default"
|
||||
suggestResp, err := h.solrClient.Suggester().Suggest(r.Context(), h.collection,
|
||||
suggester.Params{Query: q, Dictionaries: []string{dict}})
|
||||
suggestParams := solr.NewSuggesterParams("suggest").
|
||||
Build().Query(q).Dictionaries(dict)
|
||||
suggestResp, err := h.solrClient.Suggest(r.Context(), h.collection, suggestParams)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
@@ -31,23 +35,17 @@ func (h *suggestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
suggest := *suggestResp.Suggest
|
||||
termBody := suggest[dict][q]
|
||||
|
||||
suggestions := []struct {
|
||||
Term string `json:"term"`
|
||||
}{}
|
||||
suggestions := []suggestion{}
|
||||
for _, suggest := range termBody.Suggestions {
|
||||
suggestions = append(suggestions, struct {
|
||||
Term string `json:"term"`
|
||||
}{Term: suggest.Term})
|
||||
suggestions = append(suggestions, suggestion{
|
||||
Term: suggest.Term,
|
||||
})
|
||||
}
|
||||
|
||||
resp := Map{
|
||||
err = json.NewEncoder(w).Encode(solr.M{
|
||||
"numFound": termBody.NumFound,
|
||||
"suggestions": suggestions,
|
||||
}
|
||||
|
||||
w.Header().Add("content-type", "application/json")
|
||||
|
||||
err = json.NewEncoder(w).Encode(resp)
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
}
|
||||
Reference in New Issue
Block a user