From ae11d601b222d5cfaa6c64436313480dda138fd2 Mon Sep 17 00:00:00 2001 From: Arun Kumar A B Date: Fri, 6 May 2016 13:31:35 +0530 Subject: [PATCH] new parser for get facets --- solr/parser.go | 78 +++++++++++++++++++++++++++++++++++++++++++++ solr/parser_test.go | 11 ++++++- solr/query.go | 31 ++++++++++++++++++ solr/query_test.go | 51 +++++++++++++++++++++++++++++ solr/solr.go | 2 ++ 5 files changed, 172 insertions(+), 1 deletion(-) diff --git a/solr/parser.go b/solr/parser.go index b79b6a1..875bd24 100644 --- a/solr/parser.go +++ b/solr/parser.go @@ -34,6 +34,84 @@ func (parser *FireworkResultParser) Parse(resp *[]byte) (FireworkSolrResult, err return res, err } +type ExtensiveResultParser struct { +} + +func (parser *ExtensiveResultParser) Parse(resp_ *[]byte) (*SolrResult, error) { + sr := &SolrResult{} + jsonbuf, err := bytes2json(resp_) + if err != nil { + return sr, err + } + response := new(SolrResponse) + response.Response = jsonbuf + response.Status = int(jsonbuf["responseHeader"].(map[string]interface{})["status"].(float64)) + + sr.Results = new(Collection) + sr.Status = response.Status + + parser.ParseResponseHeader(response, sr) + + if 0 != response.Status { + parser.ParseError(response, sr) + return sr, nil + } + + err = parser.ParseResponse(response, sr) + + if err != nil { + return nil, err + } + + parser.ParseFacets(response, sr) + parser.ParseJsonFacets(response, sr) + + return sr, nil +} + +func (parser *ExtensiveResultParser) ParseResponseHeader(response *SolrResponse, sr *SolrResult) { + if responseHeader, ok := response.Response["responseHeader"].(map[string]interface{}); ok { + sr.ResponseHeader = responseHeader + } +} + +func (parser *ExtensiveResultParser) ParseError(response *SolrResponse, sr *SolrResult) { + if err, ok := response.Response["error"].(map[string]interface{}); ok { + sr.Error = err + } +} + +// ParseJsonFacets will assign facets and build sr.jsonfacets if there is a facet_counts +func (parser *ExtensiveResultParser) ParseFacets(response *SolrResponse, sr *SolrResult) { + if fc, ok := response.Response["facet_counts"].(map[string]interface{}); ok { + sr.FacetCounts = fc + if f, ok := fc["facet_fields"].(map[string]interface{}); ok { + sr.Facets = f + } + } +} + +// ParseJsonFacets will assign facets and build sr.jsonfacets if there is a facets +func (parser *ExtensiveResultParser) ParseJsonFacets(response *SolrResponse, sr *SolrResult) { + if jf, ok := response.Response["facets"].(map[string]interface{}); ok { + sr.JsonFacets = jf + } +} + +// ParseSolrResponse will assign result and build sr.docs if there is a response. +// If there is no response or grouped property in response it will return error +func (parser *ExtensiveResultParser) ParseResponse(response *SolrResponse, sr *SolrResult) (err error) { + if resp, ok := response.Response["response"].(map[string]interface{}); ok { + ParseDocResponse(resp, sr.Results) + } else { + err = fmt.Errorf(`Extensive parser can only parse solr response with response object, + ie response.response and response.response.docs. Or grouped response + Please use other parser or implement your own parser`) + } + + return err +} + type StandardResultParser struct { } diff --git a/solr/parser_test.go b/solr/parser_test.go index a2dd033..bb26072 100644 --- a/solr/parser_test.go +++ b/solr/parser_test.go @@ -21,7 +21,16 @@ func BenchmarkFireworkResultParser(b *testing.B) { parser := FireworkResultParser{} parser.Parse(&data) - //fmt.Printf("%v\n", d) + } +} + +func BenchmarkExtensiveResultParser(b *testing.B) { + data := []byte(`{"responseHeader":{"status":0,"QTime":0,"params":{"start":"0","q":"*:*","wt":"json","rows":"5"}},"response":{"numFound":12508577,"start":0,"docs":[{"id":"P27665908","p_name":"Archer Lady Case for Blackberry Q10","p_price_c":"175000.00,IDR","p_shop_name":"Nisa Toko","p_domain":"nisatoko","p_shop_sd_name":"Bekasi","p_key":"archer-lady-case-for-blackberry-q10","p_file_path":"product-1/2015/12/28/6208975","p_file_name":"6208975_3959f656-cb27-43df-83db-4b1c1455d3ce.jpg","p_id":27665908,"p_child_cat_id":69,"p_shop_id":733718,"p_condition":1,"p_server_id":1,"p_price":175000.0,"p_dep_id":["65","69","66"],"p_best_match_dink":-288.0,"p_best_match_price":4.756962,"_version_":1521775745340276736},{"id":"P27665917","p_name":"shampo zoku penumbuh rambut hitam berkilau","p_price_c":"115000.00,IDR","p_shop_name":"penumbuh rambut sehat10","p_domain":"penumbuhrambut93","p_shop_sd_name":"Surabaya","p_key":"shampo-zoku-penumbuh-rambut-hitam-berkilau","p_file_path":"product-1/2015/12/28/6766154","p_file_name":"6766154_3344bc2d-8069-4ef6-adc7-7de90b6604f5.jpg","p_id":27665917,"p_child_cat_id":436,"p_shop_id":801110,"p_condition":1,"p_server_id":1,"p_price":115000.0,"p_dep_id":["433","61","436"],"p_best_match_dink":-288.0,"p_best_match_price":4.939302,"_version_":1521775745341325313},{"id":"P27665953","p_name":"Archer Lady Case for HTC One M7","p_price_c":"175000.00,IDR","p_shop_name":"Nisa Toko","p_domain":"nisatoko","p_shop_sd_name":"Bekasi","p_key":"archer-lady-case-for-htc-one-m7","p_file_path":"product-1/2015/12/28/6208975","p_file_name":"6208975_68882b13-3e53-431b-bd64-d2e518db03e9.jpg","p_id":27665953,"p_child_cat_id":69,"p_shop_id":733718,"p_condition":1,"p_server_id":1,"p_price":175000.0,"p_dep_id":["65","69","66"],"p_best_match_dink":-288.0,"p_best_match_price":4.756962,"_version_":1521775745341325315},{"id":"P27665898","p_name":"Archer Lady Case for Blackberry 9900 9980","p_price_c":"175000.00,IDR","p_shop_name":"Nisa Toko","p_domain":"nisatoko","p_shop_sd_name":"Bekasi","p_key":"archer-lady-case-for-blackberry-9900-9980","p_file_path":"product-1/2015/12/28/6208975","p_file_name":"6208975_b40a60e6-1909-4a13-9d49-f881d3a8c5da.jpg","p_id":27665898,"p_child_cat_id":69,"p_shop_id":733718,"p_condition":1,"p_server_id":1,"p_price":175000.0,"p_dep_id":["65","69","66"],"p_best_match_dink":-288.0,"p_best_match_price":4.756962,"_version_":1521775745342373888},{"id":"P27665910","p_name":"VAMPIRE SERUM 30ml ORIGINAL","p_price_c":"26600.00,IDR","p_shop_name":"Dokter Cantikku","p_domain":"doktercantiku","p_shop_sd_name":"Jakarta","p_key":"vampire-serum-30ml-original","p_file_path":"product-1/2015/12/28/6634788","p_file_name":"6634788_e34791af-a170-48e8-b836-68da4c5d775a.jpg","p_id":27665910,"p_child_cat_id":598,"p_shop_id":785036,"p_condition":1,"p_server_id":1,"p_price":26600.0,"p_dep_id":["61","445","598"],"p_best_match_dink":-288.0,"p_best_match_price":5.5751185,"_version_":1521775745342373889}]}}`) + + for i := 0; i < b.N; i++ { + parser := FireworkResultParser{} + + parser.Parse(&data) } } diff --git a/solr/query.go b/solr/query.go index 42a2a59..22f75c9 100644 --- a/solr/query.go +++ b/solr/query.go @@ -55,6 +55,37 @@ func (q *Query) FieldList(fl string) { q.params.Add("fl", fl) } +// f (Facet) https://cwiki.apache.org/confluence/display/solr/Faceting#Faceting-Thefacet.fieldParameter +// Example: category +func (q *Query) AddFacet(f string) { + q.params.Set("facet", "true") + q.params.Add("facet.field", f) +} + +// mc (Facet min count) https://cwiki.apache.org/confluence/display/solr/Faceting#Faceting-Thefacet.mincountParameter +// Example: 5 +func (q *Query) SetFacetMinCount(mc int) { + q.params.Set("facet.mincount", fmt.Sprintf("%d", mc)) +} + +// f (Facet) https://wiki.apache.org/solr/SimpleFacetParameters#facet.pivot +// Example: category +func (q *Query) AddFacetPivot(f string) { + q.params.Add("facet.pivot", f) +} + +// mc (Facet pivot min count) https://wiki.apache.org/solr/SimpleFacetParameters#facet.pivot +// Example: 5 +func (q *Query) SetFacetPivotMinCount(mc int) { + q.params.Set("facet.pivot.mincount", fmt.Sprintf("%d", mc)) +} + +// jf (Json facet) https://cwiki.apache.org/confluence/display/solr/JSON+Request+API#JSONRequestAPI-FacetExample +// Example: {avg_price:"avg(price)"} +func (q *Query) AddJsonFacet(jf string) { + q.params.Add("json.facet", jf) +} + // geofilt - The distance filter http://wiki.apache.org/solr/SpatialSearch // Output example: fq={!geofilt pt=45.15,-93.85 sfield=store d=5} func (q *Query) Geofilt(latitude, longitude float64, sfield string, distance float64) { diff --git a/solr/query_test.go b/solr/query_test.go index 9e6f038..ac18199 100644 --- a/solr/query_test.go +++ b/solr/query_test.go @@ -158,3 +158,54 @@ func TestQueryQueryField(t *testing.T) { } } +func TestSolrQueryAddFacet(t *testing.T) { + q := NewQuery() + q.AddFacet("color") + q.AddFacet("size") + expected := "facet=true&facet.field=color&facet.field=size" + result := q.String() + if result != expected { + t.Errorf("expected '%s' but got '%s'", expected, result) + } +} + +func TestSolrQuerySetFacetMinCount(t *testing.T) { + q := NewQuery() + q.SetFacetMinCount(10) + expected := "facet.mincount=10" + result := q.String() + if result != expected { + t.Errorf("expected '%s' but got '%s'", expected, result) + } +} + +func TestSolrQueryAddFacetPivot(t *testing.T) { + q := NewQuery() + q.AddFacetPivot("color") + q.AddFacetPivot("size") + expected := "facet.pivot=color&facet.pivot=size" + result := q.String() + if result != expected { + t.Errorf("expected '%s' but got '%s'", expected, result) + } +} + +func TestSolrQuerySetFacetPivotMinCount(t *testing.T) { + q := NewQuery() + q.SetFacetPivotMinCount(10) + expected := "facet.pivot.mincount=10" + result := q.String() + if result != expected { + t.Errorf("expected '%s' but got '%s'", expected, result) + } +} + +func TestSolrQueryAddJsonFacet(t *testing.T) { + q := NewQuery() + q.AddJsonFacet("{categories:{type:terms,field:cat,sort:'x desc',facet:{x:'avg(price)',y:'sum(price)'}}") + expected := "json.facet=%7Bcategories%3A%7Btype%3Aterms%2Cfield%3Acat%2Csort%3A%27x+desc%27%2Cfacet%3A%7Bx%3A%27avg%28price%29%27%2Cy%3A%27sum%28price%29%27%7D%7D" + result := q.String() + if result != expected { + t.Errorf("expected '%s' but got '%s'", expected, result) + } +} diff --git a/solr/solr.go b/solr/solr.go index ba6d6df..4cfdcb9 100644 --- a/solr/solr.go +++ b/solr/solr.go @@ -77,6 +77,8 @@ type SolrResult struct { Results *Collection // results parsed documents, basically response object QTime int ResponseHeader map[string]interface{} + Facets map[string]interface{} + JsonFacets map[string]interface{} FacetCounts map[string]interface{} Highlighting map[string]interface{} Error map[string]interface{}