aboutsummaryrefslogtreecommitdiffstats
path: root/container-search/src/main/java/com/yahoo/search/grouping/vespa/ExpressionConverter.java
blob: c967f9ca1fb3276a42c684500af594e5dc75a4bd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.grouping.vespa;

import com.yahoo.search.grouping.request.AddFunction;
import com.yahoo.search.grouping.request.AggregatorNode;
import com.yahoo.search.grouping.request.AndFunction;
import com.yahoo.search.grouping.request.ArrayAtLookup;
import com.yahoo.search.grouping.request.AttributeFunction;
import com.yahoo.search.grouping.request.AttributeMapLookupValue;
import com.yahoo.search.grouping.request.AttributeValue;
import com.yahoo.search.grouping.request.AvgAggregator;
import com.yahoo.search.grouping.request.BucketValue;
import com.yahoo.search.grouping.request.CatFunction;
import com.yahoo.search.grouping.request.ConstantValue;
import com.yahoo.search.grouping.request.CountAggregator;
import com.yahoo.search.grouping.request.DateFunction;
import com.yahoo.search.grouping.request.DayOfMonthFunction;
import com.yahoo.search.grouping.request.DayOfWeekFunction;
import com.yahoo.search.grouping.request.DayOfYearFunction;
import com.yahoo.search.grouping.request.DebugWaitFunction;
import com.yahoo.search.grouping.request.DivFunction;
import com.yahoo.search.grouping.request.DocIdNsSpecificValue;
import com.yahoo.search.grouping.request.DoubleValue;
import com.yahoo.search.grouping.request.FixedWidthFunction;
import com.yahoo.search.grouping.request.GroupingExpression;
import com.yahoo.search.grouping.request.GroupingOperation;
import com.yahoo.search.grouping.request.HourOfDayFunction;
import com.yahoo.search.grouping.request.InfiniteValue;
import com.yahoo.search.grouping.request.InterpolatedLookup;
import com.yahoo.search.grouping.request.LongValue;
import com.yahoo.search.grouping.request.MathACosFunction;
import com.yahoo.search.grouping.request.MathACosHFunction;
import com.yahoo.search.grouping.request.MathASinFunction;
import com.yahoo.search.grouping.request.MathASinHFunction;
import com.yahoo.search.grouping.request.MathATanFunction;
import com.yahoo.search.grouping.request.MathATanHFunction;
import com.yahoo.search.grouping.request.MathCbrtFunction;
import com.yahoo.search.grouping.request.MathCosFunction;
import com.yahoo.search.grouping.request.MathCosHFunction;
import com.yahoo.search.grouping.request.MathExpFunction;
import com.yahoo.search.grouping.request.MathFloorFunction;
import com.yahoo.search.grouping.request.MathHypotFunction;
import com.yahoo.search.grouping.request.MathLog10Function;
import com.yahoo.search.grouping.request.MathLog1pFunction;
import com.yahoo.search.grouping.request.MathLogFunction;
import com.yahoo.search.grouping.request.MathPowFunction;
import com.yahoo.search.grouping.request.MathSinFunction;
import com.yahoo.search.grouping.request.MathSinHFunction;
import com.yahoo.search.grouping.request.MathSqrtFunction;
import com.yahoo.search.grouping.request.MathTanFunction;
import com.yahoo.search.grouping.request.MathTanHFunction;
import com.yahoo.search.grouping.request.MaxAggregator;
import com.yahoo.search.grouping.request.MaxFunction;
import com.yahoo.search.grouping.request.Md5Function;
import com.yahoo.search.grouping.request.MinAggregator;
import com.yahoo.search.grouping.request.MinFunction;
import com.yahoo.search.grouping.request.MinuteOfHourFunction;
import com.yahoo.search.grouping.request.ModFunction;
import com.yahoo.search.grouping.request.MonthOfYearFunction;
import com.yahoo.search.grouping.request.MulFunction;
import com.yahoo.search.grouping.request.NegFunction;
import com.yahoo.search.grouping.request.NormalizeSubjectFunction;
import com.yahoo.search.grouping.request.NowFunction;
import com.yahoo.search.grouping.request.OrFunction;
import com.yahoo.search.grouping.request.PredefinedFunction;
import com.yahoo.search.grouping.request.RawValue;
import com.yahoo.search.grouping.request.RelevanceValue;
import com.yahoo.search.grouping.request.ReverseFunction;
import com.yahoo.search.grouping.request.SecondOfMinuteFunction;
import com.yahoo.search.grouping.request.SizeFunction;
import com.yahoo.search.grouping.request.SortFunction;
import com.yahoo.search.grouping.request.StandardDeviationAggregator;
import com.yahoo.search.grouping.request.StrCatFunction;
import com.yahoo.search.grouping.request.StrLenFunction;
import com.yahoo.search.grouping.request.StringValue;
import com.yahoo.search.grouping.request.SubFunction;
import com.yahoo.search.grouping.request.SumAggregator;
import com.yahoo.search.grouping.request.SummaryValue;
import com.yahoo.search.grouping.request.ToDoubleFunction;
import com.yahoo.search.grouping.request.ToLongFunction;
import com.yahoo.search.grouping.request.ToRawFunction;
import com.yahoo.search.grouping.request.ToStringFunction;
import com.yahoo.search.grouping.request.UcaFunction;
import com.yahoo.search.grouping.request.XorAggregator;
import com.yahoo.search.grouping.request.XorBitFunction;
import com.yahoo.search.grouping.request.XorFunction;
import com.yahoo.search.grouping.request.YearFunction;
import com.yahoo.search.grouping.request.ZCurveXFunction;
import com.yahoo.search.grouping.request.ZCurveYFunction;
import com.yahoo.searchlib.aggregation.AggregationResult;
import com.yahoo.searchlib.aggregation.AverageAggregationResult;
import com.yahoo.searchlib.aggregation.CountAggregationResult;
import com.yahoo.searchlib.aggregation.ExpressionCountAggregationResult;
import com.yahoo.searchlib.aggregation.HitsAggregationResult;
import com.yahoo.searchlib.aggregation.MaxAggregationResult;
import com.yahoo.searchlib.aggregation.MinAggregationResult;
import com.yahoo.searchlib.aggregation.StandardDeviationAggregationResult;
import com.yahoo.searchlib.aggregation.SumAggregationResult;
import com.yahoo.searchlib.aggregation.XorAggregationResult;
import com.yahoo.searchlib.expression.AddFunctionNode;
import com.yahoo.searchlib.expression.AggregationRefNode;
import com.yahoo.searchlib.expression.AndFunctionNode;
import com.yahoo.searchlib.expression.ArrayAtLookupNode;
import com.yahoo.searchlib.expression.AttributeMapLookupNode;
import com.yahoo.searchlib.expression.AttributeNode;
import com.yahoo.searchlib.expression.BucketResultNode;
import com.yahoo.searchlib.expression.CatFunctionNode;
import com.yahoo.searchlib.expression.ConstantNode;
import com.yahoo.searchlib.expression.DebugWaitFunctionNode;
import com.yahoo.searchlib.expression.DivideFunctionNode;
import com.yahoo.searchlib.expression.ExpressionNode;
import com.yahoo.searchlib.expression.FixedWidthBucketFunctionNode;
import com.yahoo.searchlib.expression.FloatBucketResultNode;
import com.yahoo.searchlib.expression.FloatBucketResultNodeVector;
import com.yahoo.searchlib.expression.FloatResultNode;
import com.yahoo.searchlib.expression.GetDocIdNamespaceSpecificFunctionNode;
import com.yahoo.searchlib.expression.IntegerBucketResultNode;
import com.yahoo.searchlib.expression.IntegerBucketResultNodeVector;
import com.yahoo.searchlib.expression.IntegerResultNode;
import com.yahoo.searchlib.expression.InterpolatedLookupNode;
import com.yahoo.searchlib.expression.MD5BitFunctionNode;
import com.yahoo.searchlib.expression.MathFunctionNode;
import com.yahoo.searchlib.expression.MaxFunctionNode;
import com.yahoo.searchlib.expression.MinFunctionNode;
import com.yahoo.searchlib.expression.ModuloFunctionNode;
import com.yahoo.searchlib.expression.MultiArgFunctionNode;
import com.yahoo.searchlib.expression.MultiplyFunctionNode;
import com.yahoo.searchlib.expression.NegateFunctionNode;
import com.yahoo.searchlib.expression.NormalizeSubjectFunctionNode;
import com.yahoo.searchlib.expression.NumElemFunctionNode;
import com.yahoo.searchlib.expression.OrFunctionNode;
import com.yahoo.searchlib.expression.RangeBucketPreDefFunctionNode;
import com.yahoo.searchlib.expression.RawBucketResultNode;
import com.yahoo.searchlib.expression.RawBucketResultNodeVector;
import com.yahoo.searchlib.expression.RawResultNode;
import com.yahoo.searchlib.expression.RelevanceNode;
import com.yahoo.searchlib.expression.ResultNodeVector;
import com.yahoo.searchlib.expression.ReverseFunctionNode;
import com.yahoo.searchlib.expression.SortFunctionNode;
import com.yahoo.searchlib.expression.StrCatFunctionNode;
import com.yahoo.searchlib.expression.StrLenFunctionNode;
import com.yahoo.searchlib.expression.StringBucketResultNode;
import com.yahoo.searchlib.expression.StringBucketResultNodeVector;
import com.yahoo.searchlib.expression.StringResultNode;
import com.yahoo.searchlib.expression.TimeStampFunctionNode;
import com.yahoo.searchlib.expression.ToFloatFunctionNode;
import com.yahoo.searchlib.expression.ToIntFunctionNode;
import com.yahoo.searchlib.expression.ToRawFunctionNode;
import com.yahoo.searchlib.expression.ToStringFunctionNode;
import com.yahoo.searchlib.expression.UcaFunctionNode;
import com.yahoo.searchlib.expression.XorBitFunctionNode;
import com.yahoo.searchlib.expression.XorFunctionNode;
import com.yahoo.searchlib.expression.ZCurveFunctionNode;

