Skip to main content
Pentaho Documentation

Build Logical Models

The most important components that make up a schema are cubes, measures, attributes, and dimensions. Together, these components are often referred to as logical models.

There are a few key terms that you need to know before you get started with building logical models.

  • Cube—a data set describing a one or more processes over a particular time period.
  • Fact—data representing an individual occurrence of a process; for example, a line item describing the sale of a product to a customer, or a pay period of an employee in a company.
  • Measure—a quantity within a cube that you are interested in measuring; for example, unit sales of a product, or the salary of an employee.
  • Attribute—a value, possessed by every fact, by which you can divide facts into subsets. For example, you might wish to break down product sales by their color, the gender of the customer, and the store in which the product was sold; color, gender, and store are all attributes.
  • Hierarchies and Levels—a hierarchy is a set of members organized into a structure for analysis. A level is a collection of members which have the same distance from the root of the hierarchy.
  • Dimension—a grouping of related attributes. For example, name, gender, zip code and eye color are attributes of the customer dimension; color, weight, and manufacturer are attributes of the product dimension.

Take a quick look at the XML definition of a simple schema.


<Schema name="Sales" metamodelVersion="4.0">
  <PhysicalSchema>
    <Table name="sales">
      <ColumnDefs>
        <CalculatedColumnDef name='customer_id' type="Integer">
          <ExpressionView>
            <SQL dialect='generic'>$customer_id</SQL>
          </ExpressionView>
        </CalculatedColumnDef>
        <CalculatedColumnDef name='gender' type="String">
          <ExpressionView>
            <SQL dialect='generic'>$gender</SQL>
          </ExpressionView>
        </CalculatedColumnDef>
        <CalculatedColumnDef name='the_year' type="String">
          <ExpressionView>
            <SQL dialect='generic'>$the_year</SQL>
          </ExpressionView>
        </CalculatedColumnDef>
        <CalculatedColumnDef name='quarter' type="String">
          <ExpressionView>
            <SQL dialect='generic'>$quarter</SQL>
          </ExpressionView>
        </CalculatedColumnDef>
        <CalculatedColumnDef name='month_of_year' type="String">
          <ExpressionView>
            <SQL dialect='generic'>$month_of_year</SQL>
          </ExpressionView>
        </CalculatedColumnDef>
        <CalculatedColumnDef name='time_id' type="Numeric">
          <ExpressionView>
            <SQL dialect='generic'>$time_id</SQL>
          </ExpressionView>
        </CalculatedColumnDef>
        <CalculatedColumnDef name='unit_sales' type="Numeric">
          <ExpressionView>
            <SQL dialect='generic'>$unit_sales</SQL>
          </ExpressionView>
        </CalculatedColumnDef>
        <CalculatedColumnDef name='store_sales' type="Numeric">
          <ExpressionView>
            <SQL dialect='generic'>$store_sales</SQL>
          </ExpressionView>
        </CalculatedColumnDef>
        <CalculatedColumnDef name='store_cost' type="Numeric">
          <ExpressionView>
            <SQL dialect='generic'>$store_cost</SQL>
          </ExpressionView>
        </CalculatedColumnDef>
      </ColumnDefs>
    </Table>
  </PhysicalSchema>
  <Cube name="Sales">
    <Dimensions>
      <Dimension name="Customer" table="sales" key="Id">
        <Attributes>
          <Attribute name="Gender" keyColumn="gender" />
          <Attribute name="Id" keyColumn="customer_id" />
        </Attributes>
      </Dimension>
      <Dimension name="Time" table="sales" key="Day">
        <Attributes>
          <Attribute name="Year" keyColumn="the_year" />
          <Attribute name="Quarter" >
            <Key>
              <Column name="the_year" />
              <Column name="quarter" />
            </Key>
            <Name>
              <Column name="quarter" />
            </Name>
          </Attribute>
          <Attribute name="Month" >
            <Key>
              <Column name="the_year" />
              <Column name="month_of_year" />
            </Key>
            <Name>
              <Column name="month_of_year" />
            </Name>
          </Attribute>
          <Attribute name="Day" keyColumn="time_id" />
        </Attributes>
        <Hierarchies>
          <Hierarchy name="Time" hasAll="false">
            <Level attribute="Year" />
            <Level attribute="Quarter" />
            <Level attribute="Month" />
          </Hierarchy>
        </Hierarchies>
      </Dimension>
    </Dimensions>
    <MeasureGroups>
      <MeasureGroup name="Sales" table="sales">
        <Measures>
          <Measure name="Unit Sales" column="unit_sales" aggregator="sum" formatString="#,###" />
          <Measure name="Store Sales" column="store_sales" aggregator="sum" formatString="#,###.##" />
          <Measure name="Store Cost" column="store_cost" aggregator="sum" formatString="#,###.00" />
        </Measures>
        <DimensionLinks>
          <FactLink dimension="Customer" />
          <FactLink dimension="Time" />
        </DimensionLinks>
      </MeasureGroup>
    </MeasureGroups>
    <CalculatedMembers>
      <CalculatedMember name="Profit" dimension="Measures" formula="[Measures].[Store Sales] - [Measures].[Store Cost]">
        <CalculatedMemberProperty name="FORMAT_STRING" value="$#,##0.00" />
      </CalculatedMember>
    </CalculatedMembers>
  </Cube>
