Django: Фильтр совпадающих объектов по 2 полям
У меня есть следующие классы:
class Event(Model):
...
class IOCType(Model):
name = CharField(max_length=50)
class IOCInfo(Model):
event = ForeignKey(Event, on_delete=CASCADE, related_name="iocs"
ioc_type = ForeignKey(IOCType, on_delete=CASCADE)
value = CharField(max_lenght=50)
Каждое событие имеет один или несколько связанных с ним IOC, которые хранятся в таблице IOCInfo.
Вот как выглядит моя таблица IOCInfo после создания некоторых событий:
id | value | event_id | ioc_type_id |
---|---|---|---|
1 | some-value1 | eventid1 | 4 |
2 | some-value2 | eventid1 | 8 |
3 | some-value3 | eventid1 | 8 |
4 | some-value4 | eventid1 | 1 |
5 | some-value3 | eventid2 | 8 |
6 | some-value1 | eventid2 | 1 |
7 | some-value2 | eventid3 | 8 |
8 | some-value3 | eventid4 | 8 |
Я хочу сделать следующее: взять событие, сравнить его IOCInfo с другими событиями и получить обратно те события, которые совпадают.
Это то, что я сделал до сих пор, и это работает, но я боюсь, что по мере роста базы данных и увеличения числа пользователей приложения этот запрос станет узким местом
def search_matches(event):
matches = Event.objects.none()
for ioc in event.iocs.all():
matches |= Event.objects.filter(
iocs__ioc_type=ioc.ioc_type, iocs__value=ioc.value
)
return matches.exclude(event=event.id)
Если я передам eventid2 в вышеуказанную функцию, она вернет eventid1 и eventid4, как и ожидалось.
Любые предложения по улучшению этой функции с помощью любого метода django будут приняты с благодарностью.
Если я правильно понимаю:
Фильтр иок, соответствующих данному событию:
iocs = IOCInfo.objects.filter(event=event)
Получить события, которые имеют те же iocs (кроме данного)
.events = Event.objects.filter(iocs__in=iocs).exclude(pk=event.pk)
В результате получится один довольно эффективный запрос.
Посмотрим, правильно ли я понимаю...
def search_matches(event):
return Event.objects.filter(iocs__in=event.iocs.all()).exclude(pk=event.id)
сначала необходимо получить значения ioc_type
и values
для IOCInfo указанного eventid
event_filters=IOCInfo.objects.filter(event=event).values("ioc_type","value")
тогда вы должны построить объект Q
для фильтрации событий в соответствии с любым из условий ioc_type
и value
.
наконец, используйте этот объект Q
для фильтрации событий
filtering_kwargs=Q()
for filter_ in event_filters:
filtering_kwargs|=Q(**filter_)
matching_events=Event.objects.filter(**filtering_kwargs).exclude(id=event.id)
Надеюсь, это решит вашу проблему, поскольку база данных будет запрошена только дважды, независимо от того, насколько велики ваши данные
объединение всего в одну функцию становится
def search_matches(event):
event_filters=IOCInfo.objects.filter(event=event).values("ioc_type","value")
filtering_kwargs=Q()
for filter_ in event_filters:
filtering_kwargs|=Q(**filter_)
matching_events=Event.objects.filter(**filtering_kwargs).exclude(id=event.id)
return matching_events