HoldingsCsvTransformer#

Transform delimited (CSV/TSV) data into FOLIO Holdings records. Use this when your holdings data comes from item-level exports or systems without MFHD support.

When to Use This Task#

  • Migrating from systems that export item-level data (Sierra, III, etc.)

  • Creating holdings records from item data when no separate holdings exist

  • Merging multiple items into consolidated holdings records

  • Combining with previously generated MFHD-based holdings

Configuration#

{
    "name": "transform_csv_holdings",
    "migrationTaskType": "HoldingsCsvTransformer",
    "holdingsMapFileName": "holdings_mapping.json",
    "locationMapFileName": "locations.tsv",
    "defaultCallNumberTypeName": "Library of Congress classification",
    "fallbackHoldingsTypeId": "03c9c400-b9e3-4a07-ac0e-05ab470233ed",
    "holdingsMergeCriteria": ["instanceId", "permanentLocationId", "callNumber"],
    "files": [
        {
            "file_name": "items.tsv"
        }
    ]
}

Parameters#

Parameter

Type

Required

Description

name

string

Yes

The name of this task.

migrationTaskType

string

Yes

Must be "HoldingsCsvTransformer"

holdingsMapFileName

string

Yes

JSON mapping file for holdings fields

locationMapFileName

string

Yes

TSV file mapping legacy locations to FOLIO codes

defaultCallNumberTypeName

string

Yes

FOLIO call number type name for fallback

fallbackHoldingsTypeId

string

Yes

UUID of fallback holdings type

holdingsMergeCriteria

array

No

Fields used to group items into holdings. Default: ["instanceId", "permanentLocationId", "callNumber"]

callNumberTypeMapFileName

string

No

TSV file mapping call number types

holdingsTypeMapFileName

string

No

TSV file mapping holdings types

statisticalCodeMapFileName

string

No

TSV file mapping statistical codes

holdingsTypeUuidForBoundwiths

string

No

UUID of holdings type for boundwith holdings (enables automatic boundwith handling)

previouslyGeneratedHoldingsFiles

array

No

List of previous holdings result files to avoid duplicates

files

array

Yes

List of source data files to process

Source Data Requirements#

  • Location: Place CSV/TSV files in iterations/<iteration>/source_data/items/

  • Format: Tab-separated (TSV) or comma-separated (CSV) with header row

  • Prerequisite: Run BibsTransformer first to create instance_id_map

Holdings Mapping File#

Create a JSON mapping file in mapping_files/:

{
    "data": [
        {
            "folio_field": "legacyIdentifier",
            "legacy_field": "ITEM_ID",
            "description": "Legacy identifier for deterministic UUID"
        },
        {
            "folio_field": "instanceId",
            "legacy_field": "BIB_ID",
            "description": "Links to the parent instance"
        },
        {
            "folio_field": "permanentLocationId",
            "legacy_field": "LOCATION_CODE",
            "description": "Mapped via locationMapFileName"
        },
        {
            "folio_field": "callNumber",
            "legacy_field": "CALL_NUMBER",
            "description": "Call number for the holdings"
        },
        {
            "folio_field": "callNumberTypeId",
            "legacy_field": "CN_TYPE",
            "description": "Mapped via callNumberTypeMapFileName"
        }
    ]
}

Important

The legacyIdentifier field is required and must map to a unique value in your source data. This value is used to generate deterministic UUIDs.

Reference Data Mapping Files#

Reference data mapping files connect values from your legacy data to FOLIO reference data. See Reference Data Mapping for detailed documentation on how these files work.

Mapping File

FOLIO Column

Maps To

locationMapFileName

folio_code

Location code

callNumberTypeMapFileName

folio_name

Call number type name

Holdings Merge Criteria#

The holdingsMergeCriteria parameter determines how multiple rows in the source data are consolidated into single holdings records.

Example: With ["instanceId", "permanentLocationId", "callNumber"]:

  • Items with the same bib ID, location, and call number → one holdings record

  • Items with different locations → separate holdings records

Common configurations:

Strategy

Merge Criteria

Result

One holdings per item

["legacyIdentifier"]

1:1 item to holdings

Group by location

["instanceId", "permanentLocationId"]

Holdings per location

Group by location + call number

["instanceId", "permanentLocationId", "callNumber"]

Holdings per location + call number

Output Files#

Files are created in iterations/<iteration>/results/:

