Brightspot CMS Developer Guide

Relationships


A data model can include object field types, that is, fields that are set to object values. In Dari, there are two types of relationships between objects: referenced (non-embedded) or embedded.

In a referenced relationship, a referencing object stores a reference ID to another object. The referenced object is stored as another record in the underlying database.

The following example shows an Activity object that’s represented in JSON. The project field references another object.

psddev.dari.test.Activity: 0000015a-dc72-dcb9-af7b-fdfac06c0000
{
  "project" : {
    "_ref" : "0000015a-dc72-dcb9-af7b-fdfac0550000",
    "_type" : "0000015a-7bb5-d284-addf-7ff7e7c00001"
  },
  "activityDate" : 1492833600000,
  "activityType" : "Download RFP response",
  "_id" : "0000015a-dc72-dcb9-af7b-fdfac06c0000",
  "_type" : "0000015a-7bb5-d284-addf-7ff7e7c00000"
}

In an embedded relationship, a containing object stores another object within it. The embedded object only exists with the containing object. It does not exist as a separate record in the database.

In the following example, the value of the project object field is embedded in the Activity object.

psddev.dari.test.Activity: 0000015a-d91b-d454-ad5b-ffbf95100000
{
  "project" : {
    "code" : "bethany-47-k528",
    "desc" : "Evaluate brand message",
    "_id" : "0000015a-d91b-d454-ad5b-ffbf95170000",
    "_type" : "0000015a-7bb5-d284-addf-7ff7e7c00001"
  },
  "activityDate" : 1490673600000,
  "activityType" : "Checkout",
  "_id" : "0000015a-d91b-d454-ad5b-ffbf95100000",
  "_type" : "0000015a-7bb5-d284-addf-7ff7e7c00000"
}


Any object that extends Record can be referenced by another object. As objects stored as separate records in the database, referenced objects can be directly queried and retrieved from the database.

In the following example, an Activity object is created. Because the Project object is referenced, it is created and saved first, then set on the project field of the Activity object.

import com.psddev.dari.db.*;
import psddev.dari.test.*;
import java.util.*;
import java.text.*;

public class Code {

    public static Object main() throws Throwable {

        Activity activity = new Activity();

        String startDateString = "04/22/2017";
        DateFormat df = new SimpleDateFormat("MM/dd/yyyy");
        activity.setActivityDate(df.parse(startDateString));

        activity.setActivityType("Download RFP response");

        Project project = new Project();
        project.setCode("tilden-21-x439");
        project.setDesc("Customer satisfaction survey");
        project.save();

        activity.setProject(project);
        activity.save();

       /* Returns new object in JSON */
       return activity;
    }

}

You can query the Project class to retrieve the object.

import com.psddev.dari.db.*;
import psddev.dari.test.*;
import java.util.*;

public class Code {

    public static Object main() throws Throwable {
        Project project = Query.from(Project.class)
          .where("code = 'tilden-21-x439'").first();
        return project;
    }

}


Dari objects that do not extend Record cannot be persisted as database records. For example, the Dari StorageItem, Location, and Region classes do not inherit from Record. Instances of these classes cannot be saved as independent objects in the database. They can only exist as dependent objects embedded within a containing object that inherits from Record.

Embedded objects cannot be directly saved or queried. They are saved when the containing object is saved. To retrieve embedded objects, you must query the containing class, then get the embedded objects from the containing object’s field values.

Using the @Embedded annotation, you can optionally embed a Record-derived object into a containing object. For example, assuming that the Project class extends Record, an Activity object stores—by default—a reference to a Project object. But if the project field is set to embedded in the Activity class, then a Project object that is set on the field is embedded into the Activity object.

public class Activity extends Record {
    
    @Embedded
    private Project project;

 }

In the following example, an Activity object is created with an embedded Project object. The Project object is not saved separately, but is saved as part of the Activity object.

import com.psddev.dari.db.*;
import psddev.dari.test.*;
import java.util.*;
import java.text.*;

public class Code {

    public static Object main() throws Throwable {
        Activity activity = new Activity();

        String startDateString = "03/28/2017";
        DateFormat df = new SimpleDateFormat("MM/dd/yyyy");
        activity.setActivityDate(df.parse(startDateString));

        activity.setUser(psddev.dari.test.User.getUser("Curly") );
        activity.setActivityType("Checkout");

        Project project = new Project();
        project.setCode("bethany-47-k528");
        project.setDesc("Evaluate brand message");

        activity.setProject(project);
        activity.save();

        /* Returns new object in JSON */
        return activity;
    }
}

