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  }