/**
 * This is a helper class for {@link RequestBuilder} that offloads the code to convert {@link GroupingExpression} type
 * objects to back-end specific expressions. This is a straightforward one-to-one conversion.
 *
 * @author Simon Thoresen Hult
 */
class ExpressionConverter {

    public static final String DEFAULT_SUMMARY_NAME = "";
    public static final int DEFAULT_TIME_OFFSET = 0;
    private String defaultSummaryName = DEFAULT_SUMMARY_NAME;
    private int timeOffset = DEFAULT_TIME_OFFSET;

    /**
     * Sets the summary name to use when converting {@link SummaryValue} that was created without an explicit name.
     *
     * @param summaryName The default summary name to use.
     * @return This, to allow chaining.
     */
    public ExpressionConverter setDefaultSummaryName(String summaryName) {
        defaultSummaryName = summaryName;
        return this;
    }

    /**
     * Sets an offset to use for all time-based grouping expressions.
     *
     * @param millis The offset in milliseconds.
     * @return This, to allow chaining.
     */
    public ExpressionConverter setTimeOffset(int millis) {
        this.timeOffset = millis / 1000;
        return this;
    }

    /**
     * Converts the given ast type grouping expression to a corresponding back-end type aggregation result.
     *
     * @param exp The expression to convert.
     * @return The corresponding back-end result.
     * @throws UnsupportedOperationException Thrown if the given expression could not be converted.
     */
    public AggregationResult toAggregationResult(GroupingExpression exp) {
        int level = exp.getLevel();
        // Is aggregating on list of groups?
        if (level > 1) {
            /*
            * The below aggregator operates on lists of groups in the query language world.
            * Internally, it operates on hits (by evaluating the group-by expression for each hit).
            * The group-by expression is passed to the aggregator by RequestBuilder.
            */
            if (exp instanceof CountAggregator) {
                return new ExpressionCountAggregationResult();
            }
            throw new UnsupportedOperationException(
                    "Can not aggregate on " + GroupingOperation.getLevelDesc(level) + ".");
        }
        if (exp instanceof AvgAggregator) {
            return new AverageAggregationResult()
                    .setExpression(toExpressionNode(((AvgAggregator)exp).getExpression()));
        }
        if (exp instanceof CountAggregator) {
            return new CountAggregationResult()
                    .setExpression(new ConstantNode(new IntegerResultNode(0)));
        }
        if (exp instanceof MaxAggregator) {
            return new MaxAggregationResult()
                    .setExpression(toExpressionNode(((MaxAggregator)exp).getExpression()));
        }
        if (exp instanceof MinAggregator) {
            return new MinAggregationResult()
                    .setExpression(toExpressionNode(((MinAggregator)exp).getExpression()));
        }
        if (exp instanceof SumAggregator) {
            return new SumAggregationResult()
                    .setExpression(toExpressionNode(((SumAggregator)exp).getExpression()));
        }
        if (exp instanceof SummaryValue) {
            String summaryName = ((SummaryValue)exp).getSummaryName();
            return new HitsAggregationResult()
                    .setSummaryClass(summaryName != null ? summaryName : defaultSummaryName)
                    .setExpression(new ConstantNode(new IntegerResultNode(0)));
        }
        if (exp instanceof StandardDeviationAggregator) {
            return new StandardDeviationAggregationResult()
                    .setExpression(toExpressionNode(((StandardDeviationAggregator) exp).getExpression()));
        }
        if (exp instanceof XorAggregator) {
            return new XorAggregationResult()
                    .setExpression(toExpressionNode(((XorAggregator)exp).getExpression()));
        }
        throw new UnsupportedOperationException("Can not convert '" + exp + "' to an aggregator.");
    }