File

Description

folio_holdings_<task_name>.json

FOLIO Holdings records

holdings_id_map.json

Legacy ID to FOLIO UUID mapping (used by ItemsTransformer)

extradata_<task_name>.extradata

Extra data including boundwith parts (when applicable)

Examples#

Basic Holdings from Items#

{
    "name": "transform_csv_holdings",
    "migrationTaskType": "HoldingsCsvTransformer",
    "holdingsMapFileName": "holdings_mapping.json",
    "locationMapFileName": "locations.tsv",
    "defaultCallNumberTypeName": "Library of Congress classification",
    "fallbackHoldingsTypeId": "03c9c400-b9e3-4a07-ac0e-05ab470233ed",
    "files": [
        {
            "file_name": "items.tsv"
        }
    ]
}

Combining with MFHD Holdings#

When you have both MFHD-derived holdings and need additional holdings from items:

{
    "name": "transform_csv_holdings",
    "migrationTaskType": "HoldingsCsvTransformer",
    "holdingsMapFileName": "holdings_mapping.json",
    "locationMapFileName": "locations.tsv",
    "defaultCallNumberTypeName": "Library of Congress classification",
    "fallbackHoldingsTypeId": "03c9c400-b9e3-4a07-ac0e-05ab470233ed",
    "previouslyGeneratedHoldingsFiles": [
        "folio_holdings_transform_mfhd.json"
    ],
    "files": [
        {
            "file_name": "items_without_mfhd.tsv"
        }
    ]
}

With Statistical Codes#

{
    "name": "transform_csv_holdings",
    "migrationTaskType": "HoldingsCsvTransformer",
    "holdingsMapFileName": "holdings_mapping.json",
    "locationMapFileName": "locations.tsv",
    "defaultCallNumberTypeName": "Library of Congress classification",
    "fallbackHoldingsTypeId": "03c9c400-b9e3-4a07-ac0e-05ab470233ed",
    "statisticalCodeMapFileName": "stat_codes.tsv",
    "files": [
        {
            "file_name": "items.tsv",
            "statistical_code": "migrated"
        }
    ]
}

Boundwith Handling#

The HoldingsCsvTransformer handles boundwith relationships automatically when an item maps to multiple instance IDs (i.e., the source data row resolves to more than one bib). You may see these referred to as “Sierra-style boundwiths” or “Millenium-style boundwiths”. This differs from the MFHD-based approach used by HoldingsMarcTransformer, which requires a separate boundwith relationship file.

How It Works#

When a source data row maps to multiple instances:

  1. A primary holdings record is created for the first instance.

  2. Additional holdings records are generated for each subsequent instance, with their holdingsTypeId set to holdingsTypeUuidForBoundwiths.

  3. A boundwithPart record is written to the extradata file linking the item to each holdings record.

  4. Boundwith holdings are excluded from the normal holdingsMergeCriteria merge process. Instead, they are de-duplicated using their own composite key (instance ID + location + call number + the full set of bound instance IDs). If a subsequent row produces the same boundwith key, it is merged into the existing boundwith holdings record rather than creating a duplicate.

Configuration#

To enable boundwith handling, set the holdingsTypeUuidForBoundwiths parameter to the UUID of the appropriate holdings type from your FOLIO tenant (found under Settings → Inventory → Holdings types):

{
    "name": "transform_csv_holdings",
    "migrationTaskType": "HoldingsCsvTransformer",
    "holdingsMapFileName": "holdings_mapping.json",
    "locationMapFileName": "locations.tsv",
    "defaultCallNumberTypeName": "Library of Congress classification",
    "fallbackHoldingsTypeId": "03c9c400-b9e3-4a07-ac0e-05ab470233ed",
    "holdingsTypeUuidForBoundwiths": "1b6c62cf-034c-4972-ac80-fa595a9bfbde",
    "files": [
        {
            "file_name": "items.tsv"
        }
    ]
}

Note

No boundwithFlavor or boundwithRelationshipFilePath is needed for the CSV transformer. Boundwith detection is automatic based on multiple instance IDs in the source data.

Running the Task#

folio-migration-tools mapping_files/config.json transform_csv_holdings --base_folder ./

Next Steps#

  1. Transform Items: Use ItemsTransformer on the same source files

  2. Post Holdings: Use InventoryBatchPoster or BatchPoster

See Also#