Let's see how we could integrate MongoDB using ODI, first take a step back. Derby/JavaDB introduced table functions a few years ago. Table functions are really useful, they are in the Oracle database and as much fun in JavaDB! ODI is a great platform for integration and JavaDB and table functions provide a really nice way to integrate arbitrary Java APIs into your designs. What I have done here is;
- built a very simple java table function to project a table to represent the documents in a MongoDB collection. The collection is passed as a parameter to the KM and the column names are the keys for the MongoDB document. The data comes from the values.
- built a very simple LKM from a Java table function to SQL
All of this will use the JavaDB in-memory, so no admin, simple to use. Rick Hillegas wrote a nice article with some handy classes that I have used. The mongo_table class I have written uses the EnumeratorTableFunction class included in Rick's examples. The MongoDB DBCursor class is a Java Iterator, which makes it really nice to pass to the EnumeratorTableFunction class, and let it do all of the work.
The LKM I constructed declares the table function to JavaDB/Derby, for example below, the function is declared based on the source datastore name (MONGO_EMPS) and columns (my source datastore has EMPNO,ENAME,SAL, note the table function will actually project types defined in ODI's datastore), the function has the MongoDB database name and collection name as parameters.
- create function MONGO_EMPS( dbName varchar( 330), collectionName varchar( 30))
- returns table
- (
- EMPNO VARCHAR(20),
- ENAME VARCHAR(30),
- SAL NUMERIC(10),
- )
- language java
- parameter style DERBY_JDBC_RESULT_SET
- no sql
- external name 'mongo_table.readCollection'
Then the actual code to use the function as a source is executed from a source task (the target is SQL as I mentioned earlier for the LKM). Below you can see my execution using the test MongoDB and the myStuff collection;
- select
- MON.EMPNO C1_EMPNO,
- MON.ENAME C2_ENAME,
- MON.SAL C6_SAL
- fromtable(MONGO_EMPS('test', 'myStuff' )) MON
- where(1=1)
- And (MON.SAL > 4000)
Note I can also perform some filtering as an example, here it is being done in JavaDB and in my case its in-memory. No setup, no persistence just on the fly Java. Ideally I would push the filter down to MongoDB rather than reading and filtering in the driver - more on that later.
I had defined my documents in MongoDB using the following basic commands in the mongo shell;
- use test
- a1 = { EMPNO: "1", ENAME : "Fred", SAL : 10000 }
- a2 = { EMPNO: "2", ENAME : "John", SAL : 2000 }
- db.myStuff.insert( a1 )
- db.myStuff.insert( a2 )
In ODI, I can simply then use the datastore representing the MongoDB collection of documents in an interface and map it to my target;
The physical design uses the LKM JavaTableFunction to SQL KM and sets the MongoDB databases, collection and the Java table function name.
That's it. Pretty straightforward and we are reading and consuming MongoDB documents. So what about complex document types like this? These are transported, more to come. The other point here is that this is a generic LKM that you can plug in other arbitrary table functions - so you can consume from any API, its very simple. For the LKM I created, I just defined 2 parameters (because my table function only had two), but really we need a better way to handle this and ensure they are ignored if not defined etc. That's all polishing tho, anyway fun stuff you can see all with a small piece of code leveraging JavaDB!