Как использовать графовую базу данных для распространения репутации?

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

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

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

В SQL это будет выглядеть примерно так:

CREATE TABLE objects (id INTEGER PRIMARY KEY);
CREATE TABLE object_links (from_object_id INTEGER, to_object_id INTEGER);
CREATE TABLE users (id INTEGER PRIMARY KEY);
CREATE TABLE object_reputations (object_id INTEGER, user_id INTEGER, reputation FLOAT);

UPDATE
    object_reputations
SET
    object_reputations.reputation = object_reputations.reputation + ... # some formula goes here
FROM
    object_reputations
    INNER JOIN object_links
        ON object_reputations.object_id = object_links.to_object_id
WHERE
    object_links.from_object_id = ...;

Поскольку речь идет о графе, база данных графа кажется естественной, но после быстрого чтения API-интерфейсов Neo4j / OrientDB / Blazegraph / Tinkerpop я не могу понять, как сопоставить эту проблему с тем, что они могут делать вообще.

Используя Tinkerpop в качестве примера, объекты — это вершины, связи между объектами — это ребра (пока все хорошо), а репутация…? Возможно, VertexPropetries, но я не уверен, как все будет масштабироваться с потенциально таким количеством свойств на вершину, сколько пользователей. Или, возможно, репутации являются взвешенными ребрами от пользовательских вершин... которые, казалось бы, имеют другой тип проблем с производительностью.

Можете ли вы дать простой перевод такого рода задач в одну из популярных графовых баз данных?


person Alex I    schedule 19.09.2016    source источник
comment
В вашем примере SQL это похоже на то, что каждый объект имеет репутацию для каждого пользователя. Разве репутация не одинакова для всех пользователей? Не могли бы вы лучше пояснить, что делает object_reputations в вашей модели?   -  person stephen mallette    schedule 19.09.2016
comment
@stephenmallette Правильно, каждый объект имеет разную репутацию для каждого пользователя. На практике не все объекты будут (это зависит от того, насколько репутация разбросана по графу), но, возможно, 10% всех объектов будут иметь репутацию для одного конкретного пользователя.   -  person Alex I    schedule 19.09.2016


Ответы (2)


Я бы сказал, что это действительно зависит от того, как вы хотите запрашивать ваши данные. Репутация также может быть вершиной, если она имеет конечное число значений, и эти значения повторяются среди пользователей. Например, если это число от 1 до 10, то мы можем сделать так, чтобы все пользователи, имеющие репутацию 7, ссылались на эту вершину. Эта модель позволит вам начать запрос с вершины и легко найти всех пользователей с такой репутацией. Используя Gremlin, это будет примерно так.

g.V().has(label,"reputation").has("reputation","7").in()

Это вернет все вершины, которые связаны с вершиной репутации с репутацией «7».

В качестве альтернативы вы можете иметь репутацию как свойство, и вы можете искать все вершины с таким свойством.

g.V().has("reputation","7")

Количество свойств не должно быть проблемой. Titan рекомендует индексировать свойства, которые вы хотите запросить, что немного улучшает поиск.

person Alaa Mahmoud    schedule 19.09.2016
comment
Проголосовал, потому что согласен с простотой структуры. Единственное замечание, которое я хотел бы отметить, это то, что я не думаю, что вы должны делать репутацию свойством каждой вершины. Скорее у вас должны быть определенные вершины репутации (как вы делаете это в первой части этого ответа). Причина этого в том, что в вопросе указано, что репутации необходимо изменить, и гораздо проще (в больших масштабах) изменить ребра на вершины, а не изменить сами вершины. - person Filipe Teixeira; 20.09.2016

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

Однако для «перспективных» данных, таких как ваши, хорошо подходит графическая база данных. Однако вместо того, чтобы хранить все репутации, ориентированные на пользователя, как свойства объекта, переместите их в отдельные узлы, прикрепленные к объекту, чтобы их можно было превратить в часть пути от пользователя к объекту.

Итак, у вас есть вершина для Пользователя, вершина для Объекта, и у каждой есть ребро к третьей вершине, ObjectReputation. Каждый Объект будет иметь несколько смежных вершин ObjectReputation (по одной для каждого Пользователя, связанного с Объектом), но существует только один путь по ребрам от любого Пользователя к любому Объекту. Чтобы найти связанные ObjectReputations, вы можете перемещаться по краям от пользователя к объекту, перемещаться по краям между объектами, а затем возвращаться от этих объектов через вершины UserReputation к исходному пользователю.

На языке запросов Cypher neo4j это выглядело бы примерно так:

MERGE (u:User {id:1})
MERGE (o:Object {id:2})
MERGE (u) - [:KNOWS] -> (ur:ObjectReputation) - [:KNOWS] -> (o)
SET ur.score = 100
MATCH (o) - [:RELATED_TO*] - (:Object) <- [:KNOWS] - (related_ur:ObjectReputation) <- [:KNOWS] - (u)
SET related_ur.score = related_ur.score * 1.2
person Tore Eschliman    schedule 19.09.2016