github.com/unigraph-dev/dgraph@v1.1.1-0.20200923154953-8b52b426f765/query/math.go (about) 1 /* 2 * Copyright 2017-2018 Dgraph Labs, Inc. and Contributors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package query 18 19 import ( 20 "github.com/dgraph-io/dgraph/types" 21 "github.com/golang/glog" 22 "github.com/pkg/errors" 23 ) 24 25 type mathTree struct { 26 Fn string 27 Var string 28 Const types.Val // If its a const value node. 29 Val map[uint64]types.Val 30 Child []*mathTree 31 } 32 33 // processBinary handles the binary operands like 34 // +, -, *, /, %, max, min, logbase 35 func processBinary(mNode *mathTree) error { 36 destMap := make(map[uint64]types.Val) 37 aggName := mNode.Fn 38 39 mpl := mNode.Child[0].Val 40 mpr := mNode.Child[1].Val 41 cl := mNode.Child[0].Const 42 cr := mNode.Child[1].Const 43 44 f := func(k uint64) error { 45 ag := aggregator{ 46 name: aggName, 47 } 48 lVal := mpl[k] 49 if cl.Value != nil { 50 // Use the constant value that was supplied. 51 lVal = cl 52 } 53 rVal := mpr[k] 54 if cr.Value != nil { 55 // Use the constant value that was supplied. 56 rVal = cr 57 } 58 err := ag.ApplyVal(lVal) 59 if err != nil { 60 return err 61 } 62 err = ag.ApplyVal(rVal) 63 if err != nil { 64 return err 65 } 66 destMap[k], err = ag.Value() 67 if err != nil { 68 return err 69 } 70 return nil 71 } 72 73 if len(mpl) != 0 || len(mpr) != 0 { 74 for k := range mpr { 75 if err := f(k); err != nil { 76 return err 77 } 78 } 79 for k := range mpl { 80 if _, ok := mpr[k]; ok { 81 continue 82 } 83 if err := f(k); err != nil { 84 return err 85 } 86 } 87 mNode.Val = destMap 88 return nil 89 } 90 91 if cl.Value != nil && cr.Value != nil { 92 // Both maps are nil, so 2 constatns. 93 ag := aggregator{ 94 name: aggName, 95 } 96 err := ag.ApplyVal(cl) 97 if err != nil { 98 return err 99 } 100 err = ag.ApplyVal(cr) 101 if err != nil { 102 return err 103 } 104 mNode.Const, err = ag.Value() 105 return err 106 } 107 return nil 108 } 109 110 // processUnary handles the unary operands like 111 // u-, log, exp, since, floor, ceil 112 func processUnary(mNode *mathTree) error { 113 destMap := make(map[uint64]types.Val) 114 srcMap := mNode.Child[0].Val 115 aggName := mNode.Fn 116 ch := mNode.Child[0] 117 ag := aggregator{ 118 name: aggName, 119 } 120 if ch.Const.Value != nil { 121 // Use the constant value that was supplied. 122 err := ag.ApplyVal(ch.Const) 123 if err != nil { 124 return err 125 } 126 mNode.Const, err = ag.Value() 127 return err 128 } 129 130 for k, val := range srcMap { 131 err := ag.ApplyVal(val) 132 if err != nil { 133 return err 134 } 135 destMap[k], err = ag.Value() 136 if err != nil { 137 return err 138 } 139 } 140 mNode.Val = destMap 141 return nil 142 143 } 144 145 // processBinaryBoolean handles the binary operands which 146 // return a boolean value. 147 // All the inequality operators (<, >, <=, >=, !=, ==) 148 func processBinaryBoolean(mNode *mathTree) error { 149 destMap := make(map[uint64]types.Val) 150 srcMap := mNode.Child[0].Val 151 aggName := mNode.Fn 152 153 ch := mNode.Child[1] 154 curMap := ch.Val 155 for k, val := range srcMap { 156 curVal := curMap[k] 157 if ch.Const.Value != nil { 158 // Use the constant value that was supplied. 159 curVal = ch.Const 160 } 161 res, err := compareValues(aggName, val, curVal) 162 if err != nil { 163 return errors.Wrapf(err, "Wrong values in comparison function.") 164 } 165 destMap[k] = types.Val{ 166 Tid: types.BoolID, 167 Value: res, 168 } 169 } 170 mNode.Val = destMap 171 return nil 172 } 173 174 // processTernary handles the ternary operand cond() 175 func processTernary(mNode *mathTree) error { 176 destMap := make(map[uint64]types.Val) 177 aggName := mNode.Fn 178 condMap := mNode.Child[0].Val 179 if len(condMap) == 0 { 180 return errors.Errorf("Expected a value variable in %v but missing.", aggName) 181 } 182 varOne := mNode.Child[1].Val 183 varTwo := mNode.Child[2].Val 184 constOne := mNode.Child[1].Const 185 constTwo := mNode.Child[2].Const 186 for k, val := range condMap { 187 var res types.Val 188 v, ok := val.Value.(bool) 189 if !ok { 190 return errors.Errorf("First variable of conditional function not a bool value") 191 } 192 if v { 193 // Pick the value of first map. 194 if constOne.Value != nil { 195 res = constOne 196 } else { 197 res = varOne[k] 198 } 199 } else { 200 // Pick the value of second map. 201 if constTwo.Value != nil { 202 res = constTwo 203 } else { 204 res = varTwo[k] 205 } 206 } 207 destMap[k] = res 208 } 209 mNode.Val = destMap 210 return nil 211 } 212 213 func evalMathTree(mNode *mathTree) error { 214 if mNode.Const.Value != nil { 215 return nil 216 } 217 if mNode.Var != "" { 218 if len(mNode.Val) == 0 { 219 glog.V(2).Infof("Variable %v not yet populated or missing.", mNode.Var) 220 } 221 // This is a leaf node whose value is already populated. So return. 222 return nil 223 } 224 225 for _, child := range mNode.Child { 226 // Process the child nodes first. 227 err := evalMathTree(child) 228 if err != nil { 229 return err 230 } 231 } 232 233 aggName := mNode.Fn 234 if isUnary(aggName) { 235 if len(mNode.Child) != 1 { 236 return errors.Errorf("Function %v expects 1 argument. But got: %v", aggName, 237 len(mNode.Child)) 238 } 239 return processUnary(mNode) 240 } 241 242 if isBinary(aggName) { 243 if len(mNode.Child) != 2 { 244 return errors.Errorf("Function %v expects 2 argument. But got: %v", aggName, 245 len(mNode.Child)) 246 } 247 return processBinary(mNode) 248 } 249 250 if isBinaryBoolean(aggName) { 251 if len(mNode.Child) != 2 { 252 return errors.Errorf("Function %v expects 2 argument. But got: %v", aggName, 253 len(mNode.Child)) 254 } 255 return processBinaryBoolean(mNode) 256 } 257 258 if isTernary(aggName) { 259 if len(mNode.Child) != 3 { 260 return errors.Errorf("Function %v expects 3 argument. But got: %v", aggName, 261 len(mNode.Child)) 262 } 263 return processTernary(mNode) 264 } 265 266 return errors.Errorf("Unhandled Math operator: %v", aggName) 267 }