github.com/m3db/m3@v1.5.0/src/m3ninx/search/proptest/query_gen.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package proptest 22 23 import ( 24 "bytes" 25 "reflect" 26 27 "github.com/m3db/m3/src/m3ninx/doc" 28 "github.com/m3db/m3/src/m3ninx/search" 29 "github.com/m3db/m3/src/m3ninx/search/query" 30 31 "github.com/leanovate/gopter" 32 "github.com/leanovate/gopter/gen" 33 ) 34 35 // GenAllQuery generates an all query. 36 func GenAllQuery(docs []doc.Metadata) gopter.Gen { 37 return gen.Const(query.NewAllQuery()) 38 } 39 40 // GenFieldQuery generates a field query. 41 func GenFieldQuery(docs []doc.Metadata) gopter.Gen { 42 return func(genParams *gopter.GenParameters) *gopter.GenResult { 43 fieldName, _ := fieldNameAndValue(genParams, docs) 44 q := query.NewFieldQuery(fieldName) 45 return gopter.NewGenResult(q, gopter.NoShrinker) 46 } 47 } 48 49 // GenTermQuery generates a term query. 50 func GenTermQuery(docs []doc.Metadata) gopter.Gen { 51 return func(genParams *gopter.GenParameters) *gopter.GenResult { 52 fieldName, fieldValue := fieldNameAndValue(genParams, docs) 53 q := query.NewTermQuery(fieldName, fieldValue) 54 return gopter.NewGenResult(q, gopter.NoShrinker) 55 } 56 } 57 58 func fieldNameAndValue(genParams *gopter.GenParameters, docs []doc.Metadata) ([]byte, []byte) { 59 docIDRes, ok := gen.IntRange(0, len(docs)-1)(genParams).Retrieve() 60 if !ok { 61 panic("unable to generate term query") // should never happen 62 } 63 docID := docIDRes.(int) 64 65 doc := docs[docID] 66 fieldRes, ok := gen.IntRange(0, len(doc.Fields)-1)(genParams).Retrieve() 67 if !ok { 68 panic("unable to generate term query fields") // should never happen 69 } 70 71 fieldID := fieldRes.(int) 72 field := doc.Fields[fieldID] 73 return field.Name, field.Value 74 } 75 76 // GenIdenticalTermAndRegexpQuery generates a term query and regexp query with 77 // the exact same underlying field and pattern. 78 func GenIdenticalTermAndRegexpQuery(docs []doc.Metadata) gopter.Gen { 79 return func(genParams *gopter.GenParameters) *gopter.GenResult { 80 fieldName, fieldValue := fieldNameAndValue(genParams, docs) 81 termQ := query.NewTermQuery(fieldName, fieldValue) 82 regexpQ, err := query.NewRegexpQuery(fieldName, fieldValue) 83 if err != nil { 84 panic(err) 85 } 86 return gopter.NewGenResult([]search.Query{termQ, regexpQ}, gopter.NoShrinker) 87 } 88 } 89 90 // GenRegexpQuery generates a regexp query. 91 func GenRegexpQuery(docs []doc.Metadata) gopter.Gen { 92 return func(genParams *gopter.GenParameters) *gopter.GenResult { 93 docIDRes, ok := gen.IntRange(0, len(docs)-1)(genParams).Retrieve() 94 if !ok { 95 panic("unable to generate regexp query") // should never happen 96 } 97 docID := docIDRes.(int) 98 99 doc := docs[docID] 100 fieldRes, ok := gen.IntRange(0, len(doc.Fields)-1)(genParams).Retrieve() 101 if !ok { 102 panic("unable to generate regexp query fields") // should never happen 103 } 104 105 fieldID := fieldRes.(int) 106 field := doc.Fields[fieldID] 107 108 var re []byte 109 110 reType := genParams.NextUint64() % 3 111 switch reType { 112 case 0: // prefix 113 idx := genParams.NextUint64() % uint64(len(field.Value)) 114 re = append([]byte(nil), field.Value[:idx]...) 115 re = append(re, []byte(".*")...) 116 case 1: // suffix 117 idx := genParams.NextUint64() % uint64(len(field.Value)) 118 re = append([]byte(".*"), field.Value[idx:]...) 119 case 2: // middle 120 start := genParams.NextUint64() % uint64(len(field.Value)) 121 remain := uint64(len(field.Value)) - start 122 end := start + genParams.NextUint64()%remain 123 re = append(append([]byte(".*"), field.Value[start:end]...), []byte(".*")...) 124 } 125 126 // escape any '(' or ')' we see to avoid regular expression parsing failure 127 escapeFront := bytes.Replace(re, []byte("("), []byte("\\("), -1) 128 escapeBack := bytes.Replace(escapeFront, []byte(")"), []byte("\\)"), -1) 129 130 q, err := query.NewRegexpQuery(field.Name, escapeBack) 131 if err != nil { 132 panic(err) 133 } 134 135 return gopter.NewGenResult(q, gopter.NoShrinker) 136 } 137 } 138 139 // GenNegationQuery generates a negation query. 140 func GenNegationQuery(docs []doc.Metadata) gopter.Gen { 141 return gen.OneGenOf( 142 GenFieldQuery(docs), 143 GenTermQuery(docs), 144 GenRegexpQuery(docs), 145 ). 146 Map(func(q search.Query) search.Query { 147 return query.NewNegationQuery(q) 148 }) 149 } 150 151 // GenConjunctionQuery generates a conjunction query. 152 func GenConjunctionQuery(docs []doc.Metadata) gopter.Gen { 153 return gen.SliceOf( 154 gen.OneGenOf( 155 GenFieldQuery(docs), 156 GenTermQuery(docs), 157 GenRegexpQuery(docs), 158 GenNegationQuery(docs)), 159 reflect.TypeOf((*search.Query)(nil)).Elem()). 160 Map(func(qs []search.Query) search.Query { 161 return query.NewConjunctionQuery(qs) 162 }) 163 } 164 165 // GenDisjunctionQuery generates a disjunction query. 166 func GenDisjunctionQuery(docs []doc.Metadata) gopter.Gen { 167 return gen.SliceOf( 168 gen.OneGenOf( 169 GenFieldQuery(docs), 170 GenTermQuery(docs), 171 GenRegexpQuery(docs), 172 GenNegationQuery(docs)), 173 reflect.TypeOf((*search.Query)(nil)).Elem()). 174 Map(func(qs []search.Query) search.Query { 175 return query.NewDisjunctionQuery(qs) 176 }) 177 } 178 179 // GenQuery generates a query. 180 func GenQuery(docs []doc.Metadata) gopter.Gen { 181 return gen.OneGenOf( 182 GenAllQuery(docs), 183 GenFieldQuery(docs), 184 GenTermQuery(docs), 185 GenRegexpQuery(docs), 186 GenNegationQuery(docs), 187 GenConjunctionQuery(docs), 188 GenDisjunctionQuery(docs)) 189 }