</Schema>

This schema contains a single cube, which is named Sales. This Sales cube has two dimensions, Time, and Gender, and four measures, Unit Sales, Store Sales, Store Cost, and Profit.

You can write an MDX query on this schema.


SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
  {Descendants([Time].[Yearly].[1997].[Q1])} ON ROWS
FROM [Sales]
WHERE [Customer].[Gender].[F] 

This query refers to the Sales cube ([Sales]), the two dimensions [Measures], [Time], [Customer], the hierarchy [Time].[Yearly], the attribute [Customer].[Gender], and various members of those dimensions. The results are as follows:


[Time]	[Measures].[Unit Sales]	[Measures].[Store Sales]
[1997].[Q1]	32,910	$69,798.23
[1997].[Q1].[Jan]	10,932	$23,309.04
[1997].[Q1].[Feb]	10,266	$21,773.93
[1997].[Q1].[Mar]	11,712	$24,715.26

Now it is time to take a look at the schema definition in more detail.

Cube

A cube is a named collection of dimensions and measures. A measure group is a collection of measures that have the same fact table. These simple cubes have just one measure group; later, the examples of cubes will have more than one measure group.

The dimensions are children of a <Dimensions> holder element. Even though schema order does not matter, typically the dimensions are declared first. Then follow the measures, organized into measure groups and under a <MeasureGroups> holder element.

The [Sales] cube in the previous example has two dimensions. The [Customer] dimension has attributes [Gender] and [Id]; and the [Time] dimension has attributes [Year], [Quarter], [Month] and [Day].

The[Sales] cube's measure group is based on the table sales. Each table used in the logical schema must be declared within the physical schema, and sure enough, the <PhysicalSchema> element has a child element, <Table name="sales">.

The fact table contains the columns from which measures are calculated. To use these columns, Analyzer for MongoDB needs to know about them, so all of these columns occur somewhere in the <MeasureGroup> element. The unit_sales, store_sales and store_cost columns each occur within a <Measure> definition; and the customer_id and time_id columns each occur within a <FactLink> element, linking the measure group to the [Customer]and [Time] dimensions, respectively.

Measures

Check out the measures defined in the Sales cube's one and only measure group.


<Measure name="Unit Sales" column="unit_sales" aggregator="sum" formatString="#,###"/>
<Measure name="Store Sales" column="store_sales" aggregator="sum" formatString="#,###.##"/>
<Measure name="Store Cost" column="store_cost" aggregator="sum" formatString="#,###.00"/>

Each measure has a name, a column in the fact table, and an aggregator. The aggregator is usually sum, but count, min, max, avg, and distinct-count are also allowed.

The optional data-type attribute specifies how cell values are represented in the cache, and how they are returned through XML for Analysis. The data-type attribute can have the values of String, Integer, Numeric, Boolean, Date, Time, and Timestamp. The default value is Numeric, except for the counting measures, which are Integer.

An optional formatString attribute specifies how the value is to be printed. The output unit sales with no decimal places (since it is an integer) is shown here, and store sales with two decimal places, since it is in a currency value. The ',' and '.' symbols are locale-sensitive, so if you were running in Italian, store sales might appear as "48.123,45". You can achieve even more effects using advanced format strings.

A measure can have a caption attribute be returned by the Member.getCaption() method instead of the name. If you do not specify a caption, it will default to the measure's name. Defining a specific caption might make sense if you wish to localize the name of the measure as seen by end-users, or if you wish to display special letters (e.g. Σ or Π) in the user interface, as shown in this example.

<Measure name="Sum X" column="sum_x" aggregator="sum" caption="&#931; X"/>

