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)
|
# Dependency directories (remove the comment below to include it)
|
||||||
# vendor/
|
# vendor/
|
||||||
|
|
||||||
|
api
|
||||||
14
Makefile
14
Makefile
@@ -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
|
||||||
|
|||||||
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
|
## 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
|
||||||
|
|
||||||
|
|||||||
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
|
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
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 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=
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user