Hi Thomas.
I prepared a patch to allow in-memory fulltext search in H2. Also fixed a
bug - on row delete lucene index was not committed. Please take a look.
Sergi
--
You received this message because you are subscribed to the Google Groups "H2
Database" group.
To view this discussion on the web visit
https://groups.google.com/d/msg/h2-database/-/PnUlxxpttekJ.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/h2-database?hl=en.
Index: src/main/org/h2/fulltext/FullTextLucene.java
===================================================================
--- src/main/org/h2/fulltext/FullTextLucene.java (revision 4240)
+++ src/main/org/h2/fulltext/FullTextLucene.java (working copy)
@@ -48,6 +48,7 @@
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.Version;
import org.apache.lucene.index.IndexWriter;
//*/
@@ -71,6 +72,8 @@
private static final String LUCENE_FIELD_MODIFIED = "_modified";
private static final String LUCENE_FIELD_COLUMN_PREFIX = "_";
+ private static final String IN_MEMORY_PERFIX = "mem:";
+
/**
* Initializes full text search functionality for this database. This adds
* the following Java functions to the database:
@@ -262,8 +265,7 @@
access.modifier = new IndexModifier(path, analyzer, recreate);
//*/
//## LUCENE3 ##
- File f = new File(path);
- Directory indexDir = FSDirectory.open(f);
+ Directory indexDir = path.startsWith(IN_MEMORY_PERFIX) ? new RAMDirectory() : FSDirectory.open(new File(path));
boolean recreate = !IndexReader.indexExists(indexDir);
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);
IndexWriter writer = new IndexWriter(indexDir, analyzer,
@@ -296,7 +298,12 @@
rs.next();
String path = rs.getString(1);
if (path == null) {
- throw throwException("Fulltext search for in-memory databases is not supported.");
+ /*## LUCENE2 ##
+ throw throwException("Fulltext search for in-memory databases is not supported with Lucene 2. Please use Lucene 3 instead.");
+ //*/
+ //## LUCENE3 ##
+ return IN_MEMORY_PERFIX + conn.getCatalog();
+ //*/
}
int index = path.lastIndexOf(':');
// position 1 means a windows drive letter is used, ignore that
@@ -336,7 +343,9 @@
if (access != null) {
removeIndexAccess(access, path);
}
- FileUtils.deleteRecursive(path, false);
+ if (!path.startsWith(IN_MEMORY_PERFIX)) {
+ FileUtils.deleteRecursive(path, false);
+ }
}
/**
@@ -665,13 +674,7 @@
try {
indexAccess.writer.addDocument(doc);
if (commitIndex) {
- indexAccess.writer.commit();
- // recreate Searcher with the IndexWriter's reader.
- indexAccess.searcher.close();
- indexAccess.reader.close();
- IndexReader reader = indexAccess.writer.getReader();
- indexAccess.reader = reader;
- indexAccess.searcher = new IndexSearcher(reader);
+ commitIndex();
}
} catch (IOException e) {
throw convertException(e);
@@ -693,6 +696,7 @@
//*/
//## LUCENE3 ##
indexAccess.writer.deleteDocuments(term);
+ commitIndex();
//*/
} catch (IOException e) {
throw convertException(e);
Index: src/test/org/h2/test/db/TestFullText.java
===================================================================
--- src/test/org/h2/test/db/TestFullText.java (revision 4244)
+++ src/test/org/h2/test/db/TestFullText.java (working copy)
@@ -12,11 +12,14 @@
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.UUID;
import org.h2.fulltext.FullText;
import org.h2.store.fs.FileUtils;
+import org.h2.test.TestAll;
import org.h2.test.TestBase;
import org.h2.util.Task;
@@ -45,9 +48,6 @@
testNativeFeatures();
testTransaction(false);
testCreateDrop();
- if (config.memory) {
- return;
- }
testStreamLob();
test(false, "VARCHAR");
test(false, "CLOB");
@@ -76,34 +76,46 @@
deleteDb("fullTextReopen");
}
+ private void close(Collection<Connection> conns) throws SQLException {
+ for (Connection conn : conns) {
+ conn.close();
+ }
+ }
+
+ private Connection getConnection(String name, Collection<Connection> conns) throws SQLException {
+ Connection conn = getConnection(name);
+ conns.add(conn);
+ return conn;
+ }
+
private void testAutoAnalyze() throws SQLException {
- if (config.memory) {
- return;
- }
deleteDb("fullTextNative");
Connection conn;
Statement stat;
- conn = getConnection("fullTextNative");
+ ArrayList<Connection> conns = new ArrayList<Connection>();
+
+ conn = getConnection("fullTextNative", conns);
stat = conn.createStatement();
stat.execute("create alias if not exists ft_init for \"org.h2.fulltext.FullText.init\"");
stat.execute("call ft_init()");
stat.execute("create table test(id int primary key, name varchar)");
stat.execute("call ft_create_index('PUBLIC', 'TEST', 'NAME')");
- conn.close();
- conn = getConnection("fullTextNative");
+ if (!config.memory) {
+ conn.close();
+ }
+
+ conn = getConnection("fullTextNative", conns);
stat = conn.createStatement();
stat.execute("insert into test select x, 'x' from system_range(1, 3000)");
- conn.close();
+ close(conns);
}
private void testNativeFeatures() throws SQLException {
- if (config.memory) {
- return;
- }
deleteDb("fullTextNative");
- Connection conn = getConnection("fullTextNative");
+ ArrayList<Connection> conns = new ArrayList<Connection>();
+ Connection conn = getConnection("fullTextNative", conns);
Statement stat = conn.createStatement();
stat.execute("CREATE ALIAS IF NOT EXISTS FT_INIT FOR \"org.h2.fulltext.FullText.init\"");
stat.execute("CALL FT_INIT()");
@@ -137,8 +149,10 @@
rs = stat.executeQuery("SELECT * FROM FT_SEARCH('this', 0, 0)");
assertFalse(rs.next());
- conn.close();
- conn = getConnection("fullTextNative");
+ if (!config.memory) {
+ conn.close();
+ }
+ conn = getConnection("fullTextNative", conns);
stat = conn.createStatement();
conn.setAutoCommit(false);
stat.execute("delete from test");
@@ -148,8 +162,7 @@
rs = stat.executeQuery("SELECT * FROM FT_SEARCH_DATA('Welcome', 0, 0)");
assertTrue(rs.next());
conn.setAutoCommit(true);
-
- conn.close();
+ close(conns);
}
private void testUuidPrimaryKey(boolean lucene) throws SQLException {
@@ -178,13 +191,11 @@
}
private void testTransaction(boolean lucene) throws SQLException {
- if (config.memory) {
- return;
- }
String prefix = lucene ? "FTL" : "FT";
deleteDb("fullTextTransaction");
FileUtils.deleteRecursive(getBaseDir() + "/fullTextTransaction", false);
- Connection conn = getConnection("fullTextTransaction");
+ ArrayList<Connection> conns = new ArrayList<Connection>();
+ Connection conn = getConnection("fullTextTransaction", conns);
Statement stat = conn.createStatement();
String className = lucene ? "FullTextLucene" : "FullText";
stat.execute("CREATE ALIAS IF NOT EXISTS " + prefix + "_INIT FOR \"org.h2.fulltext." + className + ".init\"");
@@ -199,15 +210,17 @@
conn.setAutoCommit(false);
stat.execute("insert into test values(2, 'Hello Moon!')");
conn.rollback();
- conn.close();
- conn = getConnection("fullTextTransaction");
+ if (!config.memory) {
+ conn.close();
+ }
+ conn = getConnection("fullTextTransaction", conns);
stat = conn.createStatement();
rs = stat.executeQuery("SELECT * FROM " + prefix + "_SEARCH('Hello', 0, 0)");
assertTrue(rs.next());
rs = stat.executeQuery("SELECT * FROM " + prefix + "_SEARCH('Moon', 0, 0)");
assertFalse(rs.next());
FullText.dropAll(conn);
- conn.close();
+ close(conns);
deleteDb("fullTextTransaction");
FileUtils.deleteRecursive(getBaseDir() + "/fullTextTransaction", false);
}
@@ -216,12 +229,13 @@
final String prefix = lucene ? "FTL" : "FT";
trace("Testing multithreaded " + prefix);
deleteDb("fullText");
+ ArrayList<Connection> conns = new ArrayList<Connection>();
int len = 2;
Task[] task = new Task[len];
for (int i = 0; i < len; i++) {
// final Connection conn =
// getConnection("fullText;MULTI_THREADED=1;LOCK_TIMEOUT=10000");
- final Connection conn = getConnection("fullText");
+ final Connection conn = getConnection("fullText", conns);
Statement stat = conn.createStatement();
String className = lucene ? "FullTextLucene" : "FullText";
stat.execute("CREATE ALIAS IF NOT EXISTS " + prefix + "_INIT FOR \"org.h2.fulltext." + className + ".init\"");
@@ -257,7 +271,9 @@
}
}
trace("closing connection");
- conn.close();
+ if (!config.memory) {
+ conn.close();
+ }
trace("completed thread " + Thread.currentThread());
}
};
@@ -274,6 +290,7 @@
t.get();
trace("done joining " + t);
}
+ close(conns);
}
private void testStreamLob() throws SQLException {
@@ -331,6 +348,9 @@
}
private void testReopen(boolean lucene) throws SQLException {
+ if (config.memory) {
+ return;
+ }
String prefix = lucene ? "FTL" : "FT";
deleteDb("fullTextReopen");
FileUtils.deleteRecursive(getBaseDir() + "/fullTextReopen", false);
@@ -408,7 +428,8 @@
return;
}
deleteDb("fullText");
- Connection conn = getConnection("fullText");
+ ArrayList<Connection> conns = new ArrayList<Connection>();
+ Connection conn = getConnection("fullText", conns);
String prefix = lucene ? "FTL_" : "FT_";
Statement stat = conn.createStatement();
String className = lucene ? "FullTextLucene" : "FullText";
@@ -491,15 +512,16 @@
assertFalse(rs.next());
}
- conn.close();
+ if (!config.memory) {
+ conn.close();
+ }
- conn = getConnection("fullText");
+ conn = getConnection("fullText", conns);
stat = conn.createStatement();
stat.executeQuery("SELECT * FROM " + prefix + "SEARCH('World', 0, 0)");
stat.execute("CALL " + prefix + "DROP_ALL()");
- conn.close();
-
+ close(conns);
}
}