Pour une utilisation dans un serveur graphql, j'ai défini un type d'entrée structuré dans lequel vous pouvez spécifier un certain nombre de conditions de filtrage très similaires au fonctionnement de prisma :
Ce qui me permet de soumettre des filtres structurés dans une requête comme :
{
users(
where: {
OR: [{ email: { starts_with: "ja" } }, { email: { ends_with: ".com" } }],
AND: [{ email: { starts_with: "ja" } }, { email: { ends_with: ".com" } }],
email: {contains: "lowe"}
}
) {
id
email
}
}
Dans mon résolveur, j'envoie le fichier args.where à travers une fonction pour analyser la structure et utiliser le constructeur de requêtes de TypeOrm pour la convertir en SQL correct. L'intégralité de la fonction est :
import { Brackets } from "typeorm";
export const filterQuery = (query: any, where: any) => {
if (!where) {
return query;
}
Object.keys(where).forEach(key => {
if (key === "OR") {
where[key].map((queryArray: any) => {
query.orWhere(new Brackets(qb => filterQuery(qb, queryArray)));
});
} else if (key === "AND") {
where[key].map((queryArray: any) => {
query.andWhere(new Brackets(qb => filterQuery(qb, queryArray)));
});
} else {
const whereArgs = Object.entries(where);
whereArgs.map(whereArg => {
const [fieldName, filters] = whereArg;
const ops = Object.entries(filters);
ops.map(parameters => {
const [operation, value] = parameters;
switch (operation) {
case "is": {
query.andWhere(`${fieldName} = :isvalue`, { isvalue: value });
break;
}
case "not": {
query.andWhere(`${fieldName} != :notvalue`, { notvalue: value });
break;
}
case "in": {
query.andWhere(`${fieldName} IN :invalue`, { invalue: value });
break;
}
case "not_in": {
query.andWhere(`${fieldName} NOT IN :notinvalue`, {
notinvalue: value
});
break;
}
case "lt": {
query.andWhere(`${fieldName} < :ltvalue`, { ltvalue: value });
break;
}
case "lte": {
query.andWhere(`${fieldName} <= :ltevalue`, { ltevalue: value });
break;
}
case "gt": {
query.andWhere(`${fieldName} > :gtvalue`, { gtvalue: value });
break;
}
case "gte": {
query.andWhere(`${fieldName} >= :gtevalue`, { gtevalue: value });
break;
}
case "contains": {
query.andWhere(`${fieldName} ILIKE :convalue`, {
convalue: `%${value}%`
});
break;
}
case "not_contains": {
query.andWhere(`${fieldName} NOT ILIKE :notconvalue`, {
notconvalue: `%${value}%`
});
break;
}
case "starts_with": {
query
.andWhere(`${fieldName} ILIKE :swvalue`)
.setParameter("swvalue", `${value}%`);
break;
}
case "not_starts_with": {
query
.andWhere(`${fieldName} NOT ILIKE :nswvalue`)
.setParameter("nswvalue", `${value}%`);
break;
}
case "ends_with": {
query.andWhere(`${fieldName} ILIKE :ewvalue`, {
ewvalue: `%${value}`
});
break;
}
case "not_ends_with": {
query.andWhere(`${fieldName} ILIKE :newvalue`, {
newvalue: `%${value}`
});
break;
}
default: {
break;
}
}
});
});
}
});
return query;
};
Ce qui fonctionne (en quelque sorte) mais n'imbrique pas les requêtes AND/OR comme je l'aurais souhaité (et que j'avais déjà réussi à faire fonctionner dans KNEX). La fonction ci-dessus génère le code SQL :
SELECT
"user"."id" AS "user_id",
"user"."name" AS "user_name",
"user"."email" AS "user_email",
"user"."loginToken" AS "user_loginToken",
"user"."loginTokenExpiry" AS "user_loginTokenExpiry",
"user"."active" AS "user_active",
"user"."visible" AS "user_visible",
"user"."isStaff" AS "user_isStaff",
"user"."isBilling" AS "user_isBilling",
"user"."createdAt" AS "user_createdAt",
"user"."updatedAt" AS "user_updatedAt",
"user"."version" AS "user_version"
FROM "user" "user"
WHERE (email ILIKE $1)
AND (email ILIKE $2)
OR (email ILIKE $3)
OR (email ILIKE $4)
AND email ILIKE $5
-- PARAMETERS: ["ja%","%.com","ja%","%.com","%lowe%"]
Mais je m'attendais à voir quelque chose de plus proche :
.....
WHERE email ILIKE '%low%'
AND (
email ILIKE 'ja%' AND email ILIKE '%.com'
) AND (
email ILIKE 'ja%' OR email ILIKE '%.com'
)
Pardonnez cette question absurde et répétitive. J'essaie simplement d'illustrer les déclarations NESTED attendues.
Comment puis-je forcer les branches AND/OR de ma fonction de construction de requête à s'imbriquer correctement comme prévu ?
** Points bonus si quelqu'un peut m'aider à trouver les caractères typographiques réels ici **