> ## Documentation Index
> Fetch the complete documentation index at: https://doc.lucidworks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# JSON Facets

> Query pipeline stage configuration specifications

export const schema = {
  "type": "object",
  "title": "JSON facets",
  "description": "Adds JSON facets to the Solr query for faceted aggregations using Solr's JSON faceting API.",
  "properties": {
    "skip": {
      "type": "boolean",
      "title": "Skip This Stage",
      "description": "Controls whether this stage executes during pipeline processing at runtime. When set to `true`, the stage is completely bypassed and documents pass through unchanged to the next stage. Useful for A/B testing, gradual rollouts, or temporarily disabling a stage without removing it from the pipeline.",
      "default": false,
      "hints": ["advanced"]
    },
    "label": {
      "type": "string",
      "title": "Label",
      "description": "Human-readable identifier displayed in the Fusion Admin UI, monitoring dashboards, and log messages. Use descriptive labels like `Parse Product PDFs` to aid debugging and team collaboration. Labels appear in performance metrics and error reports, making it easier to identify which stage failed.",
      "hints": ["advanced"],
      "maxLength": 255
    },
    "condition": {
      "type": "string",
      "title": "Condition",
      "description": "JavaScript expression evaluated at runtime on each document to conditionally execute this stage. The expression must return `true` to execute or `false` to skip. Access document fields using `doc.getFieldValue('fieldName')` and request parameters via `request.getFirstParam('paramName')`. For example, `doc.getFieldValue('type') === 'premium'` executes this stage only for premium content.",
      "hints": ["code", "code/javascript", "advanced"]
    },
    "legacy": {
      "type": "boolean",
      "title": "Legacy",
      "description": "When `true`, this stage operates in legacy mode only.",
      "hints": ["readonly", "hidden"]
    },
    "fieldFacets": {
      "type": "array",
      "title": "Field facets",
      "description": "Specifies the field facets to apply. Each entry defines a field-based facet aggregation.",
      "items": {
        "type": "object",
        "required": ["field"],
        "properties": {
          "field": {
            "type": "string",
            "title": "Field name",
            "description": "Specifies the field name(s) for field name operations in the document processing pipeline."
          },
          "prefix": {
            "type": "string",
            "title": "Prefix",
            "description": "String prepended to field names or values to create namespaces, avoid collisions, or organize related fields hierarchically. For field name prefixing, transforms `title` to `product_title` when prefix is `product_`. For value prefixing, prepends the string to each value."
          },
          "sort": {
            "type": "string",
            "title": "Sort",
            "description": "Specifies how to sort the buckets produced.",
            "enum": ["count", "index"],
            "default": "count"
          },
          "limit": {
            "type": "integer",
            "title": "Limit",
            "description": "Limits the number of buckets returned.",
            "default": 10
          },
          "offset": {
            "type": "integer",
            "title": "Offset",
            "description": "Used for paging, this skips the first N buckets.",
            "default": 0
          },
          "mincount": {
            "type": "integer",
            "title": "Minimum count",
            "description": "Only return buckets with a count of at least this number.",
            "default": 1
          },
          "missing": {
            "type": "boolean",
            "title": "Count missing",
            "description": "When enabled, a special “missing” bucket should be returned that is defined by documents without a value in the field.",
            "default": false
          },
          "method": {
            "type": "string",
            "title": "Method",
            "description": "The facet algorithm to use.",
            "enum": ["dv", "uif", "dvhash", "enum", "stream", "smart"],
            "default": "smart"
          },
          "rollupFieldCount": {
            "type": "string",
            "title": "Rollup field count",
            "description": "The name of the field to apply grouping operation on each bucket."
          },
          "filterExclusion": {
            "type": "boolean",
            "title": "Filter exclusion",
            "description": "When enabled, multiple facet options can be selected.",
            "default": false
          },
          "displayZeroCount": {
            "type": "boolean",
            "title": "Display Zero Count Values",
            "description": "When enabled, empty buckets will be displayed.",
            "default": false
          },
          "boostValues": {
            "type": "array",
            "title": "Boost Values",
            "description": "Field values to boost in the facet results. (Requires Modify Response stage)",
            "items": {
              "type": "string"
            }
          },
          "buryValues": {
            "type": "array",
            "title": "Bury Values",
            "description": "Field values to bury in the facet results. (Requires Modify Response stage)",
            "items": {
              "type": "string"
            }
          },
          "suppressValues": {
            "type": "array",
            "title": "Suppress Values",
            "description": "Field values to suppress from the facet results. (Requires Modify Response stage)",
            "items": {
              "type": "string"
            }
          },
          "order": {
            "type": "integer",
            "title": "Display Order",
            "description": "Order in which this facet appears in the response. Lower values appear first. If not specified, facet appears after all ordered facets."
          }
        }
      }
    },
    "rangeFacets": {
      "type": "array",
      "title": "Range facets",
      "description": "Specifies the range facets to apply. Each entry defines a numeric or date range facet aggregation.",
      "items": {
        "type": "object",
        "required": ["field", "start", "end", "gap"],
        "properties": {
          "field": {
            "type": "string",
            "title": "Field name",
            "description": "Specifies the field name(s) for field name operations in the document processing pipeline."
          },
          "start": {
            "type": "string",
            "title": "Range start",
            "description": "Lower bound of the ranges."
          },
          "end": {
            "type": "string",
            "title": "Range end",
            "description": "Upper bound of the ranges."
          },
          "gap": {
            "type": "string",
            "title": "Range gap",
            "description": "Size of each range bucket produced."
          },
          "hardend": {
            "type": "boolean",
            "title": "Hard end",
            "description": "When enabled, it means that the last bucket will end at “end” even if it is less than “gap” wide. Otherwise, the last bucket will be “gap” wide, which may extend past “end”."
          },
          "include": {
            "type": "array",
            "title": "Include",
            "description": "Determine how the bucket boundaries should be processed.",
            "items": {
              "type": "string",
              "enum": ["lower", "upper", "edge", "outer", "all"]
            }
          },
          "other": {
            "type": "array",
            "title": "Other",
            "description": "Specifies additional counts.",
            "items": {
              "type": "string",
              "enum": ["before", "after", "between", "none", "all"]
            }
          },
          "rollupFieldCount": {
            "type": "string",
            "title": "Rollup field count",
            "description": "The name of the field to apply the grouping operation on each bucket."
          },
          "filterExclusion": {
            "type": "boolean",
            "title": "Filter exclusion",
            "description": "When enabled, multiple facet options can be selected."
          },
          "displayZeroCount": {
            "type": "boolean",
            "title": "Display Zero Count Values",
            "description": "When enabled, empty buckets will be displayed.",
            "default": false
          },
          "boostValues": {
            "type": "array",
            "title": "Boost Values",
            "description": "Field values to boost in the facet results. (Requires Modify Response stage)",
            "items": {
              "type": "string"
            }
          },
          "buryValues": {
            "type": "array",
            "title": "Bury Values",
            "description": "Field values to bury in the facet results. (Requires Modify Response stage)",
            "items": {
              "type": "string"
            }
          },
          "suppressValues": {
            "type": "array",
            "title": "Suppress Values",
            "description": "Field values to suppress from the facet results. (Requires Modify Response stage)",
            "items": {
              "type": "string"
            }
          },
          "order": {
            "type": "integer",
            "title": "Display Order",
            "description": "Order in which this facet appears in the response. Lower values appear first. If not specified, facet appears after all ordered facets."
          }
        }
      }
    }
  },
  "category": "Set Up",
  "categoryPriority": 8,
  "unsafe": false
};

