Reactivesearch: Unable to Remove Query / DynamicRangeSlider enforces value once active

Created on 16 Aug 2019  路  6Comments  路  Source: appbaseio/reactivesearch

Affected Projects
React

Library Version: x.y.z
3.0.0-rc.25

Describe the bug
As soon as it rendered, the DynamicRangeSlider will enforce the range it displays. Items that don't have this field will not be matched anymore at all. In my case, that would be an optional memory filter for devices, while not all devices do have any memory.

The SelectedFilters component however doesn't show any hint yet, that a range query is applied.

Next I put rendering of the component under a boolean condition that I stored in state and added a toogle for that. Now as soon as I enable the toggle and the DynamicRange becomes active, the restriction is applied. When I toggle it off, the query stays restricted to that range. I first have to change another value in order to update the results without the range.

To Reproduce
I dont have quick sandbox example, might provide one later.

Expected behavior

  1. If a Range is queried, always show it in SelectedFilters.
  2. When the component is removed, remove the range from the query.
  1. Idea to be more intuitive: Create a default disabled state for the DynamicRangeSlider, where the knobs are positioned at the min/max like they are now, but greyed out. Enable both when one of them has been dragged or clicked the first time, show some button that allows unselecting it again.
bug

Most helpful comment

Here's how I fixed it locally.

  1. For a rangecomponent, I store the start/end values in a separate exportable variable, keyed by the componentId
// store range defaults so our queries can check it they're set to default.
export const rangeDefaults = {
  'rent-estimate': {
    start: 0,
    end: 500000,
  },
  'rentable-square-feet': {
    start: 0,
    end: 50000,
  },
};
  1. create a custom query generation function that checks the range value against the default, and if it matches, drops that part of the range query.

// nested
export const nullableNestedRangeQuery = (value, component) => {
  if (value === null) {
    return { "query": { "match_all": {} } };
  }
  let minQuery = { gte: value[0] };
  let maxQuery = { lte: value[1] };

  if (component.componentId in rangeDefaults) {
    if (value[0] === rangeDefaults[component.componentId].start) {
      minQuery = {};
    }
    if (value[1] === rangeDefaults[component.componentId].end) {
      maxQuery = {};
    }
    if (!Object.keys(minQuery).length && !Object.keys(maxQuery).length) {
      return { "query": { "match_all": {} } };
    }
  }

  return {
    query: {
      bool: {
        must: [
          {
            nested: {
              path: component.nestedField,
              query: {
                range: {
                  [component.dataField]: {
                    ...minQuery,
                    ...maxQuery,
                  },
                },
              },
            },
          },
        ],
      },
    },
  }
};


// non-nested (note: I haven't tested this, I've only been using the nested one)
export const nullableRangeQuery = (value, component) => {
  if (value === null) {
    return { "query": { "match_all": {} } };
  }
  let minQuery = { gte: value[0] };
  let maxQuery = { lte: value[1] };

  if (component.componentId in rangeDefaults) {
    if (value[0] === rangeDefaults[component.componentId].start) {
      minQuery = {};
    }
    if (value[1] === rangeDefaults[component.componentId].end) {
      maxQuery = {};
    }
    if (!Object.keys(minQuery).length && !Object.keys(maxQuery).length) {
      return { "query": { "match_all": {} } };
    }
  }

  return {
    query: {
      range: {
        [component.dataField]: {
          ...minQuery,
          ...maxQuery,
        },
      },
    },
  }
};

  1. use it in a component
export const RoomRentableSquareFeetRangeInput = (props) => {
  return (
    <RangeInput
      componentId="rentable-square-feet"
      dataField="rooms.rsf"
      nestedField="rooms"
      stepValue={100}
      customQuery={nullableNestedRangeQuery}
      defaultValue={rangeDefaults['rentable-square-feet']}
      range={rangeDefaults['rentable-square-feet']}
    />
  );
};

All 6 comments

@janus-reith A codesandbox (when you can) would help a lot. We're looking into the set of issues related to DynamicRangeSlider next and can check on this.

The reason that I didn't provide a sandbox yet is that I would first need to have a public accessible dataset available which has a field present on only some items which can also be queried by range.

I think this is related: https://github.com/appbaseio/reactivesearch/issues/1059