Attribute Order

The ordinal property of an attribute controls the order in which members are displayed. Usually attributes are sorted by name. If a name is not specified, remember that it will be the key, or the last column in a composite key. Sometimes, the name does not give you the order that you want. The [Time].[Month] attribute is an example of this.

<Attribute name="Month">
<Key>
<Column name="the_year"/>
<Column name="month"/>
</Key>
</Attribute>

Like [Time].[Quarter], [Month] has a composite key. Twelve months per year should be covered by the data set, not just twelve months. Unlike [Quarter], the name is overridden. Here is what happens when the query executes.

SELECT [Time].[Month].Members on ROWS
FROM [Sales];

[Time].[Month].&[2011]&[April]
[Time].[Month].&[2011]&[August]
[Time].[Month].&[2011]&[December]
[Time].[Month].&[2011]&[February]
[Time].[Month].&[2011]&[January]
... 

The results look entirely arbitrary, but remember that sorting is done by name by default. The query returned what was asked for, but not the desired results, so the key columns need to be sorted like this.

<Attribute name="Month" nameColumn="month_name" orderByColumn="month">
<Key>
<Column name="the_year"/>
<Column name="month"/>
</Key>
</Attribute>

Now, the results are as expected.

[Time].[Month].&[2011]&[January]
[Time].[Month].&[2011]&[February]
[Time].[Month].&[2011]&[March]
[Time].[Month].&[2011]&[April]
[Time].[Month].&[2011]&[May]
... 

Hierarchies and Levels

Some combinations of attributes in a dimension are often used together. For example, a business user viewing a state might often wish to expand it to see the constituent cities. When viewing a month, they might wish to roll up into quarter or year. For such combinations of attributes, it is convenient to create hierarchies.

Here is the [Time] dimension, slightly extended from the earlier example:

<Dimension name="Time" table="sales" key="Day">
<Attributes>
<Attribute name="Year" column="the_year"/>
<Attribute name="Quarter">
<Key>
<Column name="the_year"/>
<Column name="quarter"/>
</Key>
</Attribute>
<Attribute name="Month">
<Key>
<Column name="the_year"/>
<Column name="month_of_year"/>
</Key>
</Attribute>
<Attribute name="Week">
<Key>
<Column name="the_year"/>
<Column name="week_of_year"/>
</Key>
</Attribute>
<Attribute name="Day" column="time_id"/>
</Attributes>
<Hierarchies>
<Hierarchy name="Yearly" hasAll="false">
<Level attribute="Year"/>
<Level attribute="Quarter"/>
<Level attribute="Month"/>
<Level attribute="Day"/>
</Hierarchy>
<Hierarchy name="Weekly" hasAll="false">
<Level attribute="Year"/>
<Level attribute="Week"/>
<Level attribute="Day"/>
</Hierarchy>
</Hierarchies>
</Dimension>

You can see that most of the lines of XML in the dimension definition are taken up by attribute definitions. Once the attributes are defined, you can build a hierarchy by choosing which attributes you want, and in which order you want them. In fact, we recommend that when you first design a schema, you just define attributes; let the business users use the cube without any hierarchies defined, and let them tell you which hierarchies are needed.

(All) and Default Members

By default, every hierarchy contains a top level named (All), which contains a single member named (All {hierarchyName}). This member is parent of all other members of the hierarchy, and thus represents a grand total. The (All) member is also the default member of the hierarchy; that is, the member that is used for calculating cell values when the hierarchy is not included on an axis or in the slicer.

Various attributes allow you to control the (All) level and member. The <Hierarchy> element's allMemberName and allLevelName attributes override the default names of the (All) level and (All) member. If the <Hierarchy> element has hasAll="false", the (All) level is suppressed. The default member of that dimension will now be the first member of the first level. For example, in a Time hierarchy, it will be the first year in the hierarchy. Changing the default member can be confusing, so you should generally use hasAll="true".

You can also set the default member of a hierarchy explicitly. The defaultMember attribute should be the fully-qualified name of the default member.

<Hierarchy name="Yearly" hasAll="false" defaultMember="[Time].[1997].[Q1].[1]">
...
</Hierarchy>

When specified in this way, the default member can even be a calculated member.

Designing Attributes for Use in Hierarchies

Although it is easy to build a hierarchy from existing attributes, the attributes you include in a hierarchy have to be defined with care. Each attribute must be functionally dependent on the attribute of the level below it. Thus, there needs to be one and only one Quarter for any given Month, and one and only one Year for any given Quarter. A Year-Month-Week-Day hierarchy would not pass muster, because some of the days in Week 5 belong to January, and some to February.