export const SchemaParamFields = ({schema}) => {
  const sanitize = str => {
    if (typeof str !== "string") return str;
    return str.replace(/^"(.*)"$/s, "$1").replace(/\\/g, "").replace(/"/g, "'");
  };
  const renderMd = str => {
    const s = sanitize(str);
    const text = (/[.!?]\)*$/).test(s) ? s : `${s}.`;
    return text.split(/(\*\*[^*]+\*\*|_[^_]+_|`[^`]+`)/g).map((part, i) => {
      if (part.startsWith("**")) return <strong key={i}>{part.slice(2, -2)}</strong>;
      if (part.startsWith("_")) return <em key={i}>{part.slice(1, -1)}</em>;
      if (part.startsWith("`")) return <code key={i}>{part.slice(1, -1)}</code>;
      return part;
    });
  };
  const {description, properties = {}, required: requiredProps = []} = schema;
  const visibleProps = useMemo(() => Object.entries(properties).filter(([, prop]) => !prop.hints?.includes("hidden")), [properties]);
  const renderProp = ([name, prop]) => {
    const isRequired = requiredProps.includes(name);
    const hasDefault = prop.default !== undefined;
    const rawDefault = prop.default;
    const hints = prop.hints || [];
    const isComplexDefault = hasDefault && (typeof rawDefault === "object" || typeof rawDefault === "string" && (rawDefault.length > 20 || rawDefault.includes('"')));
    const postBadges = [];
    if (prop.title) {
      postBadges.push(<><span className="text-stone-400 dark:text-stone-500">API property: </span>{name}</>);
    }
    const constraints = [];
    if (prop.minimum !== undefined && prop.maximum !== undefined) {
      constraints.push(`Range: ${prop.minimum} – ${prop.maximum}`);
    } else if (prop.minimum !== undefined) {
      constraints.push(`Min: ${prop.minimum}`);
    } else if (prop.maximum !== undefined) {
      constraints.push(`Max: ${prop.maximum}`);
    }
    if (prop.minLength !== undefined && prop.maxLength !== undefined) {
      constraints.push(`Length: ${prop.minLength} – ${prop.maxLength}`);
    } else if (prop.minLength !== undefined) {
      constraints.push(`Min length: ${prop.minLength}`);
    } else if (prop.maxLength !== undefined) {
      constraints.push(`Max length: ${prop.maxLength}`);
    }
    const fieldProps = {
      key: name,
      body: prop.title || name,
      type: prop.type,
      ...postBadges.length > 0 && ({
        post: postBadges
      }),
      ...isRequired && ({
        required: true
      }),
      ...!isComplexDefault && hasDefault ? {
        default: sanitize(String(rawDefault))
      } : {}
    };
    const isObject = prop.type === "object" && prop.properties;
    const isArrayOfObjects = prop.type === "array" && prop.items?.type === "object" && prop.items.properties;
    return <ParamField {...fieldProps}>
        {prop.description && <p>{renderMd(prop.description)}</p>}

        {prop.enum && <p>
            Allowed values: 
            {prop.enum.map((v, i) => <>{i > 0 && ", "}<code key={i}>{String(v)}</code></>)}
          </p>}

        {constraints.length > 0 && <p className="text-stone-500 dark:text-stone-400 text-sm">
            {constraints.join(" · ")}
          </p>}

        {isComplexDefault && <div className="flex">
            <p>
              <strong>Default:</strong>
            </p>
            <pre className="!my-0">
              <code>
                {JSON.stringify(rawDefault, null, 2)}
              </code>
            </pre>
          </div>}

        {isArrayOfObjects && <Expandable title="item properties">
            <SchemaParamFields schema={{
      properties: prop.items.properties,
      required: prop.items.required
    }} />
          </Expandable>}

        {isObject && <Expandable title="properties">
            <SchemaParamFields schema={{
      properties: prop.properties,
      required: prop.required
    }} />
          </Expandable>}
      </ParamField>;
  };
  return <div>
      {description && <p>{renderMd(description)}</p>}

      {visibleProps.map(renderProp)}
    </div>;
};

