blob: 15e4f0ebed303c80872b169a02800421af0ca362 [file] [log] [blame]
Eliza Margaretha39db1ab2014-04-01 10:39:54 +00001package de.ids_mannheim.korap.query.serialize;
2
Joachim Bingel6003b852014-12-18 14:20:55 +00003import de.ids_mannheim.korap.query.serialize.util.QueryException;
Michael Hanl2a30f422014-04-01 16:41:44 +00004import org.z3950.zing.cql.*;
5
Eliza Margaretha39db1ab2014-04-01 10:39:54 +00006import java.io.IOException;
Eliza Margaretha60514822014-04-23 11:32:43 +00007import java.util.ArrayList;
Eliza Margaretha39db1ab2014-04-01 10:39:54 +00008import java.util.LinkedHashMap;
9import java.util.List;
10import java.util.Map;
11
Eliza Margaretha724be112014-04-01 11:13:40 +000012/**
Michael Hanl2a30f422014-04-01 16:41:44 +000013 * @author margaretha
Eliza Margarethaf267d4a2014-05-09 11:50:23 +000014 * @date 09.05.14
Michael Hanl2a30f422014-04-01 16:41:44 +000015 */
Eliza Margaretha39db1ab2014-04-01 10:39:54 +000016public class CQLTree extends AbstractSyntaxTree {
Michael Hanl2a30f422014-04-01 16:41:44 +000017
18 private static final String VERSION_1_1 = "1.1";
Eliza Margaretha39db1ab2014-04-01 10:39:54 +000019 private static final String VERSION_1_2 = "1.2";
20 private static final String INDEX_CQL_SERVERCHOICE = "cql.serverChoice";
Michael Hanl2a30f422014-04-01 16:41:44 +000021 private static final String INDEX_WORDS = "words";
22 private static final String TERM_RELATION_CQL_1_1 = "scr";
Eliza Margaretha39db1ab2014-04-01 10:39:54 +000023 private static final String TERM_RELATION_CQL_1_2 = "=";
24 private static final String SUPPORTED_RELATION_EXACT = "exact"; // not in the doc
Eliza Margaretha60514822014-04-23 11:32:43 +000025 private static final String OPERATION_OR = "operation:or";
26 private static final String OPERATION_SEQUENCE = "operation:sequence";
Eliza Margaretha9544f582014-05-09 15:22:42 +000027 private static final String OPERATION_POSITION = "operation:position";
Eliza Margaretha39db1ab2014-04-01 10:39:54 +000028 private static final String KORAP_CONTEXT = "http://ids-mannheim.de/ns/KorAP/json-ld/v0.1/context.jsonld";
Eliza Margaretha39db1ab2014-04-01 10:39:54 +000029
Michael Hanl2a30f422014-04-01 16:41:44 +000030 private LinkedHashMap<String, Object> requestMap;
31 private String version;
32 private boolean isCaseSensitive; // default true
Eliza Margaretha60514822014-04-23 11:32:43 +000033
Michael Hanl2a30f422014-04-01 16:41:44 +000034 public CQLTree(String query) throws QueryException {
35 this(query, VERSION_1_2, true);
36 }
37
38 public CQLTree(String query, String version) throws QueryException {
Eliza Margaretha60514822014-04-23 11:32:43 +000039 this(query, version, true);
Michael Hanl2a30f422014-04-01 16:41:44 +000040 }
41
42 public CQLTree(String query, String version, boolean isCaseSensitive) throws QueryException {
Michael Hanl2a30f422014-04-01 16:41:44 +000043 this.version = version;
44 this.isCaseSensitive = isCaseSensitive;
Michael Hanlab3e9312014-04-01 18:52:21 +000045 this.requestMap = new LinkedHashMap<>();
Michael Hanl2a30f422014-04-01 16:41:44 +000046 requestMap.put("@context", KORAP_CONTEXT);
47 process(query);
48 }
49
50
51 @Override
52 public Map<String, Object> getRequestMap() {
53 return this.requestMap;
54 }
55
56 @Override
Eliza Margaretha46793572014-04-02 15:11:25 +000057 public void process(String query) throws QueryException {
58 if ((query == null) || query.isEmpty())
Eliza Margaretha6e8e1bd2014-09-22 15:27:06 +000059 throw new QueryException(301, "SRU diagnostic 27: An empty query is unsupported.");
Eliza Margaretha46793572014-04-02 15:11:25 +000060
Michael Hanl2a30f422014-04-01 16:41:44 +000061 CQLNode cqlNode = parseQuerytoCQLNode(query);
Eliza Margarethad5eafca2014-05-12 13:24:57 +000062 Map<String,Object> queryMap = parseCQLNode(cqlNode);
63 requestMap.put("query", queryMap);
64 //requestMap.put("query", sentenceWrapper(queryMap));
Michael Hanl2a30f422014-04-01 16:41:44 +000065 }
66
Eliza Margaretha9544f582014-05-09 15:22:42 +000067 private Map<String,Object> sentenceWrapper(Map<String,Object> m){
68 Map<String, Object> map = new LinkedHashMap<String,Object>();
69 map.put("@type", "korap:group");
70 map.put("operation", OPERATION_POSITION);
71 map.put("frame", "frame:contains");
72
73 Map<String, Object> sentence = new LinkedHashMap<String,Object>();
74 sentence.put("@type", "korap:span");
75 sentence.put("key", "s");
76
77 List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
78 list.add(sentence);
79 list.add(m);
80 map.put("operands", list);
81
82 return map;
83 }
84
Michael Hanl2a30f422014-04-01 16:41:44 +000085 private CQLNode parseQuerytoCQLNode(String query) throws QueryException {
86 try {
87 int compat = -1;
88 switch (version) {
89 case VERSION_1_1:
90 compat = CQLParser.V1POINT1;
91 break;
92 case VERSION_1_2:
93 compat = CQLParser.V1POINT2;
94 }
95 return new CQLParser(compat).parse(query);
96
97 } catch (CQLParseException | IOException e) {
98 throw new QueryException("Error parsing CQL");
99 }
100 }
101
Eliza Margaretha9544f582014-05-09 15:22:42 +0000102 private Map<String,Object> parseCQLNode(CQLNode node) throws QueryException {
103
Michael Hanl2a30f422014-04-01 16:41:44 +0000104 if (node instanceof CQLTermNode) {
Eliza Margaretha60514822014-04-23 11:32:43 +0000105 return parseTermNode((CQLTermNode) node);
Eliza Margaretha95ccd412014-10-01 10:29:56 +0000106 } else if (node instanceof CQLAndNode) {
107 return parseAndNode((CQLAndNode) node);
Michael Hanl2a30f422014-04-01 16:41:44 +0000108 } else if (node instanceof CQLOrNode) {
Eliza Margaretha60514822014-04-23 11:32:43 +0000109 return parseOrNode((CQLOrNode) node);
Michael Hanl2a30f422014-04-01 16:41:44 +0000110 } else {
Eliza Margaretha6e8e1bd2014-09-22 15:27:06 +0000111 throw new QueryException(105, "SRU diagnostic 48: Only basic search including term-only " +
Eliza Margaretha95ccd412014-10-01 10:29:56 +0000112 "and boolean (AND,OR) operator queries are currently supported.");
Michael Hanl2a30f422014-04-01 16:41:44 +0000113 }
114 }
115
Eliza Margaretha60514822014-04-23 11:32:43 +0000116 private Map<String,Object> parseTermNode(CQLTermNode node) throws QueryException {
Michael Hanl2a30f422014-04-01 16:41:44 +0000117 checkTermNode(node);
Eliza Margaretha39db1ab2014-04-01 10:39:54 +0000118 final String term = node.getTerm();
119 if ((term == null) || term.isEmpty()) {
Eliza Margaretha6e8e1bd2014-09-22 15:27:06 +0000120 throw new QueryException(301, "SRU diagnostic 27: An empty term is unsupported.");
Michael Hanl2a30f422014-04-01 16:41:44 +0000121 } else if (term.contains(" ")) {
Eliza Margaretha60514822014-04-23 11:32:43 +0000122 return writeSequence(term);
Michael Hanl2a30f422014-04-01 16:41:44 +0000123 } else {
Eliza Margaretha60514822014-04-23 11:32:43 +0000124 return writeTerm(term);
Eliza Margaretha39db1ab2014-04-01 10:39:54 +0000125 }
Michael Hanl2a30f422014-04-01 16:41:44 +0000126 }
127
Eliza Margaretha60514822014-04-23 11:32:43 +0000128 private Map<String,Object> parseAndNode(CQLAndNode node) throws QueryException {
Michael Hanl2a30f422014-04-01 16:41:44 +0000129 checkBooleanModifier(node);
Eliza Margaretha60514822014-04-23 11:32:43 +0000130
131 Map<String, Object> map = new LinkedHashMap<String,Object>();
132 map.put("@type", "korap:group");
133 map.put("operation", OPERATION_SEQUENCE);
Eliza Margaretha95ccd412014-10-01 10:29:56 +0000134 map.put("inOrder", false);
Eliza Margaretha60514822014-04-23 11:32:43 +0000135
136 List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
137 Map<String, Object> distanceMap = new LinkedHashMap<String,Object>();
138 distanceMap.put("@type", "korap:distance");
Eliza Margaretha95ccd412014-10-01 10:29:56 +0000139 distanceMap.put("key", "s");
Eliza Margaretha60514822014-04-23 11:32:43 +0000140 distanceMap.put("min", "0");
141 distanceMap.put("max", "0");
142 list.add(distanceMap);
143 map.put("distances", list);
144
145 List<Map<String, Object>> operandList = new ArrayList<Map<String, Object>>();
146 operandList.add(parseCQLNode(node.getLeftOperand()));
147 operandList.add(parseCQLNode(node.getRightOperand()));
148 map.put("operands", operandList);
149
150 return map;
Michael Hanl2a30f422014-04-01 16:41:44 +0000151 }
Eliza Margaretha39db1ab2014-04-01 10:39:54 +0000152
Eliza Margaretha60514822014-04-23 11:32:43 +0000153 private Map<String,Object> parseOrNode(CQLOrNode node) throws QueryException {
154 checkBooleanModifier(node);
155
156 Map<String, Object> map = new LinkedHashMap<String,Object>();
157 map.put("@type", "korap:group");
158 map.put("operation", OPERATION_OR);
159
160 List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
161 list.add(parseCQLNode(node.getLeftOperand()));
162 list.add(parseCQLNode(node.getRightOperand()));
163 map.put("operands", list);
164
165 return map;
Michael Hanl2a30f422014-04-01 16:41:44 +0000166 }
Eliza Margaretha39db1ab2014-04-01 10:39:54 +0000167
Eliza Margaretha60514822014-04-23 11:32:43 +0000168 private Map<String, Object> writeSequence(String str) {
169 Map<String, Object> sequenceMap = new LinkedHashMap<String,Object>();
170 sequenceMap.put("@type", "korap:group");
171 sequenceMap.put("operation", OPERATION_SEQUENCE);
172
173 List<Map<String, Object>> termList = new ArrayList<Map<String, Object>>();
Michael Hanl2a30f422014-04-01 16:41:44 +0000174 String[] terms = str.split(" ");
Eliza Margaretha60514822014-04-23 11:32:43 +0000175 for (String term : terms){
176 termList.add(writeTerm(term));
Eliza Margaretha39db1ab2014-04-01 10:39:54 +0000177 }
Eliza Margaretha60514822014-04-23 11:32:43 +0000178 sequenceMap.put("operands", termList);
179
180 return sequenceMap;
Michael Hanl2a30f422014-04-01 16:41:44 +0000181 }
182
Eliza Margaretha60514822014-04-23 11:32:43 +0000183 private Map<String, Object> writeTerm(String term) {
184 Map<String, Object> map = new LinkedHashMap<String,Object>();
185 map.put("@type", "korap:term");
186 if (!isCaseSensitive) {
187 map.put("caseInsensitive","true");
188 }
189 map.put("key", term);
190 map.put("layer", "orth");
191 map.put("match", "match:eq");
192
193 Map<String, Object> tokenMap = new LinkedHashMap<String,Object>();
194 tokenMap.put("@type", "korap:token");
195 tokenMap.put("wrap", map);
196 return tokenMap;
Michael Hanl2a30f422014-04-01 16:41:44 +0000197 }
198
199 private void checkBooleanModifier(CQLBooleanNode node) throws QueryException {
200 List<Modifier> modifiers = node.getModifiers();
201 if ((modifiers != null) && !modifiers.isEmpty()) {
202 Modifier modifier = modifiers.get(0);
Eliza Margaretha6e8e1bd2014-09-22 15:27:06 +0000203 throw new QueryException(105, "SRU diagnostic 20: Relation modifier " +
Michael Hanlab3e9312014-04-01 18:52:21 +0000204 modifier.toCQL() + " is not supported.");
Michael Hanl2a30f422014-04-01 16:41:44 +0000205 }
206 }
207
208 private void checkTermNode(CQLTermNode node) throws QueryException {
209 // only allow "cql.serverChoice" and "words" index
Eliza Margaretha39db1ab2014-04-01 10:39:54 +0000210 if (!(INDEX_CQL_SERVERCHOICE.equals(node.getIndex()) ||
211 INDEX_WORDS.equals(node.getIndex()))) {
Eliza Margaretha6e8e1bd2014-09-22 15:27:06 +0000212 throw new QueryException(105, "SRU diagnostic 16: Index " + node.getIndex() + " is not supported.");
Eliza Margaretha39db1ab2014-04-01 10:39:54 +0000213 }
214 // only allow "=" relation without any modifiers
215 CQLRelation relation = node.getRelation();
216 String baseRel = relation.getBase();
217 if (!(TERM_RELATION_CQL_1_1.equals(baseRel) ||
218 TERM_RELATION_CQL_1_2.equals(baseRel) ||
219 SUPPORTED_RELATION_EXACT.equals(baseRel))) {
Eliza Margaretha6e8e1bd2014-09-22 15:27:06 +0000220 throw new QueryException(105, "SRU diagnostic 19: Relation " +
Michael Hanlab3e9312014-04-01 18:52:21 +0000221 relation.getBase() + " is not supported.");
Eliza Margaretha39db1ab2014-04-01 10:39:54 +0000222 }
223 List<Modifier> modifiers = relation.getModifiers();
224 if ((modifiers != null) && !modifiers.isEmpty()) {
225 Modifier modifier = modifiers.get(0);
Eliza Margaretha6e8e1bd2014-09-22 15:27:06 +0000226 throw new QueryException(105, "SRU diagnostic 20: Relation modifier " +
Michael Hanlab3e9312014-04-01 18:52:21 +0000227 modifier.getValue() + " is not supported.");
Eliza Margaretha39db1ab2014-04-01 10:39:54 +0000228 }
Michael Hanl2a30f422014-04-01 16:41:44 +0000229 }
Eliza Margaretha39db1ab2014-04-01 10:39:54 +0000230
Eliza Margaretha39db1ab2014-04-01 10:39:54 +0000231}