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 ParseSearchParams(params map[string][]string) (*SearchParams, error) { // Ensure no unknown parameters unknown := make([]string, 0) for key, _ := range params { if key != "limit" && key != "offset" && key != "callback" && key != "secret" && !keyRegexp.MatchString(key) && !valueRegexp.MatchString(key) && !opRegexp.MatchString(key) { unknown = append(unknown, key) } } if len(unknown) > 0 { message := strings.Join(unknown, ", ") + " are unknown parameters" paramsMap := map[string]string{} for _, key := range unknown { paramsMap[key] = "is an unknown parameter" } return nil, api.NewParamsError(message, paramsMap) } // Ensure params in sets of key/op/value paramSets := map[string]*SearchParamSet{} for key, values := range params { if keyRegexp.MatchString(key) { sub := keyRegexp.FindStringSubmatch(key) index := sub[1] _, ok := paramSets[index] if !ok { paramSets[index] = &SearchParamSet{} } paramSets[index].SetKey(values[0]) } if valueRegexp.MatchString(key) { sub := valueRegexp.FindStringSubmatch(key) index := sub[1] _, ok := paramSets[index] if !ok { paramSets[index] = &SearchParamSet{} } paramSets[index].SetValues(values) } if opRegexp.MatchString(key) { sub := opRegexp.FindStringSubmatch(key) index := sub[1] _, ok := paramSets[index] if !ok { paramSets[index] = &SearchParamSet{} } paramSets[index].SetOp(values[0]) } } for _, set := range paramSets { if set.Key == "" { return nil, api.NewParamsError("one or more key/op/value sets are missing a key", map[string]string{}) } if set.Values == nil || len(set.Values) == 0 { return nil, api.NewParamsError("one or more key/op/value sets are missing a value", map[string]string{}) } hasValue := false for _, value := range set.Values { if value != "" { hasValue = true break } } if !hasValue { return nil, api.NewParamsError("one or more key/op/value sets are missing a value", map[string]string{}) } if set.Op == "" { return nil, api.NewParamsError("one or more key/op/value sets are missing a op", map[string]string{}) } if set.Op != "eq" && set.Op != "fuzzy" && set.Op != "prefix" && set.Op != "gte" && set.Op != "gt" && set.Op != "lte" && set.Op != "lt" { return nil, api.NewParamsError("invalid operation "+set.Op, map[string]string{}) } } var err error // Ensure offset/ limit are positive integers var offsetValue uint64 if offset, ok := params["offset"]; ok { offsetValue, err = strconv.ParseUint(offset[0], 0, 64) if err != nil { return nil, api.NewParamsError("offset must be a positive number", map[string]string{"offset": "must be a positive number"}) } } var limitValue uint64 if limit, ok := params["limit"]; ok { limitValue, err = strconv.ParseUint(limit[0], 0, 64) if err != nil { return nil, api.NewParamsError("limit must be a positive number", map[string]string{"limit": "must be a positive number"}) } // Ensure limit is less than 100 if limitValue > 100 { return nil, api.NewParamsError("limit must be less than 100", map[string]string{"limit": "must less than 100"}) } } if limitValue == 0 { limitValue = 100 } listSets := make([]*SearchParamSet, len(paramSets)) i := 0 for _, v := range paramSets { listSets[i] = v i += 1 } return &SearchParams{ offsetValue, limitValue, listSets, }, nil }
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 }