MongoDb Schema Design

I learnt that modeling a schema for a NoSQL database (Mongo, Cassandra, …) is not the same thing of drawing a schema for SQL one (Oracle, MySql, …).

In this article, I’m going to illustrate what I’ve learnt reading by who knows more than me!

Starting from the articles at the end of this post published by Francesca Krihely (Community Marketing, MongoDB) who repost an article written by William Zola (Lead Technical Support Engineer at MongoDB), I tried to figure out what means modeling a common pattern problem (as booking flight ticket) adopting a NoSQL solution instead of a more common SQL database.

The solution that I’m going to migrate looks like this.

Two tables, one with the Flight information and the other with a Passenger details.

We could modeling in different ways, the core idea wouldn’t change.

According to Francesca’s article, we’ve three different options to model a “1..N” relation in MongoDb.

The choice should depend on how we’re going to access the data. There’s not a best universal solution but the better solution depending on the application which consume the data.

However, she has defined some best guidelines that I think to be very clever.

The first one is declared as:

Embed the N side if the cardinality is one-to-few and there is no need to access the embedded object outside the context of the parent object.

Keep in mind that a single MongoDb row has the maximum size of 16Mb. The number of the contacts are quite limited for a User and they don’t get much meaning outside the contest of the user parent.

The second is declared as:

Use an array of references to the N-side objects if the cardinality is one-to-many or if the N-side objects should stand alone for any reasons.

 

The number of friends are bigger than a contact and they could exists outside the concept of the User, because they’re user themselves.

A good option is denormalizing the data adding the friend’s name in the user schema. In this way I could have the friend’s list with only one query in my application.

The last is:

Use a reference to the One-side in the N-side objects if the cardinality is one-to-squillions.

The child data could reach a large number of items. Use an array in the parent record is, definitely, not the best choice due the dimension these items.

The reference of the parent record is put in the child record, probably the closer of three solutions with the SQL modelling way.

Coming back to my SQL example, I chose the schema “One to many” to model my NoSQL schema.

So, this is the Flight collection:


{
  "_id": "U23211",
  "from": "London Gatwick",
  "to": "Milan Malpensa",
  "gate": "22",
  "departureTime": ISODate('2017-03-24T14:47:00.000Z'),
  "passenger": [
    {
      "_id": "A44gc",
      "name": "Bryan Richard",
      "seat": "12A"
    },
    {
      "_id": "EsVeo",
      "name": "John Smith",
      "seat": "12B"
    }
  ]
}

And the passengers collection:

{
 "_id": "A44gc",
 "_class": "it.blog.springmongobookingflight.entity.Passenger",
 "name": "Bryan Richard",
 "seat": "12A",
 "smoker": true,
 "payment": "Check",
 "flightCode": "U23211"
}

Have a look at Passenger array the Flight collection. I added the name and the seat of the passenger at the array items. The unique identifier is the ticket number and I use it to access the passenger detail.

I think it’s quite common at the desktop check-in, before boarding, that a flight assistants have a list of passenger for that flight with the name and the seat; it’s not important the payment type at this step.

I built a little solution with Spring data mongo support to access this data.

The complete solution si available on Gitub (https://github.com/MarcoGhise/SpringMongoData).

Anyway, I want to put the focus on a converter class that I think to be worthy for explanation.

@Component
@WritingConverter
public class FlightWriterConverter implements Converter<Flight, DBObject> {

  @Override
  public DBObject convert(final Flight flight) {
    final DBObject dbObject = new BasicDBObject();
    dbObject.put("_id", flight.getFlightCode());
    dbObject.put("from", flight.getFrom());
    dbObject.put("to", flight.getTo());
    dbObject.put("gate", flight.getGate());
    dbObject.put("departureTime", flight.getDepartureTime());

    List<DBObject> passengers = new ArrayList<DBObject>();

    if (flight.getPassenger() != null)
      for (Passenger passenger : flight.getPassenger())
      {
        final DBObject passengerDbObject = new BasicDBObject();
        passengerDbObject.put("_id", passenger.getTicketNumber());
        passengerDbObject.put("name", passenger.getName());
        passengerDbObject.put("seat", passenger.getSeat());
        passengers.add(passengerDbObject);
      }

    dbObject.put("passenger", passengers);

    dbObject.removeField("_class");
    return dbObject;
  }
}

The method convert is invoked before storing the data into the collection. I override the data stored for the collection flight when it’s persistence in the database. I added the name and the seat in the child collection.

References:

6 Rules of Thumb for MongoDB Schema Design: Part 1
https://dzone.com/articles/6-rules-thumb-mongodb-schema

6 Rules of Thumb for MongoDB Schema Design: Part 2
https://dzone.com/articles/6-rules-thumb-mongodb-schema-0

6 Rules of Thumb for MongoDB Schema Design: Part 3
https://dzone.com/articles/6-rules-thumb-mongodb-schema-1

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s