github.com/safing/portbase@v0.19.5/database/query/query.go (about) 1 package query 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/safing/portbase/database/accessor" 8 "github.com/safing/portbase/database/record" 9 ) 10 11 // Example: 12 // q.New("core:/", 13 // q.Where("a", q.GreaterThan, 0), 14 // q.Where("b", q.Equals, 0), 15 // q.Or( 16 // q.Where("c", q.StartsWith, "x"), 17 // q.Where("d", q.Contains, "y") 18 // ) 19 // ) 20 21 // Query contains a compiled query. 22 type Query struct { 23 checked bool 24 dbName string 25 dbKeyPrefix string 26 where Condition 27 orderBy string 28 limit int 29 offset int 30 } 31 32 // New creates a new query with the supplied prefix. 33 func New(prefix string) *Query { 34 dbName, dbKeyPrefix := record.ParseKey(prefix) 35 return &Query{ 36 dbName: dbName, 37 dbKeyPrefix: dbKeyPrefix, 38 } 39 } 40 41 // Where adds filtering. 42 func (q *Query) Where(condition Condition) *Query { 43 q.where = condition 44 return q 45 } 46 47 // Limit limits the number of returned results. 48 func (q *Query) Limit(limit int) *Query { 49 q.limit = limit 50 return q 51 } 52 53 // Offset sets the query offset. 54 func (q *Query) Offset(offset int) *Query { 55 q.offset = offset 56 return q 57 } 58 59 // OrderBy orders the results by the given key. 60 func (q *Query) OrderBy(key string) *Query { 61 q.orderBy = key 62 return q 63 } 64 65 // Check checks for errors in the query. 66 func (q *Query) Check() (*Query, error) { 67 if q.checked { 68 return q, nil 69 } 70 71 // check condition 72 if q.where != nil { 73 err := q.where.check() 74 if err != nil { 75 return nil, err 76 } 77 } 78 79 q.checked = true 80 return q, nil 81 } 82 83 // MustBeValid checks for errors in the query and panics if there is an error. 84 func (q *Query) MustBeValid() *Query { 85 _, err := q.Check() 86 if err != nil { 87 panic(err) 88 } 89 return q 90 } 91 92 // IsChecked returns whether they query was checked. 93 func (q *Query) IsChecked() bool { 94 return q.checked 95 } 96 97 // MatchesKey checks whether the query matches the supplied database key (key without database prefix). 98 func (q *Query) MatchesKey(dbKey string) bool { 99 return strings.HasPrefix(dbKey, q.dbKeyPrefix) 100 } 101 102 // MatchesRecord checks whether the query matches the supplied database record (value only). 103 func (q *Query) MatchesRecord(r record.Record) bool { 104 if q.where == nil { 105 return true 106 } 107 108 acc := r.GetAccessor(r) 109 if acc == nil { 110 return false 111 } 112 return q.where.complies(acc) 113 } 114 115 // MatchesAccessor checks whether the query matches the supplied accessor (value only). 116 func (q *Query) MatchesAccessor(acc accessor.Accessor) bool { 117 if q.where == nil { 118 return true 119 } 120 return q.where.complies(acc) 121 } 122 123 // Matches checks whether the query matches the supplied database record. 124 func (q *Query) Matches(r record.Record) bool { 125 if !q.MatchesKey(r.DatabaseKey()) { 126 return false 127 } 128 return q.MatchesRecord(r) 129 } 130 131 // Print returns the string representation of the query. 132 func (q *Query) Print() string { 133 var where string 134 if q.where != nil { 135 where = q.where.string() 136 if where != "" { 137 if strings.HasPrefix(where, "(") { 138 where = where[1 : len(where)-1] 139 } 140 where = fmt.Sprintf(" where %s", where) 141 } 142 } 143 144 var orderBy string 145 if q.orderBy != "" { 146 orderBy = fmt.Sprintf(" orderby %s", q.orderBy) 147 } 148 149 var limit string 150 if q.limit > 0 { 151 limit = fmt.Sprintf(" limit %d", q.limit) 152 } 153 154 var offset string 155 if q.offset > 0 { 156 offset = fmt.Sprintf(" offset %d", q.offset) 157 } 158 159 return fmt.Sprintf("query %s:%s%s%s%s%s", q.dbName, q.dbKeyPrefix, where, orderBy, limit, offset) 160 } 161 162 // DatabaseName returns the name of the database. 163 func (q *Query) DatabaseName() string { 164 return q.dbName 165 } 166 167 // DatabaseKeyPrefix returns the key prefix for the database. 168 func (q *Query) DatabaseKeyPrefix() string { 169 return q.dbKeyPrefix 170 }