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

# Solr Partial Update Indexer Stage

export const schema = {
  "type": "object",
  "title": "Solr Partial Update Indexer",
  "description": "Sends atomic partial update operations to Solr to modify specific fields of an existing document without re-indexing the entire document. Configure the target document ID, fields to update or delete, and optional concurrency control to prevent conflicting simultaneous updates. Use this stage when only a subset of fields changes and full re-indexing would be too expensive.",
  "required": ["solrDocIdFieldValue"],
  "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"]
    },
    "enforceSchema": {
      "type": "boolean",
      "title": "Map to Solr Schema",
      "default": true
    },
    "concurrencyControlEnabled": {
      "type": "boolean",
      "title": "Enable Concurrency Control",
      "description": "Enables optimistic concurrency control in Solr to prevent one partial update from overwriting another concurrent update to the same document. When enabled, Solr checks the document version before applying the update and rejects it if a conflicting update has already been committed. Disable only when last-write-wins behavior is acceptable.",
      "default": true
    },
    "rejectUpdatesIfDocNotPresent": {
      "type": "boolean",
      "title": "Reject Update if Solr Document is not Present",
      "description": "Controls whether the update is rejected when no document with the specified ID exists in Solr. When `true`, updates to non-existent documents fail. When `false`, Solr attempts the update regardless, which may create a partial document. Disable with caution, as updating a non-existent document can produce incomplete records.",
      "default": true
    },
    "updateAllDocFields": {
      "type": "boolean",
      "title": "Process All Pipeline Doc Fields",
      "description": "Controls whether all pipeline document fields are included in the partial update, not just those listed in the Updates and Deletions configuration. When enabled, fields not explicitly configured follow standard Solr atomic update rules, treating non-map values as `set` operations. Disable to limit the partial update strictly to the explicitly configured fields.",
      "default": false
    },
    "solrDocIdFieldName": {
      "type": "string",
      "title": "Solr Document ID Field Name",
      "hints": ["hidden"]
    },
    "solrDocIdFieldValue": {
      "type": "string",
      "title": "Solr Document ID Field Value",
      "default": "<doc.id>"
    },
    "dateFormats": {
      "type": "array",
      "title": "Additional Date Formats",
      "hints": ["advanced"],
      "items": {
        "type": "string"
      }
    },
    "params": {
      "type": "array",
      "title": "Additional Update Request Parameters",
      "hints": ["advanced"],
      "items": {
        "type": "object",
        "required": ["key"],
        "properties": {
          "key": {
            "type": "string",
            "title": "Parameter Name"
          },
          "value": {
            "type": "string",
            "title": "Parameter Value"
          }
        }
      }
    },
    "updatedFields": {
      "type": "array",
      "title": "Updates",
      "description": "Defines the list of Solr document fields to modify using atomic update operations such as `set`, `add`, `remove`, `remove_regex`, `increment`, or `decrement`. Each entry specifies a field name, an update type, and one or more values. Use `increment` or `decrement` with a single numeric value to adjust counters.",
      "items": {
        "type": "object",
        "required": ["fieldName", "values"],
        "properties": {
          "updateType": {
            "type": "string",
            "title": "Update Type",
            "description": "Specifies the Solr atomic update operation applied to the field. Use `set` to replace the field value, `add` to append a new value, `remove` or `remove_regex` to delete specific values, or `increment`/`decrement` to adjust a numeric counter. The operation must be compatible with the field's Solr schema type.",
            "enum": ["set", "add", "remove", "remove_regex", "increment", "decrement"],
            "default": "set"
          },
          "fieldName": {
            "type": "string",
            "title": "Field Name",
            "description": "Specifies the name of the Solr document field to modify with the atomic update operation. The field must exist in the Solr schema. Updates to undefined fields may be rejected or silently ignored depending on schema settings. Use the exact field name as defined in the collection schema."
          },
          "values": {
            "type": "string",
            "title": "Value",
            "description": "Specifies the value or values applied by the update operation. For `increment` and `decrement`, provide a single positive or negative integer. For `add`, `set`, `remove`, and `remove_regex`, provide a single value or a comma-separated list. Escape literal commas with a backslash (`\\`)."
          }
        }
      }
    },
    "deletedFields": {
      "type": "array",
      "title": "Deletions",
      "description": "Defines the list of Solr document fields to remove from the document during the partial update. Each entry specifies a field name to delete. Fields listed here are removed from the Solr document regardless of their current values.",
      "items": {
        "type": "object",
        "required": ["fields"],
        "properties": {
          "fields": {
            "type": "string",
            "title": "Field",
            "description": "Specifies the field name to delete from the Solr document during the partial update. Supports exact field names such as `category` and wildcard patterns such as `meta_*` for batch deletions. Each entry removes the matched field from the document."
          }
        }
      }
    },
    "positionalUpdates": {
      "type": "array",
      "title": "Positional Updates",
      "description": "Defines atomic updates that set or add values at a specific position within a multi-valued Solr field. Each entry specifies a position (`first`, `last`, or a numeric index), an update type, and a comma-separated list of field-value pairs. Use this for ordered multi-valued fields where position matters.",
      "hints": ["advanced"],
      "items": {
        "type": "object",
        "required": ["position", "fieldsAndValues"],
        "properties": {
          "positionalUpdateType": {
            "type": "string",
            "title": "Update Type",
            "description": "Specifies whether the positional operation sets a value at the target position, replacing the existing value, or adds a new value at that position. Use `set` to replace and `add` to insert. The operation applies to the field-value pairs listed in the `fieldsAndValues` entry.",
            "enum": ["set", "add"],
            "default": "set"
          },
          "position": {
            "type": "string",
            "title": "Position",
            "description": "Specifies the position within the multi-valued field where the update is applied. Use `first` or `last` for relative positions, or a numeric zero-based index for an exact position. An out-of-range index causes the update to be rejected by Solr."
          },
          "fieldsAndValues": {
            "type": "string",
            "title": "Fields and Values",
            "description": "Specifies the comma-separated list of `field:value` pairs to update at the configured position. Use a backslash to escape literal commas within field values. Each pair applies the positional update to the named field at the specified index."
          }
        }
      }
    },
    "positionalRemovals": {
      "type": "array",
      "title": "Positional Removals",
      "description": "Defines operations that remove values at a specific position within a multi-valued Solr field. Each entry specifies a position and a list of field names whose values at that position are deleted. Use this for ordered multi-valued fields where position-based removal is needed.",
      "hints": ["advanced"],
      "items": {
        "type": "object",
        "required": ["position", "fields"],
        "properties": {
          "position": {
            "type": "string",
            "title": "Position",
            "description": "Specifies the position within the multi-valued field from which the value is removed. Use `first` or `last` for relative positions, or a numeric zero-based index for an exact position. An out-of-range index causes the removal to be rejected by Solr."
          },
          "fields": {
            "type": "string",
            "title": "Fields List",
            "description": "Specifies the comma-separated list of field names whose values at the configured position are removed. Each named field has its positional value deleted from the Solr document. The field must be a multi-valued type in the Solr schema."
          }
        }
      }
    },
    "customRouteFieldName": {
      "type": "string",
      "title": "Custom Route Field Name",
      "description": "Specifies the field name used for custom shard routing when the Solr collection is configured with a `router.field`. The value of this field on the pipeline document is transferred to the partial update Solr document to ensure the update is routed to the correct shard. Leave empty when not using custom shard routing."
    },
    "allowReservedFields": {
      "type": "boolean",
      "title": "Allow reserved fields",
      "description": "Controls whether Fusion reserved fields such as `_lw_*` on the pipeline document are included in the partial update even when not explicitly configured in the stage. When `true`, all reserved fields are passed through to Solr. Enable only when reserved fields carry meaningful update data.",
      "default": false,
      "hints": ["advanced"]
    }
  },
  "category": "Indexing",
  "categoryPriority": 5,
  "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/5/fusion/reference/config-ref/pipeline-stages/index-stages/solr-partial-update-indexer-stage

