sparnatural
Version:
Visual client-side SPARQL query builder and knowledge graph exploration tool
169 lines (151 loc) • 6.17 kB
text/typescript
import { Branch, ISparJson } from "../../sparnatural/generators/json/ISparJson";
import { Binding, Form } from "../FormStructure";
/**
* @param query : the query to clean
* from query to use remove the branchs with o that exist on the form varibales
* the condition is that the o haven't any values, not exist in query.variables and it's optional or his father is optional
* @returns the cleaned query
*/
class CleanQuery {
private query: ISparJson;
// Thomas : this is not used anymore (05/02/2025)
private variablesUsedInFormConfig: string[];
private formConfig: Form;
constructor(query: ISparJson, formConfig: Form) {
this.query = query;
this.formConfig = formConfig;
}
// Obtenir les form variables à partir de formConfig
getFormVariables(form: Form): string[] {
return form.bindings.map((binding: Binding) => binding.variable);
}
//metthods to clean the querytouse
cleanQueryToUse(resultType: "onscreen" | "export"): ISparJson {
// deep copy of the initial query
let cleanQueryResult: ISparJson = JSON.parse(JSON.stringify(this.query));
// remove selected variables if onscreen display
// we remove variables from the SELECT clause
// further cleaning steps will remove the corresponding criteria from the WHERE clause if they are optional,
// and they have no value, and they are no more in the SELECT clause
cleanQueryResult = this.removeUnusedVariablesFromSelect(
cleanQueryResult,
resultType,
this.formConfig
);
console.log("Type", resultType);
//if (this.resultType == "onscreen") {
this.variablesUsedInFormConfig = this.getFormVariables(this.formConfig);
// re-list the variables used in the result set, after the previous filtering step
let variablesUsedInResultSet: string[] =
this.getVariablesUsedInResultSet(cleanQueryResult);
// clean the branches (= the WHERE clause)
cleanQueryResult.branches = this.cleanBranches(
cleanQueryResult.branches,
variablesUsedInResultSet
);
// } else {
//cleanQueryResult = this.query;
//}
console.log("CleanQuery: Query cleaned:", cleanQueryResult);
return cleanQueryResult;
}
private cleanBranches(
branches: Branch[],
variablesUsedInResultSet: string[]
): Branch[] {
return branches
.filter((branch) => {
const variable = branch.line?.o;
const hasValues = branch.line?.values?.length > 0;
const isOptional = branch.optional || false;
const parentOptional = this.isParentOptional(branch.line?.o);
// Vérifier si la variable existe dans `queryVariables`
// const existsInQueryVariables = variablesUsedInResultSet.includes(variable);
const existsInQueryVariables = variablesUsedInResultSet.find(v => v === variable) !== undefined;
// remove the branches with o :
// - which haven't any values
// - don't exist in query.variables
// - is optional or his father is optional
//
// note : we don't check that the variable is in the form variables, because what could happen is:
// - the variable is optional
// - it is not in the form variables (only here to be selected as a result variable)
// - it is selected in the query
// - but then it is removed for onscreen display
// - so we should remove it anyway
const shouldRemove =
// this.variablesUsedInFormConfig.includes(variable) && // La variable est dans les form variables
!existsInQueryVariables && // La variable n'existe pas dans les variables du SELECT la requête
!hasValues && // Aucune valeur pour cette branche
(isOptional || parentOptional); // La branche ou son parent est optionnel
return !shouldRemove;
})
.map((branch) => ({
...branch,
children: this.cleanBranches(
branch.children || [],
variablesUsedInResultSet
), // Nettoyer récursivement les enfants
}));
}
/**
* @return the array of all queries that are used in the query result, either directly or as aggregated variables
*/
private getVariablesUsedInResultSet(theQuery: ISparJson): string[] {
if (!theQuery.variables) return [];
else {
return Array.isArray(theQuery.variables)
? // either this is a simple variable, or this is an aggregated variable
theQuery.variables.map((v) =>
"value" in v ? v.value : v.expression.expression.value
)
: [];
}
}
// Vérifier si le parent d'une branche est optionnel
private isParentOptional(childVariable: string): boolean {
const findParent = (branches: Branch[], target: string): Branch => {
for (const branch of branches) {
if (
branch.children?.some((child: Branch) => child.line?.o === target)
) {
return branch;
}
if (branch.children) {
const result = findParent(branch.children, target);
if (result) return result;
}
}
return null;
};
const parent = findParent(this.query.branches, childVariable);
return parent?.optional || false;
}
/**
*
* @param query The query from which to remove the selected variables
* @param resultType The type of expected result. Depending on the type of result, only some columns are kept in the result set
* @returns
*/
private removeUnusedVariablesFromSelect(
query: ISparJson,
resultType: "onscreen" | "export",
formConfig: Form
): ISparJson {
if (resultType == "onscreen") {
query.variables = query.variables.filter((v) => {
// retain only the columns that are useful for an onscreen display
// the list of columns for onscreen display is a section in the form config
let varName = "value" in v ? v.value : v.expression.expression.value;
return (
!formConfig.variables?.onscreen ||
formConfig.variables?.onscreen?.includes(varName)
);
});
return query;
} else {
return query;
}
}
}
export default CleanQuery;