    /**
     * Converts the given ast type grouping expression to a corresponding back-end type expression.
     *
     * @param exp The expression to convert.
     * @return The corresponding back-end expression.
     * @throws UnsupportedOperationException Thrown if the given expression could not be converted.
     */
    public ExpressionNode toExpressionNode(GroupingExpression exp) {
        if (exp instanceof AddFunction) {
            return addArguments(new AddFunctionNode(), (AddFunction)exp);
        }
        if (exp instanceof AggregatorNode) {
            return new AggregationRefNode(toAggregationResult(exp));
        }
        if (exp instanceof AndFunction) {
            return addArguments(new AndFunctionNode(), (AndFunction)exp);
        }
        if (exp instanceof AttributeMapLookupValue) {
            AttributeMapLookupValue mapLookup = (AttributeMapLookupValue) exp;
            if (mapLookup.hasKeySourceAttribute()) {
                return AttributeMapLookupNode.fromKeySourceAttribute(mapLookup.getAttributeName(),
                        mapLookup.getKeyAttribute(), mapLookup.getValueAttribute(), mapLookup.getKeySourceAttribute());
            } else {
                return AttributeMapLookupNode.fromKey(mapLookup.getAttributeName(),
                        mapLookup.getKeyAttribute(), mapLookup.getValueAttribute(), mapLookup.getKey());
            }
        }
        if (exp instanceof AttributeValue) {
            return new AttributeNode(((AttributeValue)exp).getAttributeName());
        }
        if (exp instanceof AttributeFunction) {
            return new AttributeNode(((AttributeFunction)exp).getAttributeName());
        }
        if (exp instanceof CatFunction) {
            return addArguments(new CatFunctionNode(), (CatFunction)exp);
        }
        if (exp instanceof DebugWaitFunction) {
            return new DebugWaitFunctionNode(toExpressionNode(((DebugWaitFunction)exp).getArg(0)),
                                             ((DebugWaitFunction)exp).getWaitTime(),
                                             ((DebugWaitFunction)exp).getBusyWait());
        }
        if (exp instanceof DocIdNsSpecificValue) {
            return new GetDocIdNamespaceSpecificFunctionNode();
        }
        if (exp instanceof DoubleValue) {
            return new ConstantNode(new FloatResultNode(((DoubleValue)exp).getValue()));
        }
        if (exp instanceof DivFunction) {
            return addArguments(new DivideFunctionNode(), (DivFunction)exp);
        }
        if (exp instanceof FixedWidthFunction) {
            Number w = ((FixedWidthFunction)exp).getWidth();
            return new FixedWidthBucketFunctionNode(
                    w instanceof Double ? new FloatResultNode(w.doubleValue()) : new IntegerResultNode(w.longValue()),
                    toExpressionNode(((FixedWidthFunction)exp).getArg(0)));
        }
        if (exp instanceof LongValue) {
            return new ConstantNode(new IntegerResultNode(((LongValue)exp).getValue()));
        }
        if (exp instanceof MaxFunction) {
            return addArguments(new MaxFunctionNode(), (MaxFunction)exp);
        }
        if (exp instanceof Md5Function) {
            return new MD5BitFunctionNode().setNumBits(((Md5Function)exp).getNumBits())
                                           .addArg(toExpressionNode(((Md5Function)exp).getArg(0)));
        }
        if (exp instanceof UcaFunction) {
            UcaFunction uca = (UcaFunction)exp;
            return new UcaFunctionNode(toExpressionNode(uca.getArg(0)), uca.getLocale(), uca.getStrength());
        }
        if (exp instanceof MinFunction) {
            return addArguments(new MinFunctionNode(), (MinFunction)exp);
        }
        if (exp instanceof ModFunction) {
            return addArguments(new ModuloFunctionNode(), (ModFunction)exp);
        }
        if (exp instanceof MulFunction) {
            return addArguments(new MultiplyFunctionNode(), (MulFunction)exp);
        }
        if (exp instanceof NegFunction) {
            return new NegateFunctionNode(toExpressionNode(((NegFunction)exp).getArg(0)));
        }
        if (exp instanceof NormalizeSubjectFunction) {
            return new NormalizeSubjectFunctionNode(toExpressionNode(((NormalizeSubjectFunction)exp).getArg(0)));
        }
        if (exp instanceof NowFunction) {
            return new ConstantNode(new IntegerResultNode(System.currentTimeMillis() / 1000));
        }
        if (exp instanceof OrFunction) {
            return addArguments(new OrFunctionNode(), (OrFunction)exp);
        }
        if (exp instanceof PredefinedFunction) {
            return new RangeBucketPreDefFunctionNode(toBucketList((PredefinedFunction)exp),
                                                     toExpressionNode(((PredefinedFunction)exp).getArg(0)));
        }
        if (exp instanceof RelevanceValue) {
            return new RelevanceNode();
        }
        if (exp instanceof ReverseFunction) {
            return new ReverseFunctionNode(toExpressionNode(((ReverseFunction)exp).getArg(0)));
        }
        if (exp instanceof SizeFunction) {
            return new NumElemFunctionNode(toExpressionNode(((SizeFunction)exp).getArg(0)));
        }
        if (exp instanceof SortFunction) {
            return new SortFunctionNode(toExpressionNode(((SortFunction)exp).getArg(0)));
        }
        if (exp instanceof ArrayAtLookup) {
            ArrayAtLookup aal = (ArrayAtLookup) exp;
            return new ArrayAtLookupNode(aal.getAttributeName(), toExpressionNode(aal.getIndexArgument()));
        }
        if (exp instanceof InterpolatedLookup) {
            InterpolatedLookup sarl = (InterpolatedLookup) exp;
            return new InterpolatedLookupNode(sarl.getAttributeName(), toExpressionNode(sarl.getLookupArgument()));
        }
        if (exp instanceof StrCatFunction) {
            return addArguments(new StrCatFunctionNode(), (StrCatFunction)exp);
        }
        if (exp instanceof StringValue) {
            return new ConstantNode(new StringResultNode(((StringValue)exp).getValue()));
        }
        if (exp instanceof StrLenFunction) {
            return new StrLenFunctionNode(toExpressionNode(((StrLenFunction)exp).getArg(0)));
        }
        if (exp instanceof SubFunction) {
            return toSubNode((SubFunction)exp);
        }
        if (exp instanceof ToDoubleFunction) {
            return new ToFloatFunctionNode(toExpressionNode(((ToDoubleFunction)exp).getArg(0)));
        }
        if (exp instanceof ToLongFunction) {
            return new ToIntFunctionNode(toExpressionNode(((ToLongFunction)exp).getArg(0)));
        }
        if (exp instanceof ToRawFunction) {
            return new ToRawFunctionNode(toExpressionNode(((ToRawFunction)exp).getArg(0)));
        }
        if (exp instanceof ToStringFunction) {
            return new ToStringFunctionNode(toExpressionNode(((ToStringFunction)exp).getArg(0)));
        }
        if (exp instanceof DateFunction) {
            StrCatFunctionNode ret = new StrCatFunctionNode();
            GroupingExpression arg = ((DateFunction)exp).getArg(0);
            ret.addArg(new ToStringFunctionNode(toTime(arg, TimeStampFunctionNode.TimePart.Year)));
            ret.addArg(new ConstantNode(new StringResultNode("-")));
            ret.addArg(new ToStringFunctionNode(toTime(arg, TimeStampFunctionNode.TimePart.Month)));
            ret.addArg(new ConstantNode(new StringResultNode("-")));
            ret.addArg(new ToStringFunctionNode(toTime(arg, TimeStampFunctionNode.TimePart.MonthDay)));
            return ret;
        }
        if (exp instanceof MathSqrtFunction) {
            return new MathFunctionNode(toExpressionNode(((MathSqrtFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.SQRT);
        }
        if (exp instanceof MathCbrtFunction) {
            return new MathFunctionNode(toExpressionNode(((MathCbrtFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.CBRT);
        }
        if (exp instanceof MathLogFunction) {
            return new MathFunctionNode(toExpressionNode(((MathLogFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.LOG);
        }
        if (exp instanceof MathLog1pFunction) {
            return new MathFunctionNode(toExpressionNode(((MathLog1pFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.LOG1P);
        }
        if (exp instanceof MathLog10Function) {
            return new MathFunctionNode(toExpressionNode(((MathLog10Function)exp).getArg(0)),
                                        MathFunctionNode.Function.LOG10);
        }
        if (exp instanceof MathExpFunction) {
            return new MathFunctionNode(toExpressionNode(((MathExpFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.EXP);
        }
        if (exp instanceof MathPowFunction) {
            return new MathFunctionNode(toExpressionNode(((MathPowFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.POW)
                    .addArg(toExpressionNode(((MathPowFunction)exp).getArg(1)));
        }
        if (exp instanceof MathHypotFunction) {
            return new MathFunctionNode(toExpressionNode(((MathHypotFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.HYPOT)
                    .addArg(toExpressionNode(((MathHypotFunction)exp).getArg(1)));
        }
        if (exp instanceof MathSinFunction) {
            return new MathFunctionNode(toExpressionNode(((MathSinFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.SIN);
        }
        if (exp instanceof MathASinFunction) {
            return new MathFunctionNode(toExpressionNode(((MathASinFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.ASIN);
        }
        if (exp instanceof MathCosFunction) {
            return new MathFunctionNode(toExpressionNode(((MathCosFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.COS);
        }
        if (exp instanceof MathACosFunction) {
            return new MathFunctionNode(toExpressionNode(((MathACosFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.ACOS);
        }
        if (exp instanceof MathTanFunction) {
            return new MathFunctionNode(toExpressionNode(((MathTanFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.TAN);
        }
        if (exp instanceof MathATanFunction) {
            return new MathFunctionNode(toExpressionNode(((MathATanFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.ATAN);
        }
        if (exp instanceof MathSinHFunction) {
            return new MathFunctionNode(toExpressionNode(((MathSinHFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.SINH);
        }
        if (exp instanceof MathASinHFunction) {
            return new MathFunctionNode(toExpressionNode(((MathASinHFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.ASINH);
        }
        if (exp instanceof MathCosHFunction) {
            return new MathFunctionNode(toExpressionNode(((MathCosHFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.COSH);
        }
        if (exp instanceof MathACosHFunction) {
            return new MathFunctionNode(toExpressionNode(((MathACosHFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.ACOSH);
        }
        if (exp instanceof MathTanHFunction) {
            return new MathFunctionNode(toExpressionNode(((MathTanHFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.TANH);
        }
        if (exp instanceof MathATanHFunction) {
            return new MathFunctionNode(toExpressionNode(((MathATanHFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.ATANH);
        }
        if (exp instanceof MathFloorFunction) {
            return new MathFunctionNode(toExpressionNode(((MathFloorFunction)exp).getArg(0)),
                                        MathFunctionNode.Function.FLOOR);
        }
        if (exp instanceof ZCurveXFunction) {
            return new ZCurveFunctionNode(toExpressionNode(((ZCurveXFunction)exp).getArg(0)),
                                          ZCurveFunctionNode.Dimension.X);
        }
        if (exp instanceof ZCurveYFunction) {
            return new ZCurveFunctionNode(toExpressionNode(((ZCurveYFunction)exp).getArg(0)),
                                          ZCurveFunctionNode.Dimension.Y);
        }
        if (exp instanceof DayOfMonthFunction) {
            return toTime(((DayOfMonthFunction)exp).getArg(0), TimeStampFunctionNode.TimePart.MonthDay);
        }
        if (exp instanceof DayOfWeekFunction) {
            return toTime(((DayOfWeekFunction)exp).getArg(0), TimeStampFunctionNode.TimePart.WeekDay);
        }
        if (exp instanceof DayOfYearFunction) {
            return toTime(((DayOfYearFunction)exp).getArg(0), TimeStampFunctionNode.TimePart.YearDay);
        }
        if (exp instanceof HourOfDayFunction) {
            return toTime(((HourOfDayFunction)exp).getArg(0), TimeStampFunctionNode.TimePart.Hour);
        }
        if (exp instanceof MinuteOfHourFunction) {
            return toTime(((MinuteOfHourFunction)exp).getArg(0), TimeStampFunctionNode.TimePart.Minute);
        }
        if (exp instanceof MonthOfYearFunction) {
            return toTime(((MonthOfYearFunction)exp).getArg(0), TimeStampFunctionNode.TimePart.Month);
        }
        if (exp instanceof SecondOfMinuteFunction) {
            return toTime(((SecondOfMinuteFunction)exp).getArg(0), TimeStampFunctionNode.TimePart.Second);
        }
        if (exp instanceof YearFunction) {
            return toTime(((YearFunction)exp).getArg(0), TimeStampFunctionNode.TimePart.Year);
        }
        if (exp instanceof XorFunction) {
            return addArguments(new XorFunctionNode(), (XorFunction)exp);
        }
        if (exp instanceof XorBitFunction) {
            return new XorBitFunctionNode().setNumBits(((XorBitFunction)exp).getNumBits())
                                           .addArg(toExpressionNode(((XorBitFunction)exp).getArg(0)));
        }
        throw new UnsupportedOperationException("Can not convert '" + exp + "' of class " + exp.getClass().getName() +
                                                " to an expression.");
    }

    private TimeStampFunctionNode toTime(GroupingExpression arg, TimeStampFunctionNode.TimePart timePart) {
        if (timeOffset == 0) {
            return new TimeStampFunctionNode(toExpressionNode(arg), timePart, true);
        }
        AddFunctionNode exp = new AddFunctionNode();
        exp.addArg(toExpressionNode(arg));
        exp.addArg(new ConstantNode(new IntegerResultNode(timeOffset)));
        return new TimeStampFunctionNode(exp, timePart, true);
    }

    private MultiArgFunctionNode addArguments(MultiArgFunctionNode ret, Iterable<GroupingExpression> lst) {
        for (GroupingExpression exp : lst) {
            ret.addArg(toExpressionNode(exp));
        }
        return ret;
    }

    private MultiArgFunctionNode toSubNode(Iterable<GroupingExpression> lst) {
        MultiArgFunctionNode ret = new AddFunctionNode();
        int i = 0;
        for (GroupingExpression exp : lst) {
            ExpressionNode node = toExpressionNode(exp);
            if (++i > 1) {
                node = new NegateFunctionNode(node);
            }
            ret.addArg(node);
        }
        return ret;
    }

    private ResultNodeVector toBucketList(PredefinedFunction fnc) {
        ResultNodeVector ret = null;
        for (int i = 0, len = fnc.getNumBuckets(); i < len; ++i) {
            BucketResultNode bucket = toBucket(fnc.getBucket(i));
            if (ret == null) {
                if (bucket instanceof FloatBucketResultNode) {
                    ret = new FloatBucketResultNodeVector();
                } else if (bucket instanceof IntegerBucketResultNode) {
                    ret = new IntegerBucketResultNodeVector();
                } else if (bucket instanceof RawBucketResultNode) {
                    ret = new RawBucketResultNodeVector();
                } else {
                    ret = new StringBucketResultNodeVector();
                }
            }
            ret.add(bucket);
        }
        return ret;
    }

    private BucketResultNode toBucket(GroupingExpression exp) {
        if (!(exp instanceof BucketValue)) {
            throw new UnsupportedOperationException("Can not convert '" + exp + "' to a bucket.");
        }
        ConstantValue<?> begin = ((BucketValue)exp).getFrom();
        ConstantValue<?> end = ((BucketValue)exp).getTo();
        if (begin instanceof DoubleValue || end instanceof DoubleValue) {
            return new FloatBucketResultNode(
                    begin instanceof InfiniteValue ? FloatResultNode.getNegativeInfinity().getFloat()
                                                   : Double.valueOf(begin.toString()),
                    end instanceof InfiniteValue ? FloatResultNode.getPositiveInfinity().getFloat()
                                                 : Double.valueOf(end.toString()));
        } else if (begin instanceof LongValue || end instanceof LongValue) {
            return new IntegerBucketResultNode(
                    begin instanceof InfiniteValue ? IntegerResultNode.getNegativeInfinity().getInteger()
                                                   : Long.valueOf(begin.toString()),
                    end instanceof InfiniteValue ? IntegerResultNode.getPositiveInfinity().getInteger()
                                                 : Long.valueOf(end.toString()));
        } else if (begin instanceof StringValue || end instanceof StringValue) {
            return new StringBucketResultNode(
                    begin instanceof InfiniteValue ? StringResultNode.getNegativeInfinity()
                                                   : new StringResultNode((String)begin.getValue()),
                    end instanceof InfiniteValue ? StringResultNode.getPositiveInfinity()
                                                 : new StringResultNode((String)end.getValue()));
        } else {
            return new RawBucketResultNode(
                    begin instanceof InfiniteValue ? RawResultNode.getNegativeInfinity()
                                                   : new RawResultNode(((RawValue)begin).getValue().getBytes()),
                    end instanceof InfiniteValue ? RawResultNode.getPositiveInfinity()
                                                 : new RawResultNode(((RawValue)end).getValue().getBytes()));
        }
    }
}