Mysql to mongo
-
Upload
alex-sharp -
Category
Technology
-
view
7.047 -
download
0
description
Transcript of Mysql to mongo
MYSQL TO MONGO (FTW)Thinking Differently About Schema Design
@AJSHARP
Alex SharpLead Developer, OptimisCorp
alexjsharp.com
github.com/ajsharp
Side project
CVBEAST
interested more in domain modeling more than performance and scaling benefits of Mongo
Mongo has many cool features such as schema-free, aggregation w map/reduce and many others
EMBEDDED OBJECTS
App to represent people’s curriculum vitae, but not in an academic sense
CVBEAST
Focus on “micro” experiences not suitable for a typical CV (definitely not suitable for a resumé)
CVBEAST
Simple object model, centered around highlighting attributes of a person
CVBEAST
Started out building with MySQL, b/c it’s familiar
CVBEAST
OBJECT MODELPerson
CV
Experience
has a
has many
has many
... ... ...
OBJECT MODELPerson
CV
Experience
has a
has many
has many
... ... ...
links, tags, and other arbitrary
properties
RELATIONAL SCHEMApeople - id - name - ... cvs - id - person_id - ... experiences - id - cv_id - ...
Lots of pointless JOINs
RELATIONAL SCHEMA
RELATIONAL SCHEMApeople - id - name - ... cvs - id - person_id - ... experiences - id - cv_id - ...
RELATIONAL SCHEMApeople - id - name - ... cvs - id - person_id - ... experiences - id - cv_id - ...
links - id - name - ...
tags - id - name - ...
this bothers me
not b/c of premature optimization
It misrepresents the object model at the storage layer
It also became difficult to work withdespite my familiarity with MySQL
It makes sense to store object relationships in first-class entities/documents/tables
But true object properties should be stored as properties
Not relationships.
Especially when properties have no practical meaning without the parent
This is not always the case.
;-)
CAVEAT
if you need to:* store LOTS of embedded objects (i.e. papermill)
EXAMPLEExperience Links
EXPERIENCE.LINKS = [...]
Dead-simple collection of links attached to an experience
class Person include Mongoid::Document
field :keywords, :type => Array
index :keywords index 'cv.experiences.tags'end
RUBY MODEL CODE
class Experience include Mongoid::Document
embeds_many :linksend
class Link include Mongoid::Document
field :url field :title embedded_in :experience, :inverse_of => :linksend
class Experience include Mongoid::Document
embeds_many :linksend
class Link include Mongoid::Document
field :url field :title embedded_in :experience, :inverse_of => :linksend
Both models are embedded in the person collection
EXPERIENCE.LINKS = [...]
A link is only relevant inside the context of an experience object.
EXPERIENCE.LINKS = [...]
In other words, it is a property,not a relationship.
EXPERIENCE.LINKS = [...]
Mongo brings the storage layer closer tothe object model
{ "title": "Presented at MongoLA", "links": [ { "title": "Event Site", "url": "http://www.10gen.com/conferences/mongola2011"}, { "title": "Slides", "url": "http://alexjsharp.com/posts/mongola-2010-slides"} ]}
EXPERIENCE.LINKS = [...]
Embedded SearchEXAMPLE
An exercise in masochismMYSQL
The difficulty in MySQL came in working with properties of a person
These properties are represented as tables
And tables must be joined
SIMPLE SEARCH QUERYselect * from people inner join cvs on cvs.person_id = people.id inner join experiences on experiences.cv_id = cvs.id inner join tags on tags.experience_id = experiences.id where tags.name = 'ruby';
INDEXES NEEDED- tags.experience_id- tags.name- experiences.cv_id- cvs.person_id
Seems extremely unnecessary for such a simple object model
An exercise in ... not masochismMONGO
Embedded objects make this query easy.
{"name": "Alex Sharp", "cv": { "experiences": [ { "title": "spoke at MongoLA", "date" : "Thu Jan 13 2011", "tags" : ["mongodb", "speaking"] }, {"title": "..."} ] }}
MONGO DOCUMENT “SCHEMA”
{"name": "Alex Sharp", "cv": { "experiences": [ { "title": "spoke at MongoLA", "date" : "Thu Jan 13 2011", "tags" : ["mongodb", "speaking"] }, {"title": "..."} ] }}
we want to search for these
MONGO DOCUMENT “SCHEMA”
class Person include Mongoid::Document
field :keywords, :type => Array
index :keywords index 'cv.experiences.tags'end
RUBY MODEL CODE
RUBY MODEL CODEclass Cv include Mongoid::Document
embeds_many :experiences end
class Experience include Mongoid::Document
field :tags, :type => Array, :default => []
embedded_in :cv, :inverse_of => :experiences embeds_many :links
after_save lambda { |exp| exp.cv.person.update_keywords }end
RUBY MODEL CODEclass Cv include Mongoid::Document
embeds_many :experiences end
class Experience include Mongoid::Document
field :tags, :type => Array, :default => []
embedded_in :cv, :inverse_of => :experiences embeds_many :links
after_save lambda { |exp| exp.cv.person.update_keywords }end
simple property(not relationship)
RUBY SEARCH CODE
class Person
# i.e. db.people.find({"cv.experiences.tags": "ruby"}) def self.search_by_tag(term) collection.find('cv.experiences.tags' => 'ruby') end
end
COMPARISONselect * from people inner join cvs on cvs.person_id = people.id inner join experiences on experiences.cv_id = cvs.id inner join tags on tags.experience_id = experiences.id where tags.name = 'ruby';
db.people.find('cv.experiences.tags' => 'ruby')
vs
WINS: IMPEDENCE MIS-MATCH
Object persistence format is closer to application usage format
WINS: F(X)-ALITY
Plus, we don’t lose any functionality.
WINS: FAMILIARITY
Indexing principles are extremely familiar to relational database users.
WINS: FAMILIARITY
Only simpler ;)
WINS: SIMPLICITY
Query syntax is simple.
WINS: SIMPLICITY
Dot notation > JOIN semantics filth
SUMMARY
Mongo is perfectly suited for an app like CVBeast.
SUMMARY
Where a relational DB forces storing object properties as relationships
SUMMARY
Mongo narrows this mismatch considerably
SUMMARY
A CV is a document...
SUMMARY
So a document-oriented datastore seems appropriate.
SUMMARY
Many object models have this tree-like structure.
SUMMARY
Be willing to step outside of your comfort zone
SUMMARY
And use Mongo when it’s practical.