[mintlify link]: https://doc.lucidworks.com/docs/5/fusion/reference/config-ref/pipeline-stages/index-stages/solr-partial-update-indexer-stage

[old doc.lw link]: https://doc.lucidworks.com/fusion/5.9/233

The Solr Partial Update Indexer Stage updates of one or more fields of an existing Solr document in a collection managed by Fusion.
It provides an alternative to the Solr Indexer stage.

When a data feed consists of an ongoing flow of messages about known documents in a collection,
such as item price, inventory counts, or weather conditions at a location, this stage
provides fast indexing throughput and can be configured to enforce data atomicity to guarantee
that the index always reflects the most recent update.

This stage is configured with a set of update directives based on Solr’s
[atomic updates](https://cwiki.apache.org/confluence/display/solr/Updating+Parts+of+Documents).
At run time, it creates a Solr update by applying these directive to the data from a Fusion PipelineDocument object
and then submits this update to Solr’s update handler.

<Note>
  Solr’s atomic update functionality requires that the schema for a collection is configured
  so that all fields have the attribute stored="true",
  excepting fields which are  destinations which must be configured as stored="false".
</Note>

<LwTemplate />

## Example Stage Specification

*Configuration for a Partial Updater Stage in JSON:*

```json wrap  theme={"dark"}
{ "type" : "solr-partial-update-index",
  "enforceSchema" : false,
  "solrDocIdFieldName" : "id",
  "solrDocIdFieldValue" : "<doc.id>",
  "updatedFields" : [
    { "updateType" : "set", "fieldName" : "statusValue", "values" : "<doc.statusValue>" },
    { "updateType" : "set", "fieldName" : "lastCommunicationTime", "values" : "<doc.lastCommunicationTime>" }
  ],
  "concurrencyControlEnabled" : true,
  "skip" : false,
  "label" : "solr-partial-update-index",
  }
```

The expression \<doc.X> will evaluate to the contents of the current PipelineDocument’s field named "X".

## Types of Update Operations

The set of update operations are based on operations supported by Solr. They are:

* 'add' - add a new value or values to an existing Solr document field, or add a new field and value(s).
* 'set' - change the value or values in an existing Solr document field.
* 'remove' - remove all occurrences of the value or values from an existing Solr document field.
* 'removeregex' - remove all occurrences of the values which match the regex or list of regexes from an existing Solr document field.
* 'increment' - increment the numeric value of existing Solr document field by a specific amount.
* 'decrement' - decrement the numeric value of existing Solr document field by a specific amount.

In addition, this stage introduces experimental "Positional" operations which can be used to add, set or remove exactly one element
of a field which takes a list of values (i.e, a multi-valued field).

* 'positionalUpdates' - used to add or set the value at specific position.
* 'positionalRemoves' - used to delete an element at a specific position.

When a collection contains two or more multi-value fields which are maintained in parallel
so that taken together, they act like a table stored column by column,
a positional update operation updates several data cells across one row of the table.
To maintain this kind of column-oriented table, the positional delete directive
must specify all the fields in the document which logically comprise the table.

## Document Identifier Field

A Fusion collection is a Solr collection managed by Fusion.
Underlyingly, a Solr document is a list of named, typed fields.
The Solr [unique key field](https://wiki.apache.org/solr/UniqueKey) stores a string which is the unique identifier for that document.
There is at most one UniqueKey field per document, which is defined in the Solr schema.
The UniqueKey field value is required.
For collections created via Fusion, the UniqueKey field is named "id".
Other document fields may also store string values which can be used as a unique identifier.

Solr uses the UniqueKey field to find the document to be updated.
If the data feed information contains a document identifier which is different
than the identifier value stored in the UniqueKey field,
then this stage must do a Solr lookup to find the UniqueKey value.

## Optimistic Concurrency

Solr’s [Optimistic Concurrency](http://yonik.com/solr/optimistic-concurrency/)
is a mechanism which checks whether or not a document has changed
between the point at which an update request was submitted and the point at which the request is processed.
Solr documents have an internal field named "*version*" which is updated whenever there is any change made
to any of the other fields in that document.
When optimistic concurrency control is on, update requests will be discarded if the current version
of the document has changed since that request was made.
This guarantees that the document will always reflect the most recent update.
However, this require an additional Solr lookup to get the current document version number,
which is submitted as part of the update request.

## Performance Considerations

In order to send a single update request to Solr, without preliminary lookup requests:

* The document identifier field should match the Solr collection’s UniqueKey identifier field.
* Optimistic Concurrency should be turned off.
* Positional updates are experimental and potentially expensive, since all the values for all fields
  being updated must be fetched into memory in order to perform positional operations.

## Solr Date Formats

```json theme={"dark"}
"yyyy-MM-dd'T'HH:mm:ss'Z'", // Solr format without milliseconds
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", // standard Solr format, with literal "Z" at the end
"yyyy-MM-dd'T'HH:mm:ss.SS'Z'", // standard Solr format, with literal "Z" at the end
"yyyy-MM-dd'T'HH:mm:ss.S'Z'" // standard Solr format, with literal "Z" at the end
```

See [https://cwiki.apache.org/confluence/display/solr/Working+with+Dates](https://cwiki.apache.org/confluence/display/solr/Working+with+Dates)

## 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} />
