bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/annotate/backend/elastic2.go (about) 1 package backend 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "reflect" 8 "strings" 9 "time" 10 11 "bosun.org/annotate" 12 elastic "gopkg.in/olivere/elastic.v3" 13 ) 14 15 type Elastic2 struct { 16 *elastic.Client 17 index string 18 urls []string 19 simpleClient bool 20 clientOptionFuncs []elastic.ClientOptionFunc 21 maxResults int 22 initialized bool 23 } 24 25 func NewElastic2(urls []string, simpleclient bool, index string, clientoptions []elastic.ClientOptionFunc) *Elastic2 { 26 return &Elastic2{&elastic.Client{}, index, urls, simpleclient, clientoptions, 200, false} 27 } 28 29 func (e *Elastic2) GetAnnotations(start, end *time.Time, fieldFilters ...FieldFilter) (annotate.Annotations, error) { 30 if !e.initialized { 31 return nil, unInitErr 32 } 33 annotations := annotate.Annotations{} 34 filters := []elastic.Query{} 35 if start != nil && end != nil { 36 startQ := elastic.NewRangeQuery(annotate.EndDate).Gte(start) 37 endQ := elastic.NewRangeQuery(annotate.StartDate).Lte(end) 38 filters = append(filters, elastic.NewBoolQuery().Must(startQ, endQ)) 39 } 40 for _, filter := range fieldFilters { 41 switch filter.Field { 42 case annotate.Source, annotate.Host, annotate.CreationUser, annotate.Owner, annotate.Category: 43 default: 44 return annotations, fmt.Errorf("%v is not a field that can be filtered on", filter.Field) 45 } 46 var q elastic.Query 47 switch filter.Verb { 48 case Is, "": 49 q = elastic.NewTermQuery(filter.Field, filter.Value) 50 case Empty: 51 // Can't detect empty on a analyzed field 52 if filter.Field == annotate.Message { 53 return annotations, fmt.Errorf("message field does not support empty searches") 54 } 55 q = elastic.NewTermQuery(filter.Field, "") 56 default: 57 return annotations, fmt.Errorf("%v is not a valid query verb", filter.Verb) 58 } 59 if filter.Not { 60 q = elastic.NewBoolQuery().MustNot(q) 61 } 62 filters = append(filters, q) 63 } 64 65 var aType annotate.Annotation 66 scroll := e.Scroll(e.index).Query(elastic.NewBoolQuery().Must(filters...)).Size(e.maxResults).Pretty(true) 67 for { 68 res, err := scroll.Do() 69 if err == io.EOF { 70 break 71 } 72 if err != nil { 73 return annotations, err 74 } 75 for _, item := range res.Each(reflect.TypeOf(aType)) { 76 a := item.(annotate.Annotation) 77 annotations = append(annotations, a) 78 } 79 } 80 return annotations, nil 81 } 82 83 func (e *Elastic2) GetFieldValues(field string) ([]string, error) { 84 if !e.initialized { 85 return nil, unInitErr 86 } 87 terms := []string{} 88 switch field { 89 case annotate.Source, annotate.Host, annotate.CreationUser, annotate.Owner, annotate.Category: 90 //continue 91 default: 92 return terms, fmt.Errorf("invalid field %v", field) 93 } 94 termsAgg := elastic.NewTermsAggregation().Field(field) 95 res, err := e.Search(e.index).Aggregation(field, termsAgg).Size(e.maxResults).Do() 96 if err != nil { 97 return terms, err 98 } 99 b, found := res.Aggregations.Terms(field) 100 if !found { 101 return terms, fmt.Errorf("expected aggregation %v not found in result", field) 102 } 103 for _, bucket := range b.Buckets { 104 if v, ok := bucket.Key.(string); ok { 105 terms = append(terms, v) 106 } 107 } 108 return terms, nil 109 } 110 111 func (e *Elastic2) InitBackend() error { 112 var err error 113 var ec *elastic.Client 114 115 if e.simpleClient { 116 ec, err = elastic.NewSimpleClient(elastic.SetURL(e.urls...)) 117 } else if len(e.urls) == 0 { 118 ec, err = elastic.NewClient(e.clientOptionFuncs...) 119 } else { 120 ec, err = elastic.NewClient(elastic.SetURL(e.urls...)) 121 } 122 if err != nil { 123 return err 124 } 125 e.Client = ec 126 exists, err := e.IndexExists(e.index).Do() 127 if err != nil { 128 return err 129 } 130 if !exists { 131 res, err := e.CreateIndex(e.index).Do() 132 if (res != nil && !res.Acknowledged) || err != nil { 133 return fmt.Errorf("failed to create elastic mapping (ack: %v): %v", res != nil && res.Acknowledged, err) 134 } 135 } 136 stringNA := map[string]string{ 137 "type": "string", 138 "index": "not_analyzed", 139 } 140 stringA := map[string]string{ 141 "type": "string", 142 } 143 date := map[string]string{ 144 "type": "date", 145 } 146 p := make(map[string]interface{}) 147 p[annotate.Message] = stringA 148 p[annotate.StartDate] = date 149 p[annotate.EndDate] = date 150 p[annotate.Source] = stringNA 151 p[annotate.Host] = stringNA 152 p[annotate.CreationUser] = stringNA 153 p[annotate.Owner] = stringNA 154 p[annotate.Category] = stringNA 155 p[annotate.Url] = stringNA 156 mapping := make(map[string]interface{}) 157 mapping["properties"] = p 158 q := e.PutMapping().Index(e.index).Type(docType).BodyJson(mapping) 159 res, err := q.Do() 160 if (res != nil && !res.Acknowledged) || err != nil { 161 return fmt.Errorf("failed to create elastic mapping (ack: %v): %v", res != nil && res.Acknowledged, err) 162 } 163 e.initialized = true 164 return nil 165 } 166 167 func (e *Elastic2) InsertAnnotation(a *annotate.Annotation) error { 168 if !e.initialized { 169 return unInitErr 170 } 171 _, err := e.Index().Index(e.index).BodyJson(a).Id(a.Id).Type(docType).Do() 172 return err 173 } 174 175 func (e *Elastic2) GetAnnotation(id string) (*annotate.Annotation, bool, error) { 176 if !e.initialized { 177 return nil, false, unInitErr 178 } 179 a := annotate.Annotation{} 180 if id == "" { 181 return &a, false, fmt.Errorf("must provide id") 182 } 183 res, err := e.Get().Index(e.index).Type(docType).Id(id).Do() 184 // Ewwww... 185 if err != nil && strings.Contains(err.Error(), "Error 404") { 186 return &a, false, nil 187 } 188 if err != nil { 189 return &a, false, err 190 } 191 if err := json.Unmarshal(*res.Source, &a); err != nil { 192 return &a, res.Found, err 193 } 194 return &a, res.Found, nil 195 } 196 197 func (e *Elastic2) DeleteAnnotation(id string) error { 198 if !e.initialized { 199 return unInitErr 200 } 201 _, err := e.Delete().Index(e.index).Type(docType).Id(id).Do() 202 if err != nil { 203 return err 204 } 205 return nil 206 //TODO? Check res.Found? 207 }