По мере того, как мы переходим к моделям документов в наших базах данных, важно иметь хорошие инструменты, позволяющие адекватно отфильтровывать ненужную и конфиденциальную информацию.

В этой статье мы будем использовать библиотеку flatten-dict, доступную по адресу https://github.com/ianlini/flatten-dict и устанавливаемую через pip install flatten-dict.

Пример документа, который нужно отфильтровать:

user = {
    "name": "Bob Robertson",
    "classification": {
        "kingdom": "animalia",
        "phylum": "chordata",
        "class": "mammalia",
        "order": "primates",
        "suborder": "haplorhini",
        "infraorder": "simiiformes",
        "family": "hominidae",
        "subfamily": "homininae",
        "tribe": "hominini",
        "genus": "homo",
        "species": "h. sapiens",
    },
    "statistics": {
        "dimensions": {
            "height_cm": 194.2,
            "weight_kg": 96.52,
        },
        "digits": {
            "toes": 10,
            "fingers": 10,
        }
    },
}

Давайте представим, что большая часть этой информации используется серверной службой, но не используется панелью управления, представляющей пользователя. Мы хотим отфильтровать все содержимое, кроме выделенного жирным шрифтом: имя, классификационные виды и включать только параметры из статистики.

Первое, что нам нужно сделать, это свести словарь к списку с точечной записью. Это предполагает, что все ключи в документе еще не содержат точки (.).

from flatten_dict import flatten
flattened_user_doc = flatten(user_doc, reducer="dot")

В результате содержимое flattened_user_doc is:

{
    "name": "Bob Robertson",
    "classification.kingdom": "animalia",
    "classification.phylum": "chordata",
    "classification.class": "mammalia",
    "classification.order": "primates",
    "classification.suborder": "haplorhini",
    "classification.infraorder": "simiiformes",
    "classification.family": "hominidae",
    "classification.subfamily": "homininae",
    "classification.tribe": "hominini",
    "classification.genus": "homo",
    "classification.species": "h. sapiens",
    "statistics.dimensions.height_cm": 194.2,
    "statistics.dimensions.weight_kg": 96.52,
    "statistics.digits.toes": 10,
    "statistics.digits.fingers": 10
}

OK. Достаточно просто. Теперь давайте воспользуемся регулярными выражениями и списком сопоставителей, чтобы действовать как фильтр, а затем отфильтровать результат.

import re
filters = [
    re.compile("^name$"),
    re.compile("^classification.species$"),
    re.compile("^statistics.dimensions"),
]
filtered_flattened_user_doc = {
    k: v
    for k, v in flattened_user_doc.items()
    if [True for f in filters if f.match(k)]
}
from flatten_dict import unflatten
filtered_user_doc = unflatten(filtered_flattened_user_doc, splitter="dot")

В результате содержимое filtered_flattened_user_doc теперь выглядит следующим образом:

{
    "name": "Bob Robertson",
    "classification.species": "h. sapiens",
    "statistics.dimensions.height_cm": 194.2,
    "statistics.dimensions.weight_kg": 96.52
}

И наконец у нас есть filtered_user_doc:

{
    "name": "Bob Robertson",
    "classification": {
        "species": "h. sapiens"
    },
    "statistics": {
        "dimensions": {
            "height_cm": 194.2,
            "weight_kg": 96.52
        }
    }
}

Я считаю, что есть еще несколько модулей, которые помогают выполнять эту фильтрацию. Использование flatten и unflatten из модуля flatten-dict и встроенного регулярного выражения просто и понятно.

Необязательно использовать регулярные выражения. Я использовал их, чтобы сопоставить пути с частичными точечными обозначениями.

Вам не обязательно использовать точки. Вы можете использовать кортежи с flatten_dict, а также список кортежей для сопоставления.

В настоящее время я использую этот код для фильтрации объединенных документов из информации об аккаунте, пользователе и устройстве пользователя, полученной из различных источников. Использование явной фильтрации позволяет мне немного меньше беспокоиться о том, что передается пользователю, поскольку я вношу изменения в документы и модели во время разработки и текущего обслуживания моего проекта.