export const LwTemplate = ({title = "Key questions to get you started", icon = "sparkles", cta = "Powered by Agent Studio", linkHref = "https://lucidworks.com/demo/?utm_source=docs&utm_medium=referral&utm_campaign=docs_cta_ai"}) => {
  const [isLoaded, setIsLoaded] = useState(false);
  useEffect(() => {
    const timer = setTimeout(() => {
      setIsLoaded(true);
    }, 500);
    return () => clearTimeout(timer);
  }, []);
  return <div className="lw-template-container">
      <Card title={title} icon={icon}>
        {isLoaded && <span dangerouslySetInnerHTML={{
    __html: `<lw-template id="a029c1a9-28be-427e-b0e1-5d918920246a"></lw-template
            >`
  }} />}
        <Link href={linkHref} className="agent-studio-link text-left text-gray-600 gap-2 dark:text-gray-400 text-sm font-medium flex flex-row items-center hover:text-primary dark:hover:text-primary-light group-hover:text-primary group-hover:dark:text-primary-light">Powered by Lucidworks Agent Studio</Link>
      </Card>
    </div>;
};

[localhost link]: http://localhost:3000/docs/lucidworks-search/09-developer-documentation/config-specs/query-pipeline-stages/json-facets

[mintlify link]: https://doc.lucidworks.com/docs/lucidworks-search/09-developer-documentation/config-specs/query-pipeline-stages/json-facets

