// This function is subject to change, depending on what the client actually // expects. This may not be entirely correct. func formatRunShow(run *report.Report) map[string]interface{} { reportMap := util.MapifyObject(run) resources := reportMap["resources"] delete(reportMap, "resources") reportFmt := make(map[string]interface{}) reportFmt["run_detail"] = reportMap reportFmt["resources"] = resources return reportFmt }
func (p *PostgresSearch) Search(idx string, q string, rows int, sortOrder string, start int, partialData map[string]interface{}) ([]map[string]interface{}, error) { // check that the endpoint actually exists sqlStmt := "SELECT 1 FROM goiardi.search_collections WHERE organization_id = $1 AND name = $2" stmt, serr := datastore.Dbh.Prepare(sqlStmt) if serr != nil { return nil, serr } defer stmt.Close() var zzz int serr = stmt.QueryRow(1, idx).Scan(&zzz) // don't care about zzz if serr != nil { if serr == sql.ErrNoRows { serr = fmt.Errorf("I don't know how to search for %s data objects.", idx) } return nil, serr } // Special case "goodness". If the search term is "*:*" with no // qualifiers, short circuit everything and just get a list of the // distinct items. var qresults []string if q == "*:*" { logger.Debugf("Searching '*:*' on %s, short circuiting", idx) var builtinIdx bool if idx == "node" || idx == "client" || idx == "environment" || idx == "role" { builtinIdx = true sqlStmt = fmt.Sprintf("SELECT COALESCE(ARRAY_AGG(name), '{}'::text[]) FROM goiardi.%ss WHERE organization_id = $1", idx) } else { sqlStmt = "SELECT COALESCE(ARRAY_AGG(orig_name), '{}'::text[]) FROM goiardi.data_bag_items JOIN goiardi.data_bags ON goiardi.data_bag_items.data_bag_id = goiardi.data_bags.id WHERE goiardi.data_bags.organization_id = $1 AND goiardi.data_bags.name = $2" } var res util.StringSlice stmt, err := datastore.Dbh.Prepare(sqlStmt) if err != nil { return nil, err } defer stmt.Close() if builtinIdx { err = stmt.QueryRow(1).Scan(&res) } else { err = stmt.QueryRow(1, idx).Scan(&res) } if err != nil && err != sql.ErrNoRows { return nil, err } qresults = res } else { // keep up with the ersatz solr. qq := &Tokenizer{Buffer: q} qq.Init() if err := qq.Parse(); err != nil { return nil, err } qq.Execute() qchain := qq.Evaluate() pgQ := &PgQuery{idx: idx, queryChain: qchain} err := pgQ.execute() if err != nil { return nil, err } qresults, err = pgQ.results() if err != nil { return nil, err } } // THE WRONG WAY: // Eventually, ordering by the keys themselves would be awesome. objs := getResults(idx, qresults) res := make([]map[string]interface{}, len(objs)) for i, r := range objs { switch r := r.(type) { case *client.Client: jc := map[string]interface{}{ "name": r.Name, "chef_type": r.ChefType, "json_class": r.JSONClass, "admin": r.Admin, "public_key": r.PublicKey(), "validator": r.Validator, } res[i] = jc default: res[i] = util.MapifyObject(r) } } /* If we're doing partial search, tease out the fields we want. */ if partialData != nil { var err error res, err = formatPartials(res, objs, partialData) if err != nil { return nil, err } } // and at long last, sort ss := strings.Split(sortOrder, " ") sortKey := ss[0] if sortKey == "id" { sortKey = "name" } var ordering string if len(ss) > 1 { ordering = strings.ToLower(ss[1]) } else { ordering = "asc" } sortResults := results{res, sortKey} if ordering == "desc" { sort.Sort(sort.Reverse(sortResults)) } else { sort.Sort(sortResults) } res = sortResults.res end := start + rows if end > len(res) { end = len(res) } res = res[start:end] return res, nil }
func searchHandler(w http.ResponseWriter, r *http.Request) { /* ... and we need search to run the environment tests, so here we * go. */ w.Header().Set("Content-Type", "application/json") searchResponse := make(map[string]interface{}) pathArray := splitPath(r.URL.Path) pathArrayLen := len(pathArray) opUser, oerr := actor.GetReqUser(r.Header.Get("X-OPS-USERID")) if oerr != nil { jsonErrorReport(w, r, oerr.Error(), oerr.Status()) return } /* set up query params for searching */ var ( paramQuery string paramsRows int sortOrder string start int ) r.ParseForm() if q, found := r.Form["q"]; found { if len(q) < 0 { jsonErrorReport(w, r, "No query string specified for search", http.StatusBadRequest) return } paramQuery = q[0] } else if pathArrayLen != 1 { /* default to "*:*" for a search term */ paramQuery = "*:*" } if pr, found := r.Form["rows"]; found { if len(pr) > 0 { paramsRows, _ = strconv.Atoi(pr[0]) } } else { paramsRows = 1000 } // TODO: get a default sort in, once sorting is sorted out. if s, found := r.Form["sort"]; found { if len(s) > 0 { sortOrder = s[0] } else { sortOrder = "id ASC" } } _ = sortOrder if st, found := r.Form["start"]; found { if len(st) > 0 { start, _ = strconv.Atoi(st[0]) } } else { start = 0 } if pathArrayLen == 1 { /* base end points */ switch r.Method { case "GET": if opUser.IsValidator() { jsonErrorReport(w, r, "You are not allowed to perform this action", http.StatusForbidden) return } searchEndpoints := search.GetEndpoints() for _, s := range searchEndpoints { searchResponse[s] = util.CustomURL(fmt.Sprintf("/search/%s", s)) } default: jsonErrorReport(w, r, "Method not allowed", http.StatusMethodNotAllowed) return } } else if pathArrayLen == 2 { switch r.Method { case "GET", "POST": if opUser.IsValidator() { jsonErrorReport(w, r, "You are not allowed to perform this action", http.StatusForbidden) return } /* start figuring out what comes in POSTS now, * so the partial search tests don't complain * anymore. */ var partialData map[string]interface{} if r.Method == "POST" { var perr error partialData, perr = parseObjJSON(r.Body) if perr != nil { jsonErrorReport(w, r, perr.Error(), http.StatusBadRequest) return } } idx := pathArray[1] rObjs, err := search.Search(idx, paramQuery) if err != nil { statusCode := http.StatusBadRequest re := regexp.MustCompile(`^I don't know how to search for .*? data objects.`) if re.MatchString(err.Error()) { statusCode = http.StatusNotFound } jsonErrorReport(w, r, err.Error(), statusCode) return } res := make([]map[string]interface{}, len(rObjs)) for i, r := range rObjs { switch r := r.(type) { case *client.Client: jc := map[string]interface{}{ "name": r.Name, "chef_type": r.ChefType, "json_class": r.JSONClass, "admin": r.Admin, "public_key": r.PublicKey(), "validator": r.Validator, } res[i] = jc default: res[i] = util.MapifyObject(r) } } /* If we're doing partial search, tease out the * fields we want. */ if r.Method == "POST" { res, err = partialSearchFormat(res, partialData) if err != nil { jsonErrorReport(w, r, err.Error(), http.StatusBadRequest) return } for x, z := range res { tmpRes := make(map[string]interface{}) switch ro := rObjs[x].(type) { case *databag.DataBagItem: dbiURL := fmt.Sprintf("/data/%s/%s", ro.DataBagName, ro.RawData["id"].(string)) tmpRes["url"] = util.CustomURL(dbiURL) default: tmpRes["url"] = util.ObjURL(rObjs[x].(util.GoiardiObj)) } tmpRes["data"] = z res[x] = tmpRes } } end := start + paramsRows if end > len(res) { end = len(res) } res = res[start:end] searchResponse["total"] = len(res) searchResponse["start"] = start searchResponse["rows"] = res default: jsonErrorReport(w, r, "Method not allowed", http.StatusMethodNotAllowed) return } } else { /* Say what? Bad request. */ jsonErrorReport(w, r, "Bad request", http.StatusBadRequest) return } enc := json.NewEncoder(w) if err := enc.Encode(&searchResponse); err != nil { jsonErrorReport(w, r, err.Error(), http.StatusInternalServerError) } }
// Search parses the given query string and search the given index for any // matching results. func (t *TrieSearch) Search(idx string, query string, rows int, sortOrder string, start int, partialData map[string]interface{}) ([]map[string]interface{}, error) { m.Lock() defer m.Unlock() qq := &Tokenizer{Buffer: query} qq.Init() if err := qq.Parse(); err != nil { return nil, err } qq.Execute() qchain := qq.Evaluate() d := make(map[string]indexer.Document) solrQ := &SolrQuery{queryChain: qchain, idxName: idx, docs: d} _, err := solrQ.execute() if err != nil { return nil, err } qresults := solrQ.results() objs := getResults(idx, qresults) res := make([]map[string]interface{}, len(objs)) for i, r := range objs { switch r := r.(type) { case *client.Client: jc := map[string]interface{}{ "name": r.Name, "chef_type": r.ChefType, "json_class": r.JSONClass, "admin": r.Admin, "public_key": r.PublicKey(), "validator": r.Validator, } res[i] = jc default: res[i] = util.MapifyObject(r) } } /* If we're doing partial search, tease out the fields we want. */ if partialData != nil { res, err = formatPartials(res, objs, partialData) if err != nil { return nil, err } } // and at long last, sort ss := strings.Split(sortOrder, " ") sortKey := ss[0] if sortKey == "id" { sortKey = "name" } var ordering string if len(ss) > 1 { ordering = strings.ToLower(ss[1]) } else { ordering = "asc" } sortResults := results{res, sortKey} if ordering == "desc" { sort.Sort(sort.Reverse(sortResults)) } else { sort.Sort(sortResults) } res = sortResults.res end := start + rows if end > len(res) { end = len(res) } res = res[start:end] return res, nil }