Skip to main content
The armor migrate-from great-expectations command converts a Great Expectations project on disk into an ODCS YAML document. Pipe the output straight into armor contract apply to port your expectation suites into AnomalyArmor validity rules.
Great Expectations suites translated to ODCS YAML and applied to AnomalyArmor
armor migrate-from great-expectations ./great_expectations/ | armor contract apply --asset <asset-uuid> -f -
The adapter is file-based: it reads great_expectations.yml and the expectation-suite JSON files directly. You do not need the great_expectations pip package installed to run the migration.

What gets mapped

Native ODCS fields

These expectations land on the standard ODCS property surface so they round-trip through any ODCS tool, not just AnomalyArmor.
Great ExpectationsODCS field
expect_column_values_to_not_be_nullschema[].properties[].required = true
expect_column_values_to_be_uniqueschema[].properties[].unique = true

AnomalyArmor validity rules

These expectations map to validity rules under customProperties.anomalyarmor.validity.
Great ExpectationsAnomalyArmor rule_typeNotes
expect_column_values_to_be_in_setallowed_valuesvalue_setvalues
expect_column_values_to_match_regexregex_matchregexpattern
expect_column_values_to_be_betweenrange_boundsmin_value / max_valuemin / max
expect_column_value_lengths_to_be_betweenlength_boundsmin_value / max_valuemin_length / max_length
expect_column_values_to_match_strftime_formatformat%Y-%m-%diso_date; %Y-%m-%dT%H:%M:%Siso_datetime

Unmapped expectations

Anything not in the table above surfaces as a warning in the command summary rather than being silently dropped. The common ones that land in warnings today:
  • expect_column_pair_* and expect_multicolumn_* (cross-column logic)
  • expect_column_kl_divergence_* and other statistical distribution checks
  • Custom expectations (dbt_expectations.*, user-defined subclasses)
  • expect_column_values_to_be_of_type (type-system translation is its own problem)
  • Table-scoped expectations like expect_table_row_count_to_be_between (row-count monitoring needs its own follow-up wiring)
For expectations without a native AnomalyArmor equivalent, author a custom SQL check after the migration rather than trying to force them through the validity-rule shape.

Project shapes supported

The adapter auto-detects whether you have a legacy (pre-v1) project or a v1 project using Fluent Datasources.
  • Legacy projects. Expectation suites live as JSON under {project_root}/expectations/*.json. The adapter walks that tree directly.
  • v1 Fluent Datasources projects. The adapter reads the fluent_datasources block from great_expectations.yml to pull per-column type hints into schema[].properties[].physicalType. Suites still need to live under expectations/. If your project uses a non-default suite store (a database, S3, etc.), export the suites to disk before migrating:
    import great_expectations as gx
    context = gx.get_context()
    for name in context.list_expectation_suite_names():
        suite = context.get_expectation_suite(name)
        with open(f"expectations/{name}.json", "w") as f:
            f.write(suite.to_json_dict())
    
The adapter emits a warning with a pointer to this snippet when it detects a project shape it cannot walk via files alone.

Suite-to-table resolution

Great Expectations organizes suites by suite name, not by table. AnomalyArmor scopes every validity rule to a table_path. The adapter resolves the mapping in this order:
  1. meta.anomalyarmor.table_path inside the suite JSON (the explicit escape hatch).
  2. The suite’s expectation_suite_name field (GE’s canonical source of truth for the suite’s logical name).
  3. The suite filename without the .json extension (final fallback).
If your suite names already match your target table names, you don’t need to touch anything. When they don’t, set the escape hatch inside the suite:
{
  "expectation_suite_name": "orders_v2_suite",
  "meta": {
    "anomalyarmor": {
      "table_path": "warehouse.public.orders"
    }
  },
  "expectations": [...]
}

Worked example

Given this legacy project layout:
great_expectations/
├── great_expectations.yml
└── expectations/
    └── orders.json
…where orders.json contains:
{
  "expectation_suite_name": "public.orders",
  "expectations": [
    {
      "expectation_type": "expect_column_values_to_not_be_null",
      "kwargs": {"column": "order_id"}
    },
    {
      "expectation_type": "expect_column_values_to_be_unique",
      "kwargs": {"column": "order_id"}
    },
    {
      "expectation_type": "expect_column_values_to_be_in_set",
      "kwargs": {"column": "status", "value_set": ["pending", "shipped", "delivered"]}
    },
    {
      "expectation_type": "expect_column_values_to_match_regex",
      "kwargs": {"column": "email", "regex": "^[^@]+@[^@]+\\.[^@]+$"}
    }
  ]
}
Running:
armor migrate-from great-expectations ./great_expectations/ --output contract.yaml
…produces:
apiVersion: v3.1.0
kind: DataContract
id: <stable UUID5 derived from project path>
name: great_expectations
version: 1.0.0
status: active
schema:
  - name: public.orders
    properties:
      - name: order_id
        required: true
        unique: true
      - name: status
      - name: email
customProperties:
  - property: anomalyarmor
    value:
      validity:
        - table_path: public.orders
          column_name: status
          rule_type: allowed_values
          rule_config:
            values: [pending, shipped, delivered]
          severity: error
          treat_null_as_valid: true
          check_interval: daily
        - table_path: public.orders
          column_name: email
          rule_type: regex_match
          rule_config:
            pattern: "^[^@]+@[^@]+\\.[^@]+$"
          severity: error
          treat_null_as_valid: true
          check_interval: daily
Then validate and apply:
armor contract validate -f contract.yaml
armor contract plan --asset <asset-uuid> -f contract.yaml
armor contract apply --asset <asset-uuid> -f contract.yaml
Re-running the migration is safe: the command produces a deterministic id seeded from the project path, so re-applying the same contract will dedupe rather than create duplicates.

Common Questions

How do I migrate from Great Expectations to AnomalyArmor?

Run armor migrate-from great-expectations ./great_expectations/ to translate your expectation suites into ODCS YAML, then pipe it into armor contract apply --asset <uuid> -f -. The adapter is file-based and reads suite JSON directly, so you don’t need the great_expectations pip package installed on the migration host.

Which Great Expectations expectations are supported?

not_null and unique map to native ODCS required / unique. Six more map to AnomalyArmor validity rules: be_in_set, match_regex, be_between, value_lengths_between, and match_strftime_format. See the full mapping tables for exact field translations.

What happens to expectations the adapter can’t map?

They surface as warnings in the CLI summary, never silent drops. Common unsupported cases are expect_column_pair_*, expect_multicolumn_*, KL-divergence / distribution checks, custom subclasses, and type checks. Re-author those as custom SQL checks after the migration rather than forcing them through the validity-rule shape.

Does the adapter work with Great Expectations v1 Fluent Datasources?

Yes. The adapter auto-detects v1 projects and reads the fluent_datasources block from great_expectations.yml to pull per-column type hints into physicalType. Suites still need to live on disk under expectations/, so if you use a non-default store (database, S3), export them to disk first. See Project shapes supported.

How does AnomalyArmor know which table an expectation suite belongs to?

The adapter resolves the suite-to-table mapping in this order: meta.anomalyarmor.table_path inside the suite JSON, then the suite’s expectation_suite_name field, then the filename. Use the meta.anomalyarmor.table_path escape hatch when suite names don’t match your target table paths. See Suite-to-table resolution.

Is it safe to re-run the migration?

Yes. The command produces a deterministic contract id seeded from the project path, so re-applying the same contract deduplicates instead of creating duplicates. Combine with armor contract plan first to preview the diff before apply.

See also