Java tutorial
/** * Copyright (c) 2013 Cloudant, Inc. All rights reserved. * * Licensed 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 com.cloudant.sync.indexing; import com.cloudant.sync.datastore.ConflictException; import com.cloudant.sync.datastore.DatastoreExtended; import com.cloudant.sync.datastore.DatastoreManager; import com.cloudant.sync.datastore.DocumentBody; import com.cloudant.sync.datastore.DocumentBodyFactory; import com.cloudant.sync.datastore.DocumentRevision; import com.cloudant.sync.sqlite.Cursor; import com.cloudant.sync.sqlite.SQLDatabase; import com.cloudant.sync.util.SQLDatabaseTestUtils; import com.cloudant.sync.util.TestUtils; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import org.apache.commons.io.FileUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import java.io.File; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import static org.hamcrest.Matchers.containsInAnyOrder; public class IndexManagerIndexTest { SQLDatabase database = null; DatastoreExtended datastore = null; IndexManager indexManager = null; List<DocumentBody> dbBodies = null; private String datastoreManagerPath; @Before public void setUp() throws IOException, SQLException { datastoreManagerPath = TestUtils.createTempTestingDir(this.getClass().getName()); DatastoreManager datastoreManager = new DatastoreManager(this.datastoreManagerPath); datastore = (DatastoreExtended) datastoreManager.openDatastore(getClass().getSimpleName()); indexManager = new IndexManager(datastore); database = indexManager.getDatabase(); createTestBDBodies(); } @After public void tearDown() throws Exception { TestUtils.deleteDatabaseQuietly(database); TestUtils.deleteTempTestingDir(datastoreManagerPath); } private void createTestBDBodies() throws IOException { dbBodies = new ArrayList<DocumentBody>(); for (int i = 0; i < 7; i++) { dbBodies.add(TestUtils.createBDBody("fixture/index" + "_" + i + ".json")); } } @Test public void close_sqlDatabaseShouldBeClosed() { Assert.assertTrue(indexManager.getDatabase().isOpen()); indexManager.onDatastoreClosed(null); Assert.assertFalse(indexManager.getDatabase().isOpen()); } @Test public void getIndex_newIndex_allDataShouldBePersistent() throws IndexExistsException { indexManager.ensureIndexed("A", "a"); Index index = indexManager.getIndex("A"); Assert.assertEquals("A", index.getName()); Assert.assertTrue(index.getLastSequence().equals(-1l)); } @Test public void getIndex_indexNotExist_returnNull() { Index index = indexManager.getIndex("Z"); Assert.assertNull(index); } @Test public void ensuredIndexed_newIndex_indexShouldBeCreatedAndPersistent() throws IndexExistsException, SQLException { { // String index indexManager.ensureIndexed("Album", "album"); Index album = indexManager.getIndex("Album"); assertIndexDataFields("Album", "album", IndexType.STRING, -1L, album); String tableName = String.format(IndexManager.TABLE_INDEX_NAME_FORMAT, "Album"); assertTableCreated(tableName); } { // Integer index indexManager.ensureIndexed("a_123", "abc_123", IndexType.INTEGER); Index artist = indexManager.getIndex("a_123"); assertIndexDataFields("a_123", "abc_123", IndexType.INTEGER, -1L, artist); String tableName = String.format(IndexManager.TABLE_INDEX_NAME_FORMAT, "a_123"); assertTableCreated(tableName); } } private void assertIndexDataFields(String expectedIndexName, String expectedFieldName, IndexType expectedIndexType, Long expectLastSequence, Index actual) throws SQLException { Assert.assertEquals(expectedIndexName, actual.getName()); Assert.assertEquals(expectedIndexType, actual.getIndexType()); Assert.assertTrue(actual.getLastSequence().equals(expectLastSequence)); } private void assertTableCreated(String name) throws SQLException { Set<String> tables = SQLDatabaseTestUtils.getAllTableNames(database); Assert.assertTrue(tables.contains(name)); } @Test(expected = IndexExistsException.class) public void ensuredIndexed_duplicatedIndexNamesButDifferentField() throws IndexExistsException { indexManager.ensureIndexed("Album", "album"); indexManager.ensureIndexed("Album", "album2"); } @Test(expected = IndexExistsException.class) public void ensuredIndexed_duplicatedIndexNamesButDifferentType() throws IndexExistsException { indexManager.ensureIndexed("Album", "album"); indexManager.ensureIndexed("Album", "album", IndexType.INTEGER); } @Test(expected = IndexExistsException.class) public void ensuredIndexed_duplicatedIndexes() throws IndexExistsException { indexManager.ensureIndexed("Album", "album"); indexManager.ensureIndexed("Album", "album"); } @Test public void getAllIndexes_allIndexesShouldBeReturned() throws IndexExistsException { Assert.assertEquals(0, indexManager.getAllIndexes().size()); Index albumIndex = createAndGetIndex("album", "album", IndexType.STRING); Assert.assertEquals(1, indexManager.getAllIndexes().size()); indexManager.ensureIndexed("artist", "artist", IndexType.INTEGER); Index artistIndex = indexManager.getIndex("artist"); Set<Index> indexes = indexManager.getAllIndexes(); Assert.assertEquals(2, indexes.size()); Assert.assertTrue(indexes.contains(albumIndex)); Assert.assertTrue(indexes.contains(artistIndex)); for (Index index : indexes) { if (index.getName().equals("album")) { assertSameIndex(index, albumIndex); } else if (index.getName().equals("artist")) { assertSameIndex(index, artistIndex); } } } private void assertSameIndex(Index l, Index r) { Assert.assertEquals(l.getName(), r.getName()); Assert.assertEquals(l.getIndexType(), r.getIndexType()); Assert.assertEquals(l.getLastSequence(), r.getLastSequence()); } @Test public void deleteIndex_delete_indexShouldBeDeleted() throws IndexExistsException, SQLException { Index index = createAndGetIndex("album", "album", IndexType.STRING); Assert.assertNotNull(index); indexManager.deleteIndex(index.getName()); Assert.assertEquals(0, indexManager.getAllIndexes().size()); String indexTable = String.format(IndexManager.TABLE_INDEX_NAME_FORMAT, "album"); assertTableDeleted(indexTable); } private void assertTableDeleted(String table) throws SQLException { Set<String> tables = SQLDatabaseTestUtils.getAllTableNames(database); Assert.assertFalse(tables.contains(table)); } @Test(expected = IllegalArgumentException.class) public void deleteIndex_indexNotExist_exception() { Assert.assertNull(indexManager.getIndex("Z")); indexManager.deleteIndex("Z"); } @Test public void ensuredIndexed_documentExistBeforeIndexCreated_existingDocShouldBeIndexed() throws IndexExistsException, SQLException { DocumentRevision obj0 = datastore.createDocument(dbBodies.get(0)); DocumentRevision obj1 = datastore.createDocument(dbBodies.get(1)); DocumentRevision obj2 = datastore.createDocument(dbBodies.get(2)); Index index = createAndGetIndex("album", "album", IndexType.STRING); IndexTestUtils.assertDBObjectNotInIndex(database, index, obj0); IndexTestUtils.assertDBObjectInIndex(database, index, "album", obj1); IndexTestUtils.assertDBObjectInIndex(database, index, "album", obj2); } @Test public void ensure_custom_indexing_function_used_non_null_indexed() throws IndexExistsException, SQLException { DocumentRevision obj0 = datastore.createDocument(dbBodies.get(0)); DocumentRevision obj1 = datastore.createDocument(dbBodies.get(1)); DocumentRevision obj2 = datastore.createDocument(dbBodies.get(2)); final class TestIF implements IndexFunction<String> { public List<String> indexedValues(String indexName, Map map) { return Arrays.asList("fred"); } } this.indexManager.ensureIndexed("album", IndexType.STRING, new TestIF()); Index index = indexManager.getIndex("album"); for (DocumentRevision obj : new DocumentRevision[] { obj0, obj1, obj2 }) { IndexTestUtils.assertDBObjectInIndexWithValue(database, index, obj, "fred"); } } @Test public void ensure_custom_indexing_function_used_null_not_indexed() throws IndexExistsException, SQLException { DocumentRevision obj0 = datastore.createDocument(dbBodies.get(0)); DocumentRevision obj1 = datastore.createDocument(dbBodies.get(1)); DocumentRevision obj2 = datastore.createDocument(dbBodies.get(2)); final class TestIF implements IndexFunction<String> { public List<String> indexedValues(String indexName, Map map) { return null; } } this.indexManager.ensureIndexed("album", IndexType.STRING, new TestIF()); Index index = indexManager.getIndex("album"); for (DocumentRevision obj : new DocumentRevision[] { obj0, obj1, obj2 }) { IndexTestUtils.assertDBObjectNotInIndex(database, index, obj); } } @Test public void createStringIndex_documentInsertedAfterIndexCreated_documentShouldBeIndexed() throws IndexExistsException, SQLException { Index index = createAndGetIndex("album", "album", IndexType.STRING); DocumentRevision obj0 = datastore.createDocument(dbBodies.get(0)); DocumentRevision obj1 = datastore.createDocument(dbBodies.get(1)); DocumentRevision obj2 = datastore.createDocument(dbBodies.get(2)); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectNotInIndex(database, index, obj0); IndexTestUtils.assertDBObjectInIndex(database, index, "album", obj1); IndexTestUtils.assertDBObjectInIndex(database, index, "album", obj2); } @Test public void createLongIndex_documentInsertedAfterIndexCreated_documentShouldBeIndexed() throws IndexExistsException, SQLException { indexManager.ensureIndexed("class", "class", IndexType.INTEGER); Index index = indexManager.getIndex("class"); { DocumentRevision obj0 = datastore.createDocument(dbBodies.get(0)); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectNotInIndex(database, index, obj0); } { DocumentRevision obj1 = datastore.createDocument(dbBodies.get(1)); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectInIndex(database, index, "class", obj1); } } @Test public void createLongIndex_indexValueIsBigLong_documentShouldBeIndexed() throws IndexExistsException, SQLException, IOException { indexManager.ensureIndexed("class", "class", IndexType.INTEGER); Index index = indexManager.getIndex("class"); byte[] data = FileUtils.readFileToByteArray(new File("fixture/index_really_big_long.json")); DocumentRevision obj1 = datastore.createDocument(DocumentBodyFactory.create(data)); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectInIndex(database, index, "class", obj1); } @Test public void createLongIndex_indexValueIsFloat_documentShouldBeIndexed() throws IndexExistsException, SQLException, IOException { indexManager.ensureIndexed("class", "class", IndexType.INTEGER); Index index = indexManager.getIndex("class"); byte[] data = FileUtils.readFileToByteArray(new File("fixture/index_float.json")); DocumentRevision obj1 = datastore.createDocument(DocumentBodyFactory.create(data)); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectInIndex(database, index, "class", obj1); } @Test public void createLongIndex_indexValueConversionNotSupported_documentShouldNotBeIndexed() throws IndexExistsException, SQLException, IOException { indexManager.ensureIndexed("class", "class", IndexType.INTEGER); Index index = indexManager.getIndex("class"); byte[] data = FileUtils.readFileToByteArray(new File("fixture/index_string.json")); DocumentRevision obj1 = datastore.createDocument(DocumentBodyFactory.create(data)); IndexTestUtils.assertDBObjectNotInIndex(database, index, obj1); } @Test public void indexString_documentWithValidField_indexRowShouldBeAdded() throws IndexExistsException, SQLException, IOException { Index index = createAndGetIndex("StringIndex", "stringIndex", IndexType.STRING); byte[] data = FileUtils.readFileToByteArray(new File("fixture/string_index_valid_field.json")); DocumentRevision obj = datastore.createDocument(DocumentBodyFactory.create(data)); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectInIndex(database, index, "stringIndex", obj); } @Test public void indexString_documentWithInvalidField_indexRowShouldNotBeAdded() throws IndexExistsException, IOException, SQLException { Index index = createAndGetIndex("StringIndex", "stringIndex", IndexType.STRING); byte[] data = FileUtils.readFileToByteArray(new File("fixture/string_index_invalid_field.json")); DocumentRevision obj = datastore.createDocument(DocumentBodyFactory.create(data)); IndexTestUtils.assertDBObjectNotInIndex(database, index, obj); } @Test public void indexString_documentUpdated_indexRowShouldBeUpdated() throws Exception { Index index = createAndGetIndex("stringIndex", "stringIndex", IndexType.STRING); DocumentRevision obj = datastore .createDocument(TestUtils.createBDBody("fixture/string_index_valid_field.json")); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectInIndex(database, index, "stringIndex", obj); DocumentRevision obj2 = datastore.updateDocument(obj.getId(), obj.getRevision(), TestUtils.createBDBody("fixture/string_index_valid_field_updated.json")); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectInIndex(database, index, "stringIndex", obj2); } @Test public void indexString_documentUpdatedFromInvalidToValidValue_indexRowShouldBeUpdated() throws Exception { Index index = createAndGetIndex("stringIndex", "stringIndex", IndexType.STRING); DocumentRevision obj = datastore .createDocument(TestUtils.createBDBody("fixture/string_index_invalid_field.json")); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectNotInIndex(database, index, obj); DocumentRevision obj2 = datastore.updateDocument(obj.getId(), obj.getRevision(), TestUtils.createBDBody("fixture/string_index_valid_field.json")); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectInIndex(database, index, "stringIndex", obj2); } private Index createAndGetIndex(String indexName, String filedName, IndexType type) throws IndexExistsException { indexManager.ensureIndexed(indexName, filedName, type); return indexManager.getIndex(indexName); } @Test public void indexString_documentUpdatedWithInvalidValue_indexValueShouldBeRemoved() throws Exception { Index index = createAndGetIndex("stringIndex", "stringIndex", IndexType.STRING); byte[] data = FileUtils.readFileToByteArray(new File("fixture/string_index_valid_field.json")); DocumentRevision obj = datastore.createDocument(DocumentBodyFactory.create(data)); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectInIndex(database, index, "stringIndex", obj); byte[] data2 = FileUtils.readFileToByteArray(new File("fixture/string_index_invalid_field.json")); DocumentRevision obj2 = datastore.updateDocument(obj.getId(), obj.getRevision(), DocumentBodyFactory.create(data2)); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectNotInIndex(database, index, obj2); } @Test public void indexString_documentDeleted_indexRowShouldBeRemoved() throws Exception { Index index = createAndGetIndex("stringIndex", "stringIndex", IndexType.STRING); byte[] data = FileUtils.readFileToByteArray(new File("fixture/string_index_valid_field.json")); DocumentRevision obj = datastore.createDocument(DocumentBodyFactory.create(data)); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectInIndex(database, index, "stringIndex", obj); datastore.deleteDocument(obj.getId(), obj.getRevision()); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectNotInIndex(database, index, obj); } @Test public void indexInteger_documentCreated_indexRowShouldBeAdded() throws Exception { Index index = createAndGetIndex("integerIndex", "integerIndex", IndexType.INTEGER); byte[] data = FileUtils.readFileToByteArray(new File("fixture/integer_index_valid_field.json")); DocumentRevision obj = datastore.createDocument(DocumentBodyFactory.create(data)); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectInIndex(database, index, "integerIndex", obj); } @Test public void indexInteger_documentCreatedWithInvalidIndexValue_indexRowShouldNotBeAdded() throws Exception { Index index = createAndGetIndex("integerIndex", "integerIndex", IndexType.INTEGER); byte[] data = FileUtils.readFileToByteArray(new File("fixture/integer_index_invalid_field.json")); DocumentRevision obj = datastore.createDocument(DocumentBodyFactory.create(data)); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectNotInIndex(database, index, obj); } @Test public void indexInteger_documentUpdated_indexValueShouldBeUpdate() throws Exception { Index index = createAndGetIndex("integerIndex", "integerIndex", IndexType.INTEGER); byte[] data = FileUtils.readFileToByteArray(new File("fixture/integer_index_valid_field.json")); DocumentRevision obj = datastore.createDocument(DocumentBodyFactory.create(data)); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectInIndex(database, index, "integerIndex", obj); byte[] data2 = FileUtils.readFileToByteArray(new File("fixture/integer_index_valid_field_updated.json")); DocumentRevision obj2 = datastore.createDocument(DocumentBodyFactory.create(data2)); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectInIndex(database, index, "integerIndex", obj2); } @Test public void indexInteger_documentUpdatedFromInvalidToValidValue_indexValueShouldBeUpdate() throws Exception { Index index = createAndGetIndex("integerIndex", "integerIndex", IndexType.INTEGER); byte[] data = FileUtils.readFileToByteArray(new File("fixture/integer_index_invalid_field.json")); DocumentRevision obj = datastore.createDocument(DocumentBodyFactory.create(data)); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectNotInIndex(database, index, obj); byte[] data2 = FileUtils.readFileToByteArray(new File("fixture/integer_index_valid_field.json")); DocumentRevision obj2 = datastore.createDocument(DocumentBodyFactory.create(data2)); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectInIndex(database, index, "integerIndex", obj2); } @Test public void indexInteger_documentUpdatedWithInvalidIndexValue_indexRowShouldBeRemoved() throws Exception { Index index = createAndGetIndex("integerIndex", "integerIndex", IndexType.INTEGER); byte[] data = FileUtils.readFileToByteArray(new File("fixture/integer_index_valid_field.json")); DocumentRevision obj = datastore.createDocument(DocumentBodyFactory.create(data)); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectInIndex(database, index, "integerIndex", obj); byte[] data2 = FileUtils.readFileToByteArray(new File("fixture/integer_index_invalid_field.json")); DocumentRevision obj2 = datastore.createDocument(DocumentBodyFactory.create(data2)); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectNotInIndex(database, index, obj2); } @Test public void indexInteger_documentDeleted_indexRowShouldBeRemoved() throws Exception { Index index = createAndGetIndex("integerIndex", "integerIndex", IndexType.INTEGER); byte[] data = FileUtils.readFileToByteArray(new File("fixture/integer_index_valid_field.json")); DocumentRevision obj = datastore.createDocument(DocumentBodyFactory.create(data)); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectInIndex(database, index, "integerIndex", obj); datastore.deleteDocument(obj.getId(), obj.getRevision()); indexManager.updateAllIndexes(); IndexTestUtils.assertDBObjectNotInIndex(database, index, obj); } @Test public void multiIndex_documentWithoutTheField_notIndexed() throws IndexExistsException, SQLException { Index index = createAndGetIndex("Genre", "Genre", IndexType.STRING); DocumentRevision rev = datastore.createDocument(dbBodies.get(0)); indexManager.updateAllIndexes(); Index index2 = indexManager.getIndex("Genre"); Assert.assertEquals(Long.valueOf(datastore.getLastSequence()), Long.valueOf(index2.getLastSequence())); this.assertIndexed(database, index, rev.getId()); } @Test public void multiIndex_documentFieldWithListOfTwo_twoIndexValueAdded() throws IndexExistsException, SQLException { Index index = createAndGetIndex("Genre", "Genre", IndexType.STRING); DocumentRevision rev = datastore.createDocument(dbBodies.get(1)); indexManager.updateAllIndexes(); Index index2 = indexManager.getIndex("Genre"); Assert.assertEquals(Long.valueOf(datastore.getLastSequence()), Long.valueOf(index2.getLastSequence())); this.assertIndexed(database, index, rev.getId(), "Pop", "Rock"); } @Test public void multiIndex_documentFieldWithListOfOne_oneIndexValueAdded() throws IndexExistsException, SQLException { Index index = createAndGetIndex("Genre", "Genre", IndexType.STRING); DocumentRevision rev = datastore.createDocument(dbBodies.get(3)); indexManager.updateAllIndexes(); Index index2 = indexManager.getIndex("Genre"); Assert.assertEquals(Long.valueOf(datastore.getLastSequence()), Long.valueOf(index2.getLastSequence())); this.assertIndexed(database, index, rev.getId(), "Pop"); } @Test public void multiIndex_documentFieldWithDuplicatedValues_noDuplicatedValuesAdded() throws IndexExistsException, SQLException { Index index = createAndGetIndex("Genre", "Genre", IndexType.STRING); DocumentRevision rev = datastore.createDocument(dbBodies.get(4)); // this Document has field: genre: [ "Pop", "Pop" ], and assert // only one entry added for "Pop" indexManager.updateAllIndexes(); Index index2 = indexManager.getIndex("Genre"); Assert.assertEquals(Long.valueOf(datastore.getLastSequence()), Long.valueOf(index2.getLastSequence())); this.assertIndexed(database, index, rev.getId(), "Pop"); } @Test public void index_fieldWithUnsupportedValue_unsupportedValueShouldBeIgnored() throws IndexExistsException, SQLException, IOException { DocumentBody body = TestUtils.createBDBody("fixture/index_with_unsupported_value.json"); DocumentRevision rev = datastore.createDocument(body); Index index = createAndGetIndex("Genre", "Genre", IndexType.STRING); Assert.assertEquals(Long.valueOf(datastore.getLastSequence()), Long.valueOf(index.getLastSequence())); this.assertIndexed(database, index, rev.getId(), "Pop", "Rock"); } @Test public void index_valueWithLeadingTailingSpaces_spacesRemoved() throws IndexExistsException, SQLException, IOException { DocumentBody body = TestUtils.createBDBody("fixture/index_with_spaces.json"); DocumentRevision rev = datastore.createDocument(body); Index index = createAndGetIndex("Genre", "Genre", IndexType.STRING); Assert.assertEquals(Long.valueOf(datastore.getLastSequence()), Long.valueOf(index.getLastSequence())); this.assertIndexed(database, index, rev.getId(), " Pop", "Rock ", " R & B "); } @Test public void index_fieldWith10KValues_allValuesShouldBeAdded() throws IndexExistsException, SQLException, IOException { Map m = new HashMap<String, String>(); List<String> tags = new ArrayList<String>(100000); for (int i = 0; i < 100000; i++) { tags.add("tag" + i); } m.put("Tag", tags); DocumentBody body = DocumentBodyFactory.create(m); DocumentRevision rev = datastore.createDocument(body); Index index = createAndGetIndex("Tag", "Tag", IndexType.STRING); Assert.assertEquals(Long.valueOf(datastore.getLastSequence()), Long.valueOf(index.getLastSequence())); this.assertIndexed(database, index, rev.getId(), tags.toArray(new String[] {})); } // test that index updates itself on create/update/delete @Test public void index_UpdateCrud() throws IndexExistsException, SQLException, ConflictException { Index index = createAndGetIndex("title", "title", IndexType.STRING); // create DocumentRevision obj1 = datastore.createDocument(dbBodies.get(1)); this.assertNotIndexed(database, index, obj1.getId()); // update Map<String, Object> map = obj1.getBody().asMap(); map.put("title", "Another Green Day"); DocumentBody body = DocumentBodyFactory.create(map); DocumentRevision obj2 = datastore.updateDocument(obj1.getId(), obj1.getRevision(), body); Assert.assertEquals(obj1.getId(), obj2.getId()); this.assertNotIndexed(database, index, obj2.getId()); // delete datastore.deleteDocument(obj2.getId(), obj2.getRevision()); this.assertNotIndexed(database, index, obj2.getId()); } /** * A sanity-check that updating the datastore from many threads * doesn't cause the index manager to balk. */ @Test public void index_UpdateCrudMultiThreaded() throws IndexExistsException, SQLException, ConflictException, InterruptedException { int n_threads = 3; final int n_docs = 100; // We'll later search for search == success final Map<String, String> matching = ImmutableMap.of("search", "success"); final Map<String, String> nonmatching = ImmutableMap.of("search", "failure"); indexManager.ensureIndexed("search", "search", IndexType.STRING); final List<String> matching_ids = Collections.synchronizedList(new ArrayList<String>()); // When run, this thread creates n_docs documents with unique // names in the datastore. A subset of these // will be matched by our query to the datastore later, which // we record in the matching_ids list. class PopulateThread extends Thread { @Override public void run() { String docId; final String thread_id; DocumentBody body; thread_id = Thread.currentThread().getName(); for (int i = 0; i < n_docs; i++) { docId = String.format("%s-%s", thread_id, i); if ((i % 2) == 0) { // even numbers create matching docs body = DocumentBodyFactory.create(matching); matching_ids.add(docId); } else { body = DocumentBodyFactory.create(nonmatching); } datastore.createDocument(docId, body); } // we're not on the main thread, so we must close our own connection datastore.getSQLDatabase().close(); } } List<Thread> threads = new ArrayList<Thread>(); // Create, start and wait for the threads to complete for (int i = 0; i < n_threads; i++) { threads.add(new PopulateThread()); } for (Thread t : threads) { t.start(); } for (Thread t : threads) { t.join(); } // Check appropriate entries in index QueryBuilder q = new QueryBuilder(); q.index("search").equalTo("success"); QueryResult result = indexManager.query(q.build()); List<DocumentRevision> docRevisions = Lists.newArrayList(result); List<String> docIds = new ArrayList<String>(); for (DocumentRevision r : docRevisions) { docIds.add(r.getId()); } Assert.assertEquals(matching_ids.size(), docIds.size()); for (String id : matching_ids) { Assert.assertTrue(docIds.contains(id)); } } /** * Test to be sure that calling updateAllIndexes doesn't throw * an exception for indexes that exist in the database but haven't * yet been registered with that IndexManager object. */ @Test public void index_UpdateAllIndexesDoesNotFailForUnregisteredIndexes() throws IndexExistsException, SQLException, ConflictException, IOException { IndexManager im1 = new IndexManager(datastore); im1.ensureIndexed("title", "title", IndexType.STRING); // create Map<String, Object> map = new HashMap<String, Object>(); map.put("title", "Another Green Day"); DocumentBody body = DocumentBodyFactory.create(map); datastore.createDocument(body); IndexManager im2 = new IndexManager(datastore); im2.updateAllIndexes(); } private void assertNotIndexed(SQLDatabase database, Index index, String docId) throws SQLException { String table = String.format(IndexManager.TABLE_INDEX_NAME_FORMAT, index.getName()); Cursor cursor = database.rawQuery("SELECT count(*) FROM " + table + " where docid = ? ", new String[] { String.valueOf(docId) }); Assert.assertTrue(cursor.moveToFirst()); Assert.assertEquals(Integer.valueOf(0), Integer.valueOf(cursor.getInt(0))); } private void assertIndexed(SQLDatabase database, Index index, String docId, String... expectedValues) throws SQLException { String table = String.format(IndexManager.TABLE_INDEX_NAME_FORMAT, index.getName()); assertIndexedValueCount(database, docId, table, expectedValues); for (String value : expectedValues) { assertIndexedValue(database, docId, table, value); } } private void assertIndexedValue(SQLDatabase database, String docId, String table, String value) throws SQLException { Cursor cursor = database.rawQuery("SELECT count(*) FROM " + table + " where docid = ? and value = ? ", new String[] { String.valueOf(docId), value }); Assert.assertTrue(cursor.moveToFirst()); Assert.assertEquals(Integer.valueOf(1), Integer.valueOf(cursor.getInt(0))); } private void assertIndexedValueCount(SQLDatabase database, String docId, String table, String[] expectedValues) throws SQLException { Cursor cursor = database.rawQuery("SELECT count(*) FROM " + table + " where docid = ? ", new String[] { String.valueOf(docId) }); Assert.assertTrue(cursor.moveToFirst()); Assert.assertEquals(Integer.valueOf(expectedValues.length), Integer.valueOf(cursor.getInt(0))); } }