diff --git a/docs/content/Caching/Getting-Started-Pre-Aggregations.mdx b/docs/content/Caching/Getting-Started-Pre-Aggregations.mdx index 9157c3ab83467..8490a824c8722 100644 --- a/docs/content/Caching/Getting-Started-Pre-Aggregations.mdx +++ b/docs/content/Caching/Getting-Started-Pre-Aggregations.mdx @@ -167,8 +167,8 @@ receives via the API. The process for selection is summarized below: - The pre-aggregation contains all dimensions, filter dimensions and leaf measures from the query - - The measures aren't multiplied ([via a `hasMany` - relation][ref-schema-joins-hasmany]) + - The measures aren't multiplied ([via a `one_to_many` + relationship][ref-schema-joins-rel]) 3. If no, then check if @@ -197,7 +197,7 @@ cube(`LineItems`, { joins: { Orders: { sql: `${CUBE}.order_id = ${Orders.id}`, - relationship: `belongsTo`, + relationship: `many_to_one`, }, }, @@ -425,7 +425,7 @@ To recap what we've learnt so far: - **Additive measures** are measures whose values can be added together -- **Multiplied measures** are measures that define `hasMany` relations +- **Multiplied measures** are measures that define `one_to_many` relationships - **Leaf measures** are measures that do not reference any other measures in their definition @@ -488,7 +488,7 @@ Some extra considerations for pre-aggregation selection: /caching/using-pre-aggregations#pre-aggregations-storage [ref-schema-dims]: /schema/reference/dimensions [ref-schema-joins]: /schema/reference/joins -[ref-schema-joins-hasmany]: /schema/reference/joins#relationship +[ref-schema-joins-rel]: /schema/reference/joins#relationship [ref-schema-preaggs]: /schema/reference/pre-aggregations [ref-schema-preaggs-origsql]: /schema/reference/pre-aggregations#type-originalsql diff --git a/docs/content/Caching/Using-Pre-Aggregations.mdx b/docs/content/Caching/Using-Pre-Aggregations.mdx index a5343317db43f..f3b13d61db3e8 100644 --- a/docs/content/Caching/Using-Pre-Aggregations.mdx +++ b/docs/content/Caching/Using-Pre-Aggregations.mdx @@ -582,7 +582,7 @@ cube(`Orders`, { joins: { Users: { sql: `${CUBE.user_id} = ${Users.id}`, - relationship: `belongsTo` + relationship: `many_to_one` }, }, }); diff --git a/docs/content/Examples-Tutorials-Recipes/Recipes/column-based-access.mdx b/docs/content/Examples-Tutorials-Recipes/Recipes/column-based-access.mdx index 6fb1542d03e84..f3007302c6ed4 100644 --- a/docs/content/Examples-Tutorials-Recipes/Recipes/column-based-access.mdx +++ b/docs/content/Examples-Tutorials-Recipes/Recipes/column-based-access.mdx @@ -19,7 +19,7 @@ To implement column-based access, we will use supplier's email from a [`queryRewrite`](https://cube.dev/docs/security/context#using-query-rewrite) extension point to manage data access. -We have `Products` and `Suppliers` cubes with a `hasOne` relationship from +We have `Products` and `Suppliers` cubes with a `many_to_one` relationship from products to suppliers: ```javascript @@ -28,7 +28,7 @@ cube(`Products`, { joins: { Suppliers: { - relationship: `belongsTo`, + relationship: `many_to_one`, sql: `${CUBE}.supplier_id = ${Suppliers.id}`, }, }, diff --git a/docs/content/Examples-Tutorials-Recipes/Recipes/entity-attribute-value.mdx b/docs/content/Examples-Tutorials-Recipes/Recipes/entity-attribute-value.mdx index d6083b9372c2a..8ec7f50b4b38b 100644 --- a/docs/content/Examples-Tutorials-Recipes/Recipes/entity-attribute-value.mdx +++ b/docs/content/Examples-Tutorials-Recipes/Recipes/entity-attribute-value.mdx @@ -26,7 +26,7 @@ cube(`Users`, { joins: { Orders: { - relationship: 'hasMany', + relationship: 'one_to_many', sql: `${CUBE}.id = ${Orders.userId}`, }, }, diff --git a/docs/content/Examples-Tutorials-Recipes/Recipes/event-analytics.mdx b/docs/content/Examples-Tutorials-Recipes/Recipes/event-analytics.mdx index 57e0172a27f8b..7e06ef77b93a7 100644 --- a/docs/content/Examples-Tutorials-Recipes/Recipes/event-analytics.mdx +++ b/docs/content/Examples-Tutorials-Recipes/Recipes/event-analytics.mdx @@ -254,9 +254,10 @@ The next step is to identify the events contained within the session and the events ending the session. It’s required to get metrics such as session duration and events per session, or to identify sessions where specific events occurred (we’re going to use that for funnel analysis later on). We’re going to -[declare join](/schema/reference/joins), that Events `belongsTo` Sessions and a -specify condition, such as all users' events from session start (inclusive) till -the start of the next session (exclusive) belong to that session. +[declare a join](/schema/reference/joins), that Events have a `many_to_one` +relation to Sessions, and specify a condition, such as all users' events from +session start (inclusive) till the start of the next session (exclusive) belong +to that session. ```javascript cube('Events', { @@ -265,7 +266,7 @@ cube('Events', { // Add the joins block to the Events cube joins: { Sessions: { - relationship: `belongsTo`, + relationship: `many_to_one`, sql: ` ${Events.anonymousId} = ${Sessions.anonymousId} AND ${Events.timestamp} >= ${Sessions.startAt} @@ -382,7 +383,7 @@ cube('Sessions', { // Declare this joins block in the Sessions cube joins: { Identifies: { - relationship: `belongsTo`, + relationship: `many_to_one`, sql: `${Identifies.anonymousId} = ${Sessions.anonymousId}` } }, diff --git a/docs/content/Examples-Tutorials-Recipes/Recipes/funnels.mdx b/docs/content/Examples-Tutorials-Recipes/Recipes/funnels.mdx index 327b3526d15f9..745500e4bc7a9 100644 --- a/docs/content/Examples-Tutorials-Recipes/Recipes/funnels.mdx +++ b/docs/content/Examples-Tutorials-Recipes/Recipes/funnels.mdx @@ -264,7 +264,7 @@ steps: [ In order to provide additional dimensions, funnels can be joined with other cubes using `user_id` at the first step of a funnel. This will always use a -`belongsTo` relationship, so hence you should always join with the corresponding +`many_to_one` relationship, hence you should always join with the corresponding user cube. Here, by 'user' we understand this to be any entity that can go through a sequence of steps within funnel. It could be a real web user with an auto assigned ID or a specific email sent by an email automation that goes @@ -276,7 +276,7 @@ following: cube(`PurchaseFunnel`, { joins: { Users: { - relationship: `belongsTo`, + relationship: `many_to_one`, sql: `${CUBE}.first_step_user_id = ${Users.id}`, }, }, diff --git a/docs/content/Schema/Advanced/Code-Reusability-Extending-Cubes.mdx b/docs/content/Schema/Advanced/Code-Reusability-Extending-Cubes.mdx index 4aab28169dfae..49b94d158df15 100644 --- a/docs/content/Schema/Advanced/Code-Reusability-Extending-Cubes.mdx +++ b/docs/content/Schema/Advanced/Code-Reusability-Extending-Cubes.mdx @@ -41,7 +41,7 @@ cube(`BaseEvents`, { joins: { Users: { - relationship: `belongsTo`, + relationship: `many_to_one`, sql: `${CUBE}.user_id = ${Users.id}`, }, }, diff --git a/docs/content/Schema/Advanced/Polymorphic-Cubes.mdx b/docs/content/Schema/Advanced/Polymorphic-Cubes.mdx index 2b35a460d3d24..51d40f75623f0 100644 --- a/docs/content/Schema/Advanced/Polymorphic-Cubes.mdx +++ b/docs/content/Schema/Advanced/Polymorphic-Cubes.mdx @@ -89,11 +89,11 @@ cube(`Lessons`, { joins: { Students: { - relationship: `belongsTo`, + relationship: `many_to_one`, sql: `${CUBE}.student_id = ${Students.id}`, }, Teachers: { - relationship: `belongsTo`, + relationship: `many_to_one`, sql: `${CUBE}.teacher_id = ${Teachers.id}`, }, }, diff --git a/docs/content/Schema/Fundamentals/Additional-Concepts.mdx b/docs/content/Schema/Fundamentals/Additional-Concepts.mdx index ade69483cf2a8..0679150419372 100644 --- a/docs/content/Schema/Fundamentals/Additional-Concepts.mdx +++ b/docs/content/Schema/Fundamentals/Additional-Concepts.mdx @@ -34,12 +34,12 @@ cube(`Orders`, { joins: { Users: { - relationship: `belongsTo`, + relationship: `many_to_one`, sql: `${CUBE}.user_id = ${Users.id}`, }, Products: { - relationship: `belongsTo`, + relationship: `many_to_one`, sql: `${CUBE}.product_id = ${Products.id}`, }, }, @@ -76,10 +76,10 @@ cubes: joins: - name: Users - relationship: belongs_to + relationship: many_to_one sql: "{CUBE}.user_id = {Users}.id" - name: Products - relationship: belongs_to + relationship: many_to_one sql: "{CUBE}.product_id = {Products}.id" measures: @@ -175,7 +175,7 @@ cube(`SalesManagers`, { joins: { Deals: { - relationship: `hasMany`, + relationship: `one_to_many`, sql: `${CUBE}.id = ${Deals.salesManagerId}`, }, }, @@ -218,7 +218,7 @@ cubes: joins: - name: Deals - relationship: has_many + relationship: one_to_many sql: "{SalesManagers}.id = {Deals}.sales_manager_id" measures: diff --git a/docs/content/Schema/Fundamentals/Concepts.mdx b/docs/content/Schema/Fundamentals/Concepts.mdx index 8286b42bb4e35..6c36185e89354 100644 --- a/docs/content/Schema/Fundamentals/Concepts.mdx +++ b/docs/content/Schema/Fundamentals/Concepts.mdx @@ -331,7 +331,7 @@ cube('Orders', { joins: { LineItems: { - relationship: `belongsTo`, + relationship: `many_to_one`, // Here we use the `CUBE` global to refer to the current cube, // so the following is equivalent to `Orders.id = LineItems.order_id` sql: `${CUBE}.id = ${LineItems.order_id}`, @@ -348,19 +348,13 @@ cubes: # Here we use the `CUBE` global to refer to the current cube, # so the following is equivalent to `Orders.id = LineItems.order_id` sql: "{CUBE}.id = {LineItems.order_id}" - relationship: belongs_to + relationship: many_to_one ``` -There are three kinds of join relationships: - -- `belongsTo`/`belongs_to` -- `hasOne`/`has_one` -- `hasMany`/`has_many` - -More information can be found in the [Joins reference -documentation][ref-schema-ref-joins-types]. +There are the three [types of join relationships][ref-schema-ref-joins-types]: +`one_to_one`, `one_to_many`, and `many_to_one`. ## Segments diff --git a/docs/content/Schema/Fundamentals/Working-with-Joins.mdx b/docs/content/Schema/Fundamentals/Working-with-Joins.mdx index 0daee2a7dec99..1fae1c51588e6 100644 --- a/docs/content/Schema/Fundamentals/Working-with-Joins.mdx +++ b/docs/content/Schema/Fundamentals/Working-with-Joins.mdx @@ -10,11 +10,12 @@ redirect_from: --- A join creates a relationship between two cubes in your Cube project. Cube -supports three kinds of relationships often found in SQL databases: +supports three [types of join relationships][ref-schema-ref-joins-relationship] +often found in SQL databases: -- `hasOne`/`has_one` -- `hasMany`/`has_many` -- `belongsTo`/`belongs_to` +- `one_to_one` +- `one_to_many` +- `many_to_one` To use an example, let's use two cubes, `Customers` and `Orders`: @@ -83,7 +84,7 @@ We could add a join to the `Customers` cube: cube('Customers', { joins: { Orders: { - relationship: `hasMany`, + relationship: `one_to_many`, sql: `${CUBE}.id = ${Orders.customer_id}`, }, }, @@ -95,7 +96,7 @@ cubes: - name: Customers joins: - name: Orders - relationship: has_many + relationship: one_to_many sql: "{CUBE}.id = {Orders.customer_id}" ``` @@ -137,10 +138,10 @@ LIMIT ``` However, if we have guest checkouts, that would mean we would have orders with -no matching customer. Looking back at the `hasMany` relationship and its' +no matching customer. Looking back at the `one_to_many` relationship and its' resulting SQL, any guest checkouts would be excluded from the results. To remedy this, we'll remove the join from the `Customers` cube and instead define a join -with a `belongsTo` relationship on the `Orders` cube: +with a `many_to_one` relationship on the `Orders` cube: @@ -148,7 +149,7 @@ with a `belongsTo` relationship on the `Orders` cube: cube('Orders', { joins: { Customers: { - relationship: `belongsTo`, + relationship: `many_to_one`, sql: `${CUBE}.customer_id = ${Customers.id}`, }, }, @@ -160,7 +161,7 @@ cubes: - name: Orders joins: - name: Customers - relationship: belongs_to + relationship: many_to_one sql: "{CUBE}.customer_id = {Customers.id}" ``` @@ -194,7 +195,7 @@ retrieved. In Cube, joins only need to be defined from one direction. In the above example, -we explicitly _removed_ the `hasMany` relationship from the `Customer` cube; not +we explicitly _removed_ the `one_to_many` relationship from the `Customer` cube; not doing so would cause the query to fail as Cube would be unable to determine a valid join path. [Click here][self-join-direction] to learn more about how the direction of joins affects query results. @@ -240,7 +241,7 @@ and declare the relationships from it to `Topics` cube and from `Posts` to -The following example uses the `hasMany` relationship on the `PostTopics` cube; +The following example uses the `one_to_many` relationship on the `PostTopics` cube; this causes the direction of joins to be `Posts -> PostTopics -> Topics`. [Read more about direction of joins here][self-join-direction]. @@ -254,7 +255,7 @@ cube(`Posts`, { joins: { PostTopics: { - relationship: `belongsTo`, + relationship: `many_to_one`, sql: `${CUBE}.id = ${PostTopics.post_id}`, }, }, @@ -277,7 +278,7 @@ cube(`PostTopics`, { joins: { Topic: { - relationship: `hasMany`, + relationship: `one_to_many`, sql: `${CUBE}.topic_id = ${Topics.id}`, }, }, @@ -297,7 +298,7 @@ cubes: sql: SELECT * FROM posts joins: - name: PostTopics - relationship: belongs_to + relationship: many_to_one sql: "{CUBE}.id = {PostTopics.post_id}" - name: Topics @@ -312,7 +313,7 @@ cubes: sql: SELECT * FROM post_topics joins: - name: Topic - relationship: has_many + relationship: one_to_many sql: "{CUBE}.topic_id = {Topics.id}" dimensions: - name: post_id @@ -494,7 +495,7 @@ The following diagram shows our data schema with the `Campaigns` cube: The last piece is to finally declare a many-to-many relationship. This should be -done by declaring a [`hasMany` relationship][ref-schema-ref-joins-relationship] +done by declaring a [`one_to_many` relationship][ref-schema-ref-joins-relationship] on the associative cube, `Campaigns` in our case. @@ -505,7 +506,7 @@ cube(`Emails`, { joins: { Campaigns: { - relationship: `belongsTo`, + relationship: `many_to_one`, sql: `${CUBE}.campaign_id = ${Campaigns.id} AND ${CUBE}.customer_name = ${Campaigns.customerName}`, }, @@ -539,7 +540,7 @@ cube(`Emails`, { cube(`Campaigns`, { joins: { Transactions: { - relationship: `hasMany`, + relationship: `one_to_many`, sql: `${CUBE}.customer_name = ${Transactions.customerName} AND ${CUBE}.campaign_id = ${Transactions.campaignId}`, }, @@ -566,7 +567,7 @@ cubes: sql: SELECT * FROM emails joins: - name: Campaigns - relationship: belongs_to + relationship: many_to_one sql: "{CUBE}.campaign_id = {Campaigns.id} AND {CUBE}.customer_name = {Campaigns.customer_name}" measures: - name: count @@ -586,7 +587,7 @@ cubes: - name: Campaigns joins: - name: Transactions - relationship: has_many + relationship: one_to_many sql: "{CUBE}.customer_name = {Transactions.customer_name} AND {CUBE}.campaign_id = {Transactions.campaign_id}" dimensions: - name: id @@ -697,7 +698,7 @@ cube('Orders', { joins: { Customers: { - relationship: `belongsTo`, + relationship: `many_to_one`, sql: `${CUBE}.customer_id = ${Customers.id}`, }, }, @@ -736,7 +737,7 @@ cubes: sql: SELECT * FROM orders joins: - name: Customers - relationship: belongs_to + relationship: many_to_one sql: "{CUBE}.customer_id = {Customers.id}" measures: - name: count @@ -786,7 +787,7 @@ cube('Customers', { joins: { Orders: { - relationship: `hasMany`, + relationship: `one_to_many`, sql: `${CUBE}.id = ${Orders.customer_id}`, }, }, @@ -820,7 +821,7 @@ cubes: sql: SELECT * FROM customers joins: - name: Orders - relationship: has_many + relationship: one_to_many sql: "{CUBE}.id = {Orders.customer_id}" measures: - name: count @@ -935,11 +936,11 @@ cube(`OrganizationUsers`, { joins: { Users: { sql: `${CUBE}.user_id = ${Users.id}`, - relationship: `hasMany`, + relationship: `one_to_many`, }, Organizations: { sql: `${CUBE}.organization_id = ${Organizations.id}`, - relationship: `hasMany`, + relationship: `one_to_many`, }, }, @@ -984,10 +985,10 @@ cubes: joins: - name: Users sql: "{CUBE}.user_id = {Users.id}" - relationship: has_many + relationship: one_to_many - name: Organizations sql: "{CUBE}.organization_id = {Organizations.id}" - relationship: has_many + relationship: one_to_many dimensions: - name: id # Joins require a primary key, so we'll create one on-the-fly @@ -1032,7 +1033,7 @@ cube(`Users`, { joins: { OrganizationUsers: { sql: `${OrganizationUsers.user_id} = ${Users.id}`, - relationship: `hasMany`, + relationship: `one_to_many`, }, }, @@ -1045,7 +1046,7 @@ cube(`OrganizationUsers`, { joins: { Organizations: { sql: `${OrganizationUsers.organization_id} = ${Organizations.id}`, - relationship: `hasMany`, + relationship: `one_to_many`, }, }, }); @@ -1057,7 +1058,7 @@ cubes: joins: - name: OrganizationUsers sql: "{OrganizationUsers.user_id} = {Users.id}" - relationship: has_many + relationship: one_to_many measures: - name: type type: count @@ -1066,7 +1067,7 @@ cubes: joins: - name: Organizations sql: "{OrganizationUsers.organization_id} = {Organizations.id}" - relationship: has_many + relationship: one_to_many ``` diff --git a/docs/content/Schema/Reference/cube.mdx b/docs/content/Schema/Reference/cube.mdx index b0fa7ef132550..52a007fdcddb7 100644 --- a/docs/content/Schema/Reference/cube.mdx +++ b/docs/content/Schema/Reference/cube.mdx @@ -23,7 +23,7 @@ cube(`Users`, { joins: { Organizations: { - relationship: `belongsTo`, + relationship: `many_to_one`, sql: `${Users.organization_id} = ${Organizations.id}`, }, }, @@ -61,7 +61,7 @@ cubes: sql: SELECT * FROM USERS joins: - name: Organizations - relationship: belongs_to + relationship: many_to_one sql: "{CUBE.organization_id} = {Organizations.id}" measures: - name: count @@ -106,7 +106,7 @@ cube(`Users`, { joins: { Contacts: { sql: `${CUBE}.contact_id = ${Contacts.id}`, - relationship: `hasOne`, + relationship: `one_to_one`, }, }, @@ -144,7 +144,7 @@ cubes: joins: - name: Contacts sql: "{CUBE.contact_id} = {Contacts.id}" - relationship: has_one + relationship: one_to_one dimensions: - name: name sql: "COALESCE({CUBE.name}, {Contacts.name})" diff --git a/docs/content/Schema/Reference/joins.mdx b/docs/content/Schema/Reference/joins.mdx index a180daf7c5e77..8116ae12fdccd 100644 --- a/docs/content/Schema/Reference/joins.mdx +++ b/docs/content/Schema/Reference/joins.mdx @@ -20,13 +20,7 @@ time. cube('MyCube', { joins: { TargetCubeName: { - relationship: - `belongsTo` || - `hasMany` || - `hasOne` || - `belongs_to` || - `has_many` || - `has_one`, + relationship: `one_to_one` || `one_to_many` || `many_to_one`, sql: `SQL ON clause`, }, }, @@ -38,7 +32,7 @@ cubes: - name: MyCube joins: - name: TargetCubeName - relationship: belongsTo || hasMany || hasOne + relationship: one_to_one || one_to_many || many_to_one sql: SQL ON clause ``` @@ -77,7 +71,7 @@ would define the join as follows: cube(`orders`, { joins: { products: { - relationship: `belongsTo`, + relationship: `many_to_one`, sql: `${CUBE.id} = ${products.order_id}`, }, }, @@ -89,7 +83,7 @@ cubes: - name: orders joins: - name: products - relationship: belongs_to + relationship: many_to_one sql: '{CUBE.id} = {products.order_id}' ``` @@ -97,40 +91,59 @@ cubes: ### <--{"id" : "Parameters"}--> relationship -`relationship` enables you to describe the join relationship between joined -cubes. It’s important to properly define the type of relationship in order for -Cube to accurately calculate measures. The relationship does not need to be -defined on both cubes, but the definition can affect the [join -direction][ref-schema-fundamentals-join-dir]. +The `relationship` property is used to describe the type of the relationship +between joined cubes. It’s important to properly define the type of relationship +so Cube can accurately calculate measures. + +The cube that declares the join is considered _left_ in terms of the [left +join][wiki-left-join] semantics, and the joined cube is considered _right_. It +means that all rows of the _left_ cube are selected, while only those rows of +the _right_ cube that match the condition are selected as well. For more +information and specific examples, please see [join +directions][ref-schema-fundamentals-join-dir]. -It is very important to define the correct order of cubes in a join. It affects -data in the result-set greatly. The basic cube represents the left entity in a -join, all others would be right. That means that all rows of the left cube are -selected, while rows of the right depend on the condition. For more information -and specific examples, please read about [join directions -here][ref-schema-fundamentals-join-dir]. +The join does not need to be defined on both cubes, but the definition can +affect the [join direction][ref-schema-fundamentals-join-dir]. -The three possible values for a relationship are: +You can use the following types of relationships: + +- `one_to_one` for [one-to-one][wiki-1-1] relationships +- `one_to_many` for [one-to-many][wiki-1-m] relationships +- `many_to_one` for the opposite of [one-to-many][wiki-1-m] relationships + + + +The types of relationships listed above were introduced in v0.32.19 for clarity as +they are commonly used in the data space. The following aliases were used before and +are still valid, so there's no need to update existing data models: -#### hasOne +- `one_to_one` was known as `has_one` or `hasOne` +- `one_to_many` was known as `has_many` or `hasMany` +- `many_to_one` was known as `belongs_to` or `belongsTo` -A `hasOne` relationship indicates a one-to-one connection with another cube. -This relationship indicates that the one row in the cube can match only one row -in the joined cube. For example, in a model containing users and user profiles, -the users cube would have the following join: + + +#### One-to-one + +The `one_to_one` type indicates a [one-to-one][wiki-1-1] relationship between +the declaring cube and the joined cube. It means that one row in the declaring +cube can match only one row in the joined cube. + +For example, in a data model containing `users` and their `profiles`, the +`users` cube would declare the following join: ```javascript -cube('Users', { +cube(`users`, { joins: { - Profile: { - relationship: `hasOne`, - sql: `${CUBE}.id = ${Profile.user_id}`, + profiles: { + relationship: `one_to_one`, + sql: `${CUBE}.id = ${profiles.user_id}`, }, }, }); @@ -138,31 +151,32 @@ cube('Users', { ```yaml cubes: - - name: Users + - name: users joins: - - name: Profile - relationship: has_one - sql: "{Users}.id = {Profile.user_id}" + - name: profiles + relationship: one_to_one + sql: '{users}.id = {profiles.user_id}' ``` -#### hasMany +#### One-to-many + +The `one_to_many` type indicates a [one-to-many][wiki-1-m] relationship between +the declaring cube and the joined cube. It means that one row in the declaring +cube can match many rows in the joined cube. -A `hasMany` relationship indicates a one-to-many connection with another cube. -You'll often find this relationship on the "other side" of a `belongsTo` -relationship. This relationship indicates that the one row in the cube can match -many rows in the joined cube. For example, in a model containing authors and -books, the authors cube would have the following join: +For example, in a data model containing `authors` and the `books` they have +written, the `authors` cube would declare the following join: ```javascript -cube('Authors', { +cube(`authors`, { joins: { - Books: { - relationship: `hasMany`, - sql: `${CUBE}.id = ${Books.author_id}`, + books: { + relationship: `one_to_many`, + sql: `${CUBE}.id = ${books.author_id}`, }, }, }); @@ -170,32 +184,34 @@ cube('Authors', { ```yaml cubes: - - name: Authors + - name: authors joins: - - name: Books - relationship: has_many - sql: "{Authors}.id = {Books.author_id}" + - name: books + relationship: one_to_many + sql: '{authors}.id = {books.author_id}' ``` -#### belongsTo +#### Many-to-one -A `belongsTo` relationship indicates a many-to-one connection with another cube. -You’ll often find this relationship on the “other side” of a `hasMany` -relationship. This relationship indicates that the one row of the declaring cube -matches a row in the joined instance, while the joined instance can have many -rows in the declaring cube. For example, in a model containing orders and -customers, the orders cube would have the following join: +The `many_to_one` type indicates the many-to-one relationship between the +declaring cube and the joined cube. You’ll often find this type of relationship +on the opposite side of the [one-to-many][wiki-1-m] relationship. It means that +one row in the declaring cube matches a single row in the joined cube, while a +row in the joined cube can match many rows in the declaring cube. + +For example, in a data model containing `orders` and `customers` who made them, +the `orders` cube would have the following join: ```javascript -cube('Orders', { +cube(`orders`, { joins: { - Customers: { - relationship: `belongsTo`, - sql: `${CUBE}.customer_id = ${Customers.id}`, + customers: { + relationship: `many_to_one`, + sql: `${CUBE}.customer_id = ${customers.id}`, }, }, }); @@ -203,11 +219,11 @@ cube('Orders', { ```yaml cubes: - - name: Orders + - name: orders joins: - - name: Customers - relationship: belongsTo - sql: "{Orders}.customer_id = {Customers.id}" + - name: customers + relationship: many_to_one + sql: '{orders}.customer_id = {customers.id}' ``` @@ -221,13 +237,13 @@ example below: ```javascript -cube('Orders', { +cube(`orders`, { joins: { - Customers: { - relationship: `belongsTo`, - // The `customer_id` field on `Orders` corresponds to the - // `id` field on `Customers` - sql: `${CUBE}.customer_id = ${Customers.id}`, + customers: { + relationship: `many_to_one`, + // The `customer_id` column of the `orders` cube corresponds to the + // `id` dimension of the `customers` cube + sql: `${CUBE}.customer_id = ${customers.id}`, }, }, }); @@ -235,11 +251,11 @@ cube('Orders', { ```yaml cubes: - - name: Orders + - name: orders joins: - - name: Customers - relationship: belongs_to - sql: "{Orders}.customer_id = {Customers.id}" + - name: customers + relationship: many_to_one + sql: '{orders}.customer_id = {customers.id}' ``` @@ -252,7 +268,7 @@ handle row multiplication issues. Let's imagine you want to calculate `Order Amount` by `Order Item Product Name`. In this case, `Order` rows will be multiplied by the `Order Item` join due to -the `hasMany` relationship. In order to produce correct results, Cube will +the `one_to_many` relationship. In order to produce correct results, Cube will select distinct primary keys from `Order` first and then will join these primary keys with `Order` to get the correct `Order Amount` sum result. Please note that `primaryKey` should be defined in the `dimensions` section. @@ -260,12 +276,12 @@ keys with `Order` to get the correct `Order Amount` sum result. Please note that ```javascript -cube('Orders', { +cube('orders', { dimensions: { - customerId: { + customer_id: { sql: `id`, type: `number`, - primaryKey: true, + primary_key: true, }, }, }); @@ -273,7 +289,7 @@ cube('Orders', { ```yaml cubes: - - name: Orders + - name: orders dimensions: - name: customer_id sql: id @@ -285,7 +301,7 @@ cubes: -Setting `primaryKey` to `true` will change the default value of the `shown` +Setting `primary_key` to `true` will change the default value of the `shown` parameter to `false`. If you still want `shown` to be `true` — set it manually. @@ -293,12 +309,12 @@ parameter to `false`. If you still want `shown` to be `true` — set it manually ```javascript -cube('Orders', { +cube(`orders`, { dimensions: { - customerId: { + customer_id: { sql: `id`, type: `number`, - primaryKey: true, + primary_key: true, shown: true, }, }, @@ -307,7 +323,7 @@ cube('Orders', { ```yaml cubes: - - name: Orders + - name: orders dimensions: - name: customer_id sql: id @@ -331,12 +347,12 @@ depending on your database. ```javascript -cube('Users', { +cube(`users`, { dimensions: { id: { sql: `${CUBE}.user_id || '-' || ${CUBE}.signup_week || '-' || ${CUBE}.activity_week`, type: `string`, - primaryKey: true, + primary_key: true, }, }, }); @@ -344,7 +360,7 @@ cube('Users', { ```yaml cubes: - - name: Users + - name: users dimensions: - name: id sql: "{CUBE}.user_id || '-' || {CUBE}.signup_week || '-' || {CUBE}.activity_week" @@ -365,7 +381,7 @@ as an alias for a basic cube. Take a look at the following example: ```javascript -cube('Users', { +cube(`users`, { dimensions: { name: { sql: `${CUBE}.name`, @@ -377,10 +393,10 @@ cube('Users', { ```yaml cubes: - - name: Users + - name: users dimensions: - name: name - sql: "{CUBE}.name" + sql: '{CUBE}.name' type: string ``` @@ -390,22 +406,22 @@ cubes: -Join graph is directed and `A-B` join is different from `B-A`. [Learn more about -it here][ref-schema-fundamentals-join-dir]. +Join graph is directed and `a → b` join is different from `b → a`. [Learn more +about it here][ref-schema-fundamentals-join-dir]. -Cube automatically takes care of transitive joins. For example if you have the -following schema: +Cube automatically takes care of transitive joins. For example, consider the +following data model: ```javascript -cube(`A`, { +cube(`a`, { joins: { - B: { - sql: `${A}.b_id = ${B}.id`, - relationship: `belongsTo`, + b: { + sql: `${a}.b_id = ${b.id}`, + relationship: `many_to_one`, }, }, @@ -416,11 +432,11 @@ cube(`A`, { }, }); -cube(`B`, { +cube(`b`, { joins: { - C: { - sql: `${B}.c_id = ${C}.id`, - relationship: `belongsTo`, + c: { + sql: `${b}.c_id = ${c.id}`, + relationship: `many_to_one`, }, }, }); @@ -437,20 +453,22 @@ cube(`C`, { ```yaml cubes: - - name: A + - name: a joins: - - name: B - sql: "{A}.b_id = {B}.id" - relationship: belongs_to + - name: b + sql: '{a}.b_id = {b.id}' + relationship: many_to_one measures: - name: count type: count - - name: B + + - name: c joins: - - name: C - sql: "{B}.c_id = {C}.id" - relationship: belongs_to - - name: C + - name: c + sql: '{b}.c_id = {c.id}' + relationship: many_to_one + + - name: c dimensions: - name: category sql: category @@ -459,18 +477,18 @@ cubes: -and the following query: +Assume that the following query is run: ```json { - "measures": ["A.count"], - "dimensions": ["C.category"] + "measures": ["a.count"], + "dimensions": ["c.category"] } ``` -Joins `A-B` and `B-C` will be resolved automatically. Cube uses [Dijkstra -algorithm][wiki-djikstra-alg] to find join path between cubes given requested -members. +Joins `a → b` and `b → c` will be resolved automatically. Cube uses the +[Dijkstra algorithm][wiki-djikstra-alg] to find a join path between cubes given +requested members. [ref-restapi-query-filter-op-set]: /query-format#set [ref-schema-fundamentals-join-dir]: @@ -479,3 +497,6 @@ members. [ref-schema-data-blenging]: /schema/advanced/data-blending#data-blending [ref-naming]: /data-modeling/syntax#naming [wiki-djikstra-alg]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm +[wiki-left-join]: https://en.wikipedia.org/wiki/Join_(SQL)#Left_outer_join +[wiki-1-1]: https://en.wikipedia.org/wiki/One-to-one_(data_model) +[wiki-1-m]: https://en.wikipedia.org/wiki/One-to-many_(data_model) diff --git a/docs/content/Schema/Reference/pre-aggregations.mdx b/docs/content/Schema/Reference/pre-aggregations.mdx index 63860595a9fac..25bd7637f589b 100644 --- a/docs/content/Schema/Reference/pre-aggregations.mdx +++ b/docs/content/Schema/Reference/pre-aggregations.mdx @@ -292,7 +292,7 @@ cube('Orders', { joins: { Users: { - relationship: `belongsTo`, + relationship: `many_to_one`, // Make sure the join uses dimensions on the cube, rather than // the column names from the underlying SQL sql: `${CUBE.user_id} = ${Users.id}`, @@ -365,7 +365,7 @@ cubes: rollups: [Users.users_rollup, CUBE.orders_rollup] joins: - name: Users - relationship: belongs_to + relationship: many_to_one sql: '{CUBE.user_id} = {Users.id}' measures: - name: count