* Added support for different name/label in search parameter selection.
* Fixed resource search to form wildcard query separately for each term.
* Added utility methods to SearchQuery for lower/uppercasing the query
gitlab #291
Change-Id: I141569877abc910387eca8633a1c4462153b034e
*******************************************************************************/
package org.simantics.db.indexing;
+import java.io.IOException;
import java.io.Reader;
import java.util.HashMap;
import java.util.Map;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenFilter;
+import org.apache.lucene.analysis.TokenStream;
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.pattern.PatternReplaceFilter;
import org.apache.lucene.analysis.pattern.PatternTokenizer;
+import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
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.util.AttributeFactory;
import org.apache.lucene.util.Version;
import org.simantics.databoard.util.ObjectUtils;
+import org.simantics.db.layer0.genericrelation.Dependencies;
import org.simantics.utils.datastructures.Pair;
public class Queries {
}
}
+ final static class LowercaseFilter extends TokenFilter {
+ private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);
+
+ public LowercaseFilter(TokenStream in) {
+ super(in);
+ }
+
+ @Override
+ public boolean incrementToken() throws IOException {
+ if (!input.incrementToken()) return false;
+ String lowercase = termAtt.toString().toLowerCase();
+ termAtt.setEmpty().append(lowercase);
+ return true;
+ }
+ }
+
static final class TypeStringAnalyzer extends Analyzer {
+ private boolean lowercase;
- @Override
- protected TokenStreamComponents createComponents(String fieldName, Reader reader) {
+ public TypeStringAnalyzer(Boolean lowercase) {
+ this.lowercase = lowercase;
+ }
+
+ @Override
+ protected TokenStreamComponents createComponents(String fieldName, Reader reader) {
Tokenizer tokenizer = new PatternTokenizer(reader, Pattern.compile("(([^\\\\ ]|\\\\\\\\|\\\\ )+)( *)"), 1);
TokenFilter filter = new PatternReplaceFilter(tokenizer, Pattern.compile("(\\\\(\\\\| ))"), "$2", true);
- return new TokenStreamComponents(tokenizer, filter);
+ return new TokenStreamComponents(tokenizer, lowercase ? new LowercaseFilter(filter) : filter);
}
}
analyzerPerField.put("Resource", new KeywordAnalyzer());
analyzerPerField.put("GUID", new KeywordAnalyzer());
analyzerPerField.put("Name", new KeywordAnalyzer());
- analyzerPerField.put("Types", new TypeStringAnalyzer());
+ analyzerPerField.put("Types", new TypeStringAnalyzer(false));
+ analyzerPerField.put(Dependencies.FIELD_NAME_SEARCH, new LowerCaseWhitespaceAnalyzer(Version.LUCENE_4_9));
+ analyzerPerField.put(Dependencies.FIELD_TYPES_SEARCH, new TypeStringAnalyzer(true));
PerFieldAnalyzerWrapper analyzer = new PerFieldAnalyzerWrapper(new LowerCaseWhitespaceAnalyzer(Version.LUCENE_4_9), analyzerPerField);
return analyzer;
public static final String FIELD_NAME = "Name";
public static final String FIELD_TYPES = "Types";
public static final String FIELD_GUID = "GUID";
+ public static final String FIELD_NAME_SEARCH = "NameSearch";
+ public static final String FIELD_TYPES_SEARCH = "TypesSearch";
protected Resource getIndexRelation(ReadGraph graph) {
return Layer0X.getInstance(graph).DependenciesRelation;
}
protected static String getBindingPattern() {
- return "bfffff";
+ return "bfffffff";
}
@Override
Pair.make(Dependencies.FIELD_RESOURCE, "Long"),
Pair.make(Dependencies.FIELD_NAME, "String"),
Pair.make(Dependencies.FIELD_TYPES, "Text"),
- Pair.make(Dependencies.FIELD_GUID, "Text")
+ Pair.make(Dependencies.FIELD_GUID, "Text"),
+ Pair.make(Dependencies.FIELD_NAME_SEARCH, "Text"),
+ Pair.make(Dependencies.FIELD_TYPES_SEARCH, "Text")
};
final Resource resource;
ArrayList<Object[]> result = new ArrayList<Object[]>();
for (Entry entry : entries) {
- result.add(new Object[] { ss.getRandomAccessId(entry.parent), ss.getRandomAccessId(entry.resource), entry.name, entry.types, entry.id });
+ result.add(new Object[] { ss.getRandomAccessId(entry.parent), ss.getRandomAccessId(entry.resource), entry.name, entry.types, entry.id, entry.name, entry.types });
}
return result;
@Override
public List<Map<String, Object>> query(RequestProcessor session, String search, String bindingPattern, Object[] constants, int maxResultCount) {
- if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffff'");
+ if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffffff'");
IndexedRelations indexer = session.getService(IndexedRelations.class);
return indexer.query(null, search, session, resource, (Resource)constants[0], maxResultCount);
}
@Override
public List<Resource> queryResources(RequestProcessor session, String search, String bindingPattern, Object[] constants, int maxResultCount) {
- if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffff'");
+ if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffffff'");
IndexedRelations indexer = session.getService(IndexedRelations.class);
return indexer.queryResources(null, search, session, resource, (Resource)constants[0], maxResultCount);
}
@Override
public List<Map<String, Object>> list(RequestProcessor session, String bindingPattern, Object[] constants, int maxResultCount) {
- if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffff'");
+ if(!Dependencies.getBindingPattern().equals(bindingPattern)) throw new IllegalArgumentException("DependenciesRelation supports indexing only with 'bfffffff'");
IndexedRelations indexer = session.getService(IndexedRelations.class);
return indexer.query(null, null, session, resource, (Resource)constants[0], maxResultCount);
}
if(!entry.isValid(graph)) continue;
Resource parent = graph.getPossibleObject(entry.component, L0.PartOf);
if (parent != null) {
- _additions.add(new Object[] { ss.getRandomAccessId(parent), ss.getRandomAccessId(entry.component), name, types, id != null ? id.indexString() : "" });
+ _additions.add(new Object[] { ss.getRandomAccessId(parent), ss.getRandomAccessId(entry.component), name, types, id != null ? id.indexString() : "", name, types});
} else {
//System.err.println("resource " + entry.component + ": no parent for entry " + name + " " + types);
}
if(part != null) {
_replacementKeys.add(ss.getRandomAccessId(entry.component));
_replacementObjects.add(new Object[] { ss.getRandomAccessId(part),
- ss.getRandomAccessId(entry.component), name, types, id != null ? id.indexString() : "" });
+ ss.getRandomAccessId(entry.component), name, types, id != null ? id.indexString() : "", name, types});
}
}
}
ArrayList<Object[]> result = new ArrayList<Object[]>(entries.size());
for (Entry entry : entries) {
- result.add(new Object[] { ss.getRandomAccessId(entry.parent), ss.getRandomAccessId(entry.resource), entry.name, entry.types, entry.id });
+ result.add(new Object[] { ss.getRandomAccessId(entry.parent), ss.getRandomAccessId(entry.resource), entry.name, entry.types, entry.id, entry.name, entry.types });
}
Layer0X L0X = Layer0X.getInstance(graph);
package org.simantics.debug.ui;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.simantics.db.common.uri.UnescapedChildMapOfResource;
import org.simantics.db.common.utils.NameUtils;
import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.genericrelation.Dependencies;
+import org.simantics.db.layer0.genericrelation.IndexQueries;
import org.simantics.db.layer0.migration.OntologiesFromLibrary;
import org.simantics.db.layer0.variable.Variables.Role;
import org.simantics.db.request.Read;
}
}
} else {
+ String[] terms = pattern.trim().split("\\s+");
+ if (terms.length == 0) return;
+
Resource project = Simantics.peekProjectResource();
if (project != null) {
IResourceFilter rf = resourceFilter;
String filter = getFilterForResourceFilter(rf);
if (!filter.isEmpty())
filter += " AND ";
- filter += "Name:" + pattern + "*";
+
+ filter += Dependencies.FIELD_NAME_SEARCH + ":(";
+ filter += Arrays.stream(terms).map(term -> "+" + IndexQueries.escape(term.toLowerCase(), false) + "*").collect(Collectors.joining(" "));
+ filter +=")";
Layer0 L0 = Layer0.getInstance(graph);
import org.simantics.db.Resource;
import org.simantics.db.exception.DatabaseException;
import org.simantics.db.layer0.adapter.ActionFactory;
+import org.simantics.db.layer0.genericrelation.Dependencies;
import org.simantics.db.request.Read;
import org.simantics.document.linking.ontology.DocumentLink;
import org.simantics.layer0.Layer0;
});
ISearchService searchService = (ISearchService)PlatformUI.getWorkbench().getService(ISearchService.class);
SearchQuery query = new SearchQuery(name);
- query.setSearchFlag("Name", "on");
+ query.setSearchFlag(Dependencies.FIELD_NAME_SEARCH, "on");
query.setSearchFlag(DocumentLink.URIs.SearchFunction, "on");
searchService.performQuery(query, ISearchService.ResultBrowser.VIEW, true);
} catch (DatabaseException e) {
import org.simantics.db.common.utils.NameUtils;
import org.simantics.db.exception.DatabaseException;
import org.simantics.db.layer0.adapter.Instances;
+import org.simantics.db.layer0.genericrelation.Dependencies;
import org.simantics.document.linking.ontology.DocumentLink;
import org.simantics.document.linking.utils.SourceLinkUtil;
import org.simantics.scl.runtime.function.FunctionImpl5;
SearchResult result = new SearchResult(columns);
Set<Resource> processed = new HashSet<Resource>();
- NameComparator c = new NameComparator(query.getQuery("Name"));
+ NameComparator c = new NameComparator(query.getQuery(Dependencies.FIELD_NAME_SEARCH));
for (Resource source : results) {
// Prevent index corruption from producing duplicate results.
public SearchResult apply(IProgressMonitor monitor, ReadGraph graph, Resource model, SearchQuery query, Integer maxResults) {
try {
Collection<Map<String, Object>> results = Searching.performSearch(graph, Layer0X.getInstance(graph).Dependencies, model,
- query.escaped(false).getQuery("Name","Types"), maxResults);
+ query.escapedWithForcedCase(false, false).getQuery(Dependencies.FIELD_NAME_SEARCH, Dependencies.FIELD_TYPES_SEARCH), maxResults);
return generateSearchResults(graph, results);
} catch (DatabaseException e) {
Logger.defaultLogError(e);
</ul>
<div id="tabs-1">
<#list getAllSearchParams() as param>
- <input type="checkbox" name="${param}" value="on" <#if query.getBooleanFlag("${param}")> checked </#if>> ${param}
+ <input type="checkbox" name="${param.getName()}" value="on" <#if query.getBooleanFlag("${param.getName()}")> checked </#if>> ${param.getLabel()}
</#list>
</div>
<div id="tabs-2">
import org.simantics.db.Resource;
import org.simantics.db.common.utils.Logger;
import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.genericrelation.Dependencies;
import org.simantics.operation.Layer0X;
import org.simantics.scl.runtime.function.FunctionImpl5;
public SearchResult apply(IProgressMonitor monitor, ReadGraph graph, Resource model, SearchQuery query, Integer maxResults) {
try {
Collection<Map<String, Object>> results = Searching.performSearch(graph,
- Layer0X.getInstance(graph).Dependencies, model, query.getQuery("Name","Types"), maxResults);
+ Layer0X.getInstance(graph).Dependencies, model,
+ query.escapedWithForcedCase(false, false).getQuery(Dependencies.FIELD_NAME_SEARCH, Dependencies.FIELD_TYPES_SEARCH),
+ maxResults);
return Searching.generateDependenciesSearchResult(graph, results);
} catch (DatabaseException e) {
import java.io.File;
import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.Map;
+import java.util.TreeMap;
/**
return searchEngines;
}
- public Set<String> getAllSearchParams() {
- Set<String> params = new HashSet<String>();
- for (SearchEngine engine : searchEngines)
- params.addAll(engine.getSupportedParams());
- return params;
+ public Collection<SearchParam> getAllSearchParams() {
+ Map<String, SearchParam> params = new TreeMap<>();
+ for (SearchEngine engine : searchEngines) {
+ for (SearchParam param : engine.getSupportedParams()) {
+ params.put(param.getName(), param);
+ }
+ }
+ return params.values();
}
public NamedResource getModel() {
private String id;
private String name;
private Function5<IProgressMonitor, ReadGraph, Resource, SearchQuery, Integer, SearchResult> searchFunction;
- private Set<String> supportedParams = new HashSet<String>();
+ private Set<SearchParam> supportedParams = new HashSet<>();
private boolean enabledByDefault;
public SearchEngine(String id, Function5<IProgressMonitor, ReadGraph, Resource, SearchQuery, Integer, SearchResult> searchFunction, boolean enabledByDefault) {
return id.hashCode();
}
- public void addSupportedParam(String param) {
- this.supportedParams.add(param);
+ public void addSupportedParam(String name) {
+ addSupportedParam(name, name);
}
- public Set<String> getSupportedParams() {
+ public void addSupportedParam(String name, String label) {
+ this.supportedParams.add(new SearchParam(name, label));
+ }
+
+ public Set<SearchParam> getSupportedParams() {
return supportedParams;
}
--- /dev/null
+package org.simantics.workbench.search;
+
+public class SearchParam {
+ private String name;
+ private String label;
+
+ public SearchParam(String name, String label) {
+ this.name = name;
+ this.label = label;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+}
\ No newline at end of file
return withOriginalQuery( IndexQueries.escape( originalQuery, escapeWildcards ) );
}
+ /**
+ * @param uppercase <code>true</code> for uppercased query, <code>false</code>
+ * for lowercased query
+ * @return a clone of this query with the query string in lower or uppercase
+ */
+ public SearchQuery withForcedCase(boolean uppercase) {
+ return withOriginalQuery(uppercase ? originalQuery.toUpperCase() : originalQuery.toLowerCase());
+ }
+
+ /**
+ * @param uppercase <code>true</code> for uppercased query,
+ * <code>false</code> for lowercased query
+ * @param escapeWildcards passed to {@link #escaped(boolean)}
+ * @return a clone of this query with the query string escaped and in lower or
+ * uppercase
+ */
+ public SearchQuery escapedWithForcedCase(boolean uppercase, boolean escapeWildcards) {
+ return withForcedCase(uppercase).escaped(escapeWildcards);
+ }
+
public static URL encode(File file, SearchQuery query) throws IOException {
URL url = file.toURI().toURL();
String s = url.toString() + "?search=" + URLEncoder.encode(query.getOriginalQuery(), "UTF-8");
import org.eclipse.ui.part.ViewPart;
import org.simantics.Simantics;
import org.simantics.db.Session;
+import org.simantics.db.layer0.genericrelation.Dependencies;
import org.simantics.db.layer0.request.ActiveModels;
import org.simantics.db.management.ISessionContext;
import org.simantics.db.management.ISessionContextChangedListener;
protected void initializeViewContent() {
SearchQuery query = new SearchQuery("");
- query.setSearchFlag("Name", "on");
- query.setSearchFlag("Types", "on");
+ query.setSearchFlag(Dependencies.FIELD_NAME_SEARCH, "on");
+ query.setSearchFlag(Dependencies.FIELD_TYPES_SEARCH, "on");
ISearchService searchService = (ISearchService) PlatformUI.getWorkbench().getService(ISearchService.class);
if (searchService != null)
searchService.performQuery(query, ResultBrowser.VIEW, false);
import org.simantics.db.Session;
import org.simantics.db.exception.DatabaseException;
import org.simantics.db.layer0.adapter.Instances;
+import org.simantics.db.layer0.genericrelation.Dependencies;
import org.simantics.db.layer0.request.ActiveModels;
import org.simantics.db.request.Read;
import org.simantics.db.service.SerialisationSupport;
@SuppressWarnings("unchecked")
SearchEngine engine = new SearchEngine(id,name,(Function5<IProgressMonitor, ReadGraph, Resource, SearchQuery, Integer, SearchResult>)f, true);
{
- engine.addSupportedParam("Name");
- engine.addSupportedParam("Types");
+ engine.addSupportedParam(Dependencies.FIELD_NAME_SEARCH, "Name");
+ engine.addSupportedParam(Dependencies.FIELD_TYPES_SEARCH, "Types");
}
searchEngines.add(engine);
}
@SuppressWarnings("unchecked")
SearchEngine engine = new SearchEngine(id,name,(Function5<IProgressMonitor, ReadGraph, Resource, SearchQuery, Integer, SearchResult>)f, enabled);
{
- engine.addSupportedParam("Name");
- engine.addSupportedParam("Types");
+ engine.addSupportedParam(Dependencies.FIELD_NAME_SEARCH, "Name");
+ engine.addSupportedParam(Dependencies.FIELD_TYPES_SEARCH, "Types");
}
return engine;
}
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.swt.IFocusService;
+import org.simantics.db.layer0.genericrelation.Dependencies;
import org.simantics.utils.ui.BundleUtils;
import org.simantics.workbench.search.ISearchService;
import org.simantics.workbench.search.SearchQuery;
query = filter(query);
SearchQuery searchQuery = new SearchQuery(originalInput);
- searchQuery.setSearchFlag("Name", "on");
+ searchQuery.setSearchFlag(Dependencies.FIELD_NAME_SEARCH, "on");
performQuery(searchQuery, browserType);
}
return;
SearchQuery searchQuery = new SearchQuery(originalInput);
- searchQuery.setSearchFlag("Types", "on");
+ searchQuery.setSearchFlag(Dependencies.FIELD_TYPES_SEARCH, "on");
performQuery(searchQuery, browserType);
}
return;
SearchQuery searchQuery = new SearchQuery(originalInput);
- searchQuery.setSearchFlag("Name", "on");
- searchQuery.setSearchFlag("Types", "on");
+ searchQuery.setSearchFlag(Dependencies.FIELD_NAME_SEARCH, "on");
+ searchQuery.setSearchFlag(Dependencies.FIELD_TYPES_SEARCH, "on");
performQuery(searchQuery, browserType);
}