aboutsummaryrefslogtreecommitdiffstats
path: root/container-search/src/main/java/com/yahoo/prelude/semantics/rule/ReferenceTermProduction.java
blob: 6bf082ec27009209bc35aff4f27f7c8bd1e73805 (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
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.prelude.semantics.rule;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import com.yahoo.prelude.query.Item;
import com.yahoo.prelude.query.PhraseItem;
import com.yahoo.prelude.query.TermType;
import com.yahoo.prelude.semantics.engine.EvaluationException;
import com.yahoo.prelude.semantics.engine.Match;
import com.yahoo.prelude.semantics.engine.ReferencedMatches;
import com.yahoo.prelude.semantics.engine.RuleEvaluation;

/**
 * A term produced by a production rule which takes its actual term value
 * from one or more terms matched in the condition
 *
 * @author bratseth
 */
public class ReferenceTermProduction extends TermProduction {

    private final String reference;
    private final boolean produceAll;

    /**
     * Creates a new produced reference term
     *
     * @param reference the label of the condition this should take it's value from
     */
    public ReferenceTermProduction(String reference, boolean produceAll) {
        super();
        this.reference = Objects.requireNonNull(reference, "Reference cannot be null");
        this.produceAll = produceAll;
    }

    /**
     * Creates a new produced reference term
     *
     * @param reference the label of the condition this should take its value from
     * @param termType the type of the term to produce
     */
    public ReferenceTermProduction(String reference, TermType termType, boolean produceAll) {
        super(termType);
        this.reference = Objects.requireNonNull(reference, "Reference cannot be null");
        this.produceAll = produceAll;
    }

    /**
     * Creates a new produced reference term
     *
     * @param label the label of the produced term
     * @param reference the label of the condition this should take its value from
     */
    public ReferenceTermProduction(String label, String reference, boolean produceAll) {
        super(label);
        this.reference = Objects.requireNonNull(reference, "Reference cannot be null");
        this.produceAll = produceAll;
    }

    /**
     * Creates a new produced reference term
     *
     * @param label the label of the produced term
     * @param reference the label of the condition this should take its value from
     * @param termType the type of term to produce
     */
    public ReferenceTermProduction(String label, String reference, TermType termType, boolean produceAll) {
        super(label, termType);
        this.reference = Objects.requireNonNull(reference, "Reference cannot be null");
        this.produceAll = produceAll;
    }

    /** Returns the label of the condition this should take its value from, never null */
    public String getReference() { return reference; }

    public boolean producesAll() { return produceAll; }

    @Override
    void addMatchReferences(Set<String> matchReferences) {
        matchReferences.add(reference);
    }

    public void produce(RuleEvaluation e, int ignored) {
        ReferencedMatches referencedMatches = e.getReferencedMatches(reference);
        if (referencedMatches == null)
            throw new EvaluationException("Referred match '" + reference + "' not found");
        if (replacing) {
            e.removeMatches(referencedMatches);
        }

        var match = referencedMatches.matchIterator().next();
        if (produceAll) {
            // produce all terms in the condition
            NamedCondition namedCondition = e.getEvaluation().ruleBase().getCondition(referencedMatches.getContextName());
            ChoiceCondition choices = (ChoiceCondition)namedCondition.getCondition();
            List<Item> items = new ArrayList<>();
            for (Iterator<Condition> i = choices.conditionIterator(); i.hasNext();) {
                Condition condition = i.next();
                if (condition instanceof TermCondition) {
                    items.add(match.toItem(getLabel(), ((TermCondition)condition).term()));
                }
                else if (condition instanceof SequenceCondition) {
                    PhraseItem phrase = new PhraseItem(getLabel());
                    for (var term : ((SequenceCondition)condition).conditions())
                        phrase.addItem(match.toItem(getLabel(), ((TermCondition)term).term()));
                    items.add(phrase);
                }
                else {
                    // Until we validate this at construction time
                    throw new EvaluationException("Could not produce all terms in " + namedCondition + " as it is " +
                                                  "not a term or sequence condition");
                }
            }
            produce(e, match, items, 0);
        }
        else {
            // produce just the matching term
            produce(e, match, List.of(referencedMatches.toItem(getLabel())), 0);
        }
    }

    private void produce(RuleEvaluation e, Match match, List<Item> items, int offset) {
        if (replacing)
            insertMatch(e, match, items, offset);
        else
            e.addItems(items, getTermType());
    }

    @Override
    public String toInnerTermString() {
        return getLabelString() + "[" + reference + "]";
    }

}