The JSON Facets query pipeline stage adds field facets and range facets to the Solr query using the [Solr JSON Facet API](https://solr.apache.org/guide/solr/latest/query-guide/json-facet-api.html).
The stage combines all of the facets you configure into a single `json.facet` request parameter, with one entry per field.
If you configure more than one facet on the same field, the later facet overwrites the earlier one.

Compared to the [Field Facet stage](/docs/lucidworks-search/09-developer-documentation/config-specs/query-pipeline-stages/field-facet), the JSON Facets stage supports these additional options:

* **Rollup field count.** Count unique values of another field in each bucket, such as the number of products instead of the number of SKUs.
* **Filter exclusion.** Keep all facet values visible after a user selects one, so users can select more than one value.
* **Display Zero Count Values.** Return buckets that match no documents.

Solr returns JSON facet results under `facets.FIELD_NAME.buckets` in the response, not under `facet_counts`.

For more information about faceting in Lucidworks Search, see [Faceting](/docs/lucidworks-search/05-move-data-out/faceting).

## Boost, bury, and suppress facet values

<Note>
  The **Boost Values**, **Bury Values**, **Suppress Values**, and **Display Order** fields are available in Lucidworks Search 5.17 and later.
</Note>

Use these fields to change the order of values within a facet:

* **Boost Values.** Move these values to the top of the facet, in the order you list them.
* **Bury Values.** Move these values to the bottom of the facet.
* **Suppress Values.** Remove these values from the facet.

Values that aren't listed keep the order that Solr returns.

Boost, bury, and suppress require a [Modify Response stage](/docs/lucidworks-search/09-developer-documentation/config-specs/query-pipeline-stages/modify-response-rules) in the same pipeline.
Place the Modify Response stage after both the JSON Facets stage and the [Solr Query stage](/docs/lucidworks-search/09-developer-documentation/config-specs/query-pipeline-stages/solr-query).
If the Modify Response stage is missing or runs before either of those stages, the facet values aren't reordered.

Keep these matching rules in mind:

* Values must exactly match the facet values that Solr returns. Matching is case-sensitive and doesn't support partial matches.
* If a value appears in **Suppress Values** and in **Boost Values** or **Bury Values**, the value is suppressed.
* Don't list the same value in both **Boost Values** and **Bury Values**.

## Facet display order

Use **Display Order** to control the order in which facets appear in the response.
Facets with lower values appear first.
Facets without a value appear after all ordered facets.

**Display Order** doesn't require a Modify Response stage.

## Stage configuration examples

These examples show the stage configuration and the `json.facet` parameter that the stage adds to the Solr request.

<AccordionGroup>
  <Accordion title="Basic field facet">
    Configure a field facet with **Field name** set to `color` and all other settings left at their defaults.

    The stage adds this parameter:

    ```json theme={"dark"}
    {
      "color": {
        "type": "terms",
        "field": "color",
        "sort": "count",
        "limit": 10,
        "offset": 0,
        "mincount": 1,
        "missing": false,
        "method": "smart"
      }
    }
    ```
  </Accordion>

  <Accordion title="Count products instead of SKUs">
    In eCommerce catalogs, one product often has several SKUs.
    To count products in each bucket, configure a field facet with **Field name** set to `color` and **Rollup field count** set to `prod_name_s`.

    The stage adds a nested facet to the `color` facet:

    ```json theme={"dark"}
    "facet": {
      "prod_name_s-count": "unique(prod_name_s)"
    }
    ```

    Each bucket in the response includes both the document count and the number of unique products.
  </Accordion>

  <Accordion title="Multiselect facet">
    To let users select more than one color, configure a field facet with **Field name** set to `color` and enable **Filter exclusion**.

    The stage adds this to the `color` facet:

    ```json theme={"dark"}
    "domain": {
      "excludeTags": "color"
    }
    ```

    Your search application must tag the filter query with the same name as the field.
    For example:

    ```text theme={"dark"}
    fq={!tag=color}color:Blue
    ```

    If the filter query isn't tagged, the facet doesn't exclude the filter.
  </Accordion>

  <Accordion title="Range facet">
    Configure a range facet with **Field name** set to `price`, **Range start** set to `0`, **Range end** set to `50`, and **Range gap** set to `10`.

    The stage adds this parameter:

    ```json theme={"dark"}
    {
      "price": {
        "type": "range",
        "field": "price",
        "start": "0",
        "end": "50",
        "gap": "10",
        "hardend": false,
        "include": [],
        "other": []
      }
    }
    ```
  </Accordion>
</AccordionGroup>

To return buckets that match no documents, enable **Display Zero Count Values**.
This sets the minimum count to `0` and overrides the **Minimum count** setting.

<LwTemplate />

## Configuration

<Tip>
  When entering configuration values in the UI, use *unescaped* characters, such as `\t` for the tab character. When entering configuration values in the API, use *escaped* characters, such as `\\t` for the tab character.
</Tip>

<SchemaParamFields schema={schema} />


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.