Because the Project object is stored in the Activity record, you cannot query the Project class to retrieve the object. Instead, you must query the Activity class to get the embedded Project object.

import com.psddev.dari.db.*;
import psddev.dari.test.*;
import java.util.*;

public class Code {

    public static Object main() throws Throwable {
        for (Activity activity : Query.from(Activity.class).selectAll() ) {
            if (activity.getProject() == null) continue;
            if (activity.getProject().getCode().equals("bethany-47-k528") ) {
                Project project = activity.getProject();
                return project;
            }
        }
        return ("Can't find bethany-47-k528");

    }
}


You can model a many-to-many relationship in your Java classes. For example, a many-to-many relationship can be modeled between a Video class and a Playlist class. The Video class represents a single video, and the Playlist class represents a single playlist. Because a Playlist object can have a collection of videos, a Video object can be referenced by many Playlist objects.

The following snippets show this relationship.

public class Video extends Record {

    @Indexed
    private String title;

    @MimeTypes("+video/")
    private StorageItem video;

    /* Getters and setters */

}
public class Playlist extends Record {

    @Indexed
    private String owner;

    @Indexed
    private String name;

    @Indexed
    private List<Video> videos;
   
    /* Getters and setters */
}

Directly referencing a list of related objects can work for a relatively small number of items where minimal information is stored about the relationship. But such a model is unworkable when dealing with collections of thousands of items, and when more information is required than what can be captured in two model classes. To model a more advanced many-to-many relationship, use an intersection class.

To continue with the playlist/video scenario, let’s introduce additional requirements. An individual video or a playlist can only be represented by one Video or Playlist object. And a video must have a set position (order) within a playlist. To accommodate these requirements, an intersection class can be used to express the relationship between the Video and Playlist classes.

As shown in the schema diagram rendered by the Database Schema Viewer, the PlaylistItem class serves as the intersection class. Each PlaylistItem object represents a pairing of a single Video object and a single Playlist object, indicating that a user’s playlist includes that video at a set position.

Schema viewer Schema viewer

The following snippets show how the above schema is modeled in code. A Video object has a collection field that references all of the PlaylistItem objects associated with the video. Similarly, a Playlist object has a collection field that references all of the PlaylistItem objects associated with the playlist.

public class Video extends Record {

    @Indexed
    private String title;

    @MimeTypes("+video/")
    private StorageItem video;

    @Indexed
    private List<PlaylistItem> playlists;

    /* Getters and setters */
}
public class Playlist extends Record {

    @Indexed
    private String owner;

    @Indexed
    private String name;

    @Indexed
    private List<PlaylistItem> videos;

    /* Getters and setters */

}
public class PlaylistItem extends Record{

    @Indexed
    @Required
    private Playlist playlist;

    @Indexed
    @Required
    private Video video;

    @Indexed
    @Required
    private double position;

    /* Getters and setters */

    /* Ensures that a video can only be in a playlist one time */
    @Indexed (unique = true)
    public String getUniqueKey() {
        return this.getPlaylist().getId().toString() + "_" +
             this.getVideo().getId().toString();
    }
}

A common search that would be used in the playlist/video scenario is to query for a playlist and show all of the videos referenced by the playlist:

/* Get a playlist */
Playlist pl = Query.from(Playlist.class).first();

/* Get all videos associated with the playlist sorted by position */
List<PlaylistItem> items = Query.from(PlaylistItem.class)
          .where("playlist = ?", playlist)
          .sortAscending("position")
          .selectAll();

for (PlaylistItem item : items) {
    System.out.println("Title: " + item.getVideo().getTitle());
}

Previous Topic
Field types
Next Topic
Indexes
Was this topic helpful?
Thanks for your feedback.
Our robust, flexible Design System provides hundreds of pre-built components you can use to build the presentation layer of your dreams.

Asset types
Module types
Page types
Brightspot is packaged with content types that get you up and running in a matter of days, including assets, modules and landing pages.

Content types
Modules
Landing pages
Everything you need to know when creating, managing, and administering content within Brightspot CMS.

Dashboards
Publishing
Workflows
Admin configurations
A guide for installing, supporting, extending, modifying and administering code on the Brightspot platform.

Field types
Content modeling
Rich-text elements
Images
A guide to configuring Brightspot's library of integrations, including pre-built options and developer-configured extensions.

Google Analytics
Shopify
Apple News