react query builder basic filters

by alfredopacino

CSS

body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
  text-align: center;
}

TypeScript

function buildQuery(filters) {
  const must = [];
  const nonNested = [];
  const nestedGroups = {};

  const isRangeTerm = v => typeof v === 'object' && v !== null && ['lt', 'lte', 'gt', 'gte'].some(key => key in v);

  filters.forEach(({field, value}) => {
    const fieldParts = field.split(".");
    if (fieldParts.length === 1) {
      // Top-level field (not nested)
      if (Array.isArray(value)) {
        must.push({terms: { [field]: value }});
      } else {
        if (isRangeTerm(value)) {
          must.push( { range: { [field]: value} } )
        } else {
          must.push({term: { [field]: value }});
        }
      }
    } else {
      // Group by nested path
      const nestedPath = fieldParts.slice(0, -1).join(".");
      if (!nestedGroups[nestedPath]) {
         nestedGroups[nestedPath] = [];
      }
      nestedGroups[nestedPath].push({
        fullPath: field,
        value
      });
    }
  });

  Object.entries(nestedGroups).forEach(([nestedPath, groupFilters]) => {
    console.log("nestedPath, groupFilters", nestedPath, groupFilters)
    const mustInner = groupFilters.map(({fullPath, value}) => (
      Array.isArray(value)
        ? {terms: { [fullPath]: value }}
        : isRangeTerm(value)
          ? { range: { [fullPath]: value} }
          : {term: { [fullPath]: value }}
    ));
    const nestedLevels = nestedPath.split(".");
    let nestedQuery = { bool: { must: mustInner } };
    // loops over multilevels and nesting backwards
    for (let i = nestedLevels.length - 1; i >= 0; i--) {
      console.log("levels", nestedLevels.slice(0, i + 1).join("."))
      nestedQuery = {
        nested: {
          path: nestedLevels.slice(0, i + 1).join("."),
          query: nestedQuery
        }
      };
    }
    must.push(nestedQuery);
  });

  return { query: { bool: { must } } };
}


const filters = [
  //{ field: "variantCategory", value: "small" },
  //{ field:...