Ah, just noted that @ShahAnuj2610 already referenced the PR that fixes this, great!

Still, I'm wondering:

  • How would I go about this if I manually want to remove the query somehow that the DynamicRangeSlider created?(Or any other query) I didn't find a way for this yet.
    I also tried to use the connect handle and remove, that works for setQuery but did not work properly for removing, even if the relevant component unmounted.

  • Should there maybe be a visual distinction between a non selected range (greyed out slider handles) and actually selected minimum/maximum?

I think this is related: #1059

Ah, just noted that @ShahAnuj2610 already referenced the PR that fixes this, great!

Still, I'm wondering:

  • How would I go about this if I manually want to remove the query somehow that the DynamicRangeSlider created?(Or any other query) I didn't find a way for this yet.
    I also tried to use the connect handle and remove, that works for setQuery but did not work properly for removing, even if the relevant component unmounted.
  • Should there maybe be a visual distinction between a non selected range (greyed out slider handles) and actually selected minimum/maximum?
  • I agree that the start and end range should be computed dynamically, but query should only be applied based on a prop
  • There should be a visible difference that whether query has been applied or not
    I think this is a sort of feature enhancement. We just have to decide whether it's on priority or not?
    cc: @siddharthlatest

@janus-reith Isn't this already possible by using the customQuery prop? You can set a match_all clause for instance if you don't want the default range query to be triggered.

Here's how I fixed it locally.

  1. For a rangecomponent, I store the start/end values in a separate exportable variable, keyed by the componentId
// store range defaults so our queries can check it they're set to default.
export const rangeDefaults = {
  'rent-estimate': {
    start: 0,
    end: 500000,
  },
  'rentable-square-feet': {
    start: 0,
    end: 50000,
  },
};
  1. create a custom query generation function that checks the range value against the default, and if it matches, drops that part of the range query.

// nested
export const nullableNestedRangeQuery = (value, component) => {
  if (value === null) {
    return { "query": { "match_all": {} } };
  }
  let minQuery = { gte: value[0] };
  let maxQuery = { lte: value[1] };

  if (component.componentId in rangeDefaults) {
    if (value[0] === rangeDefaults[component.componentId].start) {
      minQuery = {};
    }
    if (value[1] === rangeDefaults[component.componentId].end) {
      maxQuery = {};
    }
    if (!Object.keys(minQuery).length && !Object.keys(maxQuery).length) {
      return { "query": { "match_all": {} } };
    }
  }

  return {
    query: {
      bool: {
        must: [
          {
            nested: {
              path: component.nestedField,
              query: {
                range: {
                  [component.dataField]: {
                    ...minQuery,
                    ...maxQuery,
                  },
                },
              },
            },
          },
        ],
      },
    },
  }
};


// non-nested (note: I haven't tested this, I've only been using the nested one)
export const nullableRangeQuery = (value, component) => {
  if (value === null) {
    return { "query": { "match_all": {} } };
  }
  let minQuery = { gte: value[0] };
  let maxQuery = { lte: value[1] };

  if (component.componentId in rangeDefaults) {
    if (value[0] === rangeDefaults[component.componentId].start) {
      minQuery = {};
    }
    if (value[1] === rangeDefaults[component.componentId].end) {
      maxQuery = {};
    }
    if (!Object.keys(minQuery).length && !Object.keys(maxQuery).length) {
      return { "query": { "match_all": {} } };
    }
  }

  return {
    query: {
      range: {
        [component.dataField]: {
          ...minQuery,
          ...maxQuery,
        },
      },
    },
  }
};

  1. use it in a component
export const RoomRentableSquareFeetRangeInput = (props) => {
  return (
    <RangeInput
      componentId="rentable-square-feet"
      dataField="rooms.rsf"
      nestedField="rooms"
      stepValue={100}
      customQuery={nullableNestedRangeQuery}
      defaultValue={rangeDefaults['rentable-square-feet']}
      range={rangeDefaults['rentable-square-feet']}
    />
  );
};
Was this page helpful?
0 / 5 - 0 ratings

Related issues

metagrover picture metagrover  路  4Comments

ymzoughi picture ymzoughi  路  4Comments

mrtinkz picture mrtinkz  路  3Comments

rbeers picture rbeers  路  3Comments

felubra picture felubra  路  3Comments