Это будет «длинный». Я включаю в себя как можно больше кода и объяснений ... Я не выше метаданных, если это необходимо.Реализация логического парсера в django-запросе
Я пытаюсь реализовать логический парсер в системе запросов django. Где пользователи могут предоставлять сложные запросы против тегов, которые применяются к образцам. Это по существу часть хранилища научных образцов, где пользователи могут применять определенные теги (тип ткани, изученные болезни и т. Д.). Затем они могут создавать постоянные «корзины» образцов, определенных логическим запросом для этих тегов.
#models.py
class Sample(models.Model):
name = models.CharField(max_length = 255)
class Tag(models.Model):
name = models.CharField(max_length = 255)
samples = models.ManyToManyField(Sample)
A quick example:
#example data:
Sample1 has TagA, TagB, TagC
Sample2 has TagB, TagC, TagD
Sample3 has TagA, TagC, TagD
Sample4 has TagB
#example query:
'TagB AND TagC AND NOT TagD'
вернет образец1. Я использую сумасшедший строку-Eval хак, чтобы создать набор из Q()
объектов:
def MakeQObject(expression):
"""
Takes an expression and uses a crazy string-eval hack to make the qobjects.
"""
log_set = {'AND':'&','OR':'|','NOT':'~'}
exp_f = []
parts = expression.split()
#if there is a) or (then we can't use this shortcut
if '(' in parts or ')' in parts:
return None
for exp in parts:
if exp in log_set:
exp_f.append(log_set[exp])
else:
exp_f.append("Q(tags__name__iexact = '%s')" % exp)
st = ' '.join(exp_f)
qobj = eval(st)
return qobj
Однако, это не будет работать на все, что нуждается в сложный порядке операций или группировки по(). Учитывая те же данные примера, запрос: (TagA OR TagB) AND NOT TagD
должен возвращать Sample1, Sample4, но этого не делать. Я реализовал функцию «один на один», которая может принимать один экземпляр объекта и выполнять запрос. Тем не менее, в моей фактической базе данных у меня есть ~ 40 000 образцов и ~ 400 тегов (около ~ 7 на выборку), и итерационная техника занимает ~ 4 минуты для завершения на всех образцах. Поэтому я каждую ночь вычисляю корзины, а затем просто замораживаю их в течение дня. Я беспокоюсь, что, когда я начинаю курировать больше корзин, образцов и тэгов, это будет плохо масштабироваться.
Любые предложения?
Я также попробовал «факторинг» запросов типа '()' в более длинные, но эквивалентные версии без парсеров, но также не смог выполнить эту работу. – JudoWill