-/*******************************************************************************\r
- * Copyright (c) 2014, 2015 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * Semantum Oy - initial API and implementation\r
- * Semantum Oy - improvements for Simantics issue #6053\r
- *******************************************************************************/\r
-package org.simantics.db.indexing;\r
-\r
-import java.io.Reader;\r
-import java.util.HashMap;\r
-import java.util.Map;\r
-import java.util.concurrent.atomic.AtomicReference;\r
-\r
-import org.apache.lucene.analysis.Analyzer;\r
-import org.apache.lucene.analysis.Tokenizer;\r
-import org.apache.lucene.analysis.core.KeywordAnalyzer;\r
-import org.apache.lucene.analysis.core.WhitespaceAnalyzer;\r
-import org.apache.lucene.analysis.miscellaneous.PerFieldAnalyzerWrapper;\r
-import org.apache.lucene.analysis.util.CharTokenizer;\r
-import org.apache.lucene.queryparser.classic.ParseException;\r
-import org.apache.lucene.queryparser.classic.QueryParser;\r
-import org.apache.lucene.search.NumericRangeQuery;\r
-import org.apache.lucene.search.Query;\r
-import org.apache.lucene.util.AttributeFactory;\r
-import org.apache.lucene.util.Version;\r
-import org.simantics.databoard.util.ObjectUtils;\r
-import org.simantics.utils.datastructures.Pair;\r
-\r
-public class Queries {\r
-\r
- static final class LowerCaseWhitespaceTokenizer extends CharTokenizer {\r
- /**\r
- * Construct a new WhitespaceTokenizer. * @param matchVersion Lucene version\r
- * to match See {@link <a href="#version">above</a>}\r
- * \r
- * @param in\r
- * the input to split up into tokens\r
- */\r
- public LowerCaseWhitespaceTokenizer(Version matchVersion, Reader in) {\r
- super(matchVersion, in);\r
- }\r
-\r
- /**\r
- * Construct a new WhitespaceTokenizer using a given\r
- * {@link org.apache.lucene.util.AttributeSource.AttributeFactory}.\r
- *\r
- * @param\r
- * matchVersion Lucene version to match See\r
- * {@link <a href="#version">above</a>}\r
- * @param factory\r
- * the attribute factory to use for this {@link Tokenizer}\r
- * @param in\r
- * the input to split up into tokens\r
- */\r
- public LowerCaseWhitespaceTokenizer(Version matchVersion, AttributeFactory factory, Reader in) {\r
- super(matchVersion, factory, in);\r
- }\r
-\r
- @Override\r
- protected int normalize(int c) {\r
- return Character.toLowerCase(c);\r
- }\r
-\r
- protected boolean isTokenChar(int c) {\r
- return !Character.isWhitespace(c);\r
- }\r
- }\r
-\r
- static final class LowerCaseWhitespaceAnalyzer extends Analyzer {\r
-\r
- private final Version matchVersion;\r
-\r
- /**\r
- * Creates a new {@link WhitespaceAnalyzer}\r
- * @param matchVersion Lucene version to match See {@link <a href="#version">above</a>}\r
- */\r
- public LowerCaseWhitespaceAnalyzer(Version matchVersion) {\r
- this.matchVersion = matchVersion;\r
- }\r
-\r
- @Override\r
- protected TokenStreamComponents createComponents(final String fieldName, final Reader reader) {\r
- return new TokenStreamComponents(new LowerCaseWhitespaceTokenizer(matchVersion, reader));\r
- }\r
- }\r
-\r
- private static AtomicReference<Pair<Query, String>> queryCache = new AtomicReference<>();\r
-\r
- final static PerFieldAnalyzerWrapper analyzer = createAnalyzer();\r
- \r
- static PerFieldAnalyzerWrapper createAnalyzer() {\r
- \r
- Map<String,Analyzer> analyzerPerField = new HashMap<>();\r
- analyzerPerField.put("Model", new KeywordAnalyzer());\r
- analyzerPerField.put("Parent", new KeywordAnalyzer());\r
- analyzerPerField.put("Resource", new KeywordAnalyzer());\r
- analyzerPerField.put("GUID", new KeywordAnalyzer());\r
- \r
- PerFieldAnalyzerWrapper analyzer = new PerFieldAnalyzerWrapper(new LowerCaseWhitespaceAnalyzer(Version.LUCENE_4_9), analyzerPerField);\r
- return analyzer;\r
- \r
- }\r
-\r
- static PerFieldAnalyzerWrapper getAnalyzer() {\r
- return analyzer;\r
- }\r
-\r
- static Query parse(String search, IndexSchema schema) throws ParseException {\r
- Pair<Query, String> cachedQuery = queryCache.get();\r
- if (cachedQuery != null && search.equals(cachedQuery.second))\r
- return cachedQuery.first;\r
-\r
- //System.err.println("parse " + search + " (cached=" + (cachedQuery != null ? cachedQuery.second : "null") + ")" );\r
- CustomQueryParser parser = new CustomQueryParser(Version.LUCENE_4_9, "Name", getAnalyzer(), schema);\r
- Query query = parser.parse(search);\r
-\r
- queryCache.set(Pair.make(query, search));\r
- return query;\r
- }\r
-\r
-\r
- public static class CustomQueryParser extends QueryParser {\r
-\r
- protected final IndexSchema schema;\r
-\r
- public CustomQueryParser(Version version, String field, Analyzer analyzer, IndexSchema schema) {\r
- super(version, field, analyzer);\r
- this.schema = schema;\r
- setAllowLeadingWildcard(true);\r
- }\r
-\r
- @Override\r
- protected Query getRangeQuery(\r
- String field,\r
- String part1,\r
- String part2,\r
- boolean startInclusive,\r
- boolean endInclusive) throws ParseException\r
- {\r
- IndexSchema.Type type = schema.typeMap.get(field);\r
- if (IndexSchema.NUMERIC_TYPES.contains(type)) {\r
- boolean equalParts = ObjectUtils.objectEquals(part1, part2);\r
- try {\r
- switch (type) {\r
- case INT: {\r
- Integer min = part1 != null ? ( Integer.valueOf(part1)) : null;\r
- Integer max = part2 != null ? (equalParts ? min : Integer.valueOf(part2)) : null;\r
- return NumericRangeQuery.newIntRange(field, min, max, startInclusive, endInclusive);\r
- }\r
- case LONG: {\r
- Long min = part1 != null ? ( Long.valueOf(part1)) : null;\r
- Long max = part2 != null ? (equalParts ? min : Long.valueOf(part2)) : null;\r
- return NumericRangeQuery.newLongRange(field, min, max, startInclusive, endInclusive);\r
- }\r
- case FLOAT: {\r
- Float min = part1 != null ? ( Float.valueOf(part1)) : null;\r
- Float max = part2 != null ? (equalParts ? min : Float.valueOf(part2)) : null;\r
- return NumericRangeQuery.newFloatRange(field, min, max, startInclusive, endInclusive);\r
- }\r
- case DOUBLE: {\r
- Double min = part1 != null ? ( Double.valueOf(part1)) : null;\r
- Double max = part2 != null ? (equalParts ? min : Double.valueOf(part2)) : null;\r
- return NumericRangeQuery.newDoubleRange(field, min, max, startInclusive, endInclusive);\r
- }\r
- default:\r
- throw new ParseException("Unrecognized numeric field type '" + type + "' for field '" + field + "'"); \r
- }\r
- } catch (NumberFormatException e) {\r
- throw new ParseException(e.getMessage()); \r
- }\r
- }\r
- return super.getRangeQuery(field, part1, part2, startInclusive, endInclusive); \r
- } \r
-\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2014, 2015 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Semantum Oy - initial API and implementation
+ * Semantum Oy - improvements for Simantics issue #6053
+ *******************************************************************************/
+package org.simantics.db.indexing;
+
+import java.io.Reader;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.Tokenizer;
+import org.apache.lucene.analysis.core.KeywordAnalyzer;
+import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
+import org.apache.lucene.analysis.miscellaneous.PerFieldAnalyzerWrapper;
+import org.apache.lucene.analysis.util.CharTokenizer;
+import org.apache.lucene.queryparser.classic.ParseException;
+import org.apache.lucene.queryparser.classic.QueryParser;
+import org.apache.lucene.search.NumericRangeQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.util.AttributeFactory;
+import org.apache.lucene.util.Version;
+import org.simantics.databoard.util.ObjectUtils;
+import org.simantics.utils.datastructures.Pair;
+
+public class Queries {
+
+ static final class LowerCaseWhitespaceTokenizer extends CharTokenizer {
+ /**
+ * Construct a new WhitespaceTokenizer. * @param matchVersion Lucene version
+ * to match See {@link <a href="#version">above</a>}
+ *
+ * @param in
+ * the input to split up into tokens
+ */
+ public LowerCaseWhitespaceTokenizer(Version matchVersion, Reader in) {
+ super(matchVersion, in);
+ }
+
+ /**
+ * Construct a new WhitespaceTokenizer using a given
+ * {@link org.apache.lucene.util.AttributeSource.AttributeFactory}.
+ *
+ * @param
+ * matchVersion Lucene version to match See
+ * {@link <a href="#version">above</a>}
+ * @param factory
+ * the attribute factory to use for this {@link Tokenizer}
+ * @param in
+ * the input to split up into tokens
+ */
+ public LowerCaseWhitespaceTokenizer(Version matchVersion, AttributeFactory factory, Reader in) {
+ super(matchVersion, factory, in);
+ }
+
+ @Override
+ protected int normalize(int c) {
+ return Character.toLowerCase(c);
+ }
+
+ protected boolean isTokenChar(int c) {
+ return !Character.isWhitespace(c);
+ }
+ }
+
+ static final class LowerCaseWhitespaceAnalyzer extends Analyzer {
+
+ private final Version matchVersion;
+
+ /**
+ * Creates a new {@link WhitespaceAnalyzer}
+ * @param matchVersion Lucene version to match See {@link <a href="#version">above</a>}
+ */
+ public LowerCaseWhitespaceAnalyzer(Version matchVersion) {
+ this.matchVersion = matchVersion;
+ }
+
+ @Override
+ protected TokenStreamComponents createComponents(final String fieldName, final Reader reader) {
+ return new TokenStreamComponents(new LowerCaseWhitespaceTokenizer(matchVersion, reader));
+ }
+ }
+
+ private static AtomicReference<Pair<Query, String>> queryCache = new AtomicReference<>();
+
+ final static PerFieldAnalyzerWrapper analyzer = createAnalyzer();
+
+ static PerFieldAnalyzerWrapper createAnalyzer() {
+
+ Map<String,Analyzer> analyzerPerField = new HashMap<>();
+ analyzerPerField.put("Model", new KeywordAnalyzer());
+ analyzerPerField.put("Parent", new KeywordAnalyzer());
+ analyzerPerField.put("Resource", new KeywordAnalyzer());
+ analyzerPerField.put("GUID", new KeywordAnalyzer());
+
+ PerFieldAnalyzerWrapper analyzer = new PerFieldAnalyzerWrapper(new LowerCaseWhitespaceAnalyzer(Version.LUCENE_4_9), analyzerPerField);
+ return analyzer;
+
+ }
+
+ static PerFieldAnalyzerWrapper getAnalyzer() {
+ return analyzer;
+ }
+
+ static Query parse(String search, IndexSchema schema) throws ParseException {
+ Pair<Query, String> cachedQuery = queryCache.get();
+ if (cachedQuery != null && search.equals(cachedQuery.second))
+ return cachedQuery.first;
+
+ //System.err.println("parse " + search + " (cached=" + (cachedQuery != null ? cachedQuery.second : "null") + ")" );
+ CustomQueryParser parser = new CustomQueryParser(Version.LUCENE_4_9, "Name", getAnalyzer(), schema);
+ Query query = parser.parse(search);
+
+ queryCache.set(Pair.make(query, search));
+ return query;
+ }
+
+
+ public static class CustomQueryParser extends QueryParser {
+
+ protected final IndexSchema schema;
+
+ public CustomQueryParser(Version version, String field, Analyzer analyzer, IndexSchema schema) {
+ super(version, field, analyzer);
+ this.schema = schema;
+ setAllowLeadingWildcard(true);
+ }
+
+ @Override
+ protected Query getRangeQuery(
+ String field,
+ String part1,
+ String part2,
+ boolean startInclusive,
+ boolean endInclusive) throws ParseException
+ {
+ IndexSchema.Type type = schema.typeMap.get(field);
+ if (IndexSchema.NUMERIC_TYPES.contains(type)) {
+ boolean equalParts = ObjectUtils.objectEquals(part1, part2);
+ try {
+ switch (type) {
+ case INT: {
+ Integer min = part1 != null ? ( Integer.valueOf(part1)) : null;
+ Integer max = part2 != null ? (equalParts ? min : Integer.valueOf(part2)) : null;
+ return NumericRangeQuery.newIntRange(field, min, max, startInclusive, endInclusive);
+ }
+ case LONG: {
+ Long min = part1 != null ? ( Long.valueOf(part1)) : null;
+ Long max = part2 != null ? (equalParts ? min : Long.valueOf(part2)) : null;
+ return NumericRangeQuery.newLongRange(field, min, max, startInclusive, endInclusive);
+ }
+ case FLOAT: {
+ Float min = part1 != null ? ( Float.valueOf(part1)) : null;
+ Float max = part2 != null ? (equalParts ? min : Float.valueOf(part2)) : null;
+ return NumericRangeQuery.newFloatRange(field, min, max, startInclusive, endInclusive);
+ }
+ case DOUBLE: {
+ Double min = part1 != null ? ( Double.valueOf(part1)) : null;
+ Double max = part2 != null ? (equalParts ? min : Double.valueOf(part2)) : null;
+ return NumericRangeQuery.newDoubleRange(field, min, max, startInclusive, endInclusive);
+ }
+ default:
+ throw new ParseException("Unrecognized numeric field type '" + type + "' for field '" + field + "'");
+ }
+ } catch (NumberFormatException e) {
+ throw new ParseException(e.getMessage());
+ }
+ }
+ return super.getRangeQuery(field, part1, part2, startInclusive, endInclusive);
+ }
+
+ }
+
+}