2013-12-11 3 views
0

У меня есть набор документов, каждый из которых относится к 1 или нескольким группам.Cypher: Top N членов группы с ex-aequo

Лица могут рекомендовать эти документы

мне нужно для каждой группы документов, которые имеют место от 1 до N в терминах количества принятых рекомендаций, а это означает, что, когда это является промежуточным результатом и N = 3

Document Recommendations 
a   7 
b   4 
c   4 
d   3 

было бы вернуть а, б и в

с этим:

Document Recommendations 
a   6 
b   5 
c   4 
d   4 

было бы вернуть а, б, в и г

и с этим

С этим:

Document Recommendations 
a   6 
b   4 
c   4 
d   4 
e   3 

было бы вернуть, б, в и г

Как я делаю такие вещи в Сайфере? Я получил это далеко (планировал поставить ссылку на консоль, но это не похоже на работу)

//Groups 
create (gr1:Group {name:"First group"}) 
create (gr2:Group {name:"Second group"}) 

//Persons 
create (p1:Person {name:"Jan"}) 
create (p2:Person {name:"Marie"}) 
create (p3:Person {name:"Willem"}) 
create (p4:Person {name:"Simone"}) 
create (p5:Person {name:"Henk"}) 
create (p6:Person {name:"Ilse"}) 
create (p7:Person {name:"Tom"}) 
create (p8:Person {name:"Detlef"}) 

//Content 
create (ci1:Contentitem { title: "Sometitle 1"}) 
create (ci2:Contentitem { title: "Sometitle 2"}) 
create (ci3:Contentitem { title: "Sometitle 3"}) 
create (ci4:Contentitem { title: "Sometitle 4"}) 
create (ci5:Contentitem { title: "Sometitle 5"}) 
create (ci6:Contentitem { title: "Sometitle 6"}) 
create (ci7:Contentitem { title: "Sometitle 7"}) 
create (ci8:Contentitem { title: "Sometitle 8"}) 
create (ci9:Contentitem { title: "Sometitle 9"}) 
create (ci10:Contentitem { title: "Sometitle 10"}) 

//Recommendations 
create (ci1)-[:IsRecommendedBy]->(p4) 
create (ci8)-[:IsRecommendedBy]->(p8) 
create (ci1)-[:IsRecommendedBy]->(p1) 
create (ci8)-[:IsRecommendedBy]->(p7) 
create (ci5)-[:IsRecommendedBy]->(p6) 
create (ci1)-[:IsRecommendedBy]->(p3) 
create (ci8)-[:IsRecommendedBy]->(p3) 
create (ci5)-[:IsRecommendedBy]->(p4) 
create (ci8)-[:IsRecommendedBy]->(p5) 
create (ci5)-[:IsRecommendedBy]->(p2) 
create (ci5)-[:IsRecommendedBy]->(p1) 
create (ci5)-[:IsRecommendedBy]->(p8) 
create (ci2)-[:IsRecommendedBy]->(p1) 
create (ci2)-[:IsRecommendedBy]->(p3) 
create (ci2)-[:IsRecommendedBy]->(p7) 
create (ci10)-[:IsRecommendedBy]->(p8) 
create (ci3)-[:IsRecommendedBy]->(p4) 
create (ci10)-[:IsRecommendedBy]->(p5) 
create (ci3)-[:IsRecommendedBy]->(p1) 
create (ci4)-[:IsRecommendedBy]->(p5) 
create (ci4)-[:IsRecommendedBy]->(p8) 
create (ci6)-[:IsRecommendedBy]->(p5) 
create (ci9)-[:IsRecommendedBy]->(p1) 
create (ci9)-[:IsRecommendedBy]->(p2) 
create (ci6)-[:IsRecommendedBy]->(p6) 
create (ci6)-[:IsRecommendedBy]->(p8) 

//Group membership 
create (ci1)-[:BelongsToGroup]->(gr1) 
create (ci1)-[:BelongsToGroup]->(gr2) 
create (ci2)-[:BelongsToGroup]->(gr1) 
create (ci3)-[:BelongsToGroup]->(gr1) 
create (ci4)-[:BelongsToGroup]->(gr1) 
create (ci4)-[:BelongsToGroup]->(gr2) 
create (ci5)-[:BelongsToGroup]->(gr1) 
create (ci6)-[:BelongsToGroup]->(gr1) 
create (ci7)-[:BelongsToGroup]->(gr1) 
create (ci8)-[:BelongsToGroup]->(gr1) 
create (ci8)-[:BelongsToGroup]->(gr2) 
create (ci10)-[:BelongsToGroup]->(gr1) 
create (ci10)-[:BelongsToGroup]->(gr2) 
; 

и запрос

match (gr)<-[:BelongsToGroup]-(ci:Contentitem)-[:IsRecommendedBy]->(p:Person) 
return gr.name,ci.title,count(p) as Recommendations 
order by gr.name, Recommendations desc 

который возвращает

gr.name   ci.title   Recommendations 
---------------------------------------------------- 
First group  Sometitle 5  5 
First group  Sometitle 8  4 
First group  Sometitle 1  3 
First group  Sometitle 6  3 
First group  Sometitle 2  3 
First group  Sometitle 4  2 
First group  Sometitle 3  2 
First group  Sometitle 10  2 
Second group  Sometitle 8  4 
Second group  Sometitle 1  3 
Second group  Sometitle 10  2 
Second group  Sometitle 4  2 

с N = 3, конечный результат должен быть

gr.name   ci.title   Recommendations 
---------------------------------------------------- 
First group  Sometitle 5  5 
First group  Sometitle 8  4 
First group  Sometitle 1  3 
First group  Sometitle 6  3 
First group  Sometitle 2  3 
Second group  Sometitle 8  4 
Second group  Sometitle 1  3 
Second group  Sometitle 10  2 
Second group  Sometitle 4  2 

