<commit_before>var validate = require('mongoose-validator').validate,
	mongoose = require('mongoose'),
	async    = require('async');

alchemy._mongoose = mongoose;

/**
 * The Model class
 *
 * @constructor
 * @augments alchemy.classes.BaseClass
 *
 * @author   <NAME>   <<EMAIL>>
 * @since    0.0.1
 * @version  0.0.1
 */
var Model = global.Model = alchemy.classes.BaseClass.extend(function Model (){

	/**
	 * The model name.
	 *
	 * @type {String}
	 */
	this.modelName = null;

	/**
	 * The Model preInit
	 *
	 * @author   <NAME>   <<EMAIL>>
	 * @since    0.0.1
	 * @version  0.0.1
	 *
	 * @return   {undefined}
	 */
	this.preInit = function preInit() {

		/**
		 * The name of the DataSource connection that this Model uses
		 *
		 * The value must be an attribute name that you defined in `config/locale/database.js`
		 *
		 * @type {String}
		 */
		this.useDbConfig = 'default';

		/**
		 * Custom database table name, or null/false if no table association is desired.
		 *
		 * @type {String}
		 */
		this.useTable = null;
		
		/**
		 * Custom display field name.
		 *
		 * @type {String}
		 */
		this.displayField = null;
		
		/**
		 * Value of the primary key ID of the record that this model is currently pointing to.
		 * Automatically set after database insertions.
		 *
		 * @type {Mixed}
		 */
		this.id = false;
		
		/**
		 * Table name for this Model.
		 *
		 * @type {String}
		 */
		this.table = false;
		
		/**
		 * Alias name for model.
		 *
		 * @type {String}
		 */
		this.alias = null;

		/**
		 * The connection to use for this model.
		 * 
		 * @type {Object}
		 */
		this.connection = false;
		
		/**
		 * The connection status
		 *
		 * @type {boolean}
		 */
		this.connected = false;
		
		/**
		 * hasOneChild associations
		 * 
		 * @type {Object}
		 */
		this.hasOneChild = {};
		
		/**
		 * hasOneParent associations
		 * 
		 * @type {Object}
		 */
		this.hasOneParent = {};
		
		/**
		 * hasMany associations
		 * 
		 * @type {Object}
		 */
		this.hasMany = {};
		
		/**
		 * HABTM associations
		 * 
		 * @type {Object}
		 */
		this.habtm = {};
		this.hasAndBelongsToMany = this.habtm;
		
		/**
		 * belongsTo associations
		 * 
		 * @type {Object}
		 */
		this.belongsTo = {};

		/**
		 * All associations
		 *
		 * @type {Object}
		 */
		this.associations = {};
		
		/**
		 * All aliases
		 *
		 * @type {Object}
		 */
		this.aliases = {};

		/**
		 * Default find options
		 *
		 * @type {Object}
		 */
		this.findOptions = {
		conditions: {},
			_conditions: {}, // 'Cleaned up' conditions are stored here
			recursive: 1,
			fields: [],
			sort: false,
			order: 1,
			limit: -1,
			page: false,
			offset: false,
			callbacks: true, // Other possible values are false, 'before', 'after,
			lean: true,      // Return simple javascript objects, not mongoose docs
			filterJoins: false, // If a join is part of a condition, also filter it
			_mongoFnc: 'find'
		};
		
		this.blueprint = {};
		this.schema = {};
		this.cache = {};

		// Data for the client
		this.safeBlueprint = {};
		
		// Index settings per fields are stored here
		this._indexFields = {};
		
		// Indexes settings per name are stored here
		this._indexes = {};
		
		this.many = {};
		this.special = {json: {}};
		
		this._prepost = {
			pre: [],
			post: []
		};
		
		// "Hidden" properties
		this._mongoose = mongoose;
		this._validate = validate;
		this._model = {};

		// Behaviours for this controller
		this.behaviours = false;
		
		// Behaviour instances
		this._behaviours = {};
	};

	/**
	 * Do when extending
	 *
	 * @author   <NAME>   <<EMAIL>>
	 * @since    0.0.1
	 * @version  0.0.1
	 */
	this.__extended__ = function __extended__(parentModel, childModel) {

		if (!childModel.prototype.hasOwnProperty('modelName')) {
			childModel.prototype.modelName = childModel.name.replace('Model', '');
		}

	};

	/**
	 * Constructor. Binds the model's database table to the object.
	 *
	 * @author   <NAME>   <<EMAIL>>
	 * @since    0.0.1
	 * @version  0.0.1
	 *
	 * This is the layout of the "options" parameter:
	 * - id       - The id to start the model on.
	 * - table    - The table to use for this model.
	 * - ds       - The connection name this model is connected to.
	 * - name     - The name of the model eg. Post.
	 * - alias    - The alias of the model, this is used for registering the instance in the `ClassRegistry`.
	 *   eg. `ParentThread`
	 *
	 *
	 * @param   {Object}   options
	 */
	this.init = function init(options) {

		if (typeof options != 'undefined') {
			if (typeof options.id != 'undefined') this.id = options.id;
			if (typeof options.table != 'undefined') this.useTable = options.table;
			if (typeof options.ds != 'undefined') this.useDbConfig = options.ds;
			if (typeof options.name != 'undefined') this.modelName = options.name;
			if (typeof options.alias != 'undefined') this.alias = options.alias;
		}

		if (this.useTable === null) {
			if (this.modelName) this.useTable = this.modelName.tableize();
		}

		if (this.useTable) {
			
			var thisModel = this;
			
			// Set the connection
			this.connection = alchemy._db_connections[this.useDbConfig];
			
			// Set the initial connection state
			thisModel.connected = this.connection._hasOpened;
			
			// Set the connection status
			this.connection.on('open', function onModelConnectionOpen () {
				thisModel.connected = true;
			});
			
			this.connection.on('error', function onModelConnectionOpen (message) {
				thisModel.connected = false;
				log.error('Connection error!');
				log.error(message);
			});
			
			// Create the schema based on our blueprint
			this._schema = this._createSchema(this.blueprint);
			
			// Create the model based on that schema
			try {
				this._model = this._createModel(this.modelName, this._schema, this.enableCache);
			} catch (err) {
				log.error(err.message, {err: err});
			}
		}

		// We'll create a reference to this model's behaviours
		var thisBehaviours = this.behaviours;
		
		// Get our parent's components, so we can merge the current ones in it later
		var parentBehaviours = this.parent('behaviours');

		if (typeof parentBehaviours === 'object' && typeof thisBehaviours === 'object') {
			// If this model and its parent have behaviours, merge them
			this.behaviours = alchemy.inject(parentBehaviours, this.behaviours);
		} else if (typeof parentBehaviours === 'object') {
			// If only the parent has behaviours, overwrite them
			this.behaviours = alchemy.inject({}, parentBehaviours);
		}
		
		// Construct the components
		if (typeof this.behaviours === 'object') {

			for (var behaviourName in this.behaviours) {

				// Store every component in here
				this._behaviours[behaviourName] = Behaviour.get(behaviourName, this, this.behaviours[behaviourName]);

				// If behaviours should be exposed easily, store them under the model
				if (this._behaviours[behaviourName].expose) {
					this[behaviourName.classify()] = this._behaviours[behaviourName];
				}
			}
		}

		// Reset the associations, otherwise all models share the same object
		this.associations = {};
		
		this._compileAssociation('hasMany', this.hasMany);
		this._compileAssociation('hasOneChild', this.hasOneChild, {foreignKey: this.modelName.foreign_key()}, {fnc: 'findOne'});
		this._compileAssociation('hasOneParent', this.hasOneParent, null, {fnc: 'findOne'});
		this._compileAssociation('belongsTo', this.belongsTo, null, {fnc: 'findOne'});
		
		// Add belongsTo fields to the blueprint
		for (var modelName in this.associations) {
			
			var assoc = this.associations[modelName];

			// Add it to the aliases
			this.aliases[assoc.alias] = assoc.modelName;
			
			// hasOne fields should be unique
			// only do this if this is the child model (so: if the association is the parent)
			if (assoc.type == 'hasOneParent') {
				this.blueprint[assoc.foreignKey] = {
					type: 'ObjectId',
					index: {
						unique: true,
						order: 'asc'
					}
				};
			} else if (assoc.type == 'belongsTo') {
				this.blueprint[assoc.foreignKey] = {
					type: 'ObjectId',
					index: {
						unique: false,
						order: 'asc'
					}
				};
			}
			
		}
	};
	
	/**
	 * Callback after this instance has been augmented
	 *
	 * @author   <NAME>   <<EMAIL>>
	 * @since    0.0.1
	 * @version  0.0.1
	 *
	 * @param    {Object}   addition
	 *
	 */
	this.augmented = function augmented(addition) {

		var name,
		    behaviours = this._behaviours, // Get the behaviours from the original
		    skip = this.__augmentNoInherit;

		// Create a behaviours entry in the augment
		this._behaviours = {};

		// Do not inherit the _behaviours property
		skip._behaviours = true;

		// Augment the behaviours aswell
		for (name in behaviours) {
			this._behaviours[name] = alchemy.augment(behaviours[name], addition);

			if (this._behaviours[name].expose) {
				this[name.classify()] = this._behaviours[name];
			}
		}
	};
	
	/**
	 * Compile an association
	 *
	 * @author   <NAME>   <<EMAIL>>
	 * @since    0.0.1
	 * @version  0.0.1
	 *
	 * @param    {String}     name            The name of the association type
	 * @param    {Object}     associations    The associations to compile
	 *
	 * @return   {undefined}  Does not return anything, directly stores in this.associations
	 */
	this._compileAssociation = function _compileAssociation(name, associations, defaults, overrides) {
		
		if (!defaults) defaults = {};
		
		var i, modelName, assoc, foreignKey;
		
		for (i in associations) {
			
			assoc = associations[i];
			
			if (assoc.modelName) modelName = assoc.modelName
			else modelName = i;
			
			// Define the foreignkey
			if (assoc.foreignKey) {
				foreignKey = assoc.foreignKey;
			} else {
				if (defaults.foreignKey) {
					foreignKey = defaults.foreignKey;
				} else {
					foreignKey = modelName.foreign_key();
				}
			}
			
			this.associations[modelName] = {type: name, foreignKey: foreignKey, modelName: modelName, alias: i, fnc: 'find', options: assoc};
			if (overrides) alchemy.inject(this.associations[modelName], overrides);
		}
	};
	
	/**
	 * Do something to the database once we're connected
	 *
	 * @author   <NAME>   <<EMAIL>>
	 * @since    0.0.1
	 * @version  0.0.1
	 *
	 * @param    {Function}   one   The function to execute, or eval object
	 * @param    {Function}   two   If eval, this is its callback
	 *
	 * @return   {Boolean}    True if the function is executed immediately
	 */
	this._whenConnected = function _whenConnected(one, two) {
		
		var callback, thisModel;
		
		if (typeof one == 'function') {
			callback = one;
		} else {
			thisModel = this;
			
			// eval's nolock is true by default
			if (typeof one.nolock == 'undefined') one.nolock = true;
			
			callback = function executeWhenConnected () {
				thisModel.connection.db.executeDbCommand(one, two);
			}
		}

		if (this.connected) {
			callback();
			return true;
		}
		
		this.connection.once('open', function on_ifConnected () {
			callback();
		});
		
		return false;
	};

	/**
	 * Return the type (as a string) of a field
	 *
	 * @author   <NAME>   <<EMAIL>>
	 * @since    0.0.1
	 * @version  0.0.1
	 *
	 * @param    {String}   fieldName   The field to get the type of
	 * 
	 * @return   {String}   The type of the field
	 */
	this.fieldType = function fieldType(fieldName) {

		if (this.blueprint[fieldName] && this.blueprint[fieldName].type) {

			// Get the type of this field
			var type = this.blueprint[fieldName].type;

			// If the type is a function and not a string (which shouldn't be)
			// throw a warning, and use the function's name
			if (typeof type === 'function') {
				type = type.name;
				log.warn('The fieldtype for "' + fieldName + '" inside model "' + this.modelName + '" is a function instead of a string');
			}

			return type.toLowerCase();
		}

		return undefined;
	};
	
	/**
	 * Create a new Mongoose schema, with certain fields auto created
	 *
	 * @author   <NAME>   <<EMAIL>>
	 * @since    0.0.1
	 * @version  0.0.1
	 *
	 * @param    {Object}   blueprint   The schema blueprint
	 * 
	 * @return   {Object}   A mongoose schema
	 */
	this._createSchema = function _createSchema(blueprint) {
		
		var thisModel = this,
			blueprintClone,
			tempSchemas,
			fieldname,
			schema,
			option,
			order,
			ns,
			o, // The original blueprint
			e; // The cloned blueprint
		
		// Add the created & updated field
		blueprint.created = {type: 'Date', default: Date.now, fieldType: 'Date'};
		blueprint.updated = {type: 'Date', default: Date.now, fieldType: 'Date'};
		
		// Add the _id field if it doesn't exist
		// This isn't strictly needed, but I prefer to have it in the blueprint anyway
		if (typeof blueprint._id == 'undefined') blueprint._id = {type: 'ObjectId', default: mongoose.Types.ObjectId};
		
		// Create a blueprint clone, one we can edit and put function references in
		blueprintClone = alchemy.cloneDeep(blueprint);
		
		// Create an object to store the temporary schemas in
		tempSchemas = {};

		// Empty the safe blueprint
		this.safeBlueprint = {};
		
		// See if any of the entries are arrays
		for (fieldname in blueprintClone) {

			o = blueprint[fieldname];
			e = blueprintClone[fieldname];
			
			// If the type is a string, fetch it from the mongoose types
			if (typeof e === 'string') {
				
				e = {type: e};
				
				// Also update it in the text-only blueprint
				blueprint[fieldname] = {type: e.type};
				o = blueprint[fieldname];
			} else {
				
				// Is this field part of a database index?
				if (e.index) {
					
					// If index is just a boolean, create an object
					if (e.index === true) e.index = {unique: false};
					else if (typeof e.index == 'string') {
						if (e.index.toLowerCase() == 'unique') e.index = {unique: true};
						else e.index = {unique: true};
					}
					
					// Disable the sparse option by default
					if (typeof e.index.sparse == 'undefined') e.index.sparse = false;
					
					// Create an index name if none is given
					if (typeof e.index.name == 'undefined') {
						e.index.name = fieldname;
						if (e.index.unique) e.index.name += '_uq';
					}
					
					// Store these settings in the _index object by field name
					this._indexFields[fieldname] = e.index;
					
					// And now store it under the index name itself
					if (typeof this._indexes[e.index.name] == 'undefined') {
						this._indexes[e.index.name] = {
							fields: {},
							options: {
								name: e.index.name,
								unique: false,
							}
						};
					}
					
					if (e.index.unique) this._indexes[e.index.name].options.unique = true;
					if (e.index.sparse) this._indexes[e.index.name].options.sparse = true;
					
					order = 1;
					if (e.index.order == 'desc') order = -1;
					
					// Finally: add this field to this index
					this._indexes[e.index.name].fields[fieldname] = order;
				}
				
				if (e.array || e.arrayOf) {

					if (!e.arrayOf) e.arrayOf = e.array;
					
					// If it's an empty object,
					// just create an array of mixed, not subdocuments
					if (alchemy.isEmpty(e.type)) {
						e.type = [{}];
					} else {
						ns = {};
						ns[fieldname] = {};
						
						// Now go over every entry in this field
						for (option in e) {
							
							// Add those options to a temporary blueprint,
							// but only if it's not the array option
							if (option !== 'array'){
								ns[fieldname][option] = e[option];
							}
						}
						
						// Create the temporary array out of the temporary blueprint
						tempSchemas[fieldname] = new mongoose.Schema(ns);
						
						// Overwrite the entry
						e.type = [tempSchemas[fieldname]];
					}
				} else if (e.translatable) {

					o.objectOf = true;
					e.objectOf = true;

					// Store translations in a simple mixed object
					e.type = {};

					// enable the translate behaviour
					if (!this.behaviours) this.behaviours = {};
					if (!this.behaviours.translate) this.behaviours.translate = {};
					if (!this.behaviours.translate.fields) this.behaviours.translate.fields = {};

					this.behaviours.translate.fields[fieldname] = true;

				} else if (e.objectOf) {
					e.type = {};
				}
			}
			
			// If the type is a string, put in the appropriate function
			if (typeof e.type === 'string') {
				
				switch (e.type.toLowerCase()) {
					
					case 'enum':
					case 'string':
						e.type = String;
						break;
				
					case 'number':
						e.type = Number;
						break;

					case 'boolean':
						e.type = Boolean;
						break;
					
					case 'date':
						e.type = Date;
						break;
					
					default:
						e.type = mongoose.Schema.Types[e.type];
						break;
				}
			}

			if (o.type.toLowerCase() === 'enum') {
				// Add an enum rule if none are set yet and the enum<commit_msg>If an invalid schema type is given, default to string<commit_after>var validate = require('mongoose-validator').validate,
	mongoose = require('mongoose'),
	async    = require('async');

alchemy._mongoose = mongoose;

/**
 * The Model class
 *
 * @constructor
 * @augments alchemy.classes.BaseClass
 *
 * @author   <NAME>   <<EMAIL>>
 * @since    0.0.1
 * @version  0.0.1
 */
var Model = global.Model = alchemy.classes.BaseClass.extend(function Model (){

	/**
	 * The model name.
	 *
	 * @type {String}
	 */
	this.modelName = null;

	/**
	 * The Model preInit
	 *
	 * @author   <NAME>   <<EMAIL>>
	 * @since    0.0.1
	 * @version  0.0.1
	 *
	 * @return   {undefined}
	 */
	this.preInit = function preInit() {

		/**
		 * The name of the DataSource connection that this Model uses
		 *
		 * The value must be an attribute name that you defined in `config/locale/database.js`
		 *
		 * @type {String}
		 */
		this.useDbConfig = 'default';

		/**
		 * Custom database table name, or null/false if no table association is desired.
		 *
		 * @type {String}
		 */
		this.useTable = null;
		
		/**
		 * Custom display field name.
		 *
		 * @type {String}
		 */
		this.displayField = null;
		
		/**
		 * Value of the primary key ID of the record that this model is currently pointing to.
		 * Automatically set after database insertions.
		 *
		 * @type {Mixed}
		 */
		this.id = false;
		
		/**
		 * Table name for this Model.
		 *
		 * @type {String}
		 */
		this.table = false;
		
		/**
		 * Alias name for model.
		 *
		 * @type {String}
		 */
		this.alias = null;

		/**
		 * The connection to use for this model.
		 * 
		 * @type {Object}
		 */
		this.connection = false;
		
		/**
		 * The connection status
		 *
		 * @type {boolean}
		 */
		this.connected = false;
		
		/**
		 * hasOneChild associations
		 * 
		 * @type {Object}
		 */
		this.hasOneChild = {};
		
		/**
		 * hasOneParent associations
		 * 
		 * @type {Object}
		 */
		this.hasOneParent = {};
		
		/**
		 * hasMany associations
		 * 
		 * @type {Object}
		 */
		this.hasMany = {};
		
		/**
		 * HABTM associations
		 * 
		 * @type {Object}
		 */
		this.habtm = {};
		this.hasAndBelongsToMany = this.habtm;
		
		/**
		 * belongsTo associations
		 * 
		 * @type {Object}
		 */
		this.belongsTo = {};

		/**
		 * All associations
		 *
		 * @type {Object}
		 */
		this.associations = {};
		
		/**
		 * All aliases
		 *
		 * @type {Object}
		 */
		this.aliases = {};

		/**
		 * Default find options
		 *
		 * @type {Object}
		 */
		this.findOptions = {
		conditions: {},
			_conditions: {}, // 'Cleaned up' conditions are stored here
			recursive: 1,
			fields: [],
			sort: false,
			order: 1,
			limit: -1,
			page: false,
			offset: false,
			callbacks: true, // Other possible values are false, 'before', 'after,
			lean: true,      // Return simple javascript objects, not mongoose docs
			filterJoins: false, // If a join is part of a condition, also filter it
			_mongoFnc: 'find'
		};
		
		this.blueprint = {};
		this.schema = {};
		this.cache = {};

		// Data for the client
		this.safeBlueprint = {};
		
		// Index settings per fields are stored here
		this._indexFields = {};
		
		// Indexes settings per name are stored here
		this._indexes = {};
		
		this.many = {};
		this.special = {json: {}};
		
		this._prepost = {
			pre: [],
			post: []
		};
		
		// "Hidden" properties
		this._mongoose = mongoose;
		this._validate = validate;
		this._model = {};

		// Behaviours for this controller
		this.behaviours = false;
		
		// Behaviour instances
		this._behaviours = {};
	};

	/**
	 * Do when extending
	 *
	 * @author   <NAME>   <<EMAIL>>
	 * @since    0.0.1
	 * @version  0.0.1
	 */
	this.__extended__ = function __extended__(parentModel, childModel) {

		if (!childModel.prototype.hasOwnProperty('modelName')) {
			childModel.prototype.modelName = childModel.name.replace('Model', '');
		}

	};

	/**
	 * Constructor. Binds the model's database table to the object.
	 *
	 * @author   <NAME>   <<EMAIL>>
	 * @since    0.0.1
	 * @version  0.0.1
	 *
	 * This is the layout of the "options" parameter:
	 * - id       - The id to start the model on.
	 * - table    - The table to use for this model.
	 * - ds       - The connection name this model is connected to.
	 * - name     - The name of the model eg. Post.
	 * - alias    - The alias of the model, this is used for registering the instance in the `ClassRegistry`.
	 *   eg. `ParentThread`
	 *
	 *
	 * @param   {Object}   options
	 */
	this.init = function init(options) {

		if (typeof options != 'undefined') {
			if (typeof options.id != 'undefined') this.id = options.id;
			if (typeof options.table != 'undefined') this.useTable = options.table;
			if (typeof options.ds != 'undefined') this.useDbConfig = options.ds;
			if (typeof options.name != 'undefined') this.modelName = options.name;
			if (typeof options.alias != 'undefined') this.alias = options.alias;
		}

		if (this.useTable === null) {
			if (this.modelName) this.useTable = this.modelName.tableize();
		}

		if (this.useTable) {
			
			var thisModel = this;
			
			// Set the connection
			this.connection = alchemy._db_connections[this.useDbConfig];
			
			// Set the initial connection state
			thisModel.connected = this.connection._hasOpened;
			
			// Set the connection status
			this.connection.on('open', function onModelConnectionOpen () {
				thisModel.connected = true;
			});
			
			this.connection.on('error', function onModelConnectionOpen (message) {
				thisModel.connected = false;
				log.error('Connection error!');
				log.error(message);
			});
			
			// Create the schema based on our blueprint
			this._schema = this._createSchema(this.blueprint);
			
			// Create the model based on that schema
			try {
				this._model = this._createModel(this.modelName, this._schema, this.enableCache);
			} catch (err) {
				log.error(err.message, {err: err});
			}
		}

		// We'll create a reference to this model's behaviours
		var thisBehaviours = this.behaviours;
		
		// Get our parent's components, so we can merge the current ones in it later
		var parentBehaviours = this.parent('behaviours');

		if (typeof parentBehaviours === 'object' && typeof thisBehaviours === 'object') {
			// If this model and its parent have behaviours, merge them
			this.behaviours = alchemy.inject(parentBehaviours, this.behaviours);
		} else if (typeof parentBehaviours === 'object') {
			// If only the parent has behaviours, overwrite them
			this.behaviours = alchemy.inject({}, parentBehaviours);
		}
		
		// Construct the components
		if (typeof this.behaviours === 'object') {

			for (var behaviourName in this.behaviours) {

				// Store every component in here
				this._behaviours[behaviourName] = Behaviour.get(behaviourName, this, this.behaviours[behaviourName]);

				// If behaviours should be exposed easily, store them under the model
				if (this._behaviours[behaviourName].expose) {
					this[behaviourName.classify()] = this._behaviours[behaviourName];
				}
			}
		}

		// Reset the associations, otherwise all models share the same object
		this.associations = {};
		
		this._compileAssociation('hasMany', this.hasMany);
		this._compileAssociation('hasOneChild', this.hasOneChild, {foreignKey: this.modelName.foreign_key()}, {fnc: 'findOne'});
		this._compileAssociation('hasOneParent', this.hasOneParent, null, {fnc: 'findOne'});
		this._compileAssociation('belongsTo', this.belongsTo, null, {fnc: 'findOne'});
		
		// Add belongsTo fields to the blueprint
		for (var modelName in this.associations) {
			
			var assoc = this.associations[modelName];

			// Add it to the aliases
			this.aliases[assoc.alias] = assoc.modelName;
			
			// hasOne fields should be unique
			// only do this if this is the child model (so: if the association is the parent)
			if (assoc.type == 'hasOneParent') {
				this.blueprint[assoc.foreignKey] = {
					type: 'ObjectId',
					index: {
						unique: true,
						order: 'asc'
					}
				};
			} else if (assoc.type == 'belongsTo') {
				this.blueprint[assoc.foreignKey] = {
					type: 'ObjectId',
					index: {
						unique: false,
						order: 'asc'
					}
				};
			}
			
		}
	};
	
	/**
	 * Callback after this instance has been augmented
	 *
	 * @author   <NAME>   <<EMAIL>>
	 * @since    0.0.1
	 * @version  0.0.1
	 *
	 * @param    {Object}   addition
	 *
	 */
	this.augmented = function augmented(addition) {

		var name,
		    behaviours = this._behaviours, // Get the behaviours from the original
		    skip = this.__augmentNoInherit;

		// Create a behaviours entry in the augment
		this._behaviours = {};

		// Do not inherit the _behaviours property
		skip._behaviours = true;

		// Augment the behaviours aswell
		for (name in behaviours) {
			this._behaviours[name] = alchemy.augment(behaviours[name], addition);

			if (this._behaviours[name].expose) {
				this[name.classify()] = this._behaviours[name];
			}
		}
	};
	
	/**
	 * Compile an association
	 *
	 * @author   <NAME>   <<EMAIL>>
	 * @since    0.0.1
	 * @version  0.0.1
	 *
	 * @param    {String}     name            The name of the association type
	 * @param    {Object}     associations    The associations to compile
	 *
	 * @return   {undefined}  Does not return anything, directly stores in this.associations
	 */
	this._compileAssociation = function _compileAssociation(name, associations, defaults, overrides) {
		
		if (!defaults) defaults = {};
		
		var i, modelName, assoc, foreignKey;
		
		for (i in associations) {
			
			assoc = associations[i];
			
			if (assoc.modelName) modelName = assoc.modelName
			else modelName = i;
			
			// Define the foreignkey
			if (assoc.foreignKey) {
				foreignKey = assoc.foreignKey;
			} else {
				if (defaults.foreignKey) {
					foreignKey = defaults.foreignKey;
				} else {
					foreignKey = modelName.foreign_key();
				}
			}
			
			this.associations[modelName] = {type: name, foreignKey: foreignKey, modelName: modelName, alias: i, fnc: 'find', options: assoc};
			if (overrides) alchemy.inject(this.associations[modelName], overrides);
		}
	};
	
	/**
	 * Do something to the database once we're connected
	 *
	 * @author   <NAME>   <<EMAIL>>
	 * @since    0.0.1
	 * @version  0.0.1
	 *
	 * @param    {Function}   one   The function to execute, or eval object
	 * @param    {Function}   two   If eval, this is its callback
	 *
	 * @return   {Boolean}    True if the function is executed immediately
	 */
	this._whenConnected = function _whenConnected(one, two) {
		
		var callback, thisModel;
		
		if (typeof one == 'function') {
			callback = one;
		} else {
			thisModel = this;
			
			// eval's nolock is true by default
			if (typeof one.nolock == 'undefined') one.nolock = true;
			
			callback = function executeWhenConnected () {
				thisModel.connection.db.executeDbCommand(one, two);
			}
		}

		if (this.connected) {
			callback();
			return true;
		}
		
		this.connection.once('open', function on_ifConnected () {
			callback();
		});
		
		return false;
	};

	/**
	 * Return the type (as a string) of a field
	 *
	 * @author   <NAME>   <<EMAIL>>
	 * @since    0.0.1
	 * @version  0.0.1
	 *
	 * @param    {String}   fieldName   The field to get the type of
	 * 
	 * @return   {String}   The type of the field
	 */
	this.fieldType = function fieldType(fieldName) {

		if (this.blueprint[fieldName] && this.blueprint[fieldName].type) {

			// Get the type of this field
			var type = this.blueprint[fieldName].type;

			// If the type is a function and not a string (which shouldn't be)
			// throw a warning, and use the function's name
			if (typeof type === 'function') {
				type = type.name;
				log.warn('The fieldtype for "' + fieldName + '" inside model "' + this.modelName + '" is a function instead of a string');
			}

			return type.toLowerCase();
		}

		return undefined;
	};
	
	/**
	 * Create a new Mongoose schema, with certain fields auto created
	 *
	 * @author   <NAME>   <<EMAIL>>
	 * @since    0.0.1
	 * @version  0.0.1
	 *
	 * @param    {Object}   blueprint   The schema blueprint
	 * 
	 * @return   {Object}   A mongoose schema
	 */
	this._createSchema = function _createSchema(blueprint) {
		
		var thisModel = this,
			blueprintClone,
			tempSchemas,
			fieldname,
			schema,
			option,
			order,
			ns,
			o, // The original blueprint
			e; // The cloned blueprint
		
		// Add the created & updated field
		blueprint.created = {type: 'Date', default: Date.now, fieldType: 'Date'};
		blueprint.updated = {type: 'Date', default: Date.now, fieldType: 'Date'};
		
		// Add the _id field if it doesn't exist
		// This isn't strictly needed, but I prefer to have it in the blueprint anyway
		if (typeof blueprint._id == 'undefined') blueprint._id = {type: 'ObjectId', default: mongoose.Types.ObjectId};
		
		// Create a blueprint clone, one we can edit and put function references in
		blueprintClone = alchemy.cloneDeep(blueprint);
		
		// Create an object to store the temporary schemas in
		tempSchemas = {};

		// Empty the safe blueprint
		this.safeBlueprint = {};
		
		// See if any of the entries are arrays
		for (fieldname in blueprintClone) {

			o = blueprint[fieldname];
			e = blueprintClone[fieldname];
			
			// If the type is a string, fetch it from the mongoose types
			if (typeof e === 'string') {
				
				e = {type: e};
				
				// Also update it in the text-only blueprint
				blueprint[fieldname] = {type: e.type};
				o = blueprint[fieldname];
			} else {
				
				// Is this field part of a database index?
				if (e.index) {
					
					// If index is just a boolean, create an object
					if (e.index === true) e.index = {unique: false};
					else if (typeof e.index == 'string') {
						if (e.index.toLowerCase() == 'unique') e.index = {unique: true};
						else e.index = {unique: true};
					}
					
					// Disable the sparse option by default
					if (typeof e.index.sparse == 'undefined') e.index.sparse = false;
					
					// Create an index name if none is given
					if (typeof e.index.name == 'undefined') {
						e.index.name = fieldname;
						if (e.index.unique) e.index.name += '_uq';
					}
					
					// Store these settings in the _index object by field name
					this._indexFields[fieldname] = e.index;
					
					// And now store it under the index name itself
					if (typeof this._indexes[e.index.name] == 'undefined') {
						this._indexes[e.index.name] = {
							fields: {},
							options: {
								name: e.index.name,
								unique: false,
							}
						};
					}
					
					if (e.index.unique) this._indexes[e.index.name].options.unique = true;
					if (e.index.sparse) this._indexes[e.index.name].options.sparse = true;
					
					order = 1;
					if (e.index.order == 'desc') order = -1;
					
					// Finally: add this field to this index
					this._indexes[e.index.name].fields[fieldname] = order;
				}
				
				if (e.array || e.arrayOf) {

					if (!e.arrayOf) e.arrayOf = e.array;
					
					// If it's an empty object,
					// just create an array of mixed, not subdocuments
					if (alchemy.isEmpty(e.type)) {
						e.type = [{}];
					} else {
						ns = {};
						ns[fieldname] = {};
						
						// Now go over every entry in this field
						for (option in e) {
							
							// Add those options to a temporary blueprint,
							// but only if it's not the array option
							if (option !== 'array'){
								ns[fieldname][option] = e[option];
							}
						}
						
						// Create the temporary array out of the temporary blueprint
						tempSchemas[fieldname] = new mongoose.Schema(ns);
						
						// Overwrite the entry
						e.type = [tempSchemas[fieldname]];
					}
				} else if (e.translatable) {

					o.objectOf = true;
					e.objectOf = true;

					// Store translations in a simple mixed object
					e.type = {};

					// enable the translate behaviour
					if (!this.behaviours) this.behaviours = {};
					if (!this.behaviours.translate) this.behaviours.translate = {};
					if (!this.behaviours.translate.fields) this.behaviours.translate.fields = {};

					this.behaviours.translate.fields[fieldname] = true;

				} else if (e.objectOf) {
					e.type = {};
				}
			}
			
			// If the type is a string, put in the appropriate function
			if (typeof e.type === 'string') {
				
				switch (e.type.toLowerCase()) {
					
					case 'enum':
					case 'string':
						e.type = String;
						break;
				
					case 'number':
						e.type = Number;
						break;

					case 'boolean':
						e.type = Boolean;
						break;
					
					case 'date':
						e.type = Date;
						break;
					
					default:

						// Get the type from mongoose if it exists
						if (mongoose.Schema.Types[e.type]) {
							e.type = mongoose.Schema.Types[e.type];
						} else {
							// If it doesn't, just use the string type
							e.type = String;
						}
						break;
				}
			}

			if (o.type.toLowerCase() === 'enum') {
				// Add an enum rule if none are set yet and the enum
{'fraction_non_alphanumeric': 0.12999312837979146, 'fraction_numerical': 0.0035254399330763944, 'mean_word_length': 2.4464579901153214, 'pattern_counts': {'":': 0, '<': 57, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2926027', 'n_tokens_mistral': 13359, 'n_tokens_neox': 10446, 'n_words': 3039}