Пары запросов MongoDB в большой коллекции - PullRequest
1 голос
/ 28 мая 2020

Я только недавно начал использовать MongoDB, пытаясь решить проблему c домена, и застрял при попытке самостоятельно присоединиться к большой коллекции. У меня есть база данных с более чем 10 миллионами документов, каждый из которых содержит элементы адреса для объекта (лицо, организация, почтовый ящик для организации и т. Д. c.). Обратите внимание, что каждая глубина (например, улица) может встречаться более одного раза для хранения различной информации, такой как псевдонимы или c идентификаторы глубины. У меня нет ограничений схемы, и я могу изменить ее, если это поможет решить вопрос.

Данные выглядят следующим образом:

{
  "some_info": "xyz",
  "tags": {
    "HOUSE_NUMBER": [
      {
        "id": "23.45678",
        "value": "18",
        "attributes": ["NU"]
      }
    ],
    "FORENAME": [
      {
        "id": "34.56789",
        "value": "MAX",
        "attributes": ["XQ4", "M"]
      },
      {
        "id": "45.67890",
        "value": "X65732862",
        "attributes": ["XID"]
      }
    ],
    "STREET": [
      {
        "id": "56.789012",
        "value": "RICHMOND STREET",
        "attributes": []
      }
    ],
    "...": []
  }
}

Я хочу запросить пары в коллекции, например " найти все пары людей с одним и тем же именем, проживающих на одной улице », или« найти все пары лиц и организаций, имеющих не менее 3 общих слов, проживающих в одном городе ». Мой текущий запрос для первой проблемы выглядит примерно так:

db.collection_name.aggregate([
    {$unwind: "$tags.STREET"},
    {$unwind: "$tags.FORENAME"},
    {
        $match: {
            "tags.FORENAME.attributes": {$nin: ["XID", "NA"]}
        }
    },
    {
        $lookup: {
            from: "collection_name",
            localField: "tags.STREET.id",
            foreignField: "tags.STREET.id",
            as: "joined"
        }
    },
    {$unwind: "$joined"},
    {$unwind: "$joined.tags.FORENAME"},
    {
        $match: {$expr: {$ne: ["$tags.FORENAME.id", "$joined.tags.FORENAME.id"]}}
    },
    {
        $match: {$expr: {$eq: ["$tags.FORENAME.value", "$joined.tags.FORENAME.value"]}}
    }
], {
    allowDiskUse: true
})

Я создал индексы для tags.STREET.id, tags.FORENAME.id, tags.FORENAME.attributes и tags.FORENAME.value.

Проблема с этим во времени выполнения. Я просто не могу добраться до приемлемого уровня, вышеупомянутый запрос занимает 3,5 минуты для 500 результатов на моей машине. Для сравнения, моя база данных PostgreSQL (со специально созданными представлениями и индексами для проблемы) занимает всего несколько секунд.

Как я могу ускорить запросы такого типа? Подходит ли MongoDB для решения такого рода проблем?

1 Ответ

1 голос
/ 28 мая 2020

$ lookup - это ОЧЕНЬ дорогостоящий этап для выполнения Mon go, и в данном случае c он совершенно не нужен. не говоря уже о том, что вы делаете это для всей коллекции.

Я бы переписал этот конвейер вот так, используя $ group вместо $lookup:

db.collection_name.aggregate([
        {
            $unwind: "$tags.STREET"
        },
        {
            $unwind: "$tags.FORENAME"
        },
        {
            $match: {
                "tags.FORENAME.attributes": {$nin: ["XID", "NA"]}
            }
        },
        {
            $group: {
                "_id": {foreName: "$tags.FORENAME.value", streetId: "tags.STREET.id"},
                docs: {$addToSet: "$$ROOT"}
            }
        },
        {
            $match: {
                "docs.1": {$exists: true}
            }
        },
        //Add whichever other structure changes you need.
    ],
    {
        allowDiskUse: true
});

Теперь это все еще довольно дорогой конвейер, так как нам нужно $unwind и $group всю коллекцию, но он будет намного быстрее, чем текущая версия.

Я не знаю, как ваши данные / product работает, поэтому трудно дать лучшее «решение» того, как должна быть построена архитектура схемы / коллекции.

С учетом сказанного, я сразу же вижу легкое улучшение - это изменение структуры street и forename с массива на объект. (если у вас не может быть несколько улиц и имен, а затем реляционная база данных работает для вас?) это сделает первые 3 этапа текущего конвейера избыточными и повысит производительность.


EDIT: отрицание при группировании невозможно, но мы можем обойти его, добавив дополнительный этап $group.

db.collection_name.aggregate([
        {
            $unwind: "$tags.STREET"
        },
        {
            $unwind: "$tags.FORENAME"
        },
        {
            $unwind: "$tags.HOUSE_NUMBER"
        },
        {
            $match: {
                "tags.FORENAME.attributes": {$nin: ["XID", "NA"]}
            }
        },
        {
            $group:{
                "_id": {foreName: "$tags.FORENAME.value", streetId: "tags.STREET.id", houseName: "tags.HOUSE_NUMBER.id"},
                docs: {$addToSet: "$$ROOT"}
            }
        },
        {
            $group: {
                "_id": {foreName: "$tags.FORENAME.value", streetId: "tags.STREET.id"},
                docs: {$addToSet: "$docs"}
            }
        },
        {
            $match: {
                "docs.1": {$exists: true}
            }
        },
        //Add whichever other structure changes you need.
    ],
    {
        allowDiskUse: true
    });
...