Commit 12617ac9 authored by fabian serin's avatar fabian serin
Browse files

work in progress search welllog by curve

parent bea93ba0
Pipeline #60551 passed with stages
in 7 minutes and 47 seconds
......@@ -126,14 +126,22 @@ async def query_request_with_specific_attribute(query_type: str, attribute: str,
query_request=query_request)
def update_query_with_names_based_search(names: str = None, user_query: str = None, name_field = "data.FacilityName") -> str:
def update_query_with_names_based_search(names: str = None, user_query: str = None,
name_field: str = "data.FacilityName") -> str:
if names is None:
return user_query
generated_query = f"{name_field}:{names}"
return added_query(generated_query, user_query)
def update_query_with_nested_names_based_search(array_field: str, nested_field: str, names: str = None,
user_query: str = None) -> str:
if names is None:
return user_query
generated_query = f"(nested({array_field}, ({nested_field}:({names}))))"
return added_query(generated_query, user_query)
def escape_forbidden_characters_for_search(input_str: str) -> str:
# Reserved character are listed here https://community.opengroup.org/osdu/documentation/-/blob/master/platform/tutorials/core-services/SearchService.md
# ? and * are allowed for wildcard search
......@@ -149,8 +157,10 @@ def escape_forbidden_characters_for_search(input_str: str) -> str:
return result_str
async def query_request_with_cursor(query_type: str, kind: str, ctx: Context, query: SimpleCursorQueryRequest = None):
returned_fields = query_type_returned_fields(query_type)
async def query_request_with_cursor(query_type: str, kind: str, ctx: Context, query: SimpleCursorQueryRequest = None,
custom_returned_fields: [str] = None):
returned_fields = query_type_returned_fields(query_type) if custom_returned_fields is None \
else custom_returned_fields
query_request = CursorQueryRequest(kind=kind,
limit=query.limit or LIMIT,
query=query.query,
......@@ -162,8 +172,10 @@ async def query_request_with_cursor(query_type: str, kind: str, ctx: Context, qu
cursor_query_request=query_request)
async def query_request_with_offset(query_type: str, kind: str, ctx: Context, query: SimpleOffsetQueryRequest = None):
returned_fields = query_type_returned_fields(query_type)
async def query_request_with_offset(query_type: str, kind: str, ctx: Context, query: SimpleOffsetQueryRequest = None,
custom_returned_fields: [str] = None):
returned_fields = query_type_returned_fields(query_type) if custom_returned_fields is None \
else custom_returned_fields
query_request = QueryRequest(kind=kind,
limit=query.limit or LIMIT,
......@@ -176,15 +188,16 @@ async def query_request_with_offset(query_type: str, kind: str, ctx: Context, qu
query_request=query_request)
async def query_request(query_type: str, kind: str, ctx: Context, query: SearchQueryRequest = None):
async def query_request(query_type: str, kind: str, ctx: Context, query: SearchQueryRequest = None,
custom_returned_fields: [str] = None):
# use offset if not not none else use cursor
query_as_dict = query.dict(exclude_none=True, exclude_unset=True)
if query.offset is not None:
cursor_query = SimpleOffsetQueryRequest(**query_as_dict)
return await query_request_with_offset(query_type, kind, ctx, cursor_query)
return await query_request_with_offset(query_type, kind, ctx, cursor_query, custom_returned_fields)
cursor_query = SimpleCursorQueryRequest(**query_as_dict)
return await query_request_with_cursor(query_type, kind, ctx, cursor_query)
return await query_request_with_cursor(query_type, kind, ctx, cursor_query, custom_returned_fields)
@router.post('/query/wellbores', summary='Query with cursor, get wellbores',
......
......@@ -12,9 +12,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Optional, Dict, List
from fastapi import APIRouter, Depends
from odes_search.models import CursorQueryResponse
from odes_search.models import CursorQueryResponse, QueryRequest
from pydantic import BaseModel, Field
from app.utils import Context
from . import search_wrapper
from .search_v3 import (
SearchQueryRequest,
DEFAULT_QUERYREQUEST,
......@@ -27,8 +32,10 @@ from .search_v3 import (
added_relationships_query,
WELLBORE_RELATIONSHIP,
get_ctx,
query_type)
query_type, update_query_with_nested_names_based_search)
from ..common_parameters import REQUIRED_ROLES_READ
from ...clients.search_service_client import get_search_service
from ...model.osdu_model import Curve110
router = APIRouter()
......@@ -57,14 +64,52 @@ async def query_trajectories_bywellbore(wellboreId: str, body: SearchQueryReques
@router.post('/query/welllogs',
summary='Query with cursor, search WellLogs by name and optionally by wellbore ID',
description=f"""Get all WellLogs objects using its name and optionally relationship Wellbore ID.
summary='Query with cursor, search WellLogs by name and optionally by wellbore ID and curves mnemonics',
description=f"""Get all WellLogs objects using its name and optionally relationship Wellbore ID. Filtering can be done on curves mnemonics
<p>The WellLogs kind is {OSDU_WELLLOG_KIND} returns all records directly based on existing schemas. The query is done on data.Name field</p>{REQUIRED_ROLES_READ}""",
response_model=CursorQueryResponse)
async def query_welllogs_by_name(names: str = None, wellbore_id: str = None, body: SearchQueryRequest = DEFAULT_QUERYREQUEST,
ctx: Context = Depends(get_ctx)):
async def query_welllogs_by_name(names: str = None, wellbore_id: str = None, mnemonics: str = None,
body: SearchQueryRequest = DEFAULT_QUERYREQUEST,
ctx: Context = Depends(get_ctx)):
if wellbore_id is not None:
body.query = added_relationships_query(wellbore_id, WELLBORE_RELATIONSHIP, body.query)
names = escape_forbidden_characters_for_search(names)
mnemonics = escape_forbidden_characters_for_search(mnemonics)
body.query = update_query_with_names_based_search(names=names, user_query=body.query, name_field="data.Name")
body.query = update_query_with_nested_names_based_search(array_field='data.Curves', nested_field='Mnemonic',
names=mnemonics, user_query=body.query)
return await query_request(query_type, OSDU_WELLLOG_KIND, ctx, body)
class CurvePer(BaseModel):
results: "Optional[Dict[str, List[Curve110]]]" = Field(None, alias="results")
class CurvesQueryResponse(BaseModel):
results: "Optional[Dict[str, List[Curve110]]]" = Field(None, alias="results")
@router.post('/query/wellbores/{wellboreid}/curves',
summary='Query with cursor',
description=f"""Get all Curves from all WellLogs using relationship Wellbore ID.<p></p>
<p>The WellLog kind is {OSDU_WELLLOG_KIND}</p>{REQUIRED_ROLES_READ}""",
response_model=CurvesQueryResponse,
response_model_exclude_unset=True)
async def query_curves_by_wellbore(wellboreid: str, ctx: Context = Depends(get_ctx)):
query = added_relationships_query(wellboreid, WELLBORE_RELATIONSHIP, None)
welllogs_query_request = QueryRequest(kind=OSDU_WELLLOG_KIND,
query=query,
returnedFields=['id', 'data.Curves'])
client = await get_search_service(ctx)
results = await search_wrapper.SearchWrapper.query_cursorless(
search_service=client,
data_partition_id=ctx.partition_id,
query_request=welllogs_query_request)
curves_response = CurvesQueryResponse()
curves_response.results = dict()
if results.results is not None:
for item in results.results:
curves_response.results[item['id']] = item.get('data', {}).get('Curves', [])
return curves_response
......@@ -1089,6 +1089,22 @@
"title": "Curve110",
"type": "object"
},
"CurvesQueryResponse": {
"properties": {
"results": {
"additionalProperties": {
"items": {
"$ref": "#/components/schemas/Curve110"
},
"type": "array"
},
"title": "Results",
"type": "object"
}
},
"title": "CurvesQueryResponse",
"type": "object"
},
"DataType": {
"description": "An enumeration.",
"enum": [
......@@ -9354,9 +9370,69 @@
]
}
},
"/alpha/ddms/v3/query/wellbores/{wellboreid}/curves": {
"post": {
"description": "Get all Curves from all WellLogs using relationship Wellbore ID.<p></p>\n <p>The WellLog kind is *:wks:work-product-component--WellLog:*</p>\n<p>Required roles: 'users.datalake.viewers' or 'users.datalake.editors' or 'users.datalake.admins'.\n\"In addition, users must be a member of data groups to access the data.</p>\n",
"operationId": "query_curves_by_wellbore_alpha_ddms_v3_query_wellbores__wellboreid__curves_post",
"parameters": [
{
"in": "path",
"name": "wellboreid",
"required": true,
"schema": {
"title": "Wellboreid",
"type": "string"
}
},
{
"description": "identifier of the data partition to query",
"in": "header",
"name": "data-partition-id",
"required": false,
"schema": {
"description": "identifier of the data partition to query",
"minLength": 1,
"title": "data partition id",
"type": "string"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CurvesQueryResponse"
}
}
},
"description": "Successful Response"
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
"description": "Validation Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Query with cursor",
"tags": [
"ALPHA feature: search v3"
]
}
},
"/alpha/ddms/v3/query/welllogs": {
"post": {
"description": "Get all WellLogs objects using its name and optionally relationship Wellbore ID. \n <p>The WellLogs kind is *:wks:work-product-component--WellLog:* returns all records directly based on existing schemas. The query is done on data.Name field</p>\n<p>Required roles: 'users.datalake.viewers' or 'users.datalake.editors' or 'users.datalake.admins'.\n\"In addition, users must be a member of data groups to access the data.</p>\n",
"description": "Get all WellLogs objects using its name and optionally relationship Wellbore ID. Filtering can be done on curves mnemonics\n <p>The WellLogs kind is *:wks:work-product-component--WellLog:* returns all records directly based on existing schemas. The query is done on data.Name field</p>\n<p>Required roles: 'users.datalake.viewers' or 'users.datalake.editors' or 'users.datalake.admins'.\n\"In addition, users must be a member of data groups to access the data.</p>\n",
"operationId": "query_welllogs_by_name_alpha_ddms_v3_query_welllogs_post",
"parameters": [
{
......@@ -9377,6 +9453,15 @@
"type": "string"
}
},
{
"in": "query",
"name": "mnemonics",
"required": false,
"schema": {
"title": "Mnemonics",
"type": "string"
}
},
{
"description": "identifier of the data partition to query",
"in": "header",
......@@ -9432,7 +9517,7 @@
"HTTPBearer": []
}
],
"summary": "Query with cursor, search WellLogs by name and optionally by wellbore ID",
"summary": "Query with cursor, search WellLogs by name and optionally by wellbore ID and curves mnemonics",
"tags": [
"ALPHA feature: search v3"
]
......
......@@ -397,6 +397,22 @@ def build_request_search_welllog_by_name() -> RequestRunner:
return RequestRunner(rq_proto)
def build_request_search_welllog_by_curve_mnemonics() -> RequestRunner:
rq_proto = Request(
name='search wellbore by name',
method='POST',
url='{{base_url}}/alpha/ddms/v3/{{search_query_type}}/welllogs?mnemonics=Example%20Mnemonic',
headers={
'accept': 'application/json',
'data-partition-id': '{{data_partition}}',
'Connection': '{{header_connection}}',
'Authorization': 'Bearer {{token}}',
},
payload='{}'
)
return RequestRunner(rq_proto)
def build_request_search_welllog_by_name_and_wellbore() -> RequestRunner:
rq_proto = Request(
......
......@@ -126,6 +126,16 @@ def test_search_wellLog_by_name(with_wdms_env):
assert resobj.totalCount >= 1
@pytest.mark.tag('search')
@pytest.mark.dependency(depends=["test_setup_for_search"])
def test_search_wellLog_by_curve_mnemonics(with_wdms_env):
#Only search and no fast search
env = with_wdms_env
env.set('search_query_type', 'query')
resobj = build_request_search_welllog_by_curve_mnemonics().call(with_wdms_env, assert_status=200).get_response_obj()
assert resobj.totalCount >= 1
@pytest.mark.tag('search')
@pytest.mark.dependency(depends=["test_setup_for_search"])
def test_search_wellLog_by_name_and_wellbore(with_wdms_env):
......
......@@ -45,6 +45,20 @@ def test_update_query_with_names_based_search(names, user_query, expected_query)
assert search_v3.update_query_with_names_based_search(names, user_query) == expected_query
NESTED_NAMES_QUERY_PARAMS = [
(None, None, None, None, None),
('array.field', 'nested_field', 'names', 'user_query', '(nested(array.field, (nested_field:(names)))) AND (user_query)'),
('array.field', 'nested_field', None, 'user query', 'user query'),
]
@pytest.mark.parametrize("array_field, nested_field, names, user_query, expected_query", NESTED_NAMES_QUERY_PARAMS)
def test_update_query_with_neted_names_based_search(array_field, nested_field, names, user_query, expected_query):
assert search_v3.update_query_with_nested_names_based_search(array_field, nested_field, names,
user_query) == expected_query
ESCAPE_CHAR_PARAMS = [
('', ''),
('not char to escape', 'not char to escape'),
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment