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.
1psddev.dari.test.Activity: 0000015a-dc72-dcb9-af7b-fdfac06c00002{3"project" : {4"_ref" : "0000015a-dc72-dcb9-af7b-fdfac0550000",5"_type" : "0000015a-7bb5-d284-addf-7ff7e7c00001"6},7"activityDate" : 1492833600000,8"activityType" : "Download RFP response",9"_id" : "0000015a-dc72-dcb9-af7b-fdfac06c0000",10"_type" : "0000015a-7bb5-d284-addf-7ff7e7c00000"11}
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.
1psddev.dari.test.Activity: 0000015a-d91b-d454-ad5b-ffbf951000002{3"project" : {4"code" : "bethany-47-k528",5"desc" : "Evaluate brand message",6"_id" : "0000015a-d91b-d454-ad5b-ffbf95170000",7"_type" : "0000015a-7bb5-d284-addf-7ff7e7c00001"8},9"activityDate" : 1490673600000,10"activityType" : "Checkout",11"_id" : "0000015a-d91b-d454-ad5b-ffbf95100000",12"_type" : "0000015a-7bb5-d284-addf-7ff7e7c00000"13}
Object references
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.
1import com.psddev.dari.db.*;2import psddev.dari.test.*;3import java.util.*;4import java.text.*;56public class Code {78public static Object main() throws Throwable {910Activity activity = new Activity();1112String startDateString = "04/22/2017";13DateFormat df = new SimpleDateFormat("MM/dd/yyyy");14activity.setActivityDate(df.parse(startDateString));1516activity.setActivityType("Download RFP response");1718Project project = new Project();19project.setCode("tilden-21-x439");20project.setDesc("Customer satisfaction survey");21project.save();2223activity.setProject(project);24activity.save();2526/* Returns new object in JSON */27return activity;28}2930}
You can query the Project class to retrieve the object.
1import com.psddev.dari.db.*;2import psddev.dari.test.*;3import java.util.*;45public class Code {67public static Object main() throws Throwable {8Project project = Query.from(Project.class)9.where("code = 'tilden-21-x439'").first();10return project;11}1213}
Embedded objects
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.
1public class Activity extends Record {23@Embedded4private Project project;56}
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.
1import com.psddev.dari.db.*;2import psddev.dari.test.*;3import java.util.*;4import java.text.*;56public class Code {78public static Object main() throws Throwable {9Activity activity = new Activity();1011String startDateString = "03/28/2017";12DateFormat df = new SimpleDateFormat("MM/dd/yyyy");13activity.setActivityDate(df.parse(startDateString));1415activity.setUser(psddev.dari.test.User.getUser("Curly") );16activity.setActivityType("Checkout");1718Project project = new Project();19project.setCode("bethany-47-k528");20project.setDesc("Evaluate brand message");2122activity.setProject(project);23activity.save();2425/* Returns new object in JSON */26return activity;27}28}
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.
1import com.psddev.dari.db.*;2import psddev.dari.test.*;3import java.util.*;45public class Code {67public static Object main() throws Throwable {8for (Activity activity : Query.from(Activity.class).selectAll() ) {9if (activity.getProject() == null) continue;10if (activity.getProject().getCode().equals("bethany-47-k528") ) {11Project project = activity.getProject();12return project;13}14}15return ("Can't find bethany-47-k528");1617}18}
Many-to-many relationships
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.
1public class Video extends Record {23@Indexed4private String title;56@MimeTypes("+video/")7private StorageItem video;89/* Getters and setters */1011}
1public class Playlist extends Record {23@Indexed4private String owner;56@Indexed7private String name;89@Indexed10private List<Video> videos;1112/* Getters and setters */13}
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.

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.
1public class Video extends Record {23@Indexed4private String title;56@MimeTypes("+video/")7private StorageItem video;89@Indexed10private List<PlaylistItem> playlists;1112/* Getters and setters */13}
1public class Playlist extends Record {23@Indexed4private String owner;56@Indexed7private String name;89@Indexed10private List<PlaylistItem> videos;1112/* Getters and setters */1314}
1public class PlaylistItem extends Record{23@Indexed4@Required5private Playlist playlist;67@Indexed8@Required9private Video video;1011@Indexed12@Required13private double position;1415/* Getters and setters */1617/* Ensures that a video can only be in a playlist one time */18@Indexed (unique = true)19public String getUniqueKey() {20return this.getPlaylist().getId().toString() + "_" +21this.getVideo().getId().toString();22}23}
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:
1/* Get a playlist */2Playlist pl = Query.from(Playlist.class).first();34/* Get all videos associated with the playlist sorted by position */5List<PlaylistItem> items = Query.from(PlaylistItem.class)6.where("playlist = ?", playlist)7.sortAscending("position")8.selectAll();910for (PlaylistItem item : items) {11System.out.println("Title: " + item.getVideo().getTitle());12}