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 будут приняты с благодарностью.

Если я правильно понимаю:

  1. Фильтр иок, соответствующих данному событию:

    iocs = IOCInfo.objects.filter(event=event)
    
  2. Получить события, которые имеют те же 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
Вернуться на верх