Is it a bad idea to use the builder pattern to construct elasticsearch queries in Python/Django?
So here's the issue. I am maintaining a somewhat large Django app and am tasked with creating a search bar that relies on elastic search data and need to in the near future convert a large number of Django DB queries into Elastic queries. I'm using python's elasticsearch.dsl package version 8.19.2 for this. I put a lot of thought into how to implement this and settled on the builder pattern . Assuming the app uses Articles as a model that needs to be represented as Elastic document. Here is my implementation of the builder pattern
from elasticsearch.dsl import Document, Text, Keyword
class ArticleDocument(Document):
id = Integer()
title = Text(
fields={
'keyword': Keyword()
}
)
class Index:
name = INDEX_VAR
from app import handle_elastic_connection
@dataclass
class ElasticSearchResults:
count: int
results: object
class ArticleRepository:
def __init__(self, client: Elasticsearch):
self.client = client
def return_results(self, search: Search):
# attach search object to elastic connection
search = search.using(self.client)
return ElasticSearchResults(results=search.execute(), count=search.count())
class ArticleQueryBuilder:
def __init__(
self
):
# handles index refer
self.search: Search = ArticleDocument.search()
def builder_method1(self, list_of_values:List):
if list_of_values:
self.search = self.search(Q('terms', key=list_of_values))
def builder_method2(self, single_value:str):
if single_value:
method2_filter = Q('term', value1=single_value) | Q('term', value2=single_value)
self.search = self.search(method2_filter)
# .... more builder methods
def build():
return self.search
# Usage, most likely in view
ec = handle_elastic_connection()
repo = ArticleRepository(ec)
artcile_search = ArticleQueryBuilder.builder_method1().builder_method2.build()
results: ElasticSearchResults = repo.return_results(artcile_search)
I know the builder pattern gets a lot of hate in some corners of the internet but I'm not using this as a replacement for named parameters lol. It's to make query construction easier and more similar to Django's ORM langage. The downside is that it appears verbose here, but some of the builder methods are a lot more complicated than the example I gave so it feels right to not do all of that directly in the view. I did consider alternatives
Using elasticsearch.dsl Q object directly. I could just do this directly in the view, constructing the Search object with queries right there and just calling Search.execute() in the view itself but there are a lot of operations that are going to be reused over and over by different views tied to a single model. I know a simplified the example but one thing I'm doing is filtering results based on user permissions which I'm going to be doing a lot in multiple views I feel like it's enough to justify tucking it behind at least one layer of abstraction
Using the repository pattern was what everyone did at my previous job, but the problem with that is people would make a method that was only applicable to one use and returned one set of results from the database, and then there would be a ton of extraneous methods. The builder pattern gives you the flexibility to chain them together on a single query, which I will need to do
Are there alternatives I should consider? There's not a lot of examples or documentation I can find of elastic being used in Django project, and while I'm the sole maintainer of this repo at the moment. It will most likely be maintained by a more people in the future, so I'm being a little bit over-analytical at this stage of development.