aboutsummaryrefslogtreecommitdiffstats
path: root/config-model/src/test/java/com/yahoo/searchdefinition/RankingExpressionInliningTestCase.java
blob: f15e5c0601274599ed731f4289fdf89b10f2110c (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
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition;

import com.yahoo.config.model.application.provider.BaseDeployLogger;
import com.yahoo.searchdefinition.derived.AttributeFields;
import com.yahoo.searchdefinition.derived.RawRankProfile;
import com.yahoo.searchdefinition.parser.ParseException;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.assertEquals;

/**
 * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
 */
public class RankingExpressionInliningTestCase extends SearchDefinitionTestCase {

    @Test
    public void testConstants() throws ParseException {
        RankProfileRegistry rankProfileRegistry = new RankProfileRegistry();
        SearchBuilder builder = new SearchBuilder(rankProfileRegistry);
        builder.importString(
                "search test {\n" +
                        "    document test { \n" +
                        "        field a type string { \n" +
                        "            indexing: index \n" +
                        "        }\n" +
                        "    }\n" +
                        "    \n" +
                        "    rank-profile parent {\n" +
                        "        constants {\n" +
                        "            p1: 7 \n" +
                        "            p2: 0 \n" +
                        "        }\n" +
                        "        first-phase {\n" +
                        "            expression: p1 + foo\n" +
                        "        }\n" +
                        "        second-phase {\n" +
                        "            expression: p2 * foo\n" +
                        "        }\n" +
                        "        macro inline foo() {\n" +
                        "            expression: 3 + p1 + p2\n" +
                        "        }\n" +
                        "    }\n" +
                        "    rank-profile child inherits parent {\n" +
                        "        first-phase {\n" +
                        "            expression: p1 + foo + baz + bar + arg(4.0)\n" +
                        "        }\n" +
                        "        constants {\n" +
                        "            p2: 2.0 \n" +
                        "        }\n" +
                        "        macro bar() {\n" +
                        "            expression: p2*p1\n" +
                        "        }\n" +
                        "        macro inline baz() {\n" +
                        "            expression: p2+p1+boz\n" +
                        "        }\n" +
                        "        macro inline boz() {\n" +
                        "            expression: 3.0\n" +
                        "        }\n" +
                        "        macro inline arg(a1) {\n" +
                        "            expression: a1*2\n" +
                        "        }\n" +
                        "    }\n" +
                        "\n" +
                        "}\n");
        builder.build();
        Search s = builder.getSearch();

        RankProfile parent = rankProfileRegistry.getRankProfile(s, "parent").compile();
        assertEquals("17.0", parent.getFirstPhaseRanking().getRoot().toString());
        assertEquals("0.0", parent.getSecondPhaseRanking().getRoot().toString());
        List<Map.Entry<String, Object>> parentRankProperties = new ArrayList<>(new RawRankProfile(parent, new AttributeFields(s)).configProperties().entrySet());
        assertEquals("rankingExpression(foo).rankingScript.part0=10.0", parentRankProperties.get(0).toString());
        assertEquals("rankingExpression(firstphase).rankingScript=17.0", parentRankProperties.get(2).toString());
        assertEquals("rankingExpression(secondphase).rankingScript=0.0", parentRankProperties.get(4).toString());

        RankProfile child = rankProfileRegistry.getRankProfile(s, "child").compile();
        assertEquals("31.0 + bar + arg(4.0)", child.getFirstPhaseRanking().getRoot().toString());
        assertEquals("24.0", child.getSecondPhaseRanking().getRoot().toString());
        List<Map.Entry<String, Object>> childRankProperties = new ArrayList<>(new RawRankProfile(child, new AttributeFields(s)).configProperties().entrySet());
        for (Object o : childRankProperties) System.out.println(o);
        assertEquals("rankingExpression(foo).rankingScript.part0=12.0", childRankProperties.get(0).toString());
        assertEquals("rankingExpression(bar).rankingScript.part1=14.0", childRankProperties.get(1).toString());
        assertEquals("rankingExpression(boz).rankingScript.part2=3.0", childRankProperties.get(2).toString());
        assertEquals("rankingExpression(baz).rankingScript.part3=9.0 + rankingExpression(boz)", childRankProperties.get(3).toString());
        assertEquals("rankingExpression(arg).rankingScript.part4=a1 * 2", childRankProperties.get(4).toString());
        assertEquals("rankingExpression(firstphase).rankingScript=31.0 + rankingExpression(bar) + rankingExpression(arg@)", censorBindingHash(childRankProperties.get(7).toString()));
        assertEquals("rankingExpression(secondphase).rankingScript=24.0", childRankProperties.get(9).toString());
    }

    /**
     * Expression evaluation has no stack so macro arguments are bound at config time creating a separate version of
     * each macro for each binding, using hashes to name the bound variants of the macro.
     * This method censors those hashes for string comparison.
     */
    private String censorBindingHash(String s) {
        StringBuilder b = new StringBuilder();
        boolean areInHash = false;
        for (int i = 0; i < s.length() ; i++) {
            char current = s.charAt(i);

            if ( ! Character.isLetterOrDigit(current)) // end of hash
                areInHash = false;

            if ( ! areInHash)
                b.append(current);

            if (current == '@') // start of hash
                areInHash = true;
        }
        return b.toString();
    }

}