с N = 2, конечный результат будет

gr.name   ci.title   Recommendations 
---------------------------------------------------- 
First group  Sometitle 5  5 
First group  Sometitle 8  4 
Second group  Sometitle 8  4 
Second group  Sometitle 1  3 

Что важно в правиле экс-на МКФ является то, что группа с наименьшим количеством рекомендаций, который начинается в положении < = N, не срезается в арбитражной позиции, но полностью включенной. Таким образом, когда мы имеем дело до обрезания, как этот

gr.name   ci.title   Recommendations 
---------------------------------------------------- 
First group  Sometitle 5  5 
First group  Sometitle 8  4 
First group  Sometitle 1  4 
First group  Sometitle 6  3 
First group  Sometitle 2  3 
First group  Sometitle 4  2 
First group  Sometitle 3  2 
First group  Sometitle 10  2 
Second group  Sometitle 8  4 
Second group  Sometitle 1  4 
Second group  Sometitle 10  2 
Second group  Sometitle 4  2 
Second group  Sometitle 7  1 

конечный результат для N = 3 будет:

gr.name   ci.title   Recommendations 
---------------------------------------------------- 
First group  Sometitle 5  5 
First group  Sometitle 8  4 
First group  Sometitle 1  4 
Second group  Sometitle 8  4 
Second group  Sometitle 1  4 
Second group  Sometitle 10  2 
Second group  Sometitle 4  2 

и для N = 2

gr.name   ci.title   Recommendations 
---------------------------------------------------- 
First group  Sometitle 5  5 
First group  Sometitle 8  4 
First group  Sometitle 1  4 
Second group  Sometitle 8  4 
Second group  Sometitle 1  4 
+0

Можете ли вы привести еще один пример для N = 2 или что-то еще? N = 3, дающий окончательный результат, не имеет для меня смысла. –

+0

Думаю, я понял это. –

+0

для N = 2, я только адаптировал коалесценцию до с gr, coalesce (обрезание [1], обрезание [0]) как обрезание , и оно отлично работало. Но есть улов. Когда первые 3 названия имеют одинаковое количество рекомендаций, а у остальных меньше. есть еще случай, который не охвачен. Будет добавлен пример – Graphileon

ответ

3

Я не» Думаю, что есть чистый способ сделать именно то, что вы хотите, но вот моя лучшая попытка. Это требует второго матча после того, как вы выясните отсечку.

Что-то вроде этого:

match (gr)<-[:BelongsToGroup]-(ci:Contentitem)-[:IsRecommendedBy]->(p:Person) 
with gr, ci, count(p) as recommendations 
order by recommendations desc 
with gr, collect(recommendations) as cutoffs 
// coalesce here to avoid null problems if you don't have N=3 distinct recommendations 
with gr, coalesce(cutoffs[2], cutoffs[1], cutoffs[0]) as cutoff 
match (gr)<-[:BelongsToGroup]-(ci:Contentitem)-[:IsRecommendedBy]->(p:Person) 
with gr, ci, count(p) as recommendations, cutoff 
where recommendations >= cutoff 
return gr.name, ci.title, recommendations, cutoff 
order by gr.name, recommendations desc; 

дает:

+------------------------------------------------------------+ 
| gr.name  | ci.title  | recommendations | cutoff | 
+------------------------------------------------------------+ 
| "First group" | "Sometitle 5" | 5    | 3  | 
| "First group" | "Sometitle 8" | 4    | 3  | 
| "First group" | "Sometitle 1" | 3    | 3  | 
| "First group" | "Sometitle 6" | 3    | 3  | 
| "First group" | "Sometitle 2" | 3    | 3  | 
| "Second group" | "Sometitle 8" | 4    | 2  | 
| "Second group" | "Sometitle 1" | 3    | 2  | 
| "Second group" | "Sometitle 4" | 2    | 2  | 
| "Second group" | "Sometitle 10" | 2    | 2  | 
+------------------------------------------------------------+ 
9 rows 

обновление: Мне пришло в голову, что вы, вероятно, хотите, чтобы пройти в N вместо иметь его закодированы, как это с COALESCE. В этом случае, вы можете сделать:

with gr, reduce(acc=cutoffs[0], x in range(0, {N}-1)| coalesce(cutoffs[x], acc)) as cutoff 

Это будет проходить в диапазоне от 0 до N-1 без необходимости жесткого кода его, как первое решение.

+0

обновил мой запрос, чтобы он не отличался. –

+0

добавил немного более дружественную к параметрам версию. –

2

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

MATCH (gr)<-[:BelongsToGroup]-(ci:Contentitem)-[r:IsRecommendedBy]->() // I dropped (p:Person) since it's not really relevant, and counted the [:IsRecommendedBy] instead 
WITH gr, [ci, count(r)] AS document 
ORDER BY document[1] desc 
WITH gr, collect(document) as documents, collect(document[1]) as recommendations 
WITH gr, documents, recommendations, 
    CASE WHEN length(recommendations) >= {n} THEN {n}-1 ELSE length(recommendations)-1 END as ix 
RETURN gr.name AS group,[doc IN documents 
    WHERE doc[1]>= recommendations[ix]| [(doc[0]).title, doc[1]]] AS documents 

Я не знаю, если это обязательно лучше, но это по-другому, с одной спички, CASE WHEN вместо REDUCE/COALESCE, чтобы избежать из проблемы индекса и возвращения на одну строку для каждой группы с упорядоченным набором пар [документов, рекомендаций]; возможно, там что-то есть.

+0

пример в консоли: http://console.neo4j.org/r/50p975 – jjaderberg

+0

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

Смежные вопросы