mirror of
https://github.com/strapi/strapi.git
synced 2025-10-12 08:36:40 +00:00
Merge pull request #1520 from strapi/api-search
Add search in generated API
This commit is contained in:
commit
b48dced09d
@ -15,7 +15,11 @@ module.exports = {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
find: async (ctx) => {
|
find: async (ctx) => {
|
||||||
|
if (ctx.query._q) {
|
||||||
|
return strapi.services.<%= id %>.search(ctx.query);
|
||||||
|
} else {
|
||||||
return strapi.services.<%= id %>.fetchAll(ctx.query);
|
return strapi.services.<%= id %>.fetchAll(ctx.query);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -154,5 +154,89 @@ module.exports = {
|
|||||||
await <%= globalID %>.updateRelations(params);
|
await <%= globalID %>.updateRelations(params);
|
||||||
|
|
||||||
return <%= globalID %>.forge(params).destroy();
|
return <%= globalID %>.forge(params).destroy();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Promise to search a/an <%= id %>.
|
||||||
|
*
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
search: async (params) => {
|
||||||
|
// Convert `params` object to filters compatible with Bookshelf.
|
||||||
|
const filters = strapi.utils.models.convertParams('<%= globalID.toLowerCase() %>', params);
|
||||||
|
// Select field to populate.
|
||||||
|
const populate = <%= globalID %>.associations
|
||||||
|
.filter(ast => ast.autoPopulate !== false)
|
||||||
|
.map(ast => ast.alias);
|
||||||
|
|
||||||
|
const associations = <%= globalID %>.associations.map(x => x.alias);
|
||||||
|
const searchText = Object.keys(<%= globalID %>._attributes)
|
||||||
|
.filter(attribute => attribute !== <%= globalID %>.primaryKey && !associations.includes(attribute))
|
||||||
|
.filter(attribute => ['string', 'text'].includes(<%= globalID %>._attributes[attribute].type));
|
||||||
|
|
||||||
|
const searchNoText = Object.keys(<%= globalID %>._attributes)
|
||||||
|
.filter(attribute => attribute !== <%= globalID %>.primaryKey && !associations.includes(attribute))
|
||||||
|
.filter(attribute => !['string', 'text', 'boolean', 'integer', 'decimal', 'float'].includes(<%= globalID %>._attributes[attribute].type));
|
||||||
|
|
||||||
|
const searchInt = Object.keys(<%= globalID %>._attributes)
|
||||||
|
.filter(attribute => attribute !== <%= globalID %>.primaryKey && !associations.includes(attribute))
|
||||||
|
.filter(attribute => ['integer', 'decimal', 'float'].includes(<%= globalID %>._attributes[attribute].type));
|
||||||
|
|
||||||
|
const searchBool = Object.keys(<%= globalID %>._attributes)
|
||||||
|
.filter(attribute => attribute !== <%= globalID %>.primaryKey && !associations.includes(attribute))
|
||||||
|
.filter(attribute => ['boolean'].includes(<%= globalID %>._attributes[attribute].type));
|
||||||
|
|
||||||
|
const query = (params._q || '').replace(/[^a-zA-Z0-9.-\s]+/g, '');
|
||||||
|
|
||||||
|
return <%= globalID %>.query(qb => {
|
||||||
|
// Search in columns which are not text value.
|
||||||
|
searchNoText.forEach(attribute => {
|
||||||
|
qb.orWhereRaw(`LOWER(${attribute}) LIKE '%${_.toLower(query)}%'`);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!_.isNaN(_.toNumber(query))) {
|
||||||
|
searchInt.forEach(attribute => {
|
||||||
|
qb.orWhereRaw(`${attribute} = ${_.toNumber(query)}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query === 'true' || query === 'false') {
|
||||||
|
searchBool.forEach(attribute => {
|
||||||
|
qb.orWhereRaw(`${attribute} = ${_.toNumber(query === 'true')}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search in columns with text using index.
|
||||||
|
switch (<%= globalID %>.client) {
|
||||||
|
case 'pg': {
|
||||||
|
const searchQuery = searchText.map(attribute =>
|
||||||
|
_.toLower(attribute) === attribute
|
||||||
|
? `to_tsvector(${attribute})`
|
||||||
|
: `to_tsvector('${attribute}')`
|
||||||
|
);
|
||||||
|
|
||||||
|
qb.orWhereRaw(`${searchQuery.join(' || ')} @@ to_tsquery(?)`, query);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
qb.orWhereRaw(`MATCH(${searchText.join(',')}) AGAINST(? IN BOOLEAN MODE)`, `*${query}*`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filters.sort) {
|
||||||
|
qb.orderBy(filters.sort.key, filters.sort.order);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filters.skip) {
|
||||||
|
qb.offset(_.toNumber(filters.skip));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filters.limit) {
|
||||||
|
qb.limit(_.toNumber(filters.limit));
|
||||||
|
}
|
||||||
|
}).fetchAll({
|
||||||
|
width: populate
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -15,7 +15,11 @@ module.exports = {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
find: async (ctx) => {
|
find: async (ctx) => {
|
||||||
|
if (ctx.query._q) {
|
||||||
|
return strapi.services.<%= id %>.search(ctx.query);
|
||||||
|
} else {
|
||||||
return strapi.services.<%= id %>.fetchAll(ctx.query);
|
return strapi.services.<%= id %>.fetchAll(ctx.query);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -142,5 +142,53 @@ module.exports = {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Promise to search a/an <%= id %>.
|
||||||
|
*
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
search: async (params) => {
|
||||||
|
// Convert `params` object to filters compatible with Mongo.
|
||||||
|
const filters = strapi.utils.models.convertParams('<%= globalID.toLowerCase() %>', params);
|
||||||
|
// Select field to populate.
|
||||||
|
const populate = <%= globalID %>.associations
|
||||||
|
.filter(ast => ast.autoPopulate !== false)
|
||||||
|
.map(ast => ast.alias)
|
||||||
|
.join(' ');
|
||||||
|
|
||||||
|
const $or = Object.keys(<%= globalID %>.attributes).reduce((acc, curr) => {
|
||||||
|
switch (<%= globalID %>.attributes[curr].type) {
|
||||||
|
case 'integer':
|
||||||
|
case 'float':
|
||||||
|
case 'decimal':
|
||||||
|
if (!_.isNaN(_.toNumber(params._q))) {
|
||||||
|
return acc.concat({ [curr]: params._q });
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
case 'string':
|
||||||
|
case 'text':
|
||||||
|
case 'password':
|
||||||
|
return acc.concat({ [curr]: { $regex: params._q, $options: 'i' } });
|
||||||
|
case 'boolean':
|
||||||
|
if (params._q === 'true' || params._q === 'false') {
|
||||||
|
return acc.concat({ [curr]: params._q === 'true' });
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
default:
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <%= globalID %>
|
||||||
|
.find({ $or })
|
||||||
|
.sort(filters.sort)
|
||||||
|
.skip(filters.start)
|
||||||
|
.limit(filters.limit)
|
||||||
|
.populate(populate);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -48,7 +48,9 @@ module.exports = function (options, cb) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const compiled = _.template(contents);
|
const compiled = _.template(contents, {
|
||||||
|
interpolate: /<%=([\s\S]+?)%>/g
|
||||||
|
});
|
||||||
contents = compiled(options);
|
contents = compiled(options);
|
||||||
|
|
||||||
// With Lodash templates, HTML entities are escaped by default.
|
// With Lodash templates, HTML entities are escaped by default.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user