This migration replaces the official Elasticsearch Python client library with a lightweight HTTP-based client that communicates directly with the Elasticsearch 7.x REST API. This approach allows you to:
- Keep your ES 7.x server running without needing to upgrade
- Remove the elasticsearch-py dependency which was causing package conflicts
- Maintain 100% backward compatibility with existing code - no changes needed to queries!
resources/es_http_client.py- A drop-in replacement for the Elasticsearch client- Implements the same interface as
elasticsearch.Elasticsearch - Uses the
requestslibrary to make HTTP calls to ES REST API - Supports all methods used in your codebase:
search()- Execute search queriesmsearch()- Multi-searchmget()- Multi-get documentsupdate()- Update documentsbulk()- Bulk operationsdelete_by_query()- Delete by queryindex(),get(),delete()- Basic document operations
- Implements the same interface as
-
resources/globals.py- Changed import:
from elasticsearch import Elasticsearch→from resources.es_http_client import ElasticsearchHTTPClient - Changed instantiation:
Elasticsearch(...)→ElasticsearchHTTPClient(...)
- Changed import:
-
requirements.txt- Removed:
elasticsearch==9.2.1 - Added:
requests==2.31.0(for HTTP communication)
- Removed:
All your query files remain completely unchanged:
queries/es_stats.pyqueries/es_user.pyqueries/es.pyqueries/variants.py
The API usage Globals.es.search(), Globals.es.bulk(), etc. works exactly as before!
The ElasticsearchHTTPClient class:
-
Parses the connection URL to extract host, port, and credentials
-
Maintains a requests.Session for connection pooling and authentication
-
Translates ES Python client calls to HTTP requests:
client.search(index="my-index", body={...})→POST /my-index/_searchclient.bulk(body=[...])→POST /_bulk(with NDJSON formatting)client.update(index="idx", id="1", doc={...})→POST /idx/_update/1
-
Returns the same JSON structure as the official client, ensuring compatibility
-
Update dependencies:
cd /Users/zv21942/Projects/opengwas/opengwas-api-internal/opengwas-api/app pip install -r requirements.txt -
Verify the changes:
python test_es_http_client.py
Run the test script to verify the HTTP client initialization:
python test_es_http_client.pyYour existing integration tests should work without modification:
pytest apis/tests/✅ No code changes needed in your query logic
✅ Works with ES 7.x server - no server upgrade required
✅ Removes package conflicts - no elasticsearch-py dependency
✅ Lightweight - only uses requests library
✅ Easy to debug - standard HTTP requests you can inspect
✅ Future-proof - easy to maintain and extend
All methods maintain the same signature as the official client:
# Search
Globals.es.search(
index="my-index",
body={"query": {...}},
request_timeout=120,
ignore_unavailable=True,
routing="some-value"
)
# Bulk operations
Globals.es.bulk(
body=[...],
index="my-index",
pipeline="geoip",
refresh="wait_for",
request_timeout=120
)
# Update with upsert
Globals.es.update(
index="my-index",
id="doc-id",
doc={"field": "value"},
doc_as_upsert=True,
request_timeout=60
)| ES Client Method | HTTP Method | Endpoint |
|---|---|---|
search() |
POST | /{index}/_search |
msearch() |
POST | /_msearch |
mget() |
POST | /{index}/_mget |
update() |
POST | /{index}/_update/{id} |
bulk() |
POST | /_bulk |
delete_by_query() |
POST | /{index}/_delete_by_query |
index() |
PUT/POST | /{index}/_doc/{id} |
get() |
GET | /{index}/_doc/{id} |
delete() |
DELETE | /{index}/_doc/{id} |
Methods like bulk() and msearch() that require newline-delimited JSON (NDJSON) format are automatically handled. The client converts Python lists to NDJSON format:
# You pass in a list
body = [
{"index": {"_index": "test"}},
{"field": "value"}
]
# Client converts to NDJSON
# {"index": {"_index": "test"}}
# {"field": "value"}If you see connection errors, verify:
- ES server is running and accessible
- Credentials in
vault/app_conf.jsonare correct - Network connectivity from the Flask app to ES server
The client supports HTTP Basic Auth via the connection URL:
ElasticsearchHTTPClient(["http://username:password@host:port"])All methods accept a request_timeout parameter (in seconds):
Globals.es.search(
index="large-index",
body={...},
request_timeout=300 # 5 minutes
)Enable debug logging to see HTTP requests:
import logging
logging.basicConfig(level=logging.DEBUG)- Create
ElasticsearchHTTPClientclass - Update imports in
globals.py - Update ES client instantiation
- Remove
elasticsearchfrom requirements.txt - Add
requeststo requirements.txt - Create test script
- Run test script
- Run existing integration tests
- Deploy to development environment
- Monitor logs for any issues
- Deploy to production
If issues arise, rollback is simple:
-
Revert changes in
globals.py:from elasticsearch import Elasticsearch es = Elasticsearch([...])
-
Revert
requirements.txt:# Remove: requests==2.31.0 # Add: elasticsearch==9.2.1 -
Run:
pip install -r requirements.txt
For questions or issues:
- Check the
ElasticsearchHTTPClientdocstrings - Review ES 7.x REST API documentation
- Check logs for HTTP request/response details