Typically some of the attributes in a hierarchy will have composite keys, to achieve the appropriate functional dependency relationships. It is useful to remember that including attributes in a hierarchy does not alter the number of members of that attribute. In a well-formed hierarchy, each successive level has more members. For example, in a Year-Quarter-Month-Day hierarchy over ten years, the levels have 10, 40, 120, 3652 members respectively. If your Quarter attribute only has four members (because you have forgotten to give it a composite key) the levels have 10, 4, 120 and 3652 members, and that non-increasing sequence should be a sign that you have done something wrong.

Attribute Hierarchies

The MDX language does not know about attributes. It only knows about dimensions, hierarchies, levels, and members. Analyzer for MongoDB gets around that by generating a single-level hierarchy for each attribute, named an attribute hierarchy.

Attribute hierarchies do not have any special properties. The effect is as if you manually created the hierarchy, but it saves you the effort of defining lots of hierarchies. The net effect is that you can easily define a dozen or so attributes and start playing with them in queries before you start even thinking about hierarchies.

To control whether an attribute has a hierarchy, use the hasHierarchy attribute of the <Attribute> element. Other attributes, mostly corresponding directly to similarly-named attributes of the <Hierarchy> element, are shown in the following table.

Table 1. Attribute Hierarchy Elements
Hierarchy attribute Attribute attribute Description
N/A hasHierarchy Whether an attribute has an attribute-hierarchy.
name N/A The name of the hierarchy. An attribute hierarchy always has the same name as the attribute.
hasAll hierarchyHasAll Whether the hierarchy has an (All) level and member.
allMemberName hierarchyAllMemberName Name of the (All) member. If not specified, the all member is named 'All <hierarchyName>'.
allMemberCaption hierarchyAllMemberCaption Caption of the (All) member. If not specified, the member's caption is the same as its name.
allLevelName hierarchyAllLevelName Name of the (All) level. If not specified, the all level is named (All).
defaultMember hierarchyDefaultMember The default member of the hierarchy. If not specified, the default member is the first member of the first level. This is the (All) member, if present.

We recommend that you think twice before disabling the (All) member of an attribute hierarchy, or changing its default member, as this will cause hidden constraints to be in play even when the attribute is not being displayed on an axis.

Attributes vs. Hierarchies

Schema designers should feel encouraged to design dimensions with many attributes. Try not to worry about creating hierarchies at first, but see which combinations of attributes your end-users regularly use together, and consider creating hierarchies to make those drill paths more convenient. Generally, expect that your end-users will still use the standalone attributes most of the time.

Some attributes have within parent and without parent forms. For example, the [Time].[Month] attribute has 120 members over ten years, whereas the [Time].[Month of Year] has just twelve members. The first can be used to compare whether snow-sleds sold better in December 2013 sales than in December 2012; the second can be used to compare whether snow-sleds sell better in December or April.

You need to define two separate attributes. There is no easy way to define one in terms of the other, or an automatic mapping between them. The best you can do is to use a naming convention, such as Attribute of Parent, for all such attributes, so that the correspondence is clear to your end-users.

Schema Short Cuts

XML is a suitable language for defining schemas because it can be read and written by both humans and machines. You can hand-write a schema in emacs, vi, notepad, or you can write one in a modeling tool. However, XML can be verbose. This is less of a problem for tools than for humans typing every character.

Several schema short cuts are provided for convenience. These are alternative ways of specifying constructs that are less verbose. They reduce the amount of typing, making sure that simple things look simple, and without reducing the power of the schema language.

Attribute as Shorthand for a Singleton Nested Collection

One common shorthand is to allow an attribute to be used instead of a collection of nested elements, if that collection has only one element. For example, if you are defining an attribute with a simple, non-composite key you could either write this:

<Attribute name="A">
<Key>
<Column>
</Key>
</Attribute>

or this:

<Attribute name="A" column="c"/>

These are equivalent, but the second is more concise, and you would probably choose to use it when writing a schema by hand.

If the attribute has a composite key, or if you wish to use the table attribute, you have to use the nested <Key> element:

<Attribute name="A">
<Key>
<Column table="t1" name="c1"/>
<Column table="t2" name="c2"/>
</Key>
</Attribute>

The nested collection in this case is <Key> and a set of <Column> elements, and the attribute is keyColumn. But the pattern occurs elsewhere in the schema, as shown by this table.

