mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-08 01:15:49 +02:00
feat: also allow searching partial tags (#5299)
This commit is contained in:
parent
f45454fbfd
commit
a5288ae0b1
@ -83,9 +83,7 @@ export default class FeatureSearchController extends Controller {
|
|||||||
sortBy,
|
sortBy,
|
||||||
} = req.query;
|
} = req.query;
|
||||||
const userId = req.user.id;
|
const userId = req.user.id;
|
||||||
const normalizedTag = tag
|
const normalizedTag = tag?.map((tag) => tag.split(':'));
|
||||||
?.map((tag) => tag.split(':'))
|
|
||||||
.filter((tag) => tag.length === 2);
|
|
||||||
const normalizedStatus = status
|
const normalizedStatus = status
|
||||||
?.map((tag) => tag.split(':'))
|
?.map((tag) => tag.split(':'))
|
||||||
.filter(
|
.filter(
|
||||||
@ -95,7 +93,7 @@ export default class FeatureSearchController extends Controller {
|
|||||||
);
|
);
|
||||||
const normalizedLimit =
|
const normalizedLimit =
|
||||||
Number(limit) > 0 && Number(limit) <= 50 ? Number(limit) : 50;
|
Number(limit) > 0 && Number(limit) <= 50 ? Number(limit) : 50;
|
||||||
const normalizedOffset = Number(offset) > 0 ? Number(limit) : 0;
|
const normalizedOffset = Number(offset) > 0 ? Number(offset) : 0;
|
||||||
const normalizedSortBy: string = sortBy ? sortBy : 'createdAt';
|
const normalizedSortBy: string = sortBy ? sortBy : 'createdAt';
|
||||||
const normalizedSortOrder =
|
const normalizedSortOrder =
|
||||||
sortOrder === 'asc' || sortOrder === 'desc' ? sortOrder : 'asc';
|
sortOrder === 'asc' || sortOrder === 'desc' ? sortOrder : 'asc';
|
||||||
|
@ -190,7 +190,7 @@ test('should filter features by environment status', async () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('filter with invalid tag should ignore filter', async () => {
|
test('should filter by partial tag', async () => {
|
||||||
await app.createFeature('my_feature_a');
|
await app.createFeature('my_feature_a');
|
||||||
await app.createFeature('my_feature_b');
|
await app.createFeature('my_feature_b');
|
||||||
await app.addTag('my_feature_a', { type: 'simple', value: 'my_tag' });
|
await app.addTag('my_feature_a', { type: 'simple', value: 'my_tag' });
|
||||||
@ -198,7 +198,7 @@ test('filter with invalid tag should ignore filter', async () => {
|
|||||||
const { body } = await filterFeaturesByTag(['simple']);
|
const { body } = await filterFeaturesByTag(['simple']);
|
||||||
|
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
features: [{ name: 'my_feature_a' }, { name: 'my_feature_b' }],
|
features: [{ name: 'my_feature_a' }],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@ import { IFeatureProjectUserParams } from './feature-toggle-controller';
|
|||||||
import { Db } from '../../db/db';
|
import { Db } from '../../db/db';
|
||||||
import Raw = Knex.Raw;
|
import Raw = Knex.Raw;
|
||||||
import { IFeatureSearchParams } from './types/feature-toggle-strategies-store-type';
|
import { IFeatureSearchParams } from './types/feature-toggle-strategies-store-type';
|
||||||
import { addMilliseconds, format, formatISO, parseISO } from 'date-fns';
|
|
||||||
|
|
||||||
const COLUMNS = [
|
const COLUMNS = [
|
||||||
'id',
|
'id',
|
||||||
@ -547,6 +546,9 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
|
|||||||
features: IFeatureOverview[];
|
features: IFeatureOverview[];
|
||||||
total: number;
|
total: number;
|
||||||
}> {
|
}> {
|
||||||
|
const normalizedFullTag = tag?.filter((tag) => tag.length === 2);
|
||||||
|
const normalizedHalfTag = tag?.filter((tag) => tag.length === 1).flat();
|
||||||
|
|
||||||
let environmentCount = 1;
|
let environmentCount = 1;
|
||||||
if (projectId) {
|
if (projectId) {
|
||||||
const rows = await this.db('project_environments')
|
const rows = await this.db('project_environments')
|
||||||
@ -559,17 +561,27 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
|
|||||||
if (projectId) {
|
if (projectId) {
|
||||||
query = query.where({ project: projectId });
|
query = query.where({ project: projectId });
|
||||||
}
|
}
|
||||||
|
const hasQueryString = Boolean(queryString?.trim());
|
||||||
if (queryString?.trim()) {
|
const hasHalfTag = normalizedHalfTag && normalizedHalfTag.length > 0;
|
||||||
|
if (hasQueryString || hasHalfTag) {
|
||||||
|
const tagQuery = this.db.from('feature_tag').select('feature_name');
|
||||||
// todo: we can run a cheaper query when no colon is detected
|
// todo: we can run a cheaper query when no colon is detected
|
||||||
const tagQuery = this.db
|
if (hasQueryString) {
|
||||||
.from('feature_tag')
|
tagQuery.whereRaw("(?? || ':' || ??) ILIKE ?", [
|
||||||
.select('feature_name')
|
|
||||||
.whereRaw("(?? || ':' || ??) LIKE ?", [
|
|
||||||
'tag_type',
|
'tag_type',
|
||||||
'tag_value',
|
'tag_value',
|
||||||
`%${queryString}%`,
|
`%${queryString}%`,
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
|
if (hasHalfTag) {
|
||||||
|
const tagParameter = normalizedHalfTag.map((tag) => `%${tag}%`);
|
||||||
|
tagQuery.orWhereRaw(
|
||||||
|
`(?? || ':' || ??) ILIKE ANY (ARRAY[${tagParameter
|
||||||
|
.map(() => '?')
|
||||||
|
.join(',')}])`,
|
||||||
|
['tag_type', 'tag_value', ...tagParameter],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
query = query.where((builder) => {
|
query = query.where((builder) => {
|
||||||
builder
|
builder
|
||||||
@ -577,11 +589,11 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
|
|||||||
.orWhereIn('features.name', tagQuery);
|
.orWhereIn('features.name', tagQuery);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (tag && tag.length > 0) {
|
if (normalizedFullTag && normalizedFullTag.length > 0) {
|
||||||
const tagQuery = this.db
|
const tagQuery = this.db
|
||||||
.from('feature_tag')
|
.from('feature_tag')
|
||||||
.select('feature_name')
|
.select('feature_name')
|
||||||
.whereIn(['tag_type', 'tag_value'], tag);
|
.whereIn(['tag_type', 'tag_value'], normalizedFullTag);
|
||||||
query = query.whereIn('features.name', tagQuery);
|
query = query.whereIn('features.name', tagQuery);
|
||||||
}
|
}
|
||||||
if (type) {
|
if (type) {
|
||||||
|
Loading…
Reference in New Issue
Block a user