1 var associations = require("../associations"),
  2         oneToMany = associations.oneToMany,
  3         manyToOne = associations.manyToOne,
  4         oneToOne = associations.oneToOne,
  5         manyToMany = associations.manyToMany,
  6         fetch = associations.fetch,
  7         comb = require("comb");
  8 
  9 /**
 10  * @class
 11  *  <p>plugin to expose association capability.</p>
 12  *
 13  * The associations exposed include
 14  *
 15  *
 16  *
 17  *
 18  * <ul>
 19  *     <li>oneToMany - Foreign key in associated model's table points to this
 20  *         model's primary key.   Each current model object can be associated with
 21  *         more than one associated model objects.  Each associated model object
 22  *         can be associated with only one current model object.</li>
 23  *     <li>manyToOne - Foreign key in current model's table points to
 24  *         associated model's primary key.  Each associated model object can
 25  *         be associated with more than one current model objects.  Each current
 26  *         model object can be associated with only one associated model object.</li>
 27  *     <li>oneToOne - Similar to one_to_many in terms of foreign keys, but
 28  *         only one object is associated to the current object through the
 29  *         association.  The methods created are similar to many_to_one, except
 30  *         that the one_to_one setter method saves the passed object./li>
 31  *     <li>manyToMany - A join table is used that has a foreign key that points
 32  *         to this model's primary key and a foreign key that points to the
 33  *          associated model's primary key.  Each current model object can be
 34  *         associated with many associated model objects, and each associated
 35  *         model object can be associated with many current model objects./li>
 36  * </ul>
 37  *
 38  * @name AssociationPlugin
 39  * @memberOf moose.plugins
 40  *
 41  */
 42 exports.AssociationPlugin = comb.define(null, {
 43     static : {
 44     /**@lends moose.plugins.AssociationPlugin*/
 45 
 46         /**
 47          * One of the most common forms of associtaions. One to Many is the inverse of Many to one. One to Many often describes a parent child reationship,
 48          * where the One To many Model is the parent, and the many to one model is the child.
 49          * <p>For example consider a BiologicalFather and his children. The father can have many children, but a child can have only one Biological Father.</p>
 50          *
 51          * @example
 52          * biological_father       child
 53          * -------------_          -------------------------
 54          * |id  | name  |          |id | bioFatherId  | name |
 55          * --------------          -------------------------
 56          * | 1  | Fred  |          | 1 |     1        | Bobby |
 57          * | 2  | Ben   |  ------> | 2 |     1        | Alice |
 58          * | 3  | Bob   |          | 3 |     1        | Susan |
 59          * | 4  | Scott |          | 4 |     4        | Brad  |
 60          * --------------          -------------------------
 61          *
 62          * @example
 63          *
 64          * //define Social security model
 65          * var BioFather = moose.addModel("biological_father");
 66          *
 67          * //define Person  model
 68          * var Child = moose.addModel("child");
 69          *
 70          * //Create oneToMany relationship from father to child
 71          * BioFather.oneToMany("children", {
 72          *                                  model : Child.tableName,
 73          *                                  key : {id : "bioFatherId"}
 74          *                     });
 75          *
 76          *
 77          * //Create oneToOne relation ship from ssn to person with a fetchtype of eager.
 78          * Child.manyToOne("biologicalFather", {
 79          *                                  model : BioFather.tableName,
 80          *                                  fetchType : BioFather.fetchType.EAGER,
 81          *                                  key : {bioFatherId : "id"}
 82          *                });
 83          *
 84          * Child.findById(1).then(function(child){
 85          *     child.father.name => "fred"
 86          * });
 87          *
 88          * BioFather.findById(1).then(function(father){
 89          *     father.children.then(function(children){
 90          *         children.length => 3
 91          *     });
 92          * });
 93          *
 94          * </p>
 95          * @param {String} name the alias of the association. The key you provide here is how the association
 96          *                      will be looked up on instances of this model.
 97          * @param {Object} options object that describes the association.
 98          * @param {String} options.model the table name of the model that this Model is associated with.
 99          * @param {Function} options.filter Custom filter to define a custom association.
100          *                  The filter is called in the scope of model that the association is added to.
101          *                  Say we have a model called BioFather that is a one to many to a model called Child.
102          * <pre class="code">
103          * BioFather.oneToMany("children", {
104          *                   model : Child.tableName,
105          *                   fetchType : BioFather.fetchType.EAGER,
106          *                   filter : function(){
107          *                       return  Child.filter({bioFatherId : this.id});
108          *                   }
109          *                });
110          * @param {AssociationPlugin.fetchType.EAGER|AssociationPlugin.fetchType.EAGER} [options.fetchType=AssociationPlugin.fetchType.LAZY]
111          *          how fetch the association, if specified to lazy then the association is lazy loaded.
112          *          Otherwise the association is loaded when the model is loaded.
113          * @param {Object} key this defines the foreign key relationship
114          *  <pre class="code">
115          *      {thisModelsKey : otherModelsKey}
116          *  </pre>
117          * @param {String|Object} [options.orderBy] column or columns to order the associated model by.
118          */
119         oneToMany : function(name, options) {
120             var assoc = new oneToMany(options, this.moose);
121             assoc.inject(this, name);
122         },
123 
124         /**
125          * See {@link AssociationPlugin.oneToMany}
126          * @param {String} name the alias of the association. The key you provide here is how the association
127          *                      will be looked up on instances of this model.
128          * @param {Object} options object that describes the association.
129          * @param {String} options.model the table name of the model that this Model is associated with.
130          * @param {Function} options.filter Custom filter to define a custom association.
131          *                  The filter is called in the scope of model that the association is added to.
132          *                  Say we have a model called Child that is a many to one to a model called BioFather.
133          * <pre class="code">
134          * Child.manyToOne("biologicalFather", {
135          *                   model : BioFather.tableName,
136          *                   fetchType : BioFather.fetchType.EAGER,
137          *                   filter : function(){
138          *                       return  BioFather.filter({id : this.bidFatherId});
139          *                   }
140          *                });
141          *
142          * @param {AssociationPlugin.fetchType.EAGER|AssociationPlugin.fetchType.EAGER} [options.fetchType=AssociationPlugin.fetchType.LAZY]
143          *          how fetch the association, if specified to lazy then the association is lazy loaded.
144          *          Otherwise the association is loaded when the model is loaded.
145          * @param {Object} key this defines the foreign key relationship
146          * @param {String|Array} [options.orderBy] column or columns to order the associated model by.
147          *  <pre class="code">
148          *      {thisModelsKey : otherModelsKey}
149          *  </pre>
150          * @param {String|Object} [options.orderBy] column or columns to order the associated model by.
151          */
152         manyToOne : function(name, options) {
153             var assoc = new manyToOne(options, this.moose);
154             assoc.inject(this, name);
155         },
156 
157         /**
158          * <p>Simplest form of association. This describes where there is a one to one relationship between classes.</p>
159          *
160          * When createing a reciprocal one to one relationship between models one of the models should be a many to one association.
161          * The table that contains the foreign key should contain have the manyToOne relationship.
162          *
163          * <p>For example consider social security numbers. There is one social security per person, this would be considered a one to one relationship.
164          *
165          * @example
166          *          Person                  SSN NUMBER
167          *-------------------------         ---------------
168          *|id         | ssn       |         |id          |
169          *-------------------------         ---------------
170          *|00000001   | 111111111 |         | 111111111  |
171          *| ......... | ......... | ------> | .........  |
172          *| ......... | ......... |         | .........  |
173          *| nnnnnnnnn | nnnnnnnn  |         | nnnnnnnnn  |
174          *-------------------------         ---------------
175          *
176          * @example
177          *
178          * //define Social security model
179          * var SocialSecurityNumber = moose.addModel("ssn");
180          *
181          * //define Person  model
182          * var Person = moose.addModel("person");
183          *
184          * //Create oneToOne relation ship from ssn to person with a fetchtype of eager.
185          * SocialSecurityNumber.oneToOne("person", {
186          *                                  model : Person.tableName,
187          *                                  fetchType : SocialSecurityNumber.fetchType.EAGER,
188          *                                  key : {id : "ssn"}
189          *                              });
190          *
191          * //Create oneToMany relation ship from person to ssn,
192          * //It is many to one because is contains the ssn foreign key.
193          *
194          * Person.manyToOne("ssn", {
195          *                          model : SocialSecurityNumber.tableName,
196          *                          key : {ssn : "id"}
197          *                 });
198          *
199          *
200 
201          *
202          * Person.findById(1).then(function(person){
203          *    person.ssn.then(function(ssn){
204          *       ssn.id => 111111111
205          *    });
206          * });
207          *
208          * SocialSecurityNumber.findById(111111111).then(function(ssn){
209          *    ssn.person.id => 1
210          * });
211          * </p>
212          * @param {String} name the alias of the association. The key you provide here is how the association
213          *                      will be looked up on instances of this model.
214          * @param {Object} options object that describes the association.
215          * @param {String} options.model the table name of the model that this Model is associated with.
216          * @param {Function} options.filter Custom filter to define a custom association.
217          *                  The filter is called in the scope of model that the association is added to.
218          *                  Say we have the same models as defined above.
219          * <pre class="code">
220          *  SocialSecurityNumber.oneToOne("person", {
221          *      model : Person.tableName,
222          *      filter : function(){
223          *          //find the worker that has my id.
224          *          return Person.filter({ssn : this.id});
225          *      }
226          * });
227          * </pre>
228          * @param {AssociationPlugin.fetchType.EAGER|AssociationPlugin.fetchType.EAGER} [options.fetchType=AssociationPlugin.fetchType.LAZY]
229          *          how fetch the association, if specified to lazy then the association is lazy loaded.
230          *          Otherwise the association is loaded when the model is loaded.
231          * @param {Object} key this defines the foreign key relationship
232          *  <pre class="code">
233          *      {thisModelsKey : otherModelsKey}
234          *  </pre>
235          * @param {String|Object} [options.orderBy] column or columns to order the associated model by.
236          */
237         oneToOne : function(name, options) {
238             var assoc = new oneToOne(options, this.moose);
239             assoc.inject(this, name);
240         },
241 
242         /**
243          * The manyToMany association allows a model to be associated to many other rows in another model.
244          * and the associated model can be associated with many rows in this model. This is done by
245          * using a join table to associate the two models.
246          * <p>For example consider phone numbers. Each person can have multiple phone numbers.
247          *
248          * @example
249          * phone          person_phone                   person
250          * ------         ----------------------         -----
251          * |id  |         |person_id | phone_id|         |id |
252          * ------         ----------------------         -----
253          * | 1  |         |        1 |       1 |         | 1 |
254          * | .  | <------ |        1 |       2 | ------> | 2 |
255          * | .  |         |        2 |       2 |         | 3 |
256          * | n  |         |        2 |       1 |         | 4 |
257          * ------         ----------------------         -----
258          *
259          * @example
260          *
261          * //define the PhoneNumber model
262          * var PhoneNumber = moose.addModel("phone");
263          *
264          * //define Person model
265          * var Person = moose.addModel("person");
266          *
267          * //Create manyToMany relationship from person to PhoneNumber
268          * Person.manyToMany("phoneNumbers", {
269          *                      model : PhoneNumber.tableName,
270          *                      joinTable : "person_phone",
271          *                      key : {person_id : "phone_id"}
272          *});
273          *
274          *
275          * PhoneNumber.manyToMany("owners", {
276          *                      model : Person.tableName,
277          *                      joinTable : "person_phone",
278          *                      key : {phone_id : "person_id"}
279          *});
280          *
281          * Person.findById(1).then(function(person){
282          *    person.phoneNumbers.then(function(numbers){
283          *       numbers.length => 2
284          *    });
285          * });
286          *
287          * PhoneNumber.findById(1).then(function(number){
288          *    number.owners.then(function(owners){
289          *        owners.length => 2;
290          *    });
291          * });
292          * </p>
293          * @param {String} name the alias of the association. The key you provide here is how the association
294          *                      will be looked up on instances of this model.
295          * @param {Object} options object that describes the association.
296          * @param {String} options.model the table name of the model that this Model is associated with.
297          * @param {String} options.joinTable the name of the joining table.
298          * @param {Function} options.filter Custom filter to define a custom association.
299          *                  The filter is called in the scope of model that the association is added to.
300          *                  Say we have the same models as defined above.
301          * <pre class="code">
302          * //Define the join table model so we can query it.
303          * PersonPhone = moose.addModel(person_phone);
304          * PhoneNumber.manyToMany("owners", {
305          *                      model : Person.tableName,
306          *                      joinTable : "person_phone",
307          *                      filter : function(){
308          *                              //find all the person ids
309          *                            var jd = PhoneNumber.dataset
310          *                                                .select('person_id')
311          *                                                .find({phone_id : this.id});
312          *                            //now query person with the ids!
313          *                            return Person.filter({id : {"in" : jd}});
314          *                      }
315          *  });
316          * </pre>
317          * @param {AssociationPlugin.fetchType.EAGER|AssociationPlugin.fetchType.EAGER} [options.fetchType=AssociationPlugin.fetchType.LAZY]
318          *          how fetch the association, if specified to lazy then the association is lazy loaded.
319          *          Otherwise the association is loaded when the model is loaded.
320          * @param {Object} key this defines the foreign key relationship
321          *  <pre class="code">
322          *      {thisModelsKey : otherModelsKey}
323          *  </pre>
324          * @param {String|Object} [options.orderBy] column or columns to order the associated model by.
325          */
326         manyToMany : function(name, options) {
327             var assoc = new manyToMany(options, this.moose);
328             assoc.inject(this, name);
329         },
330 
331         /**
332          * @borrows _Association.fetch as fetch
333          */
334         fetchType : fetch
335     }});