Table 2. Parent and Nested Elements
Parent Element Attribute Equivalent Nested Element Description
<Attribute> keyColumn <Key> Column(s) that comprise the key of this attribute.
<Attribute> nameColumn <Name> Column that defines the name of members of this attribute. If not specified, the attribute key is used.
<Attribute> orderByColumn <OrderBy> Column(s) that define the order of members. If not specified, the attribute key is used.
<Attribute> captionColumn <Caption> Column that forms the caption of members. If not specified, the attribute name is used.
<Measure> column <Arguments> Column(s) that are the arguments to this measure. When this measure is implemented by generating a SQL expression, these columns become the argument to the SQL aggregate function.
<Table> keyColumn <Key> Column(s) that form this Table's key.

Inherited Attributes

The table attribute occurs on <Dimension>, <Attribute>, and <Column> elements and, if not present, is inherited from the enclosing element. This makes definitions more concise if, for example, a dimension is based on a single table.

Default Values for Attributes

Many attributes have default values. For example, the default value of the type attribute of a <Dimension> element is OTHER. Default values are described in the schema reference.

Dimensions

Dimensions represent a grouping of related attributes. For example, name, gender, zip code and eye color are attributes of the customer dimension; color, weight, and manufacturer are attributes of the product dimension.

The Measures Dimension

Measures are treated as members of a special dimension, named Measures. The dimension has a single hierarchy and a single level.

MDX allows you to omit the hierarchy name if a dimension has only one hierarchy, including attribute hierarchies. Therefore [Measures].[Unit Sales] is a valid shorthand for [Measures].[Measures].[Unit Sales].

That measures belong to a dimension might seem strange. For example, if you come from a background in relational databases, it seems like declaring that every column is a row. But one of the defining properties of the dimensional model, and one of its strengths. Because measures are members, you can change the context from one measure to another in a calculation just as easily as you can time period or sales region. It allows for greater reuse of calculations. It also makes for easier access control, because you can grant on a cell, of which measure is just one coordinate.

Degenerate Dimensions

A degenerate dimension is a dimension with data that is contained in the Fact table. MongoDB does not have support for joining collections. Therefore, all dimensions for a MongoDB data source need to be degenerate dimensions. For example, consider the following fact table:

Table 3. Sample Fact Table Showing Degenerate Dimensions
product_id time_id payment_method customer_id store_id item_count dollars
55 20040106 Credit 123 22 3 $3.54
78 20040106 Cash 89 22 1 $20.00
199 20040107 ATM 3 22 2 $2.99
55 20040106 Cash 122 22 1 $1.18

Notice the payment_method column. In a relational database, you might choose to have a separate dimension table to represent this data. In MongoDB, because joins are not supported, you cannot model that data in this way.

To create a degenerate dimension, just declare a dimension without a table, and Analyzer for MongoDB will assume that the columns come from the fact table.

<Cube name="Checkout">
<!-- The fact table is always necessary. -->
<Table>
<Dimension name="Payment method">
<Hierarchy hasAll="true">
<!-- No table element here. Fact table is assumed. -->
<Level name="Payment method" column="payment_method" uniqueMembers="true"/>
</Hierarchy>
</Dimension>
<!-- other dimensions and measures -->
</Cube>

Note that because there is no join, the foreignKey attribute of Dimension is not necessary, and the Hierarchy element has no <Table> child element or primaryKey attribute.

Time Dimensions

Time dimensions based on year/month/week/day are coded differently in the Analyzer for MongoDB schema due to the MDX time related functions such as:

  • ParallelPeriod([level[, index[, member]]])
  • PeriodsToDate([level[, member]])
  • WTD([member])
  • MTD([member])
  • QTD([member])
  • YTD([member])
  • LastPeriod(index[, member])

Time dimensions have type="TimeDimension". The role of an attribute in a time dimension is indicated by the attributes's levelType attribute, whose allowable values are as follows:

Table 4. Time Dimension Values
leveltype Value Meaning
TimeYears Level is a year
TimeHalfYear Level is a half-year
TimeQuarters Level is a quarter
TimeMonths Level is a month
TimeWeeks Level is a week
TimeDays Level represents days
TimeHours Level represents hours
TimeMinutes Level represents minutes
TimeSeconds Level represents seconds
TimeUnknown Represents an unspecified time period

Here is an example of a time dimension:

