github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/cmd/search.go (about) 1 package cmd 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "fmt" 8 9 "github.com/qri-io/dataset" 10 "github.com/qri-io/ioes" 11 "github.com/qri-io/qri/base/params" 12 "github.com/qri-io/qri/errors" 13 "github.com/qri-io/qri/lib" 14 "github.com/spf13/cobra" 15 ) 16 17 // NewSearchCommand creates a new `qri search` command that searches for datasets 18 func NewSearchCommand(f Factory, ioStreams ioes.IOStreams) *cobra.Command { 19 o := &SearchOptions{IOStreams: ioStreams} 20 cmd := &cobra.Command{ 21 Use: "search QUERY", 22 Short: "search the registry for datasets", 23 Long: `Search datasets & peers that match your query. Search pings the qri registry. 24 25 Any dataset that has been pushed to the registry is available for search.`, 26 Example: ` # Search for datasets featuring "annual population": 27 $ qri search "annual population"`, 28 Annotations: map[string]string{ 29 "group": "network", 30 }, 31 Args: cobra.MaximumNArgs(1), 32 RunE: func(cmd *cobra.Command, args []string) error { 33 if err := o.Complete(f, args); err != nil { 34 return err 35 } 36 if err := o.Validate(); err != nil { 37 return err 38 } 39 return o.Run() 40 }, 41 } 42 43 cmd.Flags().StringVarP(&o.Format, "format", "f", "", "set output format [json|simple]") 44 cmd.Flags().IntVar(&o.Offset, "offset", 0, "number of records to skip from results, default 0") 45 cmd.Flags().IntVar(&o.Limit, "limit", 25, "size of results, default 25") 46 47 return cmd 48 } 49 50 // SearchOptions encapsulates state for the search command 51 type SearchOptions struct { 52 ioes.IOStreams 53 54 Query string 55 Format string 56 Offset int 57 Limit int 58 // Reindex bool 59 60 Instance *lib.Instance 61 } 62 63 // Complete adds any missing configuration that can only be added just before calling Run 64 func (o *SearchOptions) Complete(f Factory, args []string) (err error) { 65 if o.Instance, err = f.Instance(); err != nil { 66 return err 67 } 68 if len(args) != 0 { 69 o.Query = args[0] 70 } 71 return 72 } 73 74 // Validate checks that any user inputs are valid 75 func (o *SearchOptions) Validate() error { 76 if o.Query == "" { 77 return errors.New(lib.ErrBadArgs, "please provide search parameters, for example:\n $ qri search census\n $ qri search 'census 2018'\nsee `qri search --help` for more information") 78 } 79 return nil 80 } 81 82 // Run executes the search command 83 func (o *SearchOptions) Run() (err error) { 84 ctx := context.TODO() 85 inst := o.Instance 86 87 o.StartSpinner() 88 defer o.StopSpinner() 89 90 // TODO: add reindex option back in 91 92 p := &lib.SearchParams{ 93 Query: o.Query, 94 List: params.List{ 95 Offset: o.Offset, 96 Limit: o.Limit, 97 }, 98 } 99 100 results, err := inst.Search().Search(ctx, p) 101 if err != nil { 102 return err 103 } 104 105 switch o.Format { 106 case "": 107 fmt.Fprintf(o.Out, "showing %d results for '%s'\n", len(results), o.Query) 108 items := make([]fmt.Stringer, len(results)) 109 for i, result := range results { 110 items[i] = searchResultStringer(result) 111 } 112 o.StopSpinner() 113 printItems(o.Out, items, o.Offset) 114 return nil 115 case "simple": 116 items := make([]string, len(results)) 117 for i, r := range results { 118 items[i] = fmt.Sprintf("%s/%s", r.Value.Peername, r.Value.Name) 119 } 120 printlnStringItems(o.Out, items) 121 return nil 122 case dataset.JSONDataFormat.String(): 123 data, err := json.MarshalIndent(results, "", " ") 124 if err != nil { 125 return err 126 } 127 buf := bytes.NewBuffer(data) 128 o.StopSpinner() 129 printToPager(o.Out, buf) 130 default: 131 return fmt.Errorf("unrecognized format: %s", o.Format) 132 } 133 return nil 134 }