Как повысить производительность алгоритма соответствия

Я пишу алгоритм для сопоставления на основе интересов и местоположения. Предположим, что у меня есть данные пользователей

{
    "users": [{
            "location": "Delhi, India",
            "interests": ["Jogging", "Travelling", "Praying"],
            "groups": ["exercise", "travelling", "Praying"]
        },
        {
            "location": "Delhi, India",
            "interests": ["Running", "Eating", "Praying"],
            "groups": ["exercise", "Eating", "Praying"]
        }, {
            "location": "Delhi, India",
            "interests": ["Shopping"],
            "groups": ["Shopping"]
        }
    ]
}

Здесь они user1 и user2 имеют схожие интересы "упражнение" и "моление", а user1 и user3 не имеют схожих интересов.

Чтобы найти похожие интересы, пользователи из базы данных с более чем 10 миллионами пользователей могут повлиять на производительность моей базы данных, если я использую запрос SQL с предложением where каждый раз при получении запроса от мобильного приложения.

SELECT * FROM users WHERE groups = "exercise" OR groups = "travelling" OR groups = "Praying";

Это проверит каждый профиль, который может повлиять на производительность моего приложения. Я не хочу использовать этот подход, поскольку это не будет работать долго. Какой алгоритм следует использовать для достижения высокой производительности?

Ответы

Ответ 1

Вы можете построить инвертированный индекс, где ключ будет одним из токенов (например, упражнение, перемещение и т.д.) в "группе" и значением будет список пользователей, попадающих под эту группу. Например, ваш инвертированный индекс будет выглядеть примерно так:

Key: ListOfValues
Exercise: User1 -> User2
Praying: User1 -> User2
Travelling: User1 -> User3 -> User8 -> User14
Shopping: User3

Если вы хотите, чтобы на основе инвертированного индекса на основе дерева, растрового изображения или хеш-таблицы был выбран ваш выбор в зависимости от ваших компромиссов пространства/времени.

Теперь, когда вы получаете нового пользователя, скажем, что User99 имеет группу ( "Упражнение и молитву" ), вы можете быстро получить значения (т.е. Пользователи) для токена "Упражнение", затем получить значения для токена "Молитва", а затем, наконец, сделать 'AND' (пересечение) для этих двух.

Обратите внимание, что запуск его в первый раз будет выполняться пакетной обработкой, но когда вы начнете получать новых пользователей, ваша временная сложность будет почти постоянной (это будет верно, если у вас есть интеллектуальная структура данных, например, как сжатое растровое изображение, так как ваши списки проводок для значений "Пользователь" в инвертированном индексе, в противном случае пересечение не будет быстрее, чем O (n) AFAIK)

Ответ 2

Иллюстрация:

Если у вас есть какой-то способ получить полный список интересов (возможно, вы позволяете им выбирать определенную запись из набора интересов), вы можете использовать простое умножение матрицы с соответствующим поисковым вектором.

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

Let groups = [
  1: "exercise" 
  2: "traveling"
  3: "praying"
  4: "eating"
  5: "running"
  6: "shopping"
]

Let U = [
  1 1 1 0 0 0  // user 1
  0 0 1 1 1 0  // user 2
  0 0 0 0 0 1  // user 3
]  

Вы используете OR так, как хотите, чтобы члены в любой группе

Let V = [
  1  // exercise
  1  // traveling
  1  // praying
  0  // eating
  0  // running
  0  // shopping
]

Умножение:

U · V = [
  3  // user 1 is in all 3 groups => match
  1  // user 2 is in one group => match
  0  // user 3 is in no groups => no match
]

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

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

Выбор только тех, у кого есть только один или несколько столбцов, а не один или несколько других столбцов (XOR), будет рассматривать в качестве совпадений только те результирующие записи с ровно n значением.

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

Решение зависит прежде всего от вашего варианта использования. Например, если скорость запроса имеет первостепенное значение, а передача данных является проблемой, такой подход позволит очень простой запрос, который возвращает все (с LIMIT) строки, и тогда вы можете оптимально просеять до тех пор, пока не найдете количество пользователей, которых вы хотите использовать для данной страницы, только для выполнения последующих запросов при необходимости загрузки большего количества страниц. Поскольку вы упомянули об этом каждый раз, когда получаете запрос от мобильного приложения, возможно, вам будет лучше кэшировать управляемое количество пользователей и опросить это вместо базы данных каждый раз, если не будут найдены достаточные совпадения, внедряя подходящие проверенные временем алгоритм замены кэша (который также может быть в некоторой степени выгружен клиенту).

Заключение /TL;DR Важным выводом здесь является структура, которую вы хотите полностью, зависит от бизнес-требований вашего приложения. Вы можете сделать структуры данных как эзотерические, как вам нравится, с целью повышения производительности, но это часто менее полезно, чем просто использовать проверенное временем решение, такое как базовое кэширование.

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

Если вы считаете, что необходима база данных графа, она будет соответствовать вашим требованиям бизнеса и быть более быстрой, простой в управлении, это может быть вашим решением.

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

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

Ответ 3

Структура данных выглядит так, как будто это NoSql db, как MongoDb. В любом случае, проверьте, помогает ли вам полный текстовый индекс. Я только что увидел FULL TEXT INDEX в MSSQL (https://docs.microsoft.com/en-us/sql/t-sql/statements/create-fulltext-index-transact-sql). Я не знал об этом раньше. MongoDb также имеет полный текстовый индекс. Индексирование наверняка поможет вашим запросам, если они будут выполнены правильно. Я не уверен, сколько полного текстового индекса может быть реализовано на одной таблице. Пожалуйста, исследуйте это.