Package: fastapi-elasticsearch
Query Helper for Elasticsearch or OpenSearchThis is a helper library for creating elasticsearch or opensearch query proxies usingFastAPI.fromfastapi_elasticsearchimportElasticsearchAPIQueryBuilder# Create a new query_builder for the endpoint.query_builder=ElasticsearchAPIQueryBuilder()# Decorate a function as a filter.# The filter can declare parameters.@query_builder.filter()deffilter_category(c:Optional[List[str]]=Query([],description="Category name to filter results.")):return{"terms":{"category":c}}iflen(c)>0elseNone# Then use the query_builder in your endpoint.@app.get("/search")asyncdefsearch(es:Elasticsearch=Depends(get_elasticsearch),query_body:Dict=Depends(query_builder.build()))->JSONResponse:returnes.search(body=query_body,index=index_name)The swagger API will result in:The resulting query will be like this:{"query":{"bool":{"filter":[{"terms":{"category":["the-category"]}}]}},"size":10,"from":0}To use OpenSearch, simply change the client.fromfastapi_elasticsearchimportElasticsearchAPIQueryBuilderfromopensearchpyimportOpenSearch...@app.get("/search")asyncdefsearch(os:OpenSearch=Depends(get_opensearch),query_body:Dict=Depends(query_builder.build()))->JSONResponse:returnos.search(body=query_body,index=index_name)...# Create a new query_builder for the endpoint.query_builder=ElasticsearchAPIQueryBuilder()To control the scoring use a matcher.# Or decorate a function as a matcher# (will contribute to the query scoring).# Parameters can also be used.@query_builder.matcher()defmatch_fields(q:Optional[str]=Query(None)):return{"multi_match":{"query":q,"fuzziness":"AUTO","fields":["name^2","description"]}}ifqisnotNoneelseNoneThe swagger API will result in:The resulting query will be like this:{"query":{"bool":{"should":[{"multi_match":{"query":"bob","fuzziness":"AUTO","fields":["name^2","description"]}}],"minimum_should_match":1}},"size":10,"from":0}To control the ordering, it is possible to annotate a function as sorter.classDirection(str,Enum):asc="asc"desc="desc"# Decorate a function as a sorter.# Parameters can be declared.@query_builder.sorter()defsort_by(direction:Optional[Direction]=Query(None)):return{"name":direction}ifdirectionisnotNoneelseNoneThe swagger API will result in:The resulting query will be like this:{"query":{"match_all":{}},"size":10,"from":0,"sort":[{"name":"asc"}]}To add highlight functionality, it is possible to annotate a function as highlighter.# Decorate a function as a highlighter.# Parameters can also be declared.@query_builder.highlighter()defhighlight(q:Optional[str]=Query(None,description="Query to match the document text."),h:bool=Query(False,description="Highlight matched text and inner hits.")):return{"name":{"fragment_size":256,"number_of_fragments":1}}ifqisnotNoneandhelseNoneThe swagger API will result in:The resulting query will be like this:{"query":{"bool":{"should":[{"multi_match":{"query":"bob","fuzziness":"AUTO","fields":["name^2"]}}],"minimum_should_match":1}},"size":10,"from":0,"highlight":{"fields":{"name":{"fragment_size":256,"number_of_fragments":1}}}}Now, a complete example:app=FastAPI()query_builder=ElasticsearchAPIQueryBuilder()@query_builder.filter()deffilter_items():return{"term":{"join_field":"item"}}@query_builder.filter()deffilter_category(c:Optional[List[str]]=Query([],description="Category name to filter results.")):return{"terms":{"category":c}}iflen(c)>0elseNone@query_builder.matcher()defmatch_fields(q:Optional[str]=Query(None,description="Query to match the document text.")):return{"multi_match":{"query":q,"fuzziness":"AUTO","fields":["name^2",]}}ifqisnotNoneelseNone@query_builder.matcher()defmatch_fragments(q:Optional[str]=Query(None,description="Query to match the document text."),h:bool=Query(False,description="Highlight matched text and inner hits.")):ifqisnotNone:matcher={"has_child":{"type":"fragment","score_mode":"max","query":{"bool":{"minimum_should_match":1,"should":[{"match":{"content":{"query":q,"fuzziness":"auto"}}},{"match_phrase":{"content":{"query":q,"slop":3,"boost":50}}},]}}}}ifh:matcher["has_child"]["inner_hits"]={"size":1,"_source":"false","highlight":{"fields":{"content":{"fragment_size":256,"number_of_fragments":1}}}}returnmatcherelse:returnNoneclassDirection(str,Enum):asc="asc"desc="desc"@query_builder.sorter()defsort_by(direction:Optional[Direction]=Query(None)):return{"name":direction}ifdirectionisnotNoneelseNone@query_builder.highlighter()defhighlight(q:Optional[str]=Query(None,description="Query to match the document text."),h:bool=Query(False,description="Highlight matched text and inner hits.")):return{"name":{"fragment_size":256,"number_of_fragments":1}}ifqisnotNoneandhelseNone@app.get("/search")asyncdefsearch(query_body:Dict=Depends(query_builder.build()))->JSONResponse:returnes.search(body=query_body,index=index_name)Also it is possible to customize the generated query body using the decorator search_builder.fromtypingimportList,Dict@query_builder.search_builder()defbuild_search_body(size:int=10,start_from:int=0,source:Union[List,Dict,str]=None,minimum_should_match:int=1,filters:List[Dict]=[],matchers:List[Dict]=[],highlighters:List[Dict]=[],sorters:List[Dict]=[])->Dict:return{"query":{...},...}Adopt this project: if you like and want to adopt it, talk to me.