Skip to content

Commit

Permalink
Merge pull request #1 from alechirsch/distinct-fields
Browse files Browse the repository at this point in the history
Distinct fields
  • Loading branch information
alechirsch authored Sep 14, 2016
2 parents 54d8b3a + e6e0206 commit 32eb5eb
Showing 1 changed file with 74 additions and 43 deletions.
117 changes: 74 additions & 43 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ export default (Bookshelf, options = {}) => {
// Add qualifying table name to avoid ambiguous columns
fieldNames[fieldKey] = _map(fieldNames[fieldKey], (value) => {

if (!fieldKey){
return value;
}
return `${fieldKey}.${value}`;
});

Expand All @@ -262,6 +265,10 @@ export default (Bookshelf, options = {}) => {
// Add columns to query
internals.model.query((qb) => {

if (!fieldKey){
qb.distinct();
}

qb.select(fieldNames[fieldKey]);

// JSON API considers relationships as fields, so we
Expand Down Expand Up @@ -313,94 +320,110 @@ export default (Bookshelf, options = {}) => {
// Determine if there are multiple filters to be applied
const valueArray = typeValue.toString().indexOf(',') !== -1 ? typeValue.split(',') : typeValue;

// If the column exists as an equality filter, add 'or' to 'where'
let where = _hasIn(filterValues, typeKey) ? 'orWhere' : 'where';

// Attach different query for each type
if (key === 'like'){

// Need to add double quotes for each table/column name, this is needed if there is a relationship with a capital letter
typeKey = `"${typeKey.replace('.', '"."')}"`;
if (_isArray(valueArray)){
qb.where((qbWhere) => {
const formatedKey = `"${typeKey.replace('.', '"."')}"`;
qb.where((qbWhere) => {

if (_isArray(valueArray)){
let where = 'where';
_forEach(valueArray, (val) => {

val = `%${val}%`;

qbWhere[where](
Bookshelf.knex.raw(`LOWER(${typeKey}) like LOWER(?)`, [val])
Bookshelf.knex.raw(`LOWER(${formatedKey}) like LOWER(?)`, [val])
);

// Change to orWhere after the first where
if (where === 'where'){
where = 'orWhere';
}
});
});
}
else {
qb[where](
Bookshelf.knex.raw(`LOWER(${typeKey}) like LOWER(?)`, [`%${typeValue}%`])
);
}
}
else {
qbWhere.where(
Bookshelf.knex.raw(`LOWER(${formatedKey}) like LOWER(?)`, [`%${typeValue}%`])
);
}

// If the key is in the top level filter, filter on orWhereIn
if (_hasIn(filterValues, typeKey)){
// Determine if there are multiple filters to be applied
value = filterValues[typeKey].toString().indexOf(',') !== -1 ? filterValues[typeKey].split(',') : filterValues[typeKey];
qbWhere.orWhereIn(typeKey, value);
}
});
}
else if (key === 'not'){
qb[where + 'NotIn'](typeKey, valueArray);
qb.whereNotIn(typeKey, valueArray);
}
else if (key === 'lt'){
qb[where](typeKey, '<', typeValue);
qb.where(typeKey, '<', typeValue);
}
else if (key === 'gt'){
qb[where](typeKey, '>', typeValue);
qb.where(typeKey, '>', typeValue);
}
else if (key === 'lte'){
qb[where](typeKey, '<=', typeValue);
qb.where(typeKey, '<=', typeValue);
}
else if (key === 'gte'){
qb[where](typeKey, '>=', typeValue);
qb.where(typeKey, '>=', typeValue);
}
});
}
}
// If the value is an equality filter
else {

// Remove all but the last table name, need to get number of dots
key = internals.formatRelation(internals.formatColumnNames([key])[0]);

// Determine if there are multiple filters to be applied
value = value.toString().indexOf(',') !== -1 ? value.split(',') : value;

// If the column exists as an filter type, add 'or' to 'where'
let where = 'where';
_forEach(filterTypes, (typeKey) => {

if (_hasIn(filterValues[typeKey], key)){
where = 'orWhere';
}
});

qb[where + 'In'](key, value);
// If the key is in the like filter, ignore the filter
if (!_hasIn(filterValues.like, key)){
// Remove all but the last table name, need to get number of dots
key = internals.formatRelation(internals.formatColumnNames([key])[0]);

// Determine if there are multiple filters to be applied
value = value.toString().indexOf(',') !== -1 ? value.split(',') : value;
qb.whereIn(key, value);
}
}
});
});
}
};

/**
* Takes in an attribute string like a.b.c.d and returns c.d
* Takes in an attribute string like a.b.c.d and returns c.d, also if attribute
* looks like 'a', it will return tableName.a where tableName is the top layer table name
* @param attribute {string}
* @return {string}
*/
internals.formatRelation = (attribute) => {

if (_includes(attribute, '.')){
const splitKey = attribute.split('.');
attribute = `${splitKey[splitKey.length - 2]}.${splitKey[splitKey.length - 1]}`;
}
// Add table name to before column name if no relation to avoid ambiguous columns
else {
attribute = `${internals.modelName}.${attribute}`;
}
return attribute;
};

/**
* Takes an array from attributes and returns the only the columns and removes the table names
* @param attributes {array}
* @return {array}
*/
internals.getColumnNames = (attributes) => {

return _map(attributes, (attribute) => {

return attribute.substr(attribute.lastIndexOf('.') + 1);
});
};

/**
* Build a query based on the `include` parameter.
* @param includeValues {array}
Expand All @@ -420,14 +443,18 @@ export default (Bookshelf, options = {}) => {
relations.push({
[relation]: (qb) => {

const relationId = `${internals.modelName}_id`;

if (!internals.isBelongsToRelation(relation, this) &&
!_includes(fieldNames[relation], relationId)) {
if (!internals.isBelongsToRelation(relation, this)) {
const relatedData = this[relation]().relatedData;
const foreignKey = relatedData.foreignKey ? relatedData.foreignKey : `${inflection.singularize(relatedData.parentTableName)}_${relatedData.parentIdAttribute}`;

qb.column.apply(qb, [relationId]);
if (!_includes(fieldNames[relation], foreignKey)){
qb.column.apply(qb, [foreignKey]);
}
}
fieldNames[relation] = internals.getColumnNames(fieldNames[relation]);
if (!_includes(fieldNames[relation], 'id')){
qb.column.apply(qb, ['id']);
}

qb.column.apply(qb, [fieldNames[relation]]);
}
});
Expand Down Expand Up @@ -488,6 +515,10 @@ export default (Bookshelf, options = {}) => {
columns[columnNames[key].substr(columnNames[key].lastIndexOf('.') + 1)] = undefined;
columnNames[key] = columnNames[key].substring(0, columnNames[key].lastIndexOf('.')) + '.' + _keys(this.format(columns));
}
else if (_isArray(value) && key === '' && value.length === 1 && _includes(value[0], '.')){
columns[value[0].substr(value[0].lastIndexOf('.') + 1)] = undefined;
value[0] = value[0].substring(0, value[0].lastIndexOf('.')) + '.' + _keys(this.format(columns));
}
else {
// Convert column names to an object so it can
// be passed to Model#format
Expand Down

0 comments on commit 32eb5eb

Please sign in to comment.