<Dimension name="Time" type="TimeDimension">
<Hierarchy hasAll="true" allMemberName="All Periods" primaryKey="dateid">
<Table name="datehierarchy"/>
<Level name="Year" column="year" uniqueMembers="true" levelType="TimeYears" type="Numeric"/>
<Level name="Quarter" column="quarter" uniqueMembers="false" levelType="TimeQuarters"/>
<Level name="Month" column="month" uniqueMembers="false" ordinalColumn="month" nameColumn="month_name" levelType="TimeMonths" type="Numeric"/>
<Level name="Week" column="week_in_month" uniqueMembers="false" levelType="TimeWeeks"/>
<Level name="Day" column="day_in_month" uniqueMembers="false" ordinalColumn="day_in_month" nameColumn="day_name" levelType="TimeDays" type="Numeric"/>
</Hierarchy>
</Dimension>

Member Properties

Member properties are defined by the <Property> element within an <Attribute>.

<Attribute name="City" keyColumn="city_id">
<Property attribute="Country"/>
<Property attribute="State"/>
<Property attribute="City Population" name="Population"/>
</Attribute>

The [City] attribute and three properties are defined here. Each property is defined in terms of another attribute in the same dimension. The State and Country properties inherit the name of the attribute they are defined from; the property created from the City Population attribute has an explicit name attribute to rename it to Population.

Since properties are defined from attributes, they are not simple values. They have a key, a name, a caption, and a sort order, just like attributes.

The attribute used to define a property must be functionally dependent. It would be illegal to define a property based on the [Zipcode] attribute, for instance, because there might be more than one zip code in a given city. But each city has only one value for state, country, and population.

You can also supply a nested <PropertyFormatter> element define a property formatter, which is explained later.

Once properties have been defined in the schema, all members of that attribute will have those properties. This applies to members of its attribute hierarchy, and also to members of a level of an explicit hierarchy, if that level is based on that attribute. In an MDX query, you can access those properties via the member.Properties("propertyName") function, for example:

SELECT {[Measures].[Store Sales]} ON COLUMNS, 
TopCount( 
Filter( 
[Customer].[City].Members, 
[Customer].[City].CurrentMember.Properties("Population") < 
10000), 
10, 
[Measures].[Store Sales]) ON ROWS 
FROM [Sales]

Analyzer for MongoDB deduces the type of the property expression, if it can. If the property name is a constant string, the type is based upon the type attribute (String, Numeric or Boolean) of the property definition. If the property name is an expression - for example, CurrentMember.Properties("Popu" + "lation") - Analyzer for MongoDB will return an untyped value.

Order and Display of Levels

Notice in the time hierarchy example above, the ordinalColumn and nameColumn attributes on the <Level> element. These affect how levels are displayed in a result. The ordinalColumn attribute specifies a column in the Hierarchy table that provides the order of the members in a given level, while nameColumn specifies a column that will be displayed.

For example, in the Month level above, the date hierarchy table has month (1 .. 12) and month_name (January, February, ...) columns. The column value that will be used internally within MDX is the month column, so valid member specifications will be of the form:

[Time].[2005].[Q1].[1]. 

Members of the [Month] level will be displayed in the order of January, February, etc.

Ordinal columns may be of any data type which can legally be used in an ORDER BY clause. The scope of ordering is per-parent, so in the example above, the day_in_month column should cycle for each month. Values returned by the JDBC driver should be non-null instances of java.lang.Comparable which yield the desired ordering when their Comparable.compareTo method is named.

Levels contain a type attribute, which can have values String, Integer, Numeric, Boolean, Date, Time, and Timestamp. The default value is Numeric because key columns generally have a numeric type.

Approximate Level Cardinality

The <Level> element allows specifying the optional attribute approxRowCount. Specifying approxRowCount can improve performance by reducing the need to determine level, hierarchy, and dimension cardinality. This can have a significant impact when connecting via XMLA.

Default Measure Attribute

The <Cube> element allows specifying the optional attribute of defaultMeasure.

Specifying defaultMeasure in the <Cube> element allows users to explicitly specify any base measure as a default Measure.

Note that if a default measure is not specified, it takes the first measure defined in the cube as the default.

Specifying the defaultMeasure explicitly would be useful in cases where you would want a calculated member to be picked up as the default measure.

<Cube name="Sales" defaultMeasure="Unit Sales">
...
<CalculatedMember name="Profit" dimension="Measures">
<Formula>[Measures].[Store Sales] - [Measures].[Store Cost]</Formula>
...
</CalculatedMember>
</Cube>