> ## 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.

# Parameterized SQL Aggregation Jobs

export const schema = {
  "type": "object",
  "title": "Parameterized SQL Aggregation",
  "description": "A SQL aggregation job where users provide parameters to be injected into a built-in SQL template at runtime.",
  "required": ["id", "inputCollection", "type"],
  "properties": {
    "id": {
      "type": "string",
      "title": "Spark Job ID",
      "description": "The ID for this Spark job. Used in the API to reference this job. Allowed characters: a-z, A-Z, dash (-) and underscore (_)",
      "maxLength": 128,
      "pattern": "^[A-Za-z0-9_\\-]+$"
    },
    "inputCollection": {
      "type": "string",
      "title": "Source Collection",
      "description": "Collection containing documents to be aggregated."
    },
    "outputCollection": {
      "type": "string",
      "title": "Output Collection",
      "description": "The collection to write the aggregates to on output. Defaults to the input collection if not specified."
    },
    "notes": {
      "type": "string",
      "title": "Notes",
      "description": "A short description about this job.",
      "hints": ["lengthy"]
    },
    "parameters": {
      "type": "array",
      "title": "SQL Parameters",
      "description": "Parameters bound on the SQL template at runtime.",
      "items": {
        "type": "object",
        "required": ["key"],
        "properties": {
          "key": {
            "type": "string",
            "title": "Parameter Name"
          },
          "value": {
            "type": "string",
            "title": "Parameter Value"
          }
        }
      }
    },
    "selectQuery": {
      "type": "string",
      "title": "Query",
      "description": "The query to select the desired signals. If not set then '*:*' will be used, or equivalent.",
      "default": "*:*",
      "hints": ["advanced"]
    },
    "timeRange": {
      "type": "string",
      "title": "Time Range",
      "description": "The time range to select signals on, e.g., `[* TO NOW]`. See Solr date range for more options (https://solr.apache.org/guide/8_8/working-with-dates.html).",
      "hints": ["advanced"],
      "minLength": 1
    },
    "sourceCatchup": {
      "type": "boolean",
      "title": "Aggregate New and Merge with Existing",
      "description": "If checked, only aggregate new signals created since the last time the job was successfully run. If there is a record of such previous run then this overrides the starting time of time range set in 'timeRange' property. If unchecked, then all matching signals are aggregated and any previously aggregated docs are deleted to avoid double counting.",
      "default": true,
      "hints": ["advanced"]
    },
    "sql": {
      "type": "string",
      "title": "SQL",
      "description": "Use SQL to perform the aggregation. You do not need to include a time range filter in the WHERE clause as it gets applied automatically before executing the SQL statement.",
      "hints": ["advanced", "code/sql"],
      "minLength": 1
    },
    "rollupSql": {
      "type": "string",
      "title": "Rollup SQL",
      "description": "Use SQL to perform a rollup of previously aggregated docs. If left blank, the aggregation framework will supply a default SQL query to rollup aggregated metrics.",
      "hints": ["advanced", "code/sql"],
      "minLength": 1
    },
    "sourceRemove": {
      "type": "boolean",
      "title": "Remove Source",
      "description": "If true, the processed source signals will be removed after aggregation. Default is false.",
      "default": false,
      "hints": ["hidden"]
    },
    "outputRollup": {
      "type": "boolean",
      "title": "Rollup Output",
      "description": "Roll-up current results with all previous results for this aggregation id, which are available in \"outputCollection\".",
      "default": true,
      "hints": ["hidden"]
    },
    "referenceTime": {
      "type": "string",
      "title": "Reference Time",
      "description": "Timestamp to use for computing decays and to determine the value of NOW.",
      "hints": ["hidden"],
      "format": "date-time"
    },
    "hiddenParameters": {
      "type": "array",
      "title": "Hidden Parameters",
      "description": "Additional settings used to tune the underlying aggregation job.",
      "hints": ["hidden"],
      "items": {
        "type": "object",
        "required": ["key"],
        "properties": {
          "key": {
            "type": "string",
            "title": "Parameter Name"
          },
          "value": {
            "type": "string",
            "title": "Parameter Value"
          }
        }
      }
    },
    "readOptions": {
      "type": "array",
      "title": "Read Options",
      "description": "Additional configuration settings to fine-tune how input records are read for this aggregation.",
      "hints": ["advanced"],
      "items": {
        "type": "object",
        "required": ["key"],
        "properties": {
          "key": {
            "type": "string",
            "title": "Parameter Name"
          },
          "value": {
            "type": "string",
            "title": "Parameter Value"
          }
        }
      }
    },
    "skipCheckEnabled": {
      "type": "boolean",
      "title": "Job Skip Check Enabled?",
      "description": "If the catch-up flag is enabled and this field is checked, the job framework will execute a fast Solr query to determine if this run can be skipped.",
      "default": true,
      "hints": ["advanced"]
    },
    "type": {
      "type": "string",
      "title": "Spark Job Type",
      "enum": ["sql_template"],
      "default": "sql_template",
      "hints": ["readonly"]
    }
  },
  "additionalProperties": true,
  "category": "Other",
  "categoryPriority": 1,
  "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 formatDescription = str => {
    const s = sanitize(str);
    return (/[.!?]\)*$/).test(s) ? s : `${s}.`;
  };
  const {description, properties = {}, required: requiredProps = []} = schema;
  const visibleProps = useMemo(() => Object.entries(properties).filter(([, prop]) => !prop.hints?.includes("hidden")), [properties]);
  return <div>
      {description && <p>{formatDescription(description)}</p>}

      {visibleProps.map(([name, prop]) => {
    const isRequired = requiredProps.includes(name);
    const hasDefault = prop.default !== undefined;
    const rawDefault = prop.default;
    const isComplexDefault = hasDefault && (typeof rawDefault === "object" || typeof rawDefault === "string" && (rawDefault.length > 20 || rawDefault.includes('"')));
    const fieldProps = {
      key: name,
      body: prop.title || name,
      type: prop.type,
      ...prop.title && ({
        post: [<><span className="text-stone-400 dark:text-stone-500">API property: </span>{name}</>]
      }),
      ...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>{formatDescription(prop.description)}</p>}

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

            {isArrayOfObjects && <div className="flex">
              <p>
                <strong>Object attributes:</strong>
              </p>
              <pre className="!my-0">
                <code>
                  {'{\n'}
                  {Object.entries(prop.items.properties).map(([iname, iprop]) => <>
                      {`  ${iname}`}
                      {prop.items?.required?.includes(iname) && <span style={{
      color: 'red'
    }}> required</span>}
                      {`: {\n    display name: ${sanitize(iprop.title || '')}\n    type: ${iprop.type}\n  }\n`}
                    </>)}
                  {'}'}
                </code>
              </pre>
              </div>}

            {isObject && <Expandable title="properties">
                <SchemaParamFields schema={{
      properties: prop.properties,
      required: prop.required
    }} />
              </Expandable>}
          </ParamField>;
  })}
    </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/4/fusion-ai/reference/jobs/parameterized-sql-aggregation

[mintlify link]: https://doc.lucidworks.com/docs/4/fusion-ai/reference/jobs/parameterized-sql-aggregation

[old doc.lw link]: https://doc.lucidworks.com/fusion-ai/4.2/553

A Spark SQL aggregation job where user-defined parameters are injected into a built-in SQL template at runtime.

<LwTemplate />

<SchemaParamFields schema={schema} />
