Mondrian es un motor OLAP de bases de datos.
Se puede descargar desde sourceforge y forma parte del proyecto pentaho
Si lo descargas, descomprimes e intentas usar desde Linux puedes seguir la documentación en español que hay en el directorio doc del paquete que te has bajado. Que es una copia de la documentación on line
Sólo comentarte que si pretendes usarlo con MySql como yo puede que tengas problemas. Por lo que si los tienes y no puedes importarlos puedes usar este dump que he descargado de aqui
Una vez tienes la base de datos ya puedes seguir las instrucciones normales. ( esto es un resumen)
Provider=mondrian;Jdbc=jdbc:odbc:MondrianFoodMart;Catalog=/WEB-INF/queries/FoodMart.xml;JdbcDrivers=sun.jdbc.odbc.JdbcOdbcDriver;
por esto otro
Provider=mondrian;Jdbc=jdbc:mysql://localhost/foodmart?user=foodmart;password=foodmart;Catalog=/WEB-INF/queries/FoodMart.xml;JdbcDrivers= com.mysql.jdbc.Driver;
<jp:mondrianQuery id="query01" jdbcDriver="sun.jdbc.odbc.JdbcOdbcDriver" jdbcUrl="jdbc:odbc:MondrianFoodMart" catalogUri="/WEB-INF/queries/FoodMart.xml">
Por esto otro:
<jp:mondrianQuery id="query01" jdbcDriver="com.mysql.jdbc.Driver" jdbcUrl="jdbc:mysql://localhost/foodmart?user=foodmart&password=foodmart" catalogUri="/WEB-INF/queries/FoodMart.xml">
Y listo…. reinicas tomcat para que se entere de los cambios y a disfrutar : http://localhost:8080/mondrian
Ubicado bajo WEB-INF contiene la configuración básica de mondrian. Puedes encontrar una explicación extensa aqui
Y aqui está el listado de todas las propiedades que puedes especificar.
Las más importanets:
Un ejemplo de mondrian.properties para MySql:
# Allow the use of aggregates mondrian.rolap.aggregates.Use=true mondrian.rolap.aggregates.Read=true mondrian.native.topcount.enable=true mondrian.native.filter.enable=true # mondrian.properties mondrian.result.limit=50000 Provider=mondrian;Jdbc=jdbc:mysql://localhost/foodmart?user=foodmart;password=foodmart;Catalog=/WEB-INF/queries/FoodMart.xml;JdbcDrivers= com.mysql.jdbc.Driver;
En efecto, FoodMart.xml es el esquema mondrian que nos viene en nuestro ejemplo. En el podemos ver casi todo lo que puede haber en un esquema.
Un esquema es la definición de la base de datos multi-dimensional. Contiene el modelo lógico, los cubos, las jerarquías, los niveles, y el mapeo de estos elementos contra la base de datos relacional ( modelo físico)
El modelo lógico nos permite hacer la abstracción a MDX.
El esquema no es mas que un archivo XML. Los elementos mas importantes a la hora de crear un esquema son las dimensiones, medidas y cubos
Dentro de la etiqueta dimensión se especifica todo lo referente a una dimensión. Una dimensión se especifica por medio de una jerarquía compuesta por miembros.
Veamos un ejemplo simple:
<Dimension name="Genero" foreignKey="id_persona">
<Hierarchy hasAll="true" primaryKey="id_persona">
<Table name="persona"/>
<Level name="Genero" column="sexo" uniqueMembers="true"/>
</Hierarchy>
</Dimension>
Como puedes ver esta dimensión consiste en una jerarquía que tiene un único nivel llamado genero. La tabla origen de esta jerarquía es persona. El valor, los miembros de esta jeraquía vienen de la columna sexo de esta tabla.
Una dimensión está unida a un cubo por 2 colúmnas, una en la tabla de hechos y la otra en la tabla de dimensión. El elemento <Dimension> contiene la clave foránea ubicada en la tabla de hechos. que es la que nos permitirá relacionar el cubo con la dimensión
Por defecto cada jearquía contiene un nivel en la cima que llamaremos ALL. Que contiene un único nivel llamado TODO {nombre de la jerarquía}. Este elemento es el padre de todos los demás miembros de la jerarquía.
Si la jerarquía no tiene un único elemento en la cima se ha de poner el atributo hasAll a false ( hasAll=“false” ). En ese caso el miembro por defecto de esa dimensión será el primero del primer nivel. Por ejemplo, en la jerarquía tiempo sería el primer año. El nivel padre es el nivel por defecto. Aunque esto se puede modificar con el atributo defaultMember
veamos un ejemplo mas completo:
<Dimension name="Store"> <Hierarchy hasAll="true" primaryKey="store_id"> <Table name="store"/> <Level name="Store Country" column="store_country" uniqueMembers="true"/> <Level name="Store State" column="store_state" uniqueMembers="true"/> <Level name="Store City" column="store_city" uniqueMembers="false"/> <Level name="Store Name" column="store_name" uniqueMembers="true"> <Property name="Store Type" column="store_type"/> <Property name="Store Manager" column="store_manager"/> <Property name="Store Sqft" column="store_sqft" type="Numeric"/> <Property name="Grocery Sqft" column="grocery_sqft" type="Numeric"/> <Property name="Frozen Sqft" column="frozen_sqft" type="Numeric"/> <Property name="Meat Sqft" column="meat_sqft" type="Numeric"/> <Property name="Has coffee bar" column="coffee_bar" type="Boolean"/> <Property name="Street address" column="store_street_address" type="String"/> </Level> </Hierarchy> </Dimension>
El cubo no es otra cosa que una enumeración de miembros de jerarquías y hechos. Un cubo se suele apoyar en una tabla de hechos que sirve como base de calculo. La idea es construir un cubo imaginario que nos permita movernos por los datos a traves de la combinación de sus cardinalidades. Algo parecido a esto:
La tabla de hechos se especifica:
<Cube name="Mi_Cubo"> <Table name="Mi_Tabla_De_Hechos"/> ... </Cube>
En un cubo se almacenan medidas, hechos que tambien hay que especificar. Eso se hace de la siguiente forma:
<Cube name="Mi_Cubo"> <Table name="Mi_Tabla_De_Hechos"/> ... <Measure name="Unit Sales" column="unit_sales" aggregator="sum" datatype="Integer" formatString="#,###"/> <Measure name="Store Sales" column="store_sales" aggregator="sum" datatype="Numeric" formatString="#,###.00"/> ... </Cube>
De esta manera se establece el comportamiento de cada miembro, Su nombre, la columna de la tabla en la que se basa, como se agrega , el tipo de dato que es y el formato
El tipo de dato por defecto es Numeric aunque puede ser:
Como que una medida proviene de una tabla, una medida puede tener un “lector SQL” que indique en que forma se debe leer.
Ejemplo:
<Measure name="Promotion Sales" aggregator="sum" formatString="#,###.00"> <MeasureExpression> <SQL dialect="generic"> (case when sales_fact_1997.promotion_id = 0 then 0 else sales_fact_1997.store_sales end) </SQL> </MeasureExpression> </Measure>
Las dimensiones que existen en un cubo pueden ser epecificadas tanto dentro del propio cubo como fuera. Si se usa una dimensión ”Externa” se utiliza la etiqueta <DimensionUsage> donde se le da un nombre, se hace referencia al nombre original de la dimensión ”Externa” y se le indica cual será la columna por la que se hará el vinculo ( el join, la clave foranea)
<DimensionUsage name="Store Type" source="Store Type" foreignKey="warehouse_store_id"/>
Por otro lado, tambien es posible especificar la dimensión dentro del cubo:
<Cube name="Sales 2"> <Table name="sales_fact_1997"/> <!-- dimension definida fuera del cubo --> <DimensionUsage name="Time" source="Time" foreignKey="time_id"/> <DimensionUsage name="Product" source="Product" foreignKey="product_id"/> <!-- dimension definida dentro del cubo --> <Dimension name="Gender" foreignKey="customer_id"> <Hierarchy hasAll="true" allMemberName="All Gender" primaryKey="customer_id"> <Table name="customer"/> <Level name="Gender" column="gender" uniqueMembers="true"/> </Hierarchy> </Dimension> <!-- medidas del cubo --> <Measure name="Sales Count" column="product_id" aggregator="count" formatString="#,###"> <CalculatedMemberProperty name="MEMBER_ORDINAL" value="1"/> </Measure> <Measure name="Unit Sales" column="unit_sales" aggregator="sum" formatString="Standard"> <CalculatedMemberProperty name="MEMBER_ORDINAL" value="2"/> </Measure> <Measure name="Store Sales" column="store_sales" aggregator="sum" formatString="#,###.00"> <CalculatedMemberProperty name="MEMBER_ORDINAL" value="3"/> </Measure> <Measure name="Store Cost" column="store_cost" aggregator="sum" formatString="#,###.00"> <CalculatedMemberProperty name="MEMBER_ORDINAL" value="6"/> </Measure> <Measure name="Customer Count" column="customer_id" aggregator="distinct-count" formatString="#,###"> <CalculatedMemberProperty name="MEMBER_ORDINAL" value="7"/> </Measure> <CalculatedMember name="Profit" dimension="Measures"> <Formula> [Measures].[Store Sales] - [Measures].[Store Cost] </Formula> <CalculatedMemberProperty name="FORMAT_STRING" value="$#,##0.00"/> <CalculatedMemberProperty name="MEMBER_ORDINAL" value="4"/> </CalculatedMember> <CalculatedMember name="Profit last Period" dimension="Measures" formula="COALESCEEMPTY((Measures.[Profit], [Time].PREVMEMBER), Measures.[Profit])" visible="false"> <CalculatedMemberProperty name="MEMBER_ORDINAL" value="5"/> </CalculatedMember> </Cube>
Una vez que tenemos un cubo definido querremos interactuar con el. Para ello mondrian usa Jpivot. se ve algo así como esto.
Mondrian es un motor OLAP hibrido. Puede basarse en BBDD o puede tener los datos en cache. Si tu sistema refresca los datos frecuentemente puede que te interese desabilitar la cache.
Esto se hace en la definición del cubo. Al definir un cubo mondiran defines si tiene o no cache.
<Cube name="MiCubo" cache="false" enabled="true"> <Table name="miTabla"> </Table> <DimensionUsage source="dimension1" name="dimension1" foreignKey="id_dimension1"> </DimensionUsage> <DimensionUsage source="dimension2" name="dimension2" foreignKey="id_dimension2"> </DimensionUsage> <Measure name="Numero" column="numero" datatype="Numeric" aggregator="sum"> </Measure> </Cube>