/*
* Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.fulltext;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.h2.util.SoftHashMap;
The global settings of a full text search.
/**
* The global settings of a full text search.
*/
final class FullTextSettings {
The settings of open indexes.
/**
* The settings of open indexes.
*/
private static final Map<String, FullTextSettings> SETTINGS = new HashMap<>();
Whether this instance has been initialized.
/**
* Whether this instance has been initialized.
*/
private boolean initialized;
The set of words not to index (stop words).
/**
* The set of words not to index (stop words).
*/
private final Set<String> ignoreList = new HashSet<>();
The set of words / terms.
/**
* The set of words / terms.
*/
private final Map<String, Integer> words = new HashMap<>();
The set of indexes in this database.
/**
* The set of indexes in this database.
*/
private final ConcurrentHashMap<Integer, IndexInfo> indexes = new ConcurrentHashMap<>();
The prepared statement cache.
/**
* The prepared statement cache.
*/
private final SoftHashMap<Connection,
SoftHashMap<String, PreparedStatement>> cache =
new SoftHashMap<>();
The whitespace characters.
/**
* The whitespace characters.
*/
private String whitespaceChars = " \t\n\r\f+\"*%&/()=?'!,.;:-_#@|^~`{}[]<>\\";
Create a new instance.
/**
* Create a new instance.
*/
private FullTextSettings() {
// don't allow construction
}
Clear set of ignored words
/**
* Clear set of ignored words
*/
public void clearIgnored() {
synchronized (ignoreList) {
ignoreList.clear();
}
}
Amend set of ignored words
Params: - words – to add
/**
* Amend set of ignored words
* @param words to add
*/
public void addIgnored(Iterable<String> words) {
synchronized (ignoreList) {
for (String word : words) {
word = normalizeWord(word);
ignoreList.add(word);
}
}
}
Clear set of searchable words
/**
* Clear set of searchable words
*/
public void clearWordList() {
synchronized (words) {
words.clear();
}
}
Get id for a searchable word
Params: - word – to find id for
Returns: Integer id or null if word is not found
/**
* Get id for a searchable word
* @param word to find id for
* @return Integer id or null if word is not found
*/
public Integer getWordId(String word) {
synchronized (words) {
return words.get(word);
}
}
Register searchable word
Params: - word – to register
- id – to register with
/**
* Register searchable word
* @param word to register
* @param id to register with
*/
public void addWord(String word, Integer id) {
synchronized (words) {
if(!words.containsKey(word)) {
words.put(word, id);
}
}
}
Get the index information for the given index id.
Params: - indexId – the index id
Returns: the index info
/**
* Get the index information for the given index id.
*
* @param indexId the index id
* @return the index info
*/
protected IndexInfo getIndexInfo(int indexId) {
return indexes.get(indexId);
}
Add an index.
Params: - index – the index
/**
* Add an index.
*
* @param index the index
*/
protected void addIndexInfo(IndexInfo index) {
indexes.put(index.id, index);
}
Convert a word to uppercase. This method returns null if the word is in
the ignore list.
Params: - word – the word to convert and check
Returns: the uppercase version of the word or null
/**
* Convert a word to uppercase. This method returns null if the word is in
* the ignore list.
*
* @param word the word to convert and check
* @return the uppercase version of the word or null
*/
protected String convertWord(String word) {
word = normalizeWord(word);
synchronized (ignoreList) {
if (ignoreList.contains(word)) {
return null;
}
}
return word;
}
Get or create the fulltext settings for this database.
Params: - conn – the connection
Returns: the settings
/**
* Get or create the fulltext settings for this database.
*
* @param conn the connection
* @return the settings
*/
protected static FullTextSettings getInstance(Connection conn)
throws SQLException {
String path = getIndexPath(conn);
FullTextSettings setting;
synchronized (SETTINGS) {
setting = SETTINGS.get(path);
if (setting == null) {
setting = new FullTextSettings();
SETTINGS.put(path, setting);
}
}
return setting;
}
Get the file system path.
Params: - conn – the connection
Returns: the file system path
/**
* Get the file system path.
*
* @param conn the connection
* @return the file system path
*/
private static String getIndexPath(Connection conn) throws SQLException {
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery(
"CALL IFNULL(DATABASE_PATH(), 'MEM:' || DATABASE())");
rs.next();
String path = rs.getString(1);
if ("MEM:UNNAMED".equals(path)) {
throw FullText.throwException(
"Fulltext search for private (unnamed) " +
"in-memory databases is not supported.");
}
rs.close();
return path;
}
Prepare a statement. The statement is cached in a soft reference cache.
Params: - conn – the connection
- sql – the statement
Returns: the prepared statement
/**
* Prepare a statement. The statement is cached in a soft reference cache.
*
* @param conn the connection
* @param sql the statement
* @return the prepared statement
*/
protected synchronized PreparedStatement prepare(Connection conn, String sql)
throws SQLException {
SoftHashMap<String, PreparedStatement> c = cache.get(conn);
if (c == null) {
c = new SoftHashMap<>();
cache.put(conn, c);
}
PreparedStatement prep = c.get(sql);
if (prep != null && prep.getConnection().isClosed()) {
prep = null;
}
if (prep == null) {
prep = conn.prepareStatement(sql);
c.put(sql, prep);
}
return prep;
}
Remove all indexes from the settings.
/**
* Remove all indexes from the settings.
*/
protected void removeAllIndexes() {
indexes.clear();
}
Remove an index from the settings.
Params: - index – the index to remove
/**
* Remove an index from the settings.
*
* @param index the index to remove
*/
protected void removeIndexInfo(IndexInfo index) {
indexes.remove(index.id);
}
Set the initialized flag.
Params: - b – the new value
/**
* Set the initialized flag.
*
* @param b the new value
*/
protected void setInitialized(boolean b) {
this.initialized = b;
}
Get the initialized flag.
Returns: whether this instance is initialized
/**
* Get the initialized flag.
*
* @return whether this instance is initialized
*/
protected boolean isInitialized() {
return initialized;
}
Close all fulltext settings, freeing up memory.
/**
* Close all fulltext settings, freeing up memory.
*/
protected static void closeAll() {
synchronized (SETTINGS) {
SETTINGS.clear();
}
}
protected void setWhitespaceChars(String whitespaceChars) {
this.whitespaceChars = whitespaceChars;
}
protected String getWhitespaceChars() {
return whitespaceChars;
}
private static String normalizeWord(String word) {
// TODO this is locale specific, document
return word.toUpperCase();
}
}