func Search(sourceType string, params *SearchParams, r *http.Request) (map[string]interface{}, error) { conn := api.Conn().SearchConnection() if sourceType != "usgov.hhs.npi" { api.AddMessage(r, "Warning: Use of the dataset, '"+sourceType+"', without an API key is for development-use only. Use of this API without a key may be rate-limited in the future. For hosted, production access, please email '*****@*****.**' for an API key.") api.AddMessage(r, "Warning: This query used the experimental dataset, '"+sourceType+"'. To ensure you're notified in case breaking changes need to be made, email [email protected] and ask for an API key.") } matches := phraseMatches(params.paramSets, r) query := map[string]interface{}{ "from": params.Offset, "size": params.Limit, "query": map[string]interface{}{ "bool": map[string]interface{}{ "must": matches, }, }, } result, err := conn.Search("source", sourceType, nil, query) if err != nil { switch terr := err.(type) { case elastigo.ESError: if esTypeExceptionRegex.MatchString(terr.What) { return nil, api.NewParamsError("one or more values were of an unexpected type", map[string]string{}) } else { return nil, err } default: return nil, err } } hits := make([]interface{}, len(result.Hits.Hits)) for i, hit := range result.Hits.Hits { var source map[string]interface{} json.Unmarshal(*hit.Source, &source) hits[i] = source } cleanedResult := map[string]interface{}{ "meta": map[string]interface{}{ "rowCount": result.Hits.Total, }, "result": hits, } return cleanedResult, nil }
func ItemHandler(w http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) source := strings.ToLower(vars["source"]) id := vars["id"] conn := api.Conn().SearchConnection() if !validElasticSearchRegexp.MatchString(source) { api.Render(w, req, http.StatusNotFound, "item not found") return } if !validElasticSearchRegexp.MatchString(id) { api.Render(w, req, http.StatusNotFound, "item not found") return } if source != "usgov.hhs.npi" { api.AddMessage(req, "Warning: Use of the dataset, '"+source+"', without an API key is for development-use only. Use of this API without a key may be rate-limited in the future. For hosted, production access, please email '*****@*****.**' for an API key.") api.AddMessage(req, "Warning: This query used the experimental dataset, '"+source+"'. To ensure you're notified in case breaking changes need to be made, email [email protected] and ask for an API key.") } result, err := conn.Get("source", source, id, nil) if err != nil && err.Error() == elastigo.RecordNotFound.Error() { api.Render(w, req, http.StatusNotFound, "item not found") return } else if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } var found map[string]interface{} err = json.Unmarshal(*result.Source, &found) if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } body := map[string]interface{}{"result": found} api.Render(w, req, http.StatusOK, body) return }
func phraseMatches(paramSets []*SearchParamSet, r *http.Request) []interface{} { apiKey, ok := context.Get(r, "api_key").(string) if !ok { apiKey = "" } elasticPhrases := make([]interface{}, len(paramSets)) for index, set := range paramSets { shouldValues := make([]map[string]interface{}, len(set.Values)) api.AddFeature(r, "op:"+set.Op) for vIndex, value := range set.Values { switch set.Op { case "eq": shouldValues[vIndex] = map[string]interface{}{ "match_phrase": map[string]interface{}{ set.Key: value, }, } case "fuzzy": if apiKey == "" { api.AddMessage(r, fmt.Sprintf(experimentalOperationMessageWithoutKey, "fuzzy")) } else { api.AddMessage(r, fmt.Sprintf(experimentalOperationMessage, "fuzzy")) } shouldValues[vIndex] = map[string]interface{}{ "fuzzy": map[string]interface{}{ set.Key: value, }, } case "prefix": if apiKey == "" { api.AddMessage(r, fmt.Sprintf(experimentalOperationMessageWithoutKey, "prefix")) } else { api.AddMessage(r, fmt.Sprintf(experimentalOperationMessage, "prefix")) } shouldValues[vIndex] = map[string]interface{}{ "prefix": map[string]interface{}{ set.Key: strings.ToLower(value), }, } case "gte": shouldValues[vIndex] = map[string]interface{}{ "range": map[string]interface{}{ set.Key: map[string]interface{}{ "gte": value, }, }, } case "gt": shouldValues[vIndex] = map[string]interface{}{ "range": map[string]interface{}{ set.Key: map[string]interface{}{ "gt": value, }, }, } case "lte": shouldValues[vIndex] = map[string]interface{}{ "range": map[string]interface{}{ set.Key: map[string]interface{}{ "lte": value, }, }, } case "lt": shouldValues[vIndex] = map[string]interface{}{ "range": map[string]interface{}{ set.Key: map[string]interface{}{ "lt": value, }, }, } } } elasticPhrases[index] = map[string]interface{}{ "bool": map[string]interface{}{ "should": shouldValues, }, } } return elasticPhrases }
func Search(sourceType string, params *SearchParams, r *http.Request) (map[string]interface{}, error) { conn := api.Conn().SearchConnection() apiKey, ok := context.Get(r, "api_key").(string) if !ok { apiKey = "" } if apiKey == "" { api.AddMessage(r, "Warning: Use of the dataset, '"+sourceType+"', without an API key is for development-use only. Use of this API without a key is rate-limited. For hosted, production access, please email '*****@*****.**' for an API key.") } matches := phraseMatches(params.paramSets, r) var order = "asc" if params.Order != "" { order = params.Order } query := map[string]interface{}{ "from": params.Offset, "size": params.Limit, "query": map[string]interface{}{ "bool": map[string]interface{}{ "must": matches, }, }, } if params.Sort != "" { if _, ok := context.Get(r, "api_key").(string); !ok { return nil, api.NewParamsError("'sort' is unsupported without a BloomAPI user account", map[string]string{}) } query["sort"] = map[string]interface{}{ params.Sort: map[string]interface{}{ "order": order, }, } api.AddFeature(r, "sort") api.AddMessage(r, experimentalSort) } result, err := conn.Search(sourceType, "main", nil, query) if err != nil { switch terr := err.(type) { case elastigo.ESError: if esTypeExceptionRegex.MatchString(terr.What) { return nil, api.NewParamsError("one or more values were of an unexpected type", map[string]string{}) } else { return nil, err } default: return nil, err } } hits := make([]interface{}, len(result.Hits.Hits)) for i, hit := range result.Hits.Hits { var source map[string]interface{} json.Unmarshal(*hit.Source, &source) hits[i] = source } cleanedResult := map[string]interface{}{ "meta": map[string]interface{}{ "rowCount": result.Hits.Total, }, "result": hits, } return cleanedResult, nil }
func phraseMatches(paramSets []*SearchParamSet, r *http.Request) []interface{} { elasticPhrases := make([]interface{}, len(paramSets)) for index, set := range paramSets { shouldValues := make([]map[string]interface{}, len(set.Values)) for vIndex, value := range set.Values { switch set.Op { case "eq": shouldValues[vIndex] = map[string]interface{}{ "match_phrase": map[string]interface{}{ set.Key: value, }, } case "fuzzy": api.AddMessage(r, fmt.Sprintf(experimentalOperationMessage, "fuzzy")) shouldValues[vIndex] = map[string]interface{}{ "fuzzy": map[string]interface{}{ set.Key: value, }, } case "prefix": api.AddMessage(r, fmt.Sprintf(experimentalOperationMessage, "prefix")) shouldValues[vIndex] = map[string]interface{}{ "prefix": map[string]interface{}{ set.Key: value, }, } case "gte": shouldValues[vIndex] = map[string]interface{}{ "range": map[string]interface{}{ set.Key: map[string]interface{}{ "gte": value, }, }, } case "gt": shouldValues[vIndex] = map[string]interface{}{ "range": map[string]interface{}{ set.Key: map[string]interface{}{ "gt": value, }, }, } case "lte": shouldValues[vIndex] = map[string]interface{}{ "range": map[string]interface{}{ set.Key: map[string]interface{}{ "lte": value, }, }, } case "lt": shouldValues[vIndex] = map[string]interface{}{ "range": map[string]interface{}{ set.Key: map[string]interface{}{ "lt": value, }, }, } } } elasticPhrases[index] = map[string]interface{}{ "bool": map[string]interface{}{ "should": shouldValues, }, } } return elasticPhrases }
func ItemHandler(w http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) source := strings.ToLower(vars["source"]) id := vars["id"] bloomConn := api.Conn() searchConn := bloomConn.SearchConnection() if !validElasticSearchRegexp.MatchString(source) { api.Render(w, req, http.StatusNotFound, "item not found") return } if !validElasticSearchRegexp.MatchString(id) { api.Render(w, req, http.StatusNotFound, "item not found") return } apiKey, ok := context.Get(req, "api_key").(string) if !ok { apiKey = "" } _, ok, err := bloomConn.SearchTypeWithNameAndKey(source, apiKey) if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } if !ok { api.Render(w, req, 404, map[string]string{ "name": "Source Not Found", "message": "Please contact [email protected] if this is in error", }) return } if apiKey == "" { api.AddMessage(req, "Warning: Use of the dataset, '"+source+"', without an API key is for development-use only. Use of this API without a key without an API key is for development-use only. Use of this API without a key is rate-limited. For hosted, production access, please email '*****@*****.**' for an API key.") } result, err := searchConn.Get(source, "main", id, nil) if err != nil && err.Error() == elastigo.RecordNotFound.Error() { api.Render(w, req, http.StatusNotFound, "item not found") return } else if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } var found map[string]interface{} err = json.Unmarshal(*result.Source, &found) if err != nil { log.Println(err) api.Render(w, req, http.StatusInternalServerError, "Internal Server Error") return } body := map[string]interface{}{"result": found} api.AddFeature(req, "handler:item") api.AddFeature(req, source) api.Render(w, req, http.StatusOK, body) return }