aboutsummaryrefslogtreecommitdiffstats
path: root/container-search/src/main/java/com/yahoo/search/querytransform/QueryCombinator.java
blob: 67cb291a76054bab78e4ac522cecd3d9489c3c09 (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
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.querytransform;

import com.yahoo.component.ComponentId;
import com.yahoo.language.Language;
import com.yahoo.log.LogLevel;
import com.yahoo.prelude.Index;
import com.yahoo.prelude.IndexFacts;
import com.yahoo.prelude.query.AndItem;
import com.yahoo.prelude.query.CompositeItem;
import com.yahoo.prelude.query.IndexedItem;
import com.yahoo.prelude.query.Item;
import com.yahoo.prelude.query.NullItem;
import com.yahoo.prelude.query.parser.CustomParser;
import com.yahoo.search.Query;
import com.yahoo.search.Result;
import com.yahoo.search.Searcher;
import com.yahoo.search.query.Properties;
import com.yahoo.search.query.QueryTree;
import com.yahoo.search.query.parser.ParserEnvironment;
import com.yahoo.search.query.parser.ParserFactory;
import com.yahoo.search.searchchain.Execution;
import com.yahoo.yolean.Exceptions;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * <p>A searcher which grabs query parameters of the form "defidx.(identifier)=(index name)" and
 * "query.(identifier)=(user query)", * parses them and adds them as AND items to the query root.</p>
 *
 * <p>If the given default index does not exist in the search definition, the query part will be parsed with the
 * settings of the default index set to the "".</p>
 *
 * @author Steinar Knutsen
 * @deprecated use YQL
 */
// TODO: Remove on Vespa 7
@Deprecated // OK
public class QueryCombinator extends Searcher {

    private static final String QUERYPREFIX = "query.";

    private static class QueryPart {
        final String query;
        final String defaultIndex;

        QueryPart(String query, String defaultIndex) {
            this.query = query;
            this.defaultIndex = defaultIndex;
        }
    }

    public QueryCombinator(ComponentId id) {
        super(id);
    }

    @Override
    public Result search(Query query, Execution execution) {
        Set<QueryPart> pieces = findQuerySnippets(query.properties());
        if (pieces.size() == 0) {
            return execution.search(query);
        }
        addAndItems(query, pieces, execution.context());
        query.trace("Adding extra query parts.", true, 2);
        return execution.search(query);
    }

    private void addAndItems(Query query, Iterable<QueryPart> pieces, Execution.Context context) {
        IndexFacts indexFacts = context.getIndexFacts();
        IndexFacts.Session session = indexFacts.newSession(query);
        Set<String> usedSources = new HashSet<>(session.documentTypes());
        Language language = query.getModel().getParsingLanguage();
        for (QueryPart part : pieces) {
            String defaultIndex;
            Item item = null;
            Index index = session.getIndex(part.defaultIndex);
            if (index == Index.nullIndex) {
                defaultIndex = null;
            } else {
                defaultIndex = part.defaultIndex;
            }
            try {
                CustomParser parser = (CustomParser)ParserFactory.newInstance(query.getModel().getType(),
                                                                              ParserEnvironment.fromExecutionContext(context));
                item = parser.parse(part.query, null, language, usedSources, indexFacts, defaultIndex);
            } catch (RuntimeException e) {
                String err = Exceptions.toMessageString(e);
                query.trace("Query parser threw an exception: " + err, true, 1);
                getLogger().log(LogLevel.WARNING,
                        "Query parser threw exception searcher QueryCombinator for "
                        + query.getHttpRequest().toString() + ", query part " + part.query + ": " + err);
            }
            if (item == null) {
                continue;
            }
            if (defaultIndex == null) {
                assignDefaultIndex(item, part.defaultIndex);
            }
            addAndItem(query.getModel().getQueryTree(), item);
        }
    }

    private static void addAndItem(QueryTree queryTree, Item item) {
        Item root = queryTree.getRoot();
        // JavaDoc claims I can get null, code gives NullItem... well, well, well...
        if (root instanceof NullItem || root == null) {
            queryTree.setRoot(item);
        } else if (root.getClass() == AndItem.class) {
            ((AndItem) root).addItem(item);
        } else {
            AndItem newRoot = new AndItem();
            newRoot.addItem(root);
            newRoot.addItem(item);
            queryTree.setRoot(newRoot);
        }
    }

    private static void assignDefaultIndex(Item item, String defaultIndex) {
        if (item instanceof IndexedItem) {
            IndexedItem indexName = (IndexedItem) item;

            if ("".equals(indexName.getIndexName())) {
                indexName.setIndexName(defaultIndex);
            }
        } else if (item instanceof CompositeItem) {
            Iterator<Item> items = ((CompositeItem) item).getItemIterator();
            while (items.hasNext()) {
                Item i = items.next();
                assignDefaultIndex(i, defaultIndex);
            }
        }
    }

    private static Set<QueryPart> findQuerySnippets(Properties properties) {
        Set<QueryPart> pieces = new HashSet<>();
        for (Map.Entry<String, Object> k : properties.listProperties().entrySet()) {
            String key = k.getKey();
            if (!key.startsWith(QUERYPREFIX)) {
                continue;
            }
            String name = key.substring(QUERYPREFIX.length());
            if (hasDots(name)) {
                continue;
            }
            String index = properties.getString("defidx." + name);
            pieces.add(new QueryPart(k.getValue().toString(), index));
        }
        return pieces;
    }

    private static boolean hasDots(String name) {
        int index = name.indexOf('.', 0);
        return index != -1;
    }
}