| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.lucene.luke.app.desktop.components.fragments.search; |
| |
| import javax.swing.BorderFactory; |
| import javax.swing.BoxLayout; |
| import javax.swing.ButtonGroup; |
| import javax.swing.DefaultCellEditor; |
| import javax.swing.JCheckBox; |
| import javax.swing.JComboBox; |
| import javax.swing.JFormattedTextField; |
| import javax.swing.JLabel; |
| import javax.swing.JPanel; |
| import javax.swing.JRadioButton; |
| import javax.swing.JScrollPane; |
| import javax.swing.JSeparator; |
| import javax.swing.JTable; |
| import javax.swing.JTextField; |
| import javax.swing.ListSelectionModel; |
| import java.awt.Color; |
| import java.awt.FlowLayout; |
| import java.awt.GridLayout; |
| import java.awt.event.ActionEvent; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.TimeZone; |
| |
| import org.apache.lucene.document.DateTools; |
| import org.apache.lucene.luke.app.desktop.components.ComponentOperatorRegistry; |
| import org.apache.lucene.luke.app.desktop.components.TableColumnInfo; |
| import org.apache.lucene.luke.app.desktop.components.TableModelBase; |
| import org.apache.lucene.luke.app.desktop.util.MessageUtils; |
| import org.apache.lucene.luke.app.desktop.util.TableUtils; |
| import org.apache.lucene.luke.models.search.QueryParserConfig; |
| |
| /** Provider of the QueryParser pane (tab) */ |
| public final class QueryParserPaneProvider implements QueryParserTabOperator { |
| |
| private final JRadioButton standardRB = new JRadioButton(); |
| |
| private final JRadioButton classicRB = new JRadioButton(); |
| |
| private final JComboBox<String> dfCB = new JComboBox<>(); |
| |
| private final JComboBox<String> defOpCombo = new JComboBox<>(new String[]{QueryParserConfig.Operator.OR.name(), QueryParserConfig.Operator.AND.name()}); |
| |
| private final JCheckBox posIncCB = new JCheckBox(); |
| |
| private final JCheckBox wildCardCB = new JCheckBox(); |
| |
| private final JCheckBox splitWSCB = new JCheckBox(); |
| |
| private final JCheckBox genPhraseQueryCB = new JCheckBox(); |
| |
| private final JCheckBox genMultiTermSynonymsPhraseQueryCB = new JCheckBox(); |
| |
| private final JFormattedTextField slopFTF = new JFormattedTextField(); |
| |
| private final JFormattedTextField minSimFTF = new JFormattedTextField(); |
| |
| private final JFormattedTextField prefLenFTF = new JFormattedTextField(); |
| |
| private final JComboBox<String> dateResCB = new JComboBox<>(); |
| |
| private final JTextField locationTF = new JTextField(); |
| |
| private final JTextField timezoneTF = new JTextField(); |
| |
| private final JTable pointRangeQueryTable = new JTable(); |
| |
| private final ListenerFunctions listeners = new ListenerFunctions(); |
| |
| private final QueryParserConfig config = new QueryParserConfig.Builder().build(); |
| |
| public QueryParserPaneProvider() { |
| ComponentOperatorRegistry.getInstance().register(QueryParserTabOperator.class, this); |
| } |
| |
| public JScrollPane get() { |
| JPanel panel = new JPanel(); |
| panel.setOpaque(false); |
| panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); |
| panel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); |
| |
| panel.add(initSelectParserPane()); |
| panel.add(new JSeparator(JSeparator.HORIZONTAL)); |
| panel.add(initParserSettingsPanel()); |
| panel.add(new JSeparator(JSeparator.HORIZONTAL)); |
| panel.add(initPhraseQuerySettingsPanel()); |
| panel.add(new JSeparator(JSeparator.HORIZONTAL)); |
| panel.add(initFuzzyQuerySettingsPanel()); |
| panel.add(new JSeparator(JSeparator.HORIZONTAL)); |
| panel.add(initDateRangeQuerySettingsPanel()); |
| panel.add(new JSeparator(JSeparator.HORIZONTAL)); |
| panel.add(initPointRangeQuerySettingsPanel()); |
| |
| JScrollPane scrollPane = new JScrollPane(panel); |
| scrollPane.setOpaque(false); |
| scrollPane.getViewport().setOpaque(false); |
| return scrollPane; |
| } |
| |
| private JPanel initSelectParserPane() { |
| JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| panel.setOpaque(false); |
| |
| standardRB.setText("StandardQueryParser"); |
| standardRB.setSelected(true); |
| standardRB.addActionListener(listeners::selectStandardQParser); |
| standardRB.setOpaque(false); |
| |
| classicRB.setText("Classic QueryParser"); |
| classicRB.addActionListener(listeners::selectClassicQparser); |
| classicRB.setOpaque(false); |
| |
| ButtonGroup group = new ButtonGroup(); |
| group.add(standardRB); |
| group.add(classicRB); |
| |
| panel.add(standardRB); |
| panel.add(classicRB); |
| |
| return panel; |
| } |
| |
| private JPanel initParserSettingsPanel() { |
| JPanel panel = new JPanel(new GridLayout(3, 2)); |
| panel.setOpaque(false); |
| |
| JPanel defField = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| defField.setOpaque(false); |
| JLabel dfLabel = new JLabel(MessageUtils.getLocalizedMessage("search_parser.label.df")); |
| defField.add(dfLabel); |
| defField.add(dfCB); |
| panel.add(defField); |
| |
| JPanel defOp = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| defOp.setOpaque(false); |
| JLabel defOpLabel = new JLabel(MessageUtils.getLocalizedMessage("search_parser.label.dop")); |
| defOp.add(defOpLabel); |
| defOpCombo.setSelectedItem(config.getDefaultOperator().name()); |
| defOp.add(defOpCombo); |
| panel.add(defOp); |
| |
| posIncCB.setText(MessageUtils.getLocalizedMessage("search_parser.checkbox.pos_incr")); |
| posIncCB.setSelected(config.isEnablePositionIncrements()); |
| posIncCB.setOpaque(false); |
| panel.add(posIncCB); |
| |
| wildCardCB.setText(MessageUtils.getLocalizedMessage("search_parser.checkbox.lead_wildcard")); |
| wildCardCB.setSelected(config.isAllowLeadingWildcard()); |
| wildCardCB.setOpaque(false); |
| panel.add(wildCardCB); |
| |
| splitWSCB.setText(MessageUtils.getLocalizedMessage("search_parser.checkbox.split_ws")); |
| splitWSCB.setEnabled(config.isSplitOnWhitespace()); |
| splitWSCB.addActionListener(listeners::toggleSplitOnWhiteSpace); |
| splitWSCB.setOpaque(false); |
| panel.add(splitWSCB); |
| |
| return panel; |
| } |
| |
| private JPanel initPhraseQuerySettingsPanel() { |
| JPanel panel = new JPanel(); |
| panel.setOpaque(false); |
| panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); |
| |
| JPanel header = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| header.setOpaque(false); |
| header.add(new JLabel(MessageUtils.getLocalizedMessage("search_parser.label.phrase_query"))); |
| panel.add(header); |
| |
| JPanel genPQ = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| genPQ.setOpaque(false); |
| genPQ.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0)); |
| genPhraseQueryCB.setText(MessageUtils.getLocalizedMessage("search_parser.checkbox.gen_pq")); |
| genPhraseQueryCB.setEnabled(config.isAutoGeneratePhraseQueries()); |
| genPhraseQueryCB.setOpaque(false); |
| genPQ.add(genPhraseQueryCB); |
| panel.add(genPQ); |
| |
| JPanel genMTPQ = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| genMTPQ.setOpaque(false); |
| genMTPQ.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0)); |
| genMultiTermSynonymsPhraseQueryCB.setText(MessageUtils.getLocalizedMessage("search_parser.checkbox.gen_mts")); |
| genMultiTermSynonymsPhraseQueryCB.setEnabled(config.isAutoGenerateMultiTermSynonymsPhraseQuery()); |
| genMultiTermSynonymsPhraseQueryCB.setOpaque(false); |
| genMTPQ.add(genMultiTermSynonymsPhraseQueryCB); |
| panel.add(genMTPQ); |
| |
| JPanel slop = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| slop.setOpaque(false); |
| slop.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0)); |
| JLabel slopLabel = new JLabel(MessageUtils.getLocalizedMessage("search_parser.label.phrase_slop")); |
| slop.add(slopLabel); |
| slopFTF.setColumns(5); |
| slopFTF.setValue(config.getPhraseSlop()); |
| slop.add(slopFTF); |
| slop.add(new JLabel(MessageUtils.getLocalizedMessage("label.int_required"))); |
| panel.add(slop); |
| |
| return panel; |
| } |
| |
| private JPanel initFuzzyQuerySettingsPanel() { |
| JPanel panel = new JPanel(); |
| panel.setOpaque(false); |
| panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); |
| |
| JPanel header = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| header.setOpaque(false); |
| header.add(new JLabel(MessageUtils.getLocalizedMessage("search_parser.label.fuzzy_query"))); |
| panel.add(header); |
| |
| JPanel minSim = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| minSim.setOpaque(false); |
| minSim.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0)); |
| JLabel minSimLabel = new JLabel(MessageUtils.getLocalizedMessage("search_parser.label.fuzzy_minsim")); |
| minSim.add(minSimLabel); |
| minSimFTF.setColumns(5); |
| minSimFTF.setValue(config.getFuzzyMinSim()); |
| minSim.add(minSimFTF); |
| minSim.add(new JLabel(MessageUtils.getLocalizedMessage("label.float_required"))); |
| panel.add(minSim); |
| |
| JPanel prefLen = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| prefLen.setOpaque(false); |
| prefLen.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0)); |
| JLabel prefLenLabel = new JLabel(MessageUtils.getLocalizedMessage("search_parser.label.fuzzy_preflen")); |
| prefLen.add(prefLenLabel); |
| prefLenFTF.setColumns(5); |
| prefLenFTF.setValue(config.getFuzzyPrefixLength()); |
| prefLen.add(prefLenFTF); |
| prefLen.add(new JLabel(MessageUtils.getLocalizedMessage("label.int_required"))); |
| panel.add(prefLen); |
| |
| return panel; |
| } |
| |
| private JPanel initDateRangeQuerySettingsPanel() { |
| JPanel panel = new JPanel(); |
| panel.setOpaque(false); |
| panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); |
| |
| JPanel header = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| header.setOpaque(false); |
| header.add(new JLabel(MessageUtils.getLocalizedMessage("search_parser.label.daterange_query"))); |
| panel.add(header); |
| |
| JPanel resolution = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| resolution.setOpaque(false); |
| resolution.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0)); |
| JLabel resLabel = new JLabel(MessageUtils.getLocalizedMessage("search_parser.label.date_res")); |
| resolution.add(resLabel); |
| Arrays.stream(DateTools.Resolution.values()).map(DateTools.Resolution::name).forEach(dateResCB::addItem); |
| dateResCB.setSelectedItem(config.getDateResolution().name()); |
| dateResCB.setOpaque(false); |
| resolution.add(dateResCB); |
| panel.add(resolution); |
| |
| JPanel locale = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| locale.setOpaque(false); |
| locale.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0)); |
| JLabel locLabel = new JLabel(MessageUtils.getLocalizedMessage("search_parser.label.locale")); |
| locale.add(locLabel); |
| locationTF.setColumns(10); |
| locationTF.setText(config.getLocale().toLanguageTag()); |
| locale.add(locationTF); |
| JLabel tzLabel = new JLabel(MessageUtils.getLocalizedMessage("search_parser.label.timezone")); |
| locale.add(tzLabel); |
| timezoneTF.setColumns(10); |
| timezoneTF.setText(config.getTimeZone().getID()); |
| locale.add(timezoneTF); |
| panel.add(locale); |
| |
| return panel; |
| } |
| |
| private JPanel initPointRangeQuerySettingsPanel() { |
| JPanel panel = new JPanel(); |
| panel.setOpaque(false); |
| panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); |
| panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); |
| |
| JPanel header = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| header.setOpaque(false); |
| header.add(new JLabel(MessageUtils.getLocalizedMessage("search_parser.label.pointrange_query"))); |
| panel.add(header); |
| |
| JPanel headerNote = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| headerNote.setOpaque(false); |
| headerNote.add(new JLabel(MessageUtils.getLocalizedMessage("search_parser.label.pointrange_hint"))); |
| panel.add(headerNote); |
| |
| TableUtils.setupTable(pointRangeQueryTable, ListSelectionModel.SINGLE_SELECTION, new PointTypesTableModel(), null, PointTypesTableModel.Column.FIELD.getColumnWidth()); |
| pointRangeQueryTable.setShowGrid(true); |
| JScrollPane scrollPane = new JScrollPane(pointRangeQueryTable); |
| panel.add(scrollPane); |
| |
| return panel; |
| } |
| |
| @Override |
| public void setSearchableFields(Collection<String> searchableFields) { |
| dfCB.removeAllItems(); |
| for (String field : searchableFields) { |
| dfCB.addItem(field); |
| } |
| } |
| |
| @Override |
| public void setRangeSearchableFields(Collection<String> rangeSearchableFields) { |
| pointRangeQueryTable.setModel(new PointTypesTableModel(rangeSearchableFields)); |
| pointRangeQueryTable.setShowGrid(true); |
| String[] numTypes = Arrays.stream(PointTypesTableModel.NumType.values()) |
| .map(PointTypesTableModel.NumType::name) |
| .toArray(String[]::new); |
| JComboBox<String> numTypesCombo = new JComboBox<>(numTypes); |
| numTypesCombo.setRenderer((list, value, index, isSelected, cellHasFocus) -> new JLabel(value)); |
| pointRangeQueryTable.getColumnModel().getColumn(PointTypesTableModel.Column.TYPE.getIndex()).setCellEditor(new DefaultCellEditor(numTypesCombo)); |
| pointRangeQueryTable.getColumnModel().getColumn(PointTypesTableModel.Column.TYPE.getIndex()).setCellRenderer( |
| (table, value, isSelected, hasFocus, row, column) -> new JLabel((String) value) |
| ); |
| pointRangeQueryTable.getColumnModel().getColumn(PointTypesTableModel.Column.FIELD.getIndex()).setPreferredWidth(PointTypesTableModel.Column.FIELD.getColumnWidth()); |
| pointRangeQueryTable.setPreferredScrollableViewportSize(pointRangeQueryTable.getPreferredSize()); |
| |
| // set default type to Integer |
| for (int i = 0; i < rangeSearchableFields.size(); i++) { |
| pointRangeQueryTable.setValueAt(PointTypesTableModel.NumType.INT.name(), i, PointTypesTableModel.Column.TYPE.getIndex()); |
| } |
| |
| } |
| |
| @Override |
| public QueryParserConfig getConfig() { |
| int phraseSlop = (int) slopFTF.getValue(); |
| float fuzzyMinSimFloat = (float) minSimFTF.getValue(); |
| int fuzzyPrefLenInt = (int) prefLenFTF.getValue(); |
| |
| Map<String, Class<? extends Number>> typeMap = new HashMap<>(); |
| for (int row = 0; row < pointRangeQueryTable.getModel().getRowCount(); row++) { |
| String field = (String) pointRangeQueryTable.getValueAt(row, PointTypesTableModel.Column.FIELD.getIndex()); |
| String type = (String) pointRangeQueryTable.getValueAt(row, PointTypesTableModel.Column.TYPE.getIndex()); |
| switch (PointTypesTableModel.NumType.valueOf(type)) { |
| case INT: |
| typeMap.put(field, Integer.class); |
| break; |
| case LONG: |
| typeMap.put(field, Long.class); |
| break; |
| case FLOAT: |
| typeMap.put(field, Float.class); |
| break; |
| case DOUBLE: |
| typeMap.put(field, Double.class); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| return new QueryParserConfig.Builder() |
| .useClassicParser(classicRB.isSelected()) |
| .defaultOperator(QueryParserConfig.Operator.valueOf((String) defOpCombo.getSelectedItem())) |
| .enablePositionIncrements(posIncCB.isSelected()) |
| .allowLeadingWildcard(wildCardCB.isSelected()) |
| .splitOnWhitespace(splitWSCB.isSelected()) |
| .autoGeneratePhraseQueries(genPhraseQueryCB.isSelected()) |
| .autoGenerateMultiTermSynonymsPhraseQuery(genMultiTermSynonymsPhraseQueryCB.isSelected()) |
| .phraseSlop(phraseSlop) |
| .fuzzyMinSim(fuzzyMinSimFloat) |
| .fuzzyPrefixLength(fuzzyPrefLenInt) |
| .dateResolution(DateTools.Resolution.valueOf((String) dateResCB.getSelectedItem())) |
| .locale(new Locale(locationTF.getText())) |
| .timeZone(TimeZone.getTimeZone(timezoneTF.getText())) |
| .typeMap(typeMap) |
| .build(); |
| } |
| |
| @Override |
| public String getDefaultField() { |
| return (String) dfCB.getSelectedItem(); |
| } |
| |
| private class ListenerFunctions { |
| |
| void selectStandardQParser(ActionEvent e) { |
| splitWSCB.setEnabled(false); |
| genPhraseQueryCB.setEnabled(false); |
| genMultiTermSynonymsPhraseQueryCB.setEnabled(false); |
| TableUtils.setEnabled(pointRangeQueryTable, true); |
| } |
| |
| void selectClassicQparser(ActionEvent e) { |
| splitWSCB.setEnabled(true); |
| if (splitWSCB.isSelected()) { |
| genPhraseQueryCB.setEnabled(true); |
| } else { |
| genPhraseQueryCB.setEnabled(false); |
| genPhraseQueryCB.setSelected(false); |
| } |
| genMultiTermSynonymsPhraseQueryCB.setEnabled(true); |
| pointRangeQueryTable.setEnabled(false); |
| pointRangeQueryTable.setForeground(Color.gray); |
| TableUtils.setEnabled(pointRangeQueryTable, false); |
| } |
| |
| void toggleSplitOnWhiteSpace(ActionEvent e) { |
| if (splitWSCB.isSelected()) { |
| genPhraseQueryCB.setEnabled(true); |
| } else { |
| genPhraseQueryCB.setEnabled(false); |
| genPhraseQueryCB.setSelected(false); |
| } |
| } |
| |
| } |
| |
| static final class PointTypesTableModel extends TableModelBase<PointTypesTableModel.Column> { |
| |
| enum Column implements TableColumnInfo { |
| |
| FIELD("Field", 0, String.class, 300), |
| TYPE("Numeric Type", 1, NumType.class, 150); |
| |
| private final String colName; |
| private final int index; |
| private final Class<?> type; |
| private final int width; |
| |
| Column(String colName, int index, Class<?> type, int width) { |
| this.colName = colName; |
| this.index = index; |
| this.type = type; |
| this.width = width; |
| } |
| |
| @Override |
| public String getColName() { |
| return colName; |
| } |
| |
| @Override |
| public int getIndex() { |
| return index; |
| } |
| |
| @Override |
| public Class<?> getType() { |
| return type; |
| } |
| |
| @Override |
| public int getColumnWidth() { |
| return width; |
| } |
| } |
| |
| enum NumType { |
| |
| INT, LONG, FLOAT, DOUBLE |
| |
| } |
| |
| PointTypesTableModel() { |
| super(); |
| } |
| |
| PointTypesTableModel(Collection<String> rangeSearchableFields) { |
| super(rangeSearchableFields.size()); |
| int i = 0; |
| for (String field : rangeSearchableFields) { |
| data[i++][Column.FIELD.getIndex()] = field; |
| } |
| } |
| |
| @Override |
| public boolean isCellEditable(int rowIndex, int columnIndex) { |
| return columnIndex == Column.TYPE.getIndex(); |
| } |
| |
| @Override |
| public void setValueAt(Object value, int rowIndex, int columnIndex) { |
| data[rowIndex][columnIndex] = value; |
| fireTableCellUpdated(rowIndex, columnIndex); |
| } |
| |
| @Override |
| protected Column[] columnInfos() { |
| return Column.values(); |
| } |
| } |
| |
| } |