| name | es-ingest |
| description | [DEPRECATED] Legacy ingest skill in this repository. Use elasticsearch-file-ingest from https://github.com/elastic/agent-skills instead. |
| metadata | {"version":"0.0.2","author":"walterra","repository":"https://github.com/walterra/agent-tools"} |
ES Ingest
[!WARNING]
Deprecated: This skill is deprecated in walterra/agent-tools.
Use the official Elastic replacement: elasticsearch-file-ingest.
Stream-based ingestion and transformation of large data files into Elasticsearch. Built on node-es-transformer.
Features
- ✅ Stream-based: Handle very large files (20-30 GB tested) without running out of memory
- ✅ High throughput: Up to 20k documents/second on commodity hardware
- ✅ Cross-version: Seamlessly migrate between ES 8.x and 9.x
- ✅ Formats: NDJSON, CSV, Parquet, Arrow IPC
- ✅ Transformations: Apply custom JavaScript transforms during ingestion
- ✅ Reindexing: Copy and transform existing indices
- ✅ Wildcards: Ingest multiple files matching a pattern (e.g.,
logs/*.json)
- ✅ Document splitting: Transform one source document into multiple targets
Prerequisites
- Elasticsearch 8.x or 9.x accessible (local or remote)
- Node.js 22+ installed
Setup
Before first use, install dependencies:
cd {baseDir} && npm install
Basic Usage
Ingest a JSON file
{baseDir}/scripts/ingest.js --file data.json --target my-index
Stream NDJSON/CSV via stdin
cat data.ndjson | {baseDir}/scripts/ingest.js --stdin --target my-index
cat data.csv | {baseDir}/scripts/ingest.js --stdin --source-format csv --target my-index
Ingest CSV directly
{baseDir}/scripts/ingest.js --file users.csv --source-format csv --target users
Ingest Parquet directly
{baseDir}/scripts/ingest.js --file users.parquet --source-format parquet --target users
Ingest Arrow IPC directly
{baseDir}/scripts/ingest.js --file users.arrow --source-format arrow --target users
CSV/Parquet/Arrow support requires node-es-transformer >= 1.2.0.
Ingest CSV with parser options
{baseDir}/scripts/ingest.js --file users.csv --source-format csv --csv-options csv-options.json --target users
Infer mappings/pipeline from CSV
{baseDir}/scripts/ingest.js --file users.csv --source-format csv --infer-mappings --target users
Infer mappings with options
{baseDir}/scripts/ingest.js --file users.csv --source-format csv --infer-mappings --infer-mappings-options infer-options.json --target users
Ingest with custom mappings
{baseDir}/scripts/ingest.js --file data.json --target my-index --mappings mappings.json
Ingest with transformation
{baseDir}/scripts/ingest.js --file data.json --target my-index --transform transform.js
Reindex from another index
{baseDir}/scripts/ingest.js --source-index old-index --target new-index
Cross-cluster reindex (ES 8.x → 9.x)
{baseDir}/scripts/ingest.js --source-index logs \
--node https://es8.example.com:9200 --api-key es8-key \
--target new-logs \
--target-node https://es9.example.com:9200 --target-api-key es9-key
Command Reference
Required Options
--target <index>
Source Options (choose one)
--file <path>
--source-index <name>
--stdin
Elasticsearch Connection
--node <url>
--api-key <key>
--username <user>
--password <pass>
Target Connection (for cross-cluster)
--target-node <url>
--target-api-key <key>
--target-username <user>
--target-password <pass>
Index Configuration
--mappings <file.json>
--infer-mappings
--infer-mappings-options <file>
--delete-index
--pipeline <name>
Processing
--transform <file.js>
--query <file.json>
--source-format <fmt>
--csv-options <file>
--skip-header
Performance
--buffer-size <kb>
--search-size <n>
--total-docs <n>
--stall-warn-seconds <n>
--progress-mode <mode>
--debug-events
--quiet
Transform Functions
Transform functions let you modify documents during ingestion. Create a JavaScript file that exports a transform function:
Basic Transform (transform.js)
export default function transform(doc) {
return {
...doc,
full_name: `${doc.first_name} ${doc.last_name}`,
timestamp: new Date().toISOString(),
};
}
module.exports = function transform(doc) {
return {
...doc,
full_name: `${doc.first_name} ${doc.last_name}`,
};
};
Skip Documents
Return null or undefined to skip a document:
export default function transform(doc) {
if (!doc.email || !doc.email.includes('@')) {
return null;
}
return doc;
}
Split Documents
Return an array to create multiple target documents from one source:
export default function transform(doc) {
const hashtags = doc.text.match(/#\w+/g) || [];
return hashtags.map(tag => ({
hashtag: tag,
tweet_id: doc.id,
created_at: doc.created_at,
}));
}
Mappings
Auto-Copy Mappings (Reindexing)
When reindexing, mappings are automatically copied from the source index:
{baseDir}/scripts/ingest.js --source-index old-logs --target new-logs
Custom Mappings (mappings.json)
{
"properties": {
"@timestamp": { "type": "date" },
"message": { "type": "text" },
"user": {
"properties": {
"name": { "type": "keyword" },
"email": { "type": "keyword" }
}
}
}
}
{baseDir}/scripts/ingest.js --file data.json --target my-index --mappings mappings.json
Query Filters
Filter source documents during reindexing with a query file:
Query File (filter.json)
{
"range": {
"@timestamp": {
"gte": "2024-01-01",
"lt": "2024-02-01"
}
}
}
{baseDir}/scripts/ingest.js \
--source-index logs \
--target filtered-logs \
--query filter.json
Common Patterns
Pattern 1: Load CSV with custom mappings
cat > mappings.json << 'EOF'
{
"properties": {
"timestamp": { "type": "date" },
"user_id": { "type": "keyword" },
"action": { "type": "keyword" },
"value": { "type": "double" }
}
}
EOF
{baseDir}/scripts/ingest.js \
--file events.csv \
--target events \
--mappings mappings.json \
--skip-header
Pattern 2: Migrate ES 8.x → 9.x with transforms
cat > transform.js << 'EOF'
export default function transform(doc) {
// Update field names for ES 9.x compatibility
return {
...doc,
'@timestamp': doc.timestamp, // Rename field
user: {
id: doc.user_id,
name: doc.user_name,
},
};
}
EOF
{baseDir}/scripts/ingest.js \
--source-index logs \
--node https://es8-cluster.example.com:9200 \
--api-key $ES8_API_KEY \
--target logs-v2 \
--target-node https://es9-cluster.example.com:9200 \
--target-api-key $ES9_API_KEY \
--transform transform.js
Pattern 3: Reindex with filtering and deletion
cat > filter.json << 'EOF'
{
"bool": {
"must": [
{ "range": { "@timestamp": { "gte": "now-30d" } } }
],
"must_not": [
{ "term": { "status": "deleted" } }
]
}
}
EOF
{baseDir}/scripts/ingest.js \
--source-index logs-raw \
--target logs-filtered \
--query filter.json \
--delete-index
Pattern 4: Batch ingest multiple files
{baseDir}/scripts/ingest.js \
--file "logs/*.json" \
--target combined-logs \
--mappings mappings.json
Pattern 5: Document enrichment during ingestion
cat > enrich.js << 'EOF'
export default function transform(doc) {
return {
...doc,
enriched_at: new Date().toISOString(),
source: 'batch-import',
year: new Date(doc.timestamp).getFullYear(),
};
}
EOF
{baseDir}/scripts/ingest.js \
--file data.json \
--target enriched-data \
--transform enrich.js
Performance Tuning
For Large Files (>5GB)
{baseDir}/scripts/ingest.js \
--file huge-file.json \
--target my-index \
--buffer-size 10240
For Slow Networks
{baseDir}/scripts/ingest.js \
--source-index remote-logs \
--target local-logs \
--search-size 50
Quiet Mode (for scripts)
{baseDir}/scripts/ingest.js \
--file data.json \
--target my-index \
--quiet
When to Use
Use this skill when you need to:
- Load large files into Elasticsearch without memory issues
- Migrate indices between ES versions (8.x ↔ 9.x)
- Transform data during ingestion (enrich, split, filter)
- Reindex with modifications (rename fields, restructure documents)
- Batch process multiple files matching a pattern
- Cross-cluster replication with transformations
When NOT to Use
Consider alternatives for:
Troubleshooting
Connection refused
Elasticsearch is not running or URL is incorrect:
curl http://localhost:9200
curl -H "Authorization: ApiKey $API_KEY" https://es.example.com:9200
Out of memory errors
Reduce buffer size:
{baseDir}/scripts/ingest.js --file data.json --target my-index --buffer-size 2048
Transform function not loading
Ensure the transform file exports correctly:
export default function transform(doc) { }
module.exports = function transform(doc) { }
function transform(doc) { }
Mapping conflicts
Delete and recreate the index:
{baseDir}/scripts/ingest.js \
--file data.json \
--target my-index \
--mappings mappings.json \
--delete-index
References