--------------------NOTEBOOK_Ch10_Movies_Development--------------------
--------------------CELL_MARKDOWN_1--------------------
# ![bono](https://raw.githubusercontent.com/datastax/graph-book/master/notebooks/images/logos/grem-10.png) Rozdział 10 
## Rekomendacje w środowisku roboczym
Autor notatnika: [Denise Gosnell](https://twitter.com/DeniseKGosnell)

Konkurs Netflix Prize spopularyzował ideę wykorzystania relacji między użytkownikami i filmami w celu przewidywania i personalizacji doświadczenia cyfrowego. Ten pomysł -- o postrzeganiu danych w postaci grafu -- był jedną z przyczyn rozwoju myślenia grafowego.

Ten pomysł omawiamy w rozdziale 10. i w tym notatniku.

---------------

**&#9888;** Nie wszystkie komórki rozwijają się domyślnie. Ich treść jest ukryta, jak w przypadku tej komórki. Kliknij oko w prawym rogu komórki, aby wyświetlić i ukryć kod komórki.
--------------------CELL_MARKDOWN_2--------------------
## <div id="topKey"></div>Ten notatnik jest podzielony na cztery części:
#### [Krok 1](#step1): Tworzenie schematu grafu
#### [Krok 2](#step2): Wstawianie danych
#### [Krok 3](#step3): Przegląd zapytań: sąsiedztwa, drzewa i drogi
#### [Krok 4](#step4): Proste zapytanie o rekomendacje

--------------------CELL_MARKDOWN_3--------------------
## <div id="step1"></div>Krok 1: Tworzenie schematu grafu
[Początek &#x2191;](#topKey)
[Następne: Wstawianie danych &#x2193;](#step2)
--------------------CELL_MARKDOWN_4--------------------
Roboczy model danych dla rozdziału 10.:
![ch10_model](https://raw.githubusercontent.com/datastax/graph-book/master/notebooks/images/schema/ch10_schema.png "Schemat roboczy przykładu z rekomendacjami filmów.")
--------------------CELL_MARKDOWN_5--------------------
#### Tworzenie etykiet wierzchołków
--------------------CELL_GREMLIN_6--------------------
schema.vertexLabel('Movie').
       ifNotExists().
       partitionBy('movie_id', Bigint).
       property("tmdb_id", Text).
       property("imdb_id", Text).
       property("movie_title", Text).
       property("release_date", Text).
       property("production_company", Text).
       property("overview", Text).
       property("popularity", Double).
       property("budget", Bigint).
       property("revenue", Bigint).
       create();

schema.vertexLabel('User').
       ifNotExists().
       partitionBy('user_id', Int).
       property('user_name', Text). // Uzupełnione dane losowe
       create();

schema.vertexLabel('Tag').
       ifNotExists().
       partitionBy('tag_id', Int).
       property("tag_name", Text).
       create();
       
schema.vertexLabel('Genre').
       ifNotExists().
       partitionBy('genre_name', Text).
       create();

schema.vertexLabel('Actor').
       ifNotExists().
       partitionBy('actor_name', Text).
       create();
--------------------CELL_MARKDOWN_7--------------------
#### Tworzenie etykiet krawędzi
--------------------CELL_GREMLIN_8--------------------
schema.edgeLabel('topic_tagged').
       ifNotExists().
       from('Movie').
       to('Tag').
       property('relevance', Double).
       create()

schema.edgeLabel('belongs_to').
       ifNotExists().
       from('Movie').
       to('Genre').
       create()

schema.edgeLabel('rated').
       ifNotExists().
       from('User').
       to('Movie').
       clusterBy('timestamp', Text). // Ułatwia korzystanie ze standardu ISO 8601 w przykładach
       property('rating', Double).
       create()

schema.edgeLabel('tagged').
       ifNotExists().
       from('User').
       to('Movie').
       clusterBy('timestamp', Text). // Ułatwia korzystanie ze standardu ISO 8601 w przykładach
       property('tag_name', Text).
       create()

schema.edgeLabel('acted_in').
       ifNotExists().
       from('Actor').
       to('Movie').
       property('year', Int).
       create()

schema.edgeLabel('collaborated_with').
       ifNotExists().
       from('Actor').
       to('Actor').
       clusterBy('year', Int).
       create()
--------------------CELL_MARKDOWN_9--------------------
## <div id="step2"></div>Krok 2: Wstawianie danych
[Początek &#8593;](#topKey)
[Wstecz: Schemat &#x21b5;](#step1)
[Następne: Przegląd zapytań: sąsiedztwa, drzewa i drogi &#x2193;](#step3)
--------------------CELL_MARKDOWN_10--------------------
#### Wykonaj poniższe instrukcje dotyczące importowania danych grafowych za pomocą narzędzia DataStax Bulk Loader.

Oto instrukcje:
1. Otwórz skrypt do wczytywania danych dla rozdziału 10. <tt>./ch10_load.sh</tt>
2. Uaktualnij ścieżki, aby wskazywały na poprawne katalogi w lokalnym środowisku
3. Uruchom skrypt: <tt>./ch10_load.sh</tt>
--------------------CELL_MARKDOWN_11--------------------
#### Weryfikacja wczytywania
W grafie powinny się znajdować następujące elementy:

**Wierzchołki**
Movie: 329,469
User: 138,493
Tag: 104
Genre: 1,168
Actor: 260,859

**Krawędzie**
belongs_to: 523,688
topic_tagged: 100,000
rated: 100,139  (próbka 100k plus oceny wystawione przez użytkownika 134558 dla przeglądu zapytań)
tagged: 465,320
acted_in: 836,407
collaborated_with: 2,706,174

Następne dwa zapytania służą do weryfikacji danych: jedno zlicza wierzchołki, a drugie krawędzie
--------------------CELL_GREMLIN_12--------------------
g.V().hasLabel("Movie").count()
--------------------CELL_GREMLIN_13--------------------
g.E().hasLabel("rated").count()
--------------------CELL_MARKDOWN_14--------------------
## <div id="step3"></div>Krok 3: Przegląd zapytań: sąsiedztwa, drzewa i drogi 
[Początek &#8593;](#topKey)
[Wstecz: Wstawianie danych &#x21b5;](#step2)
[Następne: Proste zapytanie o rekomendacje &#x2193;](#step4)

--------------------CELL_MARKDOWN_15--------------------
#### Proste zapytanie o sąsiedztwa: znajdź wszystkie filmy ocenione przez użytkownika, wraz z wartościami ocen
--------------------CELL_GREMLIN_16--------------------
dev.V().has("User","user_id", 134558). // WHERE: zaczynamy od użytkownika 
        outE("rated").                 // JOIN: wyjście do wszystkich krawędzie "rated"
          as("edges").                 // SAVE: zapisanie wszystkich krawędzi do późniejszego użycia
        inV().                         // JOIN: przejście do wierzchołków filmów
        project("movie", "rating", "timestamp"). // CREATE tworzenie danych w formacie JSON
          by(values("movie_title")).             // SELECT pobieramy tytuły filmów
          by(select("edges").values("rating")).  // SELECT pobieramy oceny
          by(select("edges").values("timestamp"))// SELECT pobieramy znacznik czasu
--------------------CELL_MARKDOWN_17--------------------
#### Grupowanie filmów ocenionych przez użytkownika w następujące kategorie:
Liked = [4.5, 5]
Neutral = [3.0, 4.5)
Disliked = [0, 3.0)
--------------------CELL_GREMLIN_18--------------------
dev.V().has("User","user_id", 134558). // WHERE: zaczynamy od użytkownika
        outE("rated").                 // JOIN: wyjście do wszystkich krawędzie "rated"
        group().                       // CREATE: tworzymy grupę
          by(values("rating").         // SELECT KEYS: według ocen
             coalesce(__.is(gte(4.5)).constant("liked"),   // klucz 1: "liked"
                      __.is(gte(3.0)).constant("neutral"), // klucz 2: "neutral"
                       constant("disliked"))).             // klucz 3: "disliked"
          by(inV().values("movie_title").fold()) // SELECT VALUES: wartości
--------------------CELL_MARKDOWN_19--------------------
#### Podstawowe zapytanie wykorzystujące drzewa: tworzymy drzewo aktorów grających z Kevinem Baconem po 2009 
--------------------CELL_GREMLIN_20--------------------
dev.V().has("Actor", "actor_name", "Kevin Bacon").as("Mr. Bacon").
        repeat(outE("collaborated_with").has("year", gte(2009)).as("year").
               inV().as("collaborated_with").
               simplePath()). 
        times(3).
        path().
          by("actor_name").
          by("year")
--------------------CELL_MARKDOWN_21--------------------
#### Podstawowe zapytanie wykorzystujące drogi: znajdź 3 pierwsze najkrótsze drogi między Kevinem Baconem a Morganem Freemanem i pokaż rok, w którym grali w tym samym filmie
--------------------CELL_GREMLIN_22--------------------
dev.V().has("Actor", "actor_name", "Kevin Bacon").as("Mr. Bacon").
        repeat(outE("collaborated_with").as("year").
               inV().as("collaborated_with")). 
        until(has("Actor", "actor_name", "Morgan Freeman").as("Mr. Freeman")).
        limit(3).
        path().
          by("actor_name").
          by("year")
--------------------CELL_MARKDOWN_23--------------------
## <div id="step4"></div>Krok 4: Proste zapytanie o rekomendacje
[Początek &#8593;](#topKey)
[Wstecz: Przegląd zapytań: sąsiedztwa, drzewa i drogi &#x21b5;](#step3)
--------------------CELL_MARKDOWN_24--------------------
#### Filtrowanie kolaboratywne oparte na elemencie: liczenie dróg
--------------------CELL_GREMLIN_25--------------------
dev.V().has("User","user_id", 694).   // szukamy użytkownika
   outE("rated").                     // przechodzimy do wszystkich ocenionych przez niego filmów
     order().by("timestamp", desc).   // sortujemy wszystkie krawędzie według czasu
     limit(1).inV().                  // przechodzimy do ostatniego ocenionego filmu
     aggregate("originalMovie").      // umieszczamy go w kolekcji
   inE("rated").has("rating", gt(4.5)).outV().  // użytkownicy, którzy ocenili ten film na 5
   outE("rated").has("rating", gt(4.5)).inV().  // pełny zbiór rekomendacji
   where(without('originalMovie')).   // usuwamy oryginalny film
   group().                           // tworzymy mapę rekomendacji
     by("movie_title").               // kluczem jest tytuł filmu, 
     by(count()).                     // wartością będzie całkowita liczba ocen
   unfold().                          // rozwijamy wszystkie elementy mapy do potoku
   order().                           // sortujemy wyniki
     by(values, desc)                 // według ich liczby w kolejności malejącej

--------------------CELL_MARKDOWN_26--------------------
### Filtrowanie kolaboratywne oparte na elemencie: NPS
--------------------CELL_GREMLIN_27--------------------
dev.withSack(0.0).                  // używamy obiektu sack do obliczenia punktacji NPS
   V().has("User","user_id", 694).    
   outE("rated").                     
     order().by("timestamp", desc).   
     limit(1).inV().                  
     aggregate("originalMovie").      
   inE("rated").has("rating", gt(4.5)).outV().
   outE("rated").                           // krawędzie z ocenami wystawionych przez wszystkich użytkowników
      choose(values('rating').is(gte(4.0)), // warunek
             sack(sum).by(constant(1.0)),   // dodajemy 1, jeśli użytkownikowi film się podoba  
             sack(minus).by(constant(1.0))).// odejmujemy 1, jeśli użytkownikowi film się nie podoba
      inV().                                // powrót do filmów; to są rekomendacje
   where(without('originalMovie')). // usuwamy oryginalny film
   group().                         // tworzymy mapę, która scali ze sobą przejścia
     by(values("movie_title")).     // kluczami są tytuły filmów z wierzchołków filmów
     by(sack().sum()).              // NPS: sumujemy wartości sack przejść
   unfold().                        // rozwijamy mapę do potoku
   order().                         // sortujemy
     by(values, desc)               // według wartości NPS w kolejności malejącej
--------------------CELL_MARKDOWN_28--------------------
### Filtrowanie kolaboratywne oparte na elemencie: znormalizowana punktacja NPS
--------------------CELL_GREMLIN_29--------------------
dev.withSack(0.0).                            // używamy obiektu sack do obliczenia punktacji NPS
   V().has("User","user_id", 694).    
   outE("rated").                     
     order().by("timestamp", desc).   
     limit(1).inV().                  
     aggregate("originalMovie").      
   inE("rated").has("rating", gt(4.5)).outV().
   outE("rated").                             
      choose(values('rating').is(gte(4.0)),   
             sack(sum).by(constant(1.0)),     // dodajemy 1, jeśli użytkownikowi film się podoba 
             sack(minus).by(constant(1.0))).  // odejmujemy 1, jeśli użytkownikowi film się nie podoba
      inV().                                  // powrót do filmów; to są rekomendacje
   where(without('originalMovie')).
   sample(100).
   group().                           // tworzymy mapę, która scali ze sobą przejścia
     by("movie_title").               // kluczami są tytuły filmów z wierzchołków filmów
     by(project("numerator", "denominator").  // wartość wynikową obliczamy wzorem NPS/stopień(film)
          by(sack().sum()).                   // wartość NPS
          by(inE("rated").count()).           // stopień filmu
        math('numerator/denominator'))        // tak dzielimy wartości
--------------------CELL_MARKDOWN_30--------------------
### Koniec notatnika
[Top &#8593;](#topKey)

--------------------CELL_GREMLIN_31--------------------
schema.drop()
