Mapping embedded objects

When working with document-based databases such as MongoDB, instead of using a one-to-many relationship to link to classes that represent two tables in the database—such as contacts and phones—we can map both objects Contact and Phone. Then, instead of storing them in two different collections, we embed the phone object into the contact object. In the following recipe, we will learn how to embed one object into another.

Getting ready

We'll keep evolving the project from the previous recipe.

How to do it...

Carry out the following steps:

  1. Create a new file named Phone.scala in src/main/scala/code/model with the following code:
    package code.model
    
    import net.liftweb.mongodb.record._
    import net.liftweb.record.field.StringField
    
    class Phone extends BsonRecord[Phone] {
      def meta = Phone
    
      val number = new StringField(this, 15)
    }
    
    object Phone extends Phone with BsonMetaRecord[Phone]
  2. Change the Contact class by adding the numbers object as follows:
    class Contact extends MongoRecord[Contact] with ObjectIdPk[Contact] {
      def meta = Contact
    
      object name extends StringField(this, 150)
    
      object numbers extends BsonRecordListField(this, Phone)
    }
  3. Change the Contacts snippet as follows:
    class Contacts {
      private def createContact(name: String, phoneNumbers: List[String]) {
        val phones = phoneNumbers.map(Phone.createRecord.number(_))
        Contact.createRecord.name(name).numbers(phones).save
      }
    
      def prepareContacts_!() {
        Contact.findAll.map(_.delete_!)
        createContact("John", "555-5555" :: Nil)
        createContact("Joe", "444-4444" :: "222-2222" :: Nil)
        createContact("Lisa", "333-3333" :: Nil)
      }
    
      def list = {
        prepareContacts_!()
    
        "li *" #> Contact.findAll.map {
          contact => {
            ".name *" #> contact.name.get &
              ".phone *" #> contact.numbers.get.map(_.number.get)
          }
        }
      }
    }
  4. Change the ul tag in the index.html file as follows:
          <ul>
              <li>
                  <span class="name"></span>
                  <ul>
                      <li class="phone"></li>
                  </ul>
              </li>
          </ul>
  5. Start the application.
  6. Access http://localhost:8080 and you'll see a web page such as the one shown in the following screenshot:
    How to do it...

How it works...

The first thing to notice is that while the Contact class extends MongoRecord, the Phone class extends BsonRecord, which is a special type of Record that is used to encode and decode BSON/DBObject objects. This means that Lift will know how to convert the BSON representation of a phone number—which is the format that MongoDB uses to save the data—into a DBObject, which is a key-value map that can be saved in the database.

One important difference between the MongoRecord and BsonRecord traits is that the latter can't save itself, while the former can. The consequence of this difference is that we can't invoke the save or saveTheRecord methods of a BsonRecord object. Thus, the only thing we can do is to embed it in a MongoRecord object.

Another difference between the Phone and Contact classes is their companion object. In the Contact class, we extended MongoRecord, and its companion object extended the MongoMetaRecord trait. In the Phone class, we extended BsonRecord, and its companion object extended the BsonMetaRecord trait. As you can imagine, MongoMetaRecord is used to deal with MongoRecord objects, and BsonMetaRecord is used to deal with BsonRecord objects.

So, if the only thing we can do with BsonRecord is to embed it into MongoRecord, how do we do it?

If you take a look at the Contact class, you'll see that the numbers object we added extends the BsonRecordListField class, which can hold a list of BsonRecord objects.

Note that the BsonRecordListField class takes two arguments, the first one being the owner—that's why we passed the value this—and the second argument being the object we want to embed, in this case, the Phone object's BsonMetaRecord trait.

There's more...

We have embeded a list of Phone objects into the Contact class. But, what if you just want to embed one object instead of a list? In this case, you can use the BsonRecordField class.

The difference between the BsonRecordListField and BsonRecordField classes is that the latter contains only one object, while the former can contain one or more objects. This means that when saving the data into MongoDB, the BsonRecordField class will be saved as a subobject and BsonRecordListField will be saved as an array of objects.

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.103.5