 
     @param callback [Function] The calback function, called with a (possible) error object and the server versio
   ###
-  getVersion: (callback) =>
+  getVersion: (callback) ->
     if @version?
       callback null, @version
     else

    @option return bindVars [Object] The variables, bound in the conditions
    @option return geoObject [Object] An query builder object containing possible parameters for a geo query
  ###
  _filter2where: (model, filter, returnVariable) =>
    debug "Evaluating where object #{JSON.stringify filter} for Model #{model}: " if @debug

    # variable to name the result
    returnVariable = returnVariable or 'result'
    #  the object holding the assignments of conditional values to temporary variables
    boundVars = {}
    # index for condition parameter binding
    index = 0
    #  helper function to fill boundVars with the upcoming temporary variables that the where sentence will generate
    assignNewQueryVariable = (value) ->
      partName = 'param_' + (index++)
      boundVars[partName] = value
      return '@'+partName

    #  array holding the filter
    aqlArray = []
    ###
      the where object comes in two flavors

       - where[prop] = value: this is short for "prop" equals "value"
       - where[prop][op] = value: this is the long version and stands for "prop" "op" "value"
    ###
    for condProp, condValue of filter.where

      # correct if the conditionProperty falsely references to 'id'
      condProp = '_key' if condProp is 'id'

      #  special case: if conditionValue is a Object (instead of a string or number) we have a conditionOperator
      if condValue and condValue.constructor.name is 'Object'
        #  condition operator is the only keys value, the new condition value is shifted one level deeper and can be a object with keys and values
        condOp = Object.keys(condValue)[0]
        condValue = condValue[condOp]
      else
        # condition operator is 'equals' or shortly 'eq'
        # and condition value is stringified for the comparison
        condOp = 'eq'
        condValue = condValue.toString()

      # special treatment for "and" and "or" operator, since there value is an array of conditions
      if condOp in ['and', 'or']
        # 'and' and 'or' have multiple conditions so we run buildWhere recursively on their array to
        if _.isArray condValue
          # condValue is an array of conditions so get the conditions from it via a recursive buildWhere call
          conditionalPart = @_filter2where model, condValue, returnVariable

          # add the condArray resp. the boundVars
          aqlArray.push qb[condOp].apply null, conditionalPart.aqlArray
          merge true, boundVars, conditionalPart.boundVars

      # If the value is not an array, fall back to regular fields
      switch
        # number comparison
        when condOp in ['lte', 'lt', 'gte', 'gt', 'eq', 'neq']
          aqlArray.push qb[condOp] "#{returnVariable}.#{condProp}", "#{assignNewQueryVariable(condValue)}"

        # range comparison
        when condOp is 'between'
          aqlArray.push [qb.gte("#{returnVariable}.#{condProp}", "#{assignNewQueryVariable(condValue[0])}"),  qb.lte("#{returnVariable}.#{condProp}", "#{assignNewQueryVariable(condValue[1])}")]

        # string comparison
        when condOp is 'like'
          aqlArray.push qb.not qb.LIKE "#{returnVariable}.#{condProp}", "#{assignNewQueryVariable(condValue)}"
        when condOp is 'nlike'
          aqlArray.push qb.LIKE "#{returnVariable}.#{condProp}", "#{assignNewQueryVariable(condValue)}"

        # array comparison
        when condOp is 'nin'
          aqlArray.push qb.not qb.in "#{returnVariable}.#{condProp}", "#{assignNewQueryVariable(condValue)}"
        when condOp is 'in'
          aqlArray.push qb.in "#{returnVariable}.#{condProp}", "#{assignNewQueryVariable(condValue)}"

        # geo comparison (extra object)
        when 'near'
          # 'near' does not create a condition in the filter part, it returnes the lat/long pair
          # the query will be done from the querying method itself
          [lat, long] = condValue.split(',')
          collection = @getCollectionName model
          if filter.limit?
            geoExpr = qb.NEAR collection, lat, long, filter.limit
          else
            geoExpr = qb.NEAR collection, lat, long

        #  if we don't have a matching operator or no operator at all (condOp = false) then use the equivalence operator
        else
          qb.eq "(#{returnVariable}.#{condProp}", "#{assignNewQueryVariable(condValue)}"

    console.log 'aqlArray ', aqlArray
    return {
      aqlArray: aqlArray
      boundVars: boundVars
      geoExpr: geoExpr
    }



  ###
  ###
  _filter2fields: (model, filter, returnVariable) =>
    # variable to name the result
    returnVariable = returnVariable or 'result'
    includes = []
    excludes = []

    if Array.isArray filter.fields
      includes = filter.fields
    else
      _.each filter.fields, (field, key) -> if field then includes.push key else excludes.push key

    # KEEP only the specified fields to include if includes not empty
    return qb.fn('KEEP') returnVariable, includes if includes.length > 0

    # remove/UNSET the
    return qb.fn('UNSET') returnVariable, excludes if excludes.length > 0



  ###
  ###
  _filter2order: (model, filter, returnVariable) =>
    splitOrderClause = (order) ->
      [prop, dir] = order.split ' '
      dir = dir or 'ASC'
      return [prop, dir]

    if Array.isArray filter.order
      return _.chain(filter.order).map(splitOrderClause).flatten().value()
    else
      return splitOrderClause(filter.order)

  ###
  ###
  _filter2limit: (model, filter, returnVariable) =>
    if filter.skip?
      return qb.limit filter.skip, filter.limit
    else
      return qb.limit filter.limit

  ###
  ###
  # _filter2include: (model, filter, returnVariable) =>
  # TODO:
  # if (filter && filter.include) [
  #   self._models[model].model.include(objs, filter.include, callback);
  # ] else [
  #   callback(null, objs);
  # ]


  ###
  ###
  # TODO: create a function that can be called with the filter array only
  _filter2parts: (model, filter, returnVariable) =>
    # ================
    # = filter.where =
    # ================
    # TODO: where part is FILTER part of query
    # filter.where -> use buildWhere to create FILTER statement
    if not filter.where? or typeof filter.where isnt 'object'
      filterWhere = null
    else
      filterWhere = @_filter2where model, filter, returnVariable

    # =================
    # = filter.fields =
    # =================
    # TODO: fields part should be used in RETURN part of query
    # filter.fields -> use AQL-KEEP or AQL-UNSET to reduce final returned object
    if not filter.fields? or typeof filter.fields isnt 'object'
      filterFields = null
    else
      filterFields = @_filter2fields model, filter, returnVariable

    # ================
    # = filter.order =
    # ================
    # TODO: order part should be used in ORDER part of query
    # filter.order -> query-builder for sorting
    if not filter.order? or typeof filter.order not in ['object', 'string']
      filterOrder = null
    else
      filterOrder = @_filter2order model, filter, returnVariable

    # ==============================
    # = filter.limit & filter.skip =
    # ==============================
    # TODO: order part should be used in LIMIT part of query
    # filter.limit & filter.skip -> query-builder for limit & skip
    if not filter.limit?
      filterLimit = null
    else
      filterLimit = @_filter2limit model, filter, returnVariable

    # ==================
    # = filter.include =
    # ==================
    # TODO:
    # if (filter && filter.include) {
    #   self._models[model].model.include(objs, filter.include, callback);
    # } else {
    #   callback(null, objs);
    # }

    return {
      where: filterWhere
      fields: filterFields
      order: filterOrder
      limit: filterLimit
      # include: filterInclude
    }

  ###
  ###
  _filter2query: (model, filter) =>
    debug '_filter2query', model, filter if @debug


    collection = @getCollectionName model
    collVariable = collection.charAt 0
    returnVariable = 'result'

    boundVars = {}

    parts = @_filter2parts model, filter, returnVariable

    # define returnVariable
    aql = qb.for(returnVariable)
    console.log 'aql_for ', aql
    # 1) check where-part if we have a geo expression that we must include in the in-clause, if not use in-collection
    coll = if parts.where.geoObject? then parts.where.geoObject else '@@collection'
    aql = aql.in(coll)
    console.log 'aql_in ', aql
    # 2) use filter from where-part to filter, merge the boundVars from where into the global boundVars
    merge true, boundVars, parts.where.boundVars
    aql = aql.filter qb.and.apply null, parts.where.aqlArray

    # 3) use order to sort result on item level
    aql = aql.sort.apply null, parts.order if parts.order?

    # 4) use limit to slice result on item level
    aql = aql.limit.apply null, parts.limit if parts.limit?

    # 5) use field to return fields-filterd result on attribute level or plain object
    returnExpr = if parts.fields? then parts.fields else returnVariable
    aql = aql.return returnExpr
    console.log 'aql', aql
    return {
      aql: aql
      boundVars: boundVars
    }



    @param id [String] id The id value
    @param callback [Function] The callback function, called with a (possible) error object and the found object
  ###
  find: (model, id, callback) ->
    debug "find for #{model} with id: #{id}" if @debug

    aql = qb.for('doc').in('@@collection').filter(qb.eq('doc._key', '@id')).limit(1).return(qb.fn('UNSET') 'doc', ['"_id"','"_rev"'])

    bindVars =
      '@collection': @getCollectionName model
      id: id

    @execute aql, bindVars, (err, result) ->
      return callback err if err
      return callback null, result._result[0] if result._result.length > 0
      callback null, result._result


  ###
   Decide if id should be included
   @param {Object} fields
   @returns {Boolean}
   @private
  ###
  _idIncluded: (fields, idName) ->
    if !fields then return true

    if Array.isArray(fields)
      return fields.indexOf(idName) >= 0

    if fields[idName]
      # Included
      return true

    if idName in fields and !fields[idName]
      # Excluded
      return false

    for f in fields
      return !fields[f]; # If the fields has exclusion

    return true


  # ========================
  # = Collection functions =
