Repository: metamodel Updated Branches: refs/heads/master 6fc258f8e -> 39947f559
http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/mongodb/mongo3/src/main/java/org/apache/metamodel/mongodb/mongo3/MongoDbDataContext.java ---------------------------------------------------------------------- diff --git a/mongodb/mongo3/src/main/java/org/apache/metamodel/mongodb/mongo3/MongoDbDataContext.java b/mongodb/mongo3/src/main/java/org/apache/metamodel/mongodb/mongo3/MongoDbDataContext.java index c7a7487..c625103 100644 --- a/mongodb/mongo3/src/main/java/org/apache/metamodel/mongodb/mongo3/MongoDbDataContext.java +++ b/mongodb/mongo3/src/main/java/org/apache/metamodel/mongodb/mongo3/MongoDbDataContext.java @@ -26,11 +26,13 @@ import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; +import java.util.function.Consumer; import java.util.regex.Pattern; import java.util.stream.Collectors; import org.apache.metamodel.DataContext; import org.apache.metamodel.MetaModelException; +import org.apache.metamodel.MetaModelHelper; import org.apache.metamodel.QueryPostprocessDataContext; import org.apache.metamodel.UpdateScript; import org.apache.metamodel.UpdateSummary; @@ -71,10 +73,8 @@ import com.mongodb.client.MongoIterable; /** * DataContext implementation for MongoDB. * - * Since MongoDB has no schema, a virtual schema will be used in this - * DataContext. This implementation supports either automatic discovery of a - * schema or manual specification of a schema, through the - * {@link SimpleTableDef} class. + * Since MongoDB has no schema, a virtual schema will be used in this DataContext. This implementation supports either + * automatic discovery of a schema or manual specification of a schema, through the {@link SimpleTableDef} class. */ public class MongoDbDataContext extends QueryPostprocessDataContext implements UpdateableDataContext { @@ -86,17 +86,13 @@ public class MongoDbDataContext extends QueryPostprocessDataContext implements U private Schema _schema; /** - * Constructs a {@link MongoDbDataContext}. This constructor accepts a - * custom array of {@link SimpleTableDef}s which allows the user to define - * his own view on the collections in the database. + * Constructs a {@link MongoDbDataContext}. This constructor accepts a custom array of {@link SimpleTableDef}s which + * allows the user to define his own view on the collections in the database. * - * @param mongoDb - * the mongo db connection - * @param tableDefs - * an array of {@link SimpleTableDef}s, which define the table - * and column model of the mongo db collections. (consider using - * {@link #detectSchema(MongoDatabase)} or {@link #detectTable(MongoDatabase, String)} - * ). + * @param mongoDb the mongo db connection + * @param tableDefs an array of {@link SimpleTableDef}s, which define the table and column model of the mongo db + * collections. (consider using {@link #detectSchema(MongoDatabase)} or + * {@link #detectTable(MongoDatabase, String)} ). */ public MongoDbDataContext(MongoDatabase mongoDb, SimpleTableDef... tableDefs) { super(false); @@ -106,25 +102,21 @@ public class MongoDbDataContext extends QueryPostprocessDataContext implements U } /** - * Constructs a {@link MongoDbDataContext} and automatically detects the - * schema structure/view on all collections (see {@link #detectSchema(MongoDatabase)}). + * Constructs a {@link MongoDbDataContext} and automatically detects the schema structure/view on all collections + * (see {@link #detectSchema(MongoDatabase)}). * - * @param mongoDb - * the mongo db connection + * @param mongoDb the mongo db connection */ public MongoDbDataContext(MongoDatabase mongoDb) { this(mongoDb, detectSchema(mongoDb)); } /** - * Performs an analysis of the available collections in a Mongo {@link DB} - * instance and tries to detect the table's structure based on the first - * 1000 documents in each collection. + * Performs an analysis of the available collections in a Mongo {@link DB} instance and tries to detect the table's + * structure based on the first 1000 documents in each collection. * - * @param mongoDb - * the mongo db to inspect - * @return a mutable schema instance, useful for further fine tuning by the - * user. + * @param mongoDb the mongo db to inspect + * @return a mutable schema instance, useful for further fine tuning by the user. * @see #detectTable(MongoDatabase, String) */ public static SimpleTableDef[] detectSchema(MongoDatabase mongoDb) { @@ -139,18 +131,15 @@ public class MongoDbDataContext extends QueryPostprocessDataContext implements U } /** - * Performs an analysis of an available collection in a Mongo {@link DB} - * instance and tries to detect the table structure based on the first 1000 - * documents in the collection. + * Performs an analysis of an available collection in a Mongo {@link DB} instance and tries to detect the table + * structure based on the first 1000 documents in the collection. * - * @param mongoDb - * the mongo DB - * @param collectionName - * the name of the collection + * @param mongoDb the mongo DB + * @param collectionName the name of the collection * @return a table definition for mongo db. */ public static SimpleTableDef detectTable(MongoDatabase mongoDb, String collectionName) { - + final MongoCollection<Document> collection = mongoDb.getCollection(collectionName); final FindIterable<Document> iterable = collection.find().limit(1000); @@ -169,7 +158,7 @@ public class MongoDbDataContext extends QueryPostprocessDataContext implements U } } } - + final String[] columnNames = new String[columnsAndTypes.size()]; final ColumnType[] columnTypes = new ColumnType[columnsAndTypes.size()]; @@ -226,7 +215,15 @@ public class MongoDbDataContext extends QueryPostprocessDataContext implements U protected Number executeCountQuery(Table table, List<FilterItem> whereItems, boolean functionApproximationAllowed) { final MongoCollection<Document> collection = _mongoDb.getCollection(table.getName()); - final Document query = createMongoDbQuery(table, whereItems); + final List<FilterItem> postProcessFilters = new ArrayList<>(); + final Document query = createMongoDbQuery(table, whereItems, whereItem -> { + postProcessFilters.add(whereItem); + }); + + if (!postProcessFilters.isEmpty()) { + // not possible to use the native API for this + return null; + } logger.info("Executing MongoDB 'count' query: {}", query); final long count = collection.count(query); @@ -239,16 +236,16 @@ public class MongoDbDataContext extends QueryPostprocessDataContext implements U Object keyValue) { final MongoCollection<Document> collection = _mongoDb.getCollection(table.getName()); - List<FilterItem> whereItems = new ArrayList<FilterItem>(); - SelectItem selectItem = new SelectItem(primaryKeyColumn); - FilterItem primaryKeyWhereItem = new FilterItem(selectItem, OperatorType.EQUALS_TO, keyValue); + final List<FilterItem> whereItems = new ArrayList<FilterItem>(); + final SelectItem selectItem = new SelectItem(primaryKeyColumn); + final FilterItem primaryKeyWhereItem = new FilterItem(selectItem, OperatorType.EQUALS_TO, keyValue); whereItems.add(primaryKeyWhereItem); - final Document query = createMongoDbQuery(table, whereItems); + final Document query = createMongoDbQuery(table, whereItems, null); final Document resultDoc = collection.find(query).first(); - DataSetHeader header = new SimpleDataSetHeader(selectItems); + final DataSetHeader header = new SimpleDataSetHeader(selectItems); - Row row = MongoDBUtils.toRow(resultDoc, header); + final Row row = MongoDBUtils.toRow(resultDoc, header); return row; } @@ -289,7 +286,6 @@ public class MongoDbDataContext extends QueryPostprocessDataContext implements U // prepare for a non-post-processed query - // checking if the query is a primary key lookup query if (whereItems.size() == 1) { final FilterItem whereItem = whereItems.get(0); @@ -297,12 +293,14 @@ public class MongoDbDataContext extends QueryPostprocessDataContext implements U if (!whereItem.isCompoundFilter() && selectItem != null && selectItem.getColumn() != null) { final Column column = selectItem.getColumn(); if (column.isPrimaryKey() && OperatorType.EQUALS_TO.equals(whereItem.getOperator())) { - logger.debug("Query is a primary key lookup query. Trying executePrimaryKeyLookupQuery(...)"); + logger.debug( + "Query is a primary key lookup query. Trying executePrimaryKeyLookupQuery(...)"); final Object operand = whereItem.getOperand(); final Row row = executePrimaryKeyLookupQuery(table, selectItems, column, operand); if (row == null) { - logger.debug("DataContext did not return any primary key lookup query results. Proceeding " - + "with manual lookup."); + logger.debug( + "DataContext did not return any primary key lookup query results. Proceeding " + + "with manual lookup."); } else { final DataSetHeader header = new SimpleDataSetHeader(selectItems); return new InMemoryDataSet(header, row); @@ -324,16 +322,12 @@ public class MongoDbDataContext extends QueryPostprocessDataContext implements U } if (thereIsAtLeastOneAlias) { - final DataSet dataSet = materializeMainSchemaTableInternal( - table, - selectItems, - whereItems, - firstRow, - maxRows, false); + final DataSet dataSet = materializeMainSchemaTableInternal(table, selectItems, whereItems, + firstRow, maxRows, false); return dataSet; } else { - final DataSet dataSet = materializeMainSchemaTableInternal(table, selectItems, whereItems, firstRow, - maxRows, false); + final DataSet dataSet = materializeMainSchemaTableInternal(table, selectItems, whereItems, + firstRow, maxRows, false); return dataSet; } } @@ -344,19 +338,38 @@ public class MongoDbDataContext extends QueryPostprocessDataContext implements U return super.executeQuery(query); } + private DataSet materializeMainSchemaTableInternal(Table table, List<SelectItem> selectItems, + List<FilterItem> whereItems, int firstRow, int maxRows, boolean queryPostProcessed) { + final List<FilterItem> postProcessWhereItems = new ArrayList<>(); + final MongoCursor<Document> cursor = getDocumentMongoCursor(table, whereItems, firstRow, maxRows, whereItem -> { + postProcessWhereItems.add(whereItem); + }); - private DataSet materializeMainSchemaTableInternal(Table table, List<SelectItem> selectItems, List<FilterItem> whereItems, - int firstRow, int maxRows, boolean queryPostProcessed) { - MongoCursor<Document> cursor = getDocumentMongoCursor(table, whereItems, firstRow, maxRows); - - return new MongoDbDataSet(cursor, selectItems, queryPostProcessed); + final DataSet dataSet; + if (postProcessWhereItems.isEmpty()) { + dataSet = new MongoDbDataSet(cursor, selectItems, queryPostProcessed); + } else { + final List<SelectItem> selectItemsToQuery = new ArrayList<>(selectItems); + postProcessWhereItems.forEach(whereItem -> { + final Column column = whereItem.getSelectItem().getColumn(); + if (column != null) { + // TODO: Minor optimization possible here to avoid having multiple select items for the same column. + // We could check if the column is already being queried. + selectItemsToQuery.add(new SelectItem(column)); + } + }); + final DataSet innerDataSet1 = new MongoDbDataSet(cursor, selectItemsToQuery, queryPostProcessed); + final DataSet innerDataSet2 = MetaModelHelper.getFiltered(innerDataSet1, postProcessWhereItems); + dataSet = MetaModelHelper.getSelection(selectItems, innerDataSet2); + } + return dataSet; } private MongoCursor<Document> getDocumentMongoCursor(Table table, List<FilterItem> whereItems, int firstRow, - int maxRows) { + int maxRows, Consumer<FilterItem> filterItemsToPostProcessConsumer) { final MongoCollection<Document> collection = _mongoDb.getCollection(table.getName()); - final Document query = createMongoDbQuery(table, whereItems); + final Document query = createMongoDbQuery(table, whereItems, filterItemsToPostProcessConsumer); logger.info("Executing MongoDB 'find' query: {}", query); FindIterable<Document> iterable = collection.find(query); @@ -372,13 +385,18 @@ public class MongoDbDataContext extends QueryPostprocessDataContext implements U return iterable.iterator(); } - protected Document createMongoDbQuery(Table table, List<FilterItem> whereItems) { + protected Document createMongoDbQuery(Table table, List<FilterItem> whereItems, + Consumer<FilterItem> whereItemToPostProcessConsumer) { assert _schema == table.getSchema(); final Document query = new Document(); if (whereItems != null && !whereItems.isEmpty()) { for (FilterItem item : whereItems) { - convertToCursorObject(query, item); + final boolean converted = convertToCursorObject(query, item); + if (!converted) { + // it wasn't possible to push down the filter item + whereItemToPostProcessConsumer.accept(item); + } } } @@ -387,52 +405,74 @@ public class MongoDbDataContext extends QueryPostprocessDataContext implements U private static Object convertArrayToList(Object arr) { if (arr instanceof boolean[]) { - return Arrays.asList((boolean[])arr); + return Arrays.asList((boolean[]) arr); } else if (arr instanceof byte[]) { - return Arrays.asList((byte[])arr); + return Arrays.asList((byte[]) arr); } else if (arr instanceof short[]) { - return Arrays.asList((short[])arr); + return Arrays.asList((short[]) arr); } else if (arr instanceof char[]) { - return Arrays.asList((char[])arr); + return Arrays.asList((char[]) arr); } else if (arr instanceof int[]) { - return Arrays.asList((int[])arr); + return Arrays.asList((int[]) arr); } else if (arr instanceof long[]) { - return Arrays.asList((long[])arr); + return Arrays.asList((long[]) arr); } else if (arr instanceof float[]) { - return Arrays.asList((float[])arr); + return Arrays.asList((float[]) arr); } else if (arr instanceof double[]) { - return Arrays.asList((double[])arr); + return Arrays.asList((double[]) arr); } else if (arr instanceof Object[]) { - return Arrays.asList((Object[])arr); + return Arrays.asList((Object[]) arr); } // It's not an array. return null; } - - private void convertToCursorObject(Document query, FilterItem item) { + + /** + * Attempts to convert a FilterItem into a refinement of a MongoDB query + * + * @param query + * @param item + * @return true if the conversion was successful, false if not + */ + private boolean convertToCursorObject(Document query, FilterItem item) { if (item.isCompoundFilter()) { - List<Document> orList = new ArrayList<Document>(); + final List<Document> orList = new ArrayList<Document>(); final FilterItem[] childItems = item.getChildItems(); for (FilterItem childItem : childItems) { - Document childDoc = new Document(); - convertToCursorObject(childDoc, childItem); + final Document childDoc = new Document(); + boolean converted = convertToCursorObject(childDoc, childItem); + if (!converted) { + return false; + } orList.add(childDoc); } query.put("$or", orList); - + return true; } else { - final Column column = item.getSelectItem().getColumn(); + final SelectItem selectItem = item.getSelectItem(); + if (selectItem.hasFunction()) { + // at this point, (scalar) functions in filters aren't possible to push down to the query + return false; + } + + final Column column = selectItem.getColumn(); final String columnName = column.getName(); - final String operatorName = getOperatorName(item); + final String operatorName; + try { + operatorName = getOperatorName(item); + } catch (UnsupportedOperationException e) { + // not possible to push this operator down to the query + return false; + } Object operand = item.getOperand(); if (ObjectId.isValid(String.valueOf(operand))) { operand = new ObjectId(String.valueOf(operand)); - } else if (operand != null && operand.getClass().isArray()){ + } else if (operand != null && operand.getClass().isArray()) { operand = convertArrayToList(operand); } @@ -449,15 +489,18 @@ public class MongoDbDataContext extends QueryPostprocessDataContext implements U } } else { if (operatorName == null) { - throw new IllegalStateException("Cannot retrieve records for a column with two EQUALS_TO operators"); + throw new IllegalStateException( + "Cannot retrieve records for a column with two EQUALS_TO operators"); } else { existingFilterObject.append(operatorName, operand); } } + + return true; } } - private String getOperatorName(FilterItem item) { + private String getOperatorName(FilterItem item) throws UnsupportedOperationException { final OperatorType operator = item.getOperator(); if (OperatorType.EQUALS_TO.equals(operator)) { @@ -485,7 +528,7 @@ public class MongoDbDataContext extends QueryPostprocessDataContext implements U return "$in"; } - throw new IllegalStateException("Unsupported operator type: " + operator); + throw new UnsupportedOperationException("Unsupported operator type: " + operator); } private Pattern turnOperandIntoRegExp(Object operand) { @@ -501,24 +544,14 @@ public class MongoDbDataContext extends QueryPostprocessDataContext implements U @Override protected DataSet materializeMainSchemaTable(Table table, List<Column> columns, int maxRows) { - return materializeMainSchemaTableInternal( - table, - columns.stream().map(SelectItem::new).collect(Collectors.toList()), - null, - 1, - maxRows, - true); + return materializeMainSchemaTableInternal(table, + columns.stream().map(SelectItem::new).collect(Collectors.toList()), null, 1, maxRows, true); } @Override protected DataSet materializeMainSchemaTable(Table table, List<Column> columns, int firstRow, int maxRows) { - return materializeMainSchemaTableInternal( - table, - columns.stream().map(SelectItem::new).collect(Collectors.toList()), - null, - firstRow, - maxRows, - true); + return materializeMainSchemaTableInternal(table, + columns.stream().map(SelectItem::new).collect(Collectors.toList()), null, firstRow, maxRows, true); } /** @@ -547,8 +580,7 @@ public class MongoDbDataContext extends QueryPostprocessDataContext implements U } /** - * Gets the {@link WriteConcernAdvisor} to use on - * {@link #executeUpdate(UpdateScript)} calls. + * Gets the {@link WriteConcernAdvisor} to use on {@link #executeUpdate(UpdateScript)} calls. */ public WriteConcernAdvisor getWriteConcernAdvisor() { if (_writeConcernAdvisor == null) { @@ -558,8 +590,7 @@ public class MongoDbDataContext extends QueryPostprocessDataContext implements U } /** - * Sets a global {@link WriteConcern} advisor to use on - * {@link #executeUpdate(UpdateScript)}. + * Sets a global {@link WriteConcern} advisor to use on {@link #executeUpdate(UpdateScript)}. */ public void setWriteConcernAdvisor(WriteConcernAdvisor writeConcernAdvisor) { _writeConcernAdvisor = writeConcernAdvisor; @@ -567,7 +598,8 @@ public class MongoDbDataContext extends QueryPostprocessDataContext implements U /** * Gets the {@link DB} instance that this {@link DataContext} is backed by. - * @return + * + * @return */ public MongoDatabase getMongoDb() { return _mongoDb; http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/mongodb/mongo3/src/main/java/org/apache/metamodel/mongodb/mongo3/MongoDbDeleteBuilder.java ---------------------------------------------------------------------- diff --git a/mongodb/mongo3/src/main/java/org/apache/metamodel/mongodb/mongo3/MongoDbDeleteBuilder.java b/mongodb/mongo3/src/main/java/org/apache/metamodel/mongodb/mongo3/MongoDbDeleteBuilder.java index 714b8c2..be667d2 100644 --- a/mongodb/mongo3/src/main/java/org/apache/metamodel/mongodb/mongo3/MongoDbDeleteBuilder.java +++ b/mongodb/mongo3/src/main/java/org/apache/metamodel/mongodb/mongo3/MongoDbDeleteBuilder.java @@ -44,8 +44,10 @@ final class MongoDbDeleteBuilder extends AbstractRowDeletionBuilder { final MongoCollection<Document> collection = _updateCallback.getCollection(getTable().getName()); final MongoDbDataContext dataContext = _updateCallback.getDataContext(); - final Document query = dataContext.createMongoDbQuery(getTable(), getWhereItems()); - + final Document query = dataContext.createMongoDbQuery(getTable(), getWhereItems(), whereItem -> { + throw new UnsupportedOperationException("Unable to handle WHERE item in DELETE: " + whereItem.toSql()); + }); + DeleteResult result = collection.deleteMany(query); logger.info("Remove returned result: {}", result); } http://git-wip-us.apache.org/repos/asf/metamodel/blob/39947f55/mongodb/mongo3/src/test/java/org/apache/metamodel/mongodb/mongo3/MongoDbDataContextTest.java ---------------------------------------------------------------------- diff --git a/mongodb/mongo3/src/test/java/org/apache/metamodel/mongodb/mongo3/MongoDbDataContextTest.java b/mongodb/mongo3/src/test/java/org/apache/metamodel/mongodb/mongo3/MongoDbDataContextTest.java index 78fbddc..35c74cc 100644 --- a/mongodb/mongo3/src/test/java/org/apache/metamodel/mongodb/mongo3/MongoDbDataContextTest.java +++ b/mongodb/mongo3/src/test/java/org/apache/metamodel/mongodb/mongo3/MongoDbDataContextTest.java @@ -32,10 +32,10 @@ import org.apache.metamodel.data.InMemoryDataSet; import org.apache.metamodel.query.FunctionType; import org.apache.metamodel.query.SelectItem; import org.apache.metamodel.schema.ColumnType; -import org.apache.metamodel.schema.Schema; import org.apache.metamodel.schema.Table; import org.apache.metamodel.util.SimpleTableDef; import org.bson.Document; +import org.junit.Test; import com.mongodb.MongoClient; import com.mongodb.MongoClientURI; @@ -47,12 +47,12 @@ public class MongoDbDataContextTest extends MongoDbTestCase { private MongoDatabase mongoDb; private MongoClient client; - + @Override protected void setUp() throws Exception { super.setUp(); if (isConfigured()) { - client = new MongoClient(new MongoClientURI("mongodb://"+getHostname()+":27017")); + client = new MongoClient(new MongoClientURI("mongodb://" + getHostname() + ":27017")); mongoDb = client.getDatabase(getDatabaseName()); } } @@ -66,6 +66,7 @@ public class MongoDbDataContextTest extends MongoDbTestCase { } } + @Test public void testNestedObjectFetching() throws Exception { if (!isConfigured()) { System.err.println(getInvalidConfigurationMessage()); @@ -84,16 +85,16 @@ public class MongoDbDataContextTest extends MongoDbTestCase { dbRow.append("addresses", list); col.insertOne(dbRow); - final MongoDbDataContext dc = new MongoDbDataContext(mongoDb, new SimpleTableDef(getCollectionName(), new String[] { - "name.first", "name.last", "gender", "addresses", "addresses[0].city", "addresses[0].country", - "addresses[5].foobar" })); + final MongoDbDataContext dc = new MongoDbDataContext(mongoDb, + new SimpleTableDef(getCollectionName(), new String[] { "name.first", "name.last", "gender", "addresses", + "addresses[0].city", "addresses[0].country", "addresses[5].foobar" })); final DataSet ds = dc.query().from(getCollectionName()).selectAll().execute(); try { assertTrue(ds.next()); final Object addresses = ds.getRow().getValue(3); - assertEquals("Row[values=[John, Doe, MALE, " + addresses + ", Copenhagen, Denmark, null]]", ds.getRow() - .toString()); + assertEquals("Row[values=[John, Doe, MALE, " + addresses + ", Copenhagen, Denmark, null]]", + ds.getRow().toString()); assertTrue(addresses instanceof List); assertFalse(ds.next()); } finally { @@ -101,12 +102,13 @@ public class MongoDbDataContextTest extends MongoDbTestCase { } } + @Test public void testQueriesWithAutoGeneratedID() throws Exception { if (!isConfigured()) { System.err.println(getInvalidConfigurationMessage()); return; } - + mongoDb.createCollection(getCollectionName()); MongoCollection<Document> col = mongoDb.getCollection(getCollectionName()); col.withWriteConcern(WriteConcern.ACKNOWLEDGED); @@ -138,8 +140,7 @@ public class MongoDbDataContextTest extends MongoDbTestCase { DataSet ds; // check all 3 entries inserted - ds = dc.query().from(getCollectionName()).selectAll() - .where("category").eq("gen_id").execute(); + ds = dc.query().from(getCollectionName()).selectAll().where("category").eq("gen_id").execute(); assertEquals(3, ds.toRows().size()); ds.close(); @@ -150,17 +151,13 @@ public class MongoDbDataContextTest extends MongoDbTestCase { ds.close(); // select by multiple autogenerated ids - ds = dc.query().from(getCollectionName()).select("name") - .where("_id").eq(autoGenID1) - .or("_id").eq(autoGenID2) + ds = dc.query().from(getCollectionName()).select("name").where("_id").eq(autoGenID1).or("_id").eq(autoGenID2) .execute(); assertEquals(2, ds.toRows().size()); ds.close(); // select by both autogenerated id and fixed id - ds = dc.query().from(getCollectionName()).select("name") - .where("_id").eq(autoGenID1) - .or("_id").eq(fixedID3) + ds = dc.query().from(getCollectionName()).select("name").where("_id").eq(autoGenID1).or("_id").eq(fixedID3) .execute(); assertEquals(2, ds.toRows().size()); ds.close(); @@ -169,9 +166,7 @@ public class MongoDbDataContextTest extends MongoDbTestCase { dc.executeUpdate(new UpdateScript() { @Override public void run(UpdateCallback callback) { - callback.deleteFrom(getCollectionName()) - .where("_id").eq(autoGenID1) - .execute(); + callback.deleteFrom(getCollectionName()).where("_id").eq(autoGenID1).execute(); } }); @@ -182,6 +177,7 @@ public class MongoDbDataContextTest extends MongoDbTestCase { } + @Test public void testFirstRowAndMaxRows() throws Exception { if (!isConfigured()) { System.err.println(getInvalidConfigurationMessage()); @@ -226,8 +222,79 @@ public class MongoDbDataContextTest extends MongoDbTestCase { ds.close(); } + @Test + public void testSelectNestedObject() { + if (!isConfigured()) { + System.err.println(getInvalidConfigurationMessage()); + return; + } + + try (DataSet ds = + executeNestedObjectQuery("SELECT name.given FROM " + getCollectionName() + " WHERE id = 42")) { + assertTrue(ds.next()); + assertEquals("John", (String) ds.getRow().getValue(0)); + assertFalse(ds.next()); + } + } + + @Test + public void testWhereNestedObject() { + if (!isConfigured()) { + System.err.println(getInvalidConfigurationMessage()); + return; + } + + try (DataSet ds = + executeNestedObjectQuery("SELECT id FROM " + getCollectionName() + " WHERE name.given = 'Jane'")) { + assertTrue(ds.next()); + assertEquals(43, ((Number) ds.getRow().getValue(0)).intValue()); + assertFalse(ds.next()); + } + } + + @Test + public void testSelectAndWhereNestedObject() { + if (!isConfigured()) { + System.err.println(getInvalidConfigurationMessage()); + return; + } + + try (DataSet ds = executeNestedObjectQuery( + "SELECT name.family FROM " + getCollectionName() + " WHERE name.given = 'Jane'")) { + assertTrue(ds.next()); + assertEquals("Johnson", (String) ds.getRow().getValue(0)); + assertFalse(ds.next()); + } + } + + // reusable method for a couple of test cases above + private DataSet executeNestedObjectQuery(String sql) { + if (mongoDb.getCollection(getCollectionName()) != null) { + mongoDb.getCollection(getCollectionName()).drop(); + } + mongoDb.createCollection(getCollectionName()); + final MongoCollection<Document> col = mongoDb.getCollection(getCollectionName()); + + // record 1: 42 John Doe + col.insertOne(new Document().append("id", 42).append("name", + new Document().append("given", "John").append("family", "Doe"))); + + // record 2: 43 Jane Johnson + col.insertOne(new Document().append("id", 43).append("name", + new Document().append("given", "Jane").append("family", "Johnson"))); + + final DataContext dataContext = new MongoDbDataContext(mongoDb); + + assertTrue(dataContext.getDefaultSchema().getTableNames().contains(getCollectionName())); + + final Table table = dataContext.getDefaultSchema().getTableByName(getCollectionName()); + assertEquals("[_id, id, name]", table.getColumnNames().toString()); + + return dataContext.executeQuery(sql); + } + + @Test public void testRead() throws Exception { - // Adding a comment to commit something and invoke a build in Travis... if (!isConfigured()) { System.err.println(getInvalidConfigurationMessage()); return; @@ -259,9 +326,9 @@ public class MongoDbDataContextTest extends MongoDbTestCase { // Instantiate the actual data context final DataContext dataContext = new MongoDbDataContext(mongoDb); - assertTrue(Arrays.asList(dataContext.getDefaultSchema().getTableNames()).contains(getCollectionName())); - - Table table = dataContext.getDefaultSchema().getTableByName(getCollectionName()); + assertTrue(dataContext.getDefaultSchema().getTableNames().contains(getCollectionName())); + + final Table table = dataContext.getDefaultSchema().getTableByName(getCollectionName()); assertEquals("[_id, baz, foo, id, list, name]", Arrays.toString(table.getColumnNames().toArray())); assertEquals(ColumnType.MAP, table.getColumnByName("baz").getType()); @@ -276,26 +343,22 @@ public class MongoDbDataContextTest extends MongoDbTestCase { assertFalse(((MongoDbDataSet) ds).isQueryPostProcessed()); try { assertTrue(ds.next()); - assertEquals( - "Row[values=[record no. 0, bar, Document{{count=0, constant=foobarbaz}}, [l1, l2, l3, 0]]]", + assertEquals("Row[values=[record no. 0, bar, Document{{count=0, constant=foobarbaz}}, [l1, l2, l3, 0]]]", ds.getRow().toString()); assertTrue(ds.next()); - assertEquals( - "Row[values=[record no. 5, bar, Document{{count=5, constant=foobarbaz}}, [l1, l2, l3, 5]]]", + assertEquals("Row[values=[record no. 5, bar, Document{{count=5, constant=foobarbaz}}, [l1, l2, l3, 5]]]", ds.getRow().toString()); assertTrue(ds.next()); - assertEquals( - "Row[values=[record no. 10, bar, Document{{count=10, constant=foobarbaz}}, [l1, l2, l3, 10]]]", + assertEquals("Row[values=[record no. 10, bar, Document{{count=10, constant=foobarbaz}}, [l1, l2, l3, 10]]]", ds.getRow().toString()); for (int j = 15; j < 801; j++) { if (j % 5 == 0) { assertTrue(ds.next()); assertEquals("Row[values=[record no. " + j + ", bar, Document{{count=" + j - + ", constant=foobarbaz}}, [l1, l2, l3, " + j + "]]]", ds.getRow() - .toString()); + + ", constant=foobarbaz}}, [l1, l2, l3, " + j + "]]]", ds.getRow().toString()); } } @@ -365,8 +428,8 @@ public class MongoDbDataContextTest extends MongoDbTestCase { } // test GREATER_THAN_OR_EQUAL - ds = dataContext.query().from(getCollectionName()).select("id").and("name").where("id") - .greaterThanOrEquals(500).and("foo").isEquals("bar").execute(); + ds = dataContext.query().from(getCollectionName()).select("id").and("name").where("id").greaterThanOrEquals(500) + .and("foo").isEquals("bar").execute(); assertEquals(MongoDbDataSet.class, ds.getClass()); assertFalse(((MongoDbDataSet) ds).isQueryPostProcessed()); @@ -378,8 +441,8 @@ public class MongoDbDataContextTest extends MongoDbTestCase { ds.close(); } - ds = dataContext.query().from(getCollectionName()).select("id").and("name").where("id") - .greaterThanOrEquals(501).and("foo").isEquals("bar").execute(); + ds = dataContext.query().from(getCollectionName()).select("id").and("name").where("id").greaterThanOrEquals(501) + .and("foo").isEquals("bar").execute(); assertEquals(MongoDbDataSet.class, ds.getClass()); assertFalse(((MongoDbDataSet) ds).isQueryPostProcessed()); @@ -449,30 +512,24 @@ public class MongoDbDataContextTest extends MongoDbTestCase { ds.close(); } + @Test public void testCreateAndWriteData() throws Exception { if (!isConfigured()) { System.err.println(getInvalidConfigurationMessage()); return; } + + if (mongoDb.getCollection(getCollectionName()) != null) { + mongoDb.getCollection(getCollectionName()).drop(); + } + final MongoDbDataContext dc = new MongoDbDataContext(mongoDb); - final Schema defaultSchema = dc.getDefaultSchema(); - - dc.executeUpdate(new UpdateScript() { - @Override - public void run(UpdateCallback callback) { - for (Table table : defaultSchema.getTables()) { - callback.deleteFrom(table).execute(); - } - } - }); - - assertEquals(0, defaultSchema.getTableCount()); dc.executeUpdate(new UpdateScript() { @Override public void run(UpdateCallback callback) { - Table table = callback.createTable(defaultSchema, "some_entries").withColumn("foo").withColumn("bar") + Table table = callback.createTable(dc.getDefaultSchema(), getCollectionName()).withColumn("foo").withColumn("bar") .withColumn("baz").withColumn("list").execute(); callback.insertInto(table).value("foo", 1).value("bar", "hello").execute(); @@ -487,12 +544,13 @@ public class MongoDbDataContextTest extends MongoDbTestCase { .value("list", Arrays.asList(1, 2, 3)).execute(); } }); + dc.refreshSchemas(); DataSet dataSet; - assertEquals(1, defaultSchema.getTableCount()); + assertEquals(1, dc.getDefaultSchema().getTableCount()); // "Pure" SELECT COUNT(*) query - dataSet = dc.query().from("some_entries").selectCount().execute(); + dataSet = dc.query().from(getCollectionName()).selectCount().execute(); dataSet.close(); assertTrue(dataSet.next()); assertEquals(1, dataSet.getSelectItems().size()); @@ -502,7 +560,7 @@ public class MongoDbDataContextTest extends MongoDbTestCase { assertEquals(InMemoryDataSet.class, dataSet.getClass()); // A conditional SELECT COUNT(*) query - dataSet = dc.query().from("some_entries").selectCount().where("foo").greaterThan(2).execute(); + dataSet = dc.query().from(getCollectionName()).selectCount().where("foo").greaterThan(2).execute(); dataSet.close(); assertTrue(dataSet.next()); assertEquals(1, dataSet.getSelectItems().size()); @@ -512,7 +570,7 @@ public class MongoDbDataContextTest extends MongoDbTestCase { assertEquals(InMemoryDataSet.class, dataSet.getClass()); // Select columns - dataSet = dc.query().from("some_entries").select("foo").and("bar").and("baz").and("list").execute(); + dataSet = dc.query().from(getCollectionName()).select("foo").and("bar").and("baz").and("list").execute(); assertTrue(dataSet.next()); assertEquals("Row[values=[1, hello, null, null]]", dataSet.getRow().toString()); assertTrue(dataSet.next()); @@ -529,11 +587,11 @@ public class MongoDbDataContextTest extends MongoDbTestCase { dc.executeUpdate(new UpdateScript() { @Override public void run(UpdateCallback callback) { - callback.deleteFrom("some_entries").where("foo").greaterThan(2).where("baz").isNotNull().execute(); + callback.deleteFrom(getCollectionName()).where("foo").greaterThan(2).where("baz").isNotNull().execute(); } }); - dataSet = dc.query().from("some_entries").select("foo").execute(); + dataSet = dc.query().from(getCollectionName()).select("foo").execute(); assertTrue(dataSet.next()); assertEquals("Row[values=[1]]", dataSet.getRow().toString()); assertTrue(dataSet.next()); @@ -548,16 +606,17 @@ public class MongoDbDataContextTest extends MongoDbTestCase { dc.executeUpdate(new UpdateScript() { @Override public void run(UpdateCallback callback) { - callback.dropTable("some_entries").execute(); + callback.dropTable(getCollectionName()).execute(); } }); - assertNull(dc.getTableByQualifiedLabel("some_entries")); + assertNull(dc.getTableByQualifiedLabel(getCollectionName())); dc.refreshSchemas(); - assertEquals(0, defaultSchema.getTableCount()); + assertEquals(0, dc.getDefaultSchema().getTableCount()); } + @Test public void testSelectWithLikeOperator() throws Exception { if (!isConfigured()) { System.err.println(getInvalidConfigurationMessage()); @@ -582,9 +641,9 @@ public class MongoDbDataContextTest extends MongoDbTestCase { dbRow3.append("gender", "UNKNOWN"); col.insertOne(dbRow3); - final MongoDbDataContext dc = new MongoDbDataContext(mongoDb, new SimpleTableDef(getCollectionName(), new String[] { - "name.first", "name.last", "gender", "addresses", "addresses[0].city", "addresses[0].country", - "addresses[5].foobar" })); + final MongoDbDataContext dc = new MongoDbDataContext(mongoDb, + new SimpleTableDef(getCollectionName(), new String[] { "name.first", "name.last", "gender", "addresses", + "addresses[0].city", "addresses[0].country", "addresses[5].foobar" })); final DataSet ds1 = dc.executeQuery("select * from my_collection where gender LIKE '%MALE%'"); final DataSet ds2 = dc.executeQuery("select * from my_collection where gender LIKE 'MALE%'"); @@ -609,6 +668,7 @@ public class MongoDbDataContextTest extends MongoDbTestCase { } } + @Test public void testSelectWithAlias() throws Exception { if (!isConfigured()) { System.err.println(getInvalidConfigurationMessage()); @@ -624,11 +684,11 @@ public class MongoDbDataContextTest extends MongoDbTestCase { col.insertOne(dbRow); final MongoDbDataContext dc = new MongoDbDataContext(mongoDb, - new SimpleTableDef(getCollectionName(), new String[] { - "name.first", "name.last", "gender", "addresses", "addresses[0].city", "addresses[0].country", - "addresses[5].foobar" })); + new SimpleTableDef(getCollectionName(), new String[] { "name.first", "name.last", "gender", "addresses", + "addresses[0].city", "addresses[0].country", "addresses[5].foobar" })); - final DataSet ds1 = dc.executeQuery("select gender as my_gender, name.first as my_name from my_collection where gender LIKE '%MALE%'"); + final DataSet ds1 = dc.executeQuery( + "select gender as my_gender, name.first as my_name from my_collection where gender LIKE '%MALE%'"); final SelectItem[] selectItems = ds1.getSelectItems().toArray(new SelectItem[ds1.getSelectItems().size()]); SelectItem firstSelectItem = selectItems[0]; SelectItem secondSelectItem = selectItems[1];
