diff --git a/Dockerfile b/Dockerfile index b7d15c6..2daef1c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,9 @@ RUN apk update && \ apk add g++ make #upgrade openssl \ -#RUN apk add openssl=3.1.4-r4 + +RUN apk add openssl=3.1.4-r5 + RUN pip install --upgrade pip # Create a non-root user. diff --git a/docker-compose.yaml b/docker-compose.yaml index 8e8d27d..664524d 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -40,11 +40,9 @@ services: REDIS_PASSWORD: "$REDIS_PASSWORD" FLASK_ENV: "development" PYTHONUNBUFFERED: "TRUE" - entrypoint: [ "gunicorn", - "--workers=$API_WORKERS", "--name=dug", - "--bind=0.0.0.0:$API_PORT", "--timeout=$API_TIMEOUT", - "--log-level=DEBUG", "--enable-stdio-inheritance", - "-k", "uvicorn.workers.UvicornWorker", "--reload", "dug.server:APP" ] + entrypoint: [ "uvicorn", + "--host", "0.0.0.0" , "--port" , "$API_PORT", + "--log-level=debug", "--reload-dir", "/home/dug/dug/", "--reload", "dug.server:APP" ] volumes: - ./src:/home/dug/dug/ ports: diff --git a/src/dug/core/async_search.py b/src/dug/core/async_search.py index b39e6a9..7308354 100644 --- a/src/dug/core/async_search.py +++ b/src/dug/core/async_search.py @@ -690,3 +690,93 @@ async def search_kg(self, unique_id, query, offset=0, size=None, ) search_results.update({'total_items': total_items['count']}) return search_results + + async def search_study(self, study_id=None, study_name=None, offset=0, size=None): + """ + Search for studies by unique_id (ID or name) and/or study_name. + """ + # Define the base query + # Define the base query + query_body = { + "bool": { + "must": [] + } + } + + # Add conditions based on user input + if study_id: + query_body["bool"]["must"].append({ + "match": {"collection_id": study_id} + }) + + if study_name: + query_body["bool"]["must"].append({ + "match": {"collection_name": study_name} + }) + + print("query_body",query_body) + body = {'query': query_body} + total_items = await self.es.count(body=body, index="variables_index") + search_results = await self.es.search( + index="variables_index", + body=body, + filter_path=['hits.hits._id', 'hits.hits._type', 'hits.hits._source'], + from_=offset, + size=size + ) + search_results.update({'total_items': total_items['count']}) + return search_results + + + async def search_program(self, program_name=None, offset=0, size=None): + """ + Search for studies by unique_id (ID or name) and/or study_name. + """ + + query_body = { + "query": { + "bool": { + "must": [] + } + }, + "aggs": { + "unique_collection_ids": { + "terms": { + "field": "collection_id.keyword" + } + } + } + } + + # specify the fields to be returned + query_body["_source"] = ["collection_id", "collection_name", "collection_action"] + + # search for program_name based on uses input + if program_name: + query_body["query"]["bool"]["must"].append({ + "match": {"data_type": program_name} + }) + + print("query_body", query_body) + + # Prepare the query body for execution + body = query_body + #print(body) + + # Execute the search query + search_results = await self.es.search( + index="variables_index", + body=body, + filter_path=['hits.hits._id', 'hits.hits._type', 'hits.hits._source', 'aggregations.unique_collection_ids.buckets'], + from_=offset, + size=size + ) + + # The unique collection_ids will be in the 'aggregations' field of the response + unique_collection_ids = search_results['aggregations']['unique_collection_ids']['buckets'] + + #print("Unique collection_ids:", unique_collection_ids) + + + #print(search_results) + return search_results \ No newline at end of file diff --git a/src/dug/server.py b/src/dug/server.py index f7a8466..7d3fc25 100644 --- a/src/dug/server.py +++ b/src/dug/server.py @@ -8,12 +8,13 @@ from dug.core.async_search import Search from pydantic import BaseModel import asyncio +from typing import Optional logger = logging.getLogger (__name__) APP = FastAPI( title="Dug Search API", - root_path=os.environ.get("ROOT_PATH", "/"), + # root_path=os.environ.get("ROOT_PATH", "/"), ) APP.add_middleware( @@ -49,6 +50,19 @@ class SearchKgQuery(BaseModel): index: str = "kg_index" size:int = 100 +class SearchStudyQuery(BaseModel): + #query: str + study_id: Optional[str] = None + study_name: Optional[str] = None + #index: str = "variables_index" + size:int = 100 +class SearchProgramQuery(BaseModel): + #query: str + program_id: Optional[str] = None + program_name: Optional[str] = None + #index: str = "variables_index" + size:int = 100 + search = Search(Config.from_env()) @APP.on_event("shutdown") @@ -107,5 +121,36 @@ async def search_var(search_query: SearchVariablesQuery): } + +@APP.get('/search_study') +async def search_study(study_id: Optional[str] = None, study_name: Optional[str] = None): + """ + Search for studies by unique_id (ID or name) and/or study_name. + """ + result = await search.search_study(study_id=study_id, study_name=study_name) + return { + "message": "Search result", + # Although index in provided by the query we will keep it around for backward compatibility, but + # search concepts should always search against "variables_index" + "result": result, + "status": "success" + } + + +@APP.get('/search_program') +async def search_program( program_name: Optional[str] = None): + """ + Search for studies by unique_id (ID or name) and/or study_name. + """ + result = await search.search_program(program_name=program_name) + return { + "message": "Search result", + # Although index in provided by the query we will keep it around for backward compatibility, but + # search concepts should always search against "variables_index" + "result": result, + "status": "success" + } + + if __name__ == '__main__': uvicorn.run(APP)