Aggregate related nodes across all collections in Neo4j cypher












1














I recently started working with Neo4j/cypher and have been able to successfully build most basic queries that come to mind but the solution to this one escapes me.



The nodes have a very simple relationship model: Books are grouped into Categories



The books will be unique and can be related to multiple categories.



My base query collects the categories, resulting in a set of books with their associated categories:



match (c:Category)-[:contains]-(b:Book)
return b as book, collect(distinct c) as categories


I can then collect the books, resulting in a set of related books and categories:



match (c:Category)-[:contains]-(b:Book)
with b, collect(distinct c) as categories
return collect(distinct b) as books, categories


This seems to be going in the right direction but there are many duplicate books and categories throughout. Here is a pseudo example:



Books                         Categories
-----------------------------------------------
[Easy Home Updates] [Home and Garden]
-----------------------------------------------
[Gardening Today, [Outdoors,
Gardening for Kids, Hobbies,
Green Thumb Made Easy] Gardening]
-----------------------------------------------
[Conversational Spanish, [Spanish,
Spanish for Travelers, Travel,
Advanced Spanish] Language]
-----------------------------------------------
[Gardening Today, [Gardening,
Gardening for Kids] Kids]
-----------------------------------------------
[Home Improvement, [Home Improvement,
Easy Home Updates, Home and Garden,
Family Home Projects] Family]
-----------------------------------------------
[Gardening Today] [Gardening]
-----------------------------------------------
[Conversational Spanish, [Language,
Advanced Spanish] Spanish]


I cannot seem to find a way to aggregate the duplicates either in the initial match with filtering or the reduce and apoc functions.



The desired result would be to reduce both the book and category collections. Something like this:



Books                         Categories
----------------------------------------------
[Gardening Today, [Gardening,
Gardening for Kids, Outdoors,
Green Thumb Made Easy] Hobbies,
Kids,
Family]
----------------------------------------------
[Conversational Spanish, [Spanish,
Spanish for Travelers, Language,
Advanced Spanish] Travel,
Education]
----------------------------------------------
[Home Improvement, [Home and Garden,
Easy Home Updates, Home Improvement,
Family Home Projects] Construction]


Or maybe my approach is completely off and there is a better, more efficient way to group the related nodes.



Any help you can provide to point me in the right direction would be greatly appreciated. Please let me know if you need any further clarification.










share|improve this question





























    1














    I recently started working with Neo4j/cypher and have been able to successfully build most basic queries that come to mind but the solution to this one escapes me.



    The nodes have a very simple relationship model: Books are grouped into Categories



    The books will be unique and can be related to multiple categories.



    My base query collects the categories, resulting in a set of books with their associated categories:



    match (c:Category)-[:contains]-(b:Book)
    return b as book, collect(distinct c) as categories


    I can then collect the books, resulting in a set of related books and categories:



    match (c:Category)-[:contains]-(b:Book)
    with b, collect(distinct c) as categories
    return collect(distinct b) as books, categories


    This seems to be going in the right direction but there are many duplicate books and categories throughout. Here is a pseudo example:



    Books                         Categories
    -----------------------------------------------
    [Easy Home Updates] [Home and Garden]
    -----------------------------------------------
    [Gardening Today, [Outdoors,
    Gardening for Kids, Hobbies,
    Green Thumb Made Easy] Gardening]
    -----------------------------------------------
    [Conversational Spanish, [Spanish,
    Spanish for Travelers, Travel,
    Advanced Spanish] Language]
    -----------------------------------------------
    [Gardening Today, [Gardening,
    Gardening for Kids] Kids]
    -----------------------------------------------
    [Home Improvement, [Home Improvement,
    Easy Home Updates, Home and Garden,
    Family Home Projects] Family]
    -----------------------------------------------
    [Gardening Today] [Gardening]
    -----------------------------------------------
    [Conversational Spanish, [Language,
    Advanced Spanish] Spanish]


    I cannot seem to find a way to aggregate the duplicates either in the initial match with filtering or the reduce and apoc functions.



    The desired result would be to reduce both the book and category collections. Something like this:



    Books                         Categories
    ----------------------------------------------
    [Gardening Today, [Gardening,
    Gardening for Kids, Outdoors,
    Green Thumb Made Easy] Hobbies,
    Kids,
    Family]
    ----------------------------------------------
    [Conversational Spanish, [Spanish,
    Spanish for Travelers, Language,
    Advanced Spanish] Travel,
    Education]
    ----------------------------------------------
    [Home Improvement, [Home and Garden,
    Easy Home Updates, Home Improvement,
    Family Home Projects] Construction]


    Or maybe my approach is completely off and there is a better, more efficient way to group the related nodes.



    Any help you can provide to point me in the right direction would be greatly appreciated. Please let me know if you need any further clarification.










    share|improve this question



























      1












      1








      1







      I recently started working with Neo4j/cypher and have been able to successfully build most basic queries that come to mind but the solution to this one escapes me.



      The nodes have a very simple relationship model: Books are grouped into Categories



      The books will be unique and can be related to multiple categories.



      My base query collects the categories, resulting in a set of books with their associated categories:



      match (c:Category)-[:contains]-(b:Book)
      return b as book, collect(distinct c) as categories


      I can then collect the books, resulting in a set of related books and categories:



      match (c:Category)-[:contains]-(b:Book)
      with b, collect(distinct c) as categories
      return collect(distinct b) as books, categories


      This seems to be going in the right direction but there are many duplicate books and categories throughout. Here is a pseudo example:



      Books                         Categories
      -----------------------------------------------
      [Easy Home Updates] [Home and Garden]
      -----------------------------------------------
      [Gardening Today, [Outdoors,
      Gardening for Kids, Hobbies,
      Green Thumb Made Easy] Gardening]
      -----------------------------------------------
      [Conversational Spanish, [Spanish,
      Spanish for Travelers, Travel,
      Advanced Spanish] Language]
      -----------------------------------------------
      [Gardening Today, [Gardening,
      Gardening for Kids] Kids]
      -----------------------------------------------
      [Home Improvement, [Home Improvement,
      Easy Home Updates, Home and Garden,
      Family Home Projects] Family]
      -----------------------------------------------
      [Gardening Today] [Gardening]
      -----------------------------------------------
      [Conversational Spanish, [Language,
      Advanced Spanish] Spanish]


      I cannot seem to find a way to aggregate the duplicates either in the initial match with filtering or the reduce and apoc functions.



      The desired result would be to reduce both the book and category collections. Something like this:



      Books                         Categories
      ----------------------------------------------
      [Gardening Today, [Gardening,
      Gardening for Kids, Outdoors,
      Green Thumb Made Easy] Hobbies,
      Kids,
      Family]
      ----------------------------------------------
      [Conversational Spanish, [Spanish,
      Spanish for Travelers, Language,
      Advanced Spanish] Travel,
      Education]
      ----------------------------------------------
      [Home Improvement, [Home and Garden,
      Easy Home Updates, Home Improvement,
      Family Home Projects] Construction]


      Or maybe my approach is completely off and there is a better, more efficient way to group the related nodes.



      Any help you can provide to point me in the right direction would be greatly appreciated. Please let me know if you need any further clarification.










      share|improve this question















      I recently started working with Neo4j/cypher and have been able to successfully build most basic queries that come to mind but the solution to this one escapes me.



      The nodes have a very simple relationship model: Books are grouped into Categories



      The books will be unique and can be related to multiple categories.



      My base query collects the categories, resulting in a set of books with their associated categories:



      match (c:Category)-[:contains]-(b:Book)
      return b as book, collect(distinct c) as categories


      I can then collect the books, resulting in a set of related books and categories:



      match (c:Category)-[:contains]-(b:Book)
      with b, collect(distinct c) as categories
      return collect(distinct b) as books, categories


      This seems to be going in the right direction but there are many duplicate books and categories throughout. Here is a pseudo example:



      Books                         Categories
      -----------------------------------------------
      [Easy Home Updates] [Home and Garden]
      -----------------------------------------------
      [Gardening Today, [Outdoors,
      Gardening for Kids, Hobbies,
      Green Thumb Made Easy] Gardening]
      -----------------------------------------------
      [Conversational Spanish, [Spanish,
      Spanish for Travelers, Travel,
      Advanced Spanish] Language]
      -----------------------------------------------
      [Gardening Today, [Gardening,
      Gardening for Kids] Kids]
      -----------------------------------------------
      [Home Improvement, [Home Improvement,
      Easy Home Updates, Home and Garden,
      Family Home Projects] Family]
      -----------------------------------------------
      [Gardening Today] [Gardening]
      -----------------------------------------------
      [Conversational Spanish, [Language,
      Advanced Spanish] Spanish]


      I cannot seem to find a way to aggregate the duplicates either in the initial match with filtering or the reduce and apoc functions.



      The desired result would be to reduce both the book and category collections. Something like this:



      Books                         Categories
      ----------------------------------------------
      [Gardening Today, [Gardening,
      Gardening for Kids, Outdoors,
      Green Thumb Made Easy] Hobbies,
      Kids,
      Family]
      ----------------------------------------------
      [Conversational Spanish, [Spanish,
      Spanish for Travelers, Language,
      Advanced Spanish] Travel,
      Education]
      ----------------------------------------------
      [Home Improvement, [Home and Garden,
      Easy Home Updates, Home Improvement,
      Family Home Projects] Construction]


      Or maybe my approach is completely off and there is a better, more efficient way to group the related nodes.



      Any help you can provide to point me in the right direction would be greatly appreciated. Please let me know if you need any further clarification.







      neo4j cypher






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 15 '18 at 3:29

























      asked Nov 14 '18 at 6:07









      Jon McGee

      63




      63
























          1 Answer
          1






          active

          oldest

          votes


















          0














          Creating your model



          For the ease of possible further answers and solutions I note my graph creating statement:



          CREATE
          (categoryHome:Category {name: 'Home and Garden'}),
          (categoryOutdoor:Category {name: 'Outdoors'}),
          (categoryHobby:Category {name: 'Hobbies'}),
          (categoryGarden:Category {name: 'Gardening'}),
          (categorySpanish:Category {name: 'Spanish'}),
          (categoryTravel:Category {name: 'Travel'}),
          (categoryLanguage:Category {name: 'Language'}),
          (categoryKids:Category {name: 'Kids'}),
          (categoryImprovement:Category {name: 'Home Improvement'}),
          (categoryFamily:Category {name: 'Family'}),
          (book1:Book {name: 'Easy Home Updates'}),
          (book2:Book {name: 'Gardening Today'}),
          (book3:Book {name: 'Gardening for Kids'}),
          (book4:Book {name: 'Green Thumb Made Easy'}),
          (book5:Book {name: 'Conversational Spanish'}),
          (book6:Book {name: 'Spanish for Travelers'}),
          (book7:Book {name: 'Advanced Spanish'}),
          (book8:Book {name: 'Home Improvement'}),
          (book9:Book {name: 'Easy Home Updates'}),
          (book10:Book {name: 'Family Home Projects'}),
          (categoryHome)-[:CONTAINS]->(book1),
          (categoryHome)-[:CONTAINS]->(book8),
          (categoryHome)-[:CONTAINS]->(book9),
          (categoryHome)-[:CONTAINS]->(book10),
          (categoryOutdoor)-[:CONTAINS]->(book2),
          (categoryOutdoor)-[:CONTAINS]->(book3),
          (categoryOutdoor)-[:CONTAINS]->(book4),
          (categoryHobby)-[:CONTAINS]->(book2),
          (categoryHobby)-[:CONTAINS]->(book3),
          (categoryHobby)-[:CONTAINS]->(book4),
          (categoryGarden)-[:CONTAINS]->(book2),
          (categoryGarden)-[:CONTAINS]->(book3),
          (categoryGarden)-[:CONTAINS]->(book4),
          (categorySpanish)-[:CONTAINS]->(book5),
          (categorySpanish)-[:CONTAINS]->(book6),
          (categorySpanish)-[:CONTAINS]->(book7),
          (categoryTravel)-[:CONTAINS]->(book5),
          (categoryTravel)-[:CONTAINS]->(book6),
          (categoryTravel)-[:CONTAINS]->(book7),
          (categoryLanguage)-[:CONTAINS]->(book5),
          (categoryLanguage)-[:CONTAINS]->(book6),
          (categoryLanguage)-[:CONTAINS]->(book7),
          (categoryKids)-[:CONTAINS]->(book2),
          (categoryKids)-[:CONTAINS]->(book3),
          (categoryImprovement)-[:CONTAINS]->(book8),
          (categoryImprovement)-[:CONTAINS]->(book9),
          (categoryImprovement)-[:CONTAINS]->(book10),
          (categoryFamily)-[:CONTAINS]->(book8),
          (categoryFamily)-[:CONTAINS]->(book9),
          (categoryFamily)-[:CONTAINS]->(book10);


          Explanation



          In my eyes, your technical implementation is right, but your requirements from a professional point of view are not consistent.
          Let us choose an example. You expect the following record:



          BOOKS:                        CATEGORIES:
          Gardening Today, Gardening,
          Gardening for Kids, Outdoors,
          Green Thumb Made Easy Hobbies,
          Kids,
          Family


          By executing the following Cypher query, the Family entry is not a valid category for the book Gardening Today.



          MATCH (book:Book {name: 'Gardening Today'})<-[:CONTAINS]-(category:Category)
          RETURN DISTINCT book.name, collect(category.name);

          ╒═════════════════╤═════════════════════════════════════════╕
          │"book.name" │"collect(category.name)" │
          ╞═════════════════╪═════════════════════════════════════════╡
          │"Gardening Today"│["Kids","Gardening","Hobbies","Outdoors"]│
          └─────────────────┴─────────────────────────────────────────┘


          Doing the cross check confirms category Family contains completly other books.



          MATCH (category:Category {name: 'Family'})-[:CONTAINS]->(book:Book)
          RETURN DISTINCT category.name, collect(book.name);
          ╒═══════════════╤═══════════════════════════════════════════════════════════════╕
          │"category.name"│"collect(book.name)" │
          ╞═══════════════╪═══════════════════════════════════════════════════════════════╡
          │"Family" │["Family Home Projects","Easy Home Updates","Home Improvement"]│
          └───────────────┴───────────────────────────────────────────────────────────────┘


          This procedure continues to propagate. That's the reason why you get differently sliced result sets as expected. So your already implemented approach is correct:



          MATCH path = (category:Category)-[:CONTAINS]->(book:Book)
          WITH collect(category.name) AS categoryGroup, book.name AS bookName
          RETURN categoryGroup, collect(bookName);

          ╒═════════════════════════════════════════════════════════════════╤═════════════════════════════════════════════════════════════════════╕
          │"categoryGroup" │"collect(bookName)" │
          ╞═════════════════════════════════════════════════════════════════╪═════════════════════════════════════════════════════════════════════╡
          │["Spanish","Travel","Language"] │["Spanish for Travelers","Advanced Spanish","Conversational Spanish"]│
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Home Improvement","Family","Home and Garden","Home and Garden"]│["Easy Home Updates"] │
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Hobbies","Gardening","Kids","Outdoors"] │["Gardening Today","Gardening for Kids"] │
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Hobbies","Gardening","Outdoors"] │["Green Thumb Made Easy"] │
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Home Improvement","Family","Home and Garden"] │["Home Improvement","Family Home Projects"] │
          └─────────────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────┘




          Extension



          Underlying Idea



          Because the requested mapping violates the assignment rules (set theory), we can not use the usual pattern matching. Instead, we can achieve our goal through a trick by finding all connected nodes for a given book and preparing them afterwards.



          Please ensure you have installed the Neo4j APOC library.



          graph



          Solution



          MATCH (selectedBook:Book)
          WHERE selectedBook.name = 'Gardening for Kids'
          CALL apoc.path.subgraphNodes(selectedBook, {uniqueness: 'NODE_GLOBAL'}) YIELD node
          WITH collect(DISTINCT node) AS subgraphNodes
          WITH
          filter (node IN subgraphNodes
          WHERE node:Category) AS categories,
          filter (node IN subgraphNodes
          WHERE node:Book) AS books
          WITH categories, books
          UNWIND categories AS category
          UNWIND books AS book
          RETURN collect(DISTINCT category.name) AS categoryNames, collect(DISTINCT book.name) AS bookNames;


          Explanation




          • lines 1-2: selecting the book under inspection

          • line 3: using the APOC procedure apoc.path.subgraphNodes for locating all connected nodes

          • lines 6-9: sorting the identified nodes by label Category and Book

          • lines 10-13: result preparation


          Results



          Easy Home Updates:



          ╒═══════════════════════════════════════════════╤═══════════════════════════════════════════════════════════════╕
          │"categoryNames" │"bookNames" │
          ╞═══════════════════════════════════════════════╪═══════════════════════════════════════════════════════════════╡
          │["Home and Garden","Family","Home Improvement"]│["Easy Home Updates","Family Home Projects","Home Improvement"]│
          └───────────────────────────────────────────────┴───────────────────────────────────────────────────────────────┘


          Gardening for Kids:



          ╒════════════════════════════════════════╤════════════════════════════════════════╕
          │"categoryNames" │"bookNames" │
          ╞════════════════════════════════════════╪════════════════════════════════════════╡
          │["Kids","Gardening","Hobbies","Outdoors"│["Gardening for Kids","Gardening Today",│
          │] │"Green Thumb Made Easy"] │
          └────────────────────────────────────────┴────────────────────────────────────────┘





          share|improve this answer























          • Thanks for taking the time to respond. Your final result set is where I am still stuck. I can get to this point but need to further reduce the results. For instance, how would one approach combining rows with related nodes such as rows 3 and 4, 2 and 5, etc. Rows 3 and 4 for example are clearly related but are still separate. I would like to combine those together along with the associated books in a single row. Hopefully that makes sense.
            – Jon McGee
            Nov 14 '18 at 14:45












          • @Jon McGee I extended my answer by an solution for your requirements.
            – ThirstForKnowledge
            Nov 14 '18 at 20:02











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53294084%2faggregate-related-nodes-across-all-collections-in-neo4j-cypher%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          0














          Creating your model



          For the ease of possible further answers and solutions I note my graph creating statement:



          CREATE
          (categoryHome:Category {name: 'Home and Garden'}),
          (categoryOutdoor:Category {name: 'Outdoors'}),
          (categoryHobby:Category {name: 'Hobbies'}),
          (categoryGarden:Category {name: 'Gardening'}),
          (categorySpanish:Category {name: 'Spanish'}),
          (categoryTravel:Category {name: 'Travel'}),
          (categoryLanguage:Category {name: 'Language'}),
          (categoryKids:Category {name: 'Kids'}),
          (categoryImprovement:Category {name: 'Home Improvement'}),
          (categoryFamily:Category {name: 'Family'}),
          (book1:Book {name: 'Easy Home Updates'}),
          (book2:Book {name: 'Gardening Today'}),
          (book3:Book {name: 'Gardening for Kids'}),
          (book4:Book {name: 'Green Thumb Made Easy'}),
          (book5:Book {name: 'Conversational Spanish'}),
          (book6:Book {name: 'Spanish for Travelers'}),
          (book7:Book {name: 'Advanced Spanish'}),
          (book8:Book {name: 'Home Improvement'}),
          (book9:Book {name: 'Easy Home Updates'}),
          (book10:Book {name: 'Family Home Projects'}),
          (categoryHome)-[:CONTAINS]->(book1),
          (categoryHome)-[:CONTAINS]->(book8),
          (categoryHome)-[:CONTAINS]->(book9),
          (categoryHome)-[:CONTAINS]->(book10),
          (categoryOutdoor)-[:CONTAINS]->(book2),
          (categoryOutdoor)-[:CONTAINS]->(book3),
          (categoryOutdoor)-[:CONTAINS]->(book4),
          (categoryHobby)-[:CONTAINS]->(book2),
          (categoryHobby)-[:CONTAINS]->(book3),
          (categoryHobby)-[:CONTAINS]->(book4),
          (categoryGarden)-[:CONTAINS]->(book2),
          (categoryGarden)-[:CONTAINS]->(book3),
          (categoryGarden)-[:CONTAINS]->(book4),
          (categorySpanish)-[:CONTAINS]->(book5),
          (categorySpanish)-[:CONTAINS]->(book6),
          (categorySpanish)-[:CONTAINS]->(book7),
          (categoryTravel)-[:CONTAINS]->(book5),
          (categoryTravel)-[:CONTAINS]->(book6),
          (categoryTravel)-[:CONTAINS]->(book7),
          (categoryLanguage)-[:CONTAINS]->(book5),
          (categoryLanguage)-[:CONTAINS]->(book6),
          (categoryLanguage)-[:CONTAINS]->(book7),
          (categoryKids)-[:CONTAINS]->(book2),
          (categoryKids)-[:CONTAINS]->(book3),
          (categoryImprovement)-[:CONTAINS]->(book8),
          (categoryImprovement)-[:CONTAINS]->(book9),
          (categoryImprovement)-[:CONTAINS]->(book10),
          (categoryFamily)-[:CONTAINS]->(book8),
          (categoryFamily)-[:CONTAINS]->(book9),
          (categoryFamily)-[:CONTAINS]->(book10);


          Explanation



          In my eyes, your technical implementation is right, but your requirements from a professional point of view are not consistent.
          Let us choose an example. You expect the following record:



          BOOKS:                        CATEGORIES:
          Gardening Today, Gardening,
          Gardening for Kids, Outdoors,
          Green Thumb Made Easy Hobbies,
          Kids,
          Family


          By executing the following Cypher query, the Family entry is not a valid category for the book Gardening Today.



          MATCH (book:Book {name: 'Gardening Today'})<-[:CONTAINS]-(category:Category)
          RETURN DISTINCT book.name, collect(category.name);

          ╒═════════════════╤═════════════════════════════════════════╕
          │"book.name" │"collect(category.name)" │
          ╞═════════════════╪═════════════════════════════════════════╡
          │"Gardening Today"│["Kids","Gardening","Hobbies","Outdoors"]│
          └─────────────────┴─────────────────────────────────────────┘


          Doing the cross check confirms category Family contains completly other books.



          MATCH (category:Category {name: 'Family'})-[:CONTAINS]->(book:Book)
          RETURN DISTINCT category.name, collect(book.name);
          ╒═══════════════╤═══════════════════════════════════════════════════════════════╕
          │"category.name"│"collect(book.name)" │
          ╞═══════════════╪═══════════════════════════════════════════════════════════════╡
          │"Family" │["Family Home Projects","Easy Home Updates","Home Improvement"]│
          └───────────────┴───────────────────────────────────────────────────────────────┘


          This procedure continues to propagate. That's the reason why you get differently sliced result sets as expected. So your already implemented approach is correct:



          MATCH path = (category:Category)-[:CONTAINS]->(book:Book)
          WITH collect(category.name) AS categoryGroup, book.name AS bookName
          RETURN categoryGroup, collect(bookName);

          ╒═════════════════════════════════════════════════════════════════╤═════════════════════════════════════════════════════════════════════╕
          │"categoryGroup" │"collect(bookName)" │
          ╞═════════════════════════════════════════════════════════════════╪═════════════════════════════════════════════════════════════════════╡
          │["Spanish","Travel","Language"] │["Spanish for Travelers","Advanced Spanish","Conversational Spanish"]│
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Home Improvement","Family","Home and Garden","Home and Garden"]│["Easy Home Updates"] │
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Hobbies","Gardening","Kids","Outdoors"] │["Gardening Today","Gardening for Kids"] │
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Hobbies","Gardening","Outdoors"] │["Green Thumb Made Easy"] │
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Home Improvement","Family","Home and Garden"] │["Home Improvement","Family Home Projects"] │
          └─────────────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────┘




          Extension



          Underlying Idea



          Because the requested mapping violates the assignment rules (set theory), we can not use the usual pattern matching. Instead, we can achieve our goal through a trick by finding all connected nodes for a given book and preparing them afterwards.



          Please ensure you have installed the Neo4j APOC library.



          graph



          Solution



          MATCH (selectedBook:Book)
          WHERE selectedBook.name = 'Gardening for Kids'
          CALL apoc.path.subgraphNodes(selectedBook, {uniqueness: 'NODE_GLOBAL'}) YIELD node
          WITH collect(DISTINCT node) AS subgraphNodes
          WITH
          filter (node IN subgraphNodes
          WHERE node:Category) AS categories,
          filter (node IN subgraphNodes
          WHERE node:Book) AS books
          WITH categories, books
          UNWIND categories AS category
          UNWIND books AS book
          RETURN collect(DISTINCT category.name) AS categoryNames, collect(DISTINCT book.name) AS bookNames;


          Explanation




          • lines 1-2: selecting the book under inspection

          • line 3: using the APOC procedure apoc.path.subgraphNodes for locating all connected nodes

          • lines 6-9: sorting the identified nodes by label Category and Book

          • lines 10-13: result preparation


          Results



          Easy Home Updates:



          ╒═══════════════════════════════════════════════╤═══════════════════════════════════════════════════════════════╕
          │"categoryNames" │"bookNames" │
          ╞═══════════════════════════════════════════════╪═══════════════════════════════════════════════════════════════╡
          │["Home and Garden","Family","Home Improvement"]│["Easy Home Updates","Family Home Projects","Home Improvement"]│
          └───────────────────────────────────────────────┴───────────────────────────────────────────────────────────────┘


          Gardening for Kids:



          ╒════════════════════════════════════════╤════════════════════════════════════════╕
          │"categoryNames" │"bookNames" │
          ╞════════════════════════════════════════╪════════════════════════════════════════╡
          │["Kids","Gardening","Hobbies","Outdoors"│["Gardening for Kids","Gardening Today",│
          │] │"Green Thumb Made Easy"] │
          └────────────────────────────────────────┴────────────────────────────────────────┘





          share|improve this answer























          • Thanks for taking the time to respond. Your final result set is where I am still stuck. I can get to this point but need to further reduce the results. For instance, how would one approach combining rows with related nodes such as rows 3 and 4, 2 and 5, etc. Rows 3 and 4 for example are clearly related but are still separate. I would like to combine those together along with the associated books in a single row. Hopefully that makes sense.
            – Jon McGee
            Nov 14 '18 at 14:45












          • @Jon McGee I extended my answer by an solution for your requirements.
            – ThirstForKnowledge
            Nov 14 '18 at 20:02
















          0














          Creating your model



          For the ease of possible further answers and solutions I note my graph creating statement:



          CREATE
          (categoryHome:Category {name: 'Home and Garden'}),
          (categoryOutdoor:Category {name: 'Outdoors'}),
          (categoryHobby:Category {name: 'Hobbies'}),
          (categoryGarden:Category {name: 'Gardening'}),
          (categorySpanish:Category {name: 'Spanish'}),
          (categoryTravel:Category {name: 'Travel'}),
          (categoryLanguage:Category {name: 'Language'}),
          (categoryKids:Category {name: 'Kids'}),
          (categoryImprovement:Category {name: 'Home Improvement'}),
          (categoryFamily:Category {name: 'Family'}),
          (book1:Book {name: 'Easy Home Updates'}),
          (book2:Book {name: 'Gardening Today'}),
          (book3:Book {name: 'Gardening for Kids'}),
          (book4:Book {name: 'Green Thumb Made Easy'}),
          (book5:Book {name: 'Conversational Spanish'}),
          (book6:Book {name: 'Spanish for Travelers'}),
          (book7:Book {name: 'Advanced Spanish'}),
          (book8:Book {name: 'Home Improvement'}),
          (book9:Book {name: 'Easy Home Updates'}),
          (book10:Book {name: 'Family Home Projects'}),
          (categoryHome)-[:CONTAINS]->(book1),
          (categoryHome)-[:CONTAINS]->(book8),
          (categoryHome)-[:CONTAINS]->(book9),
          (categoryHome)-[:CONTAINS]->(book10),
          (categoryOutdoor)-[:CONTAINS]->(book2),
          (categoryOutdoor)-[:CONTAINS]->(book3),
          (categoryOutdoor)-[:CONTAINS]->(book4),
          (categoryHobby)-[:CONTAINS]->(book2),
          (categoryHobby)-[:CONTAINS]->(book3),
          (categoryHobby)-[:CONTAINS]->(book4),
          (categoryGarden)-[:CONTAINS]->(book2),
          (categoryGarden)-[:CONTAINS]->(book3),
          (categoryGarden)-[:CONTAINS]->(book4),
          (categorySpanish)-[:CONTAINS]->(book5),
          (categorySpanish)-[:CONTAINS]->(book6),
          (categorySpanish)-[:CONTAINS]->(book7),
          (categoryTravel)-[:CONTAINS]->(book5),
          (categoryTravel)-[:CONTAINS]->(book6),
          (categoryTravel)-[:CONTAINS]->(book7),
          (categoryLanguage)-[:CONTAINS]->(book5),
          (categoryLanguage)-[:CONTAINS]->(book6),
          (categoryLanguage)-[:CONTAINS]->(book7),
          (categoryKids)-[:CONTAINS]->(book2),
          (categoryKids)-[:CONTAINS]->(book3),
          (categoryImprovement)-[:CONTAINS]->(book8),
          (categoryImprovement)-[:CONTAINS]->(book9),
          (categoryImprovement)-[:CONTAINS]->(book10),
          (categoryFamily)-[:CONTAINS]->(book8),
          (categoryFamily)-[:CONTAINS]->(book9),
          (categoryFamily)-[:CONTAINS]->(book10);


          Explanation



          In my eyes, your technical implementation is right, but your requirements from a professional point of view are not consistent.
          Let us choose an example. You expect the following record:



          BOOKS:                        CATEGORIES:
          Gardening Today, Gardening,
          Gardening for Kids, Outdoors,
          Green Thumb Made Easy Hobbies,
          Kids,
          Family


          By executing the following Cypher query, the Family entry is not a valid category for the book Gardening Today.



          MATCH (book:Book {name: 'Gardening Today'})<-[:CONTAINS]-(category:Category)
          RETURN DISTINCT book.name, collect(category.name);

          ╒═════════════════╤═════════════════════════════════════════╕
          │"book.name" │"collect(category.name)" │
          ╞═════════════════╪═════════════════════════════════════════╡
          │"Gardening Today"│["Kids","Gardening","Hobbies","Outdoors"]│
          └─────────────────┴─────────────────────────────────────────┘


          Doing the cross check confirms category Family contains completly other books.



          MATCH (category:Category {name: 'Family'})-[:CONTAINS]->(book:Book)
          RETURN DISTINCT category.name, collect(book.name);
          ╒═══════════════╤═══════════════════════════════════════════════════════════════╕
          │"category.name"│"collect(book.name)" │
          ╞═══════════════╪═══════════════════════════════════════════════════════════════╡
          │"Family" │["Family Home Projects","Easy Home Updates","Home Improvement"]│
          └───────────────┴───────────────────────────────────────────────────────────────┘


          This procedure continues to propagate. That's the reason why you get differently sliced result sets as expected. So your already implemented approach is correct:



          MATCH path = (category:Category)-[:CONTAINS]->(book:Book)
          WITH collect(category.name) AS categoryGroup, book.name AS bookName
          RETURN categoryGroup, collect(bookName);

          ╒═════════════════════════════════════════════════════════════════╤═════════════════════════════════════════════════════════════════════╕
          │"categoryGroup" │"collect(bookName)" │
          ╞═════════════════════════════════════════════════════════════════╪═════════════════════════════════════════════════════════════════════╡
          │["Spanish","Travel","Language"] │["Spanish for Travelers","Advanced Spanish","Conversational Spanish"]│
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Home Improvement","Family","Home and Garden","Home and Garden"]│["Easy Home Updates"] │
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Hobbies","Gardening","Kids","Outdoors"] │["Gardening Today","Gardening for Kids"] │
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Hobbies","Gardening","Outdoors"] │["Green Thumb Made Easy"] │
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Home Improvement","Family","Home and Garden"] │["Home Improvement","Family Home Projects"] │
          └─────────────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────┘




          Extension



          Underlying Idea



          Because the requested mapping violates the assignment rules (set theory), we can not use the usual pattern matching. Instead, we can achieve our goal through a trick by finding all connected nodes for a given book and preparing them afterwards.



          Please ensure you have installed the Neo4j APOC library.



          graph



          Solution



          MATCH (selectedBook:Book)
          WHERE selectedBook.name = 'Gardening for Kids'
          CALL apoc.path.subgraphNodes(selectedBook, {uniqueness: 'NODE_GLOBAL'}) YIELD node
          WITH collect(DISTINCT node) AS subgraphNodes
          WITH
          filter (node IN subgraphNodes
          WHERE node:Category) AS categories,
          filter (node IN subgraphNodes
          WHERE node:Book) AS books
          WITH categories, books
          UNWIND categories AS category
          UNWIND books AS book
          RETURN collect(DISTINCT category.name) AS categoryNames, collect(DISTINCT book.name) AS bookNames;


          Explanation




          • lines 1-2: selecting the book under inspection

          • line 3: using the APOC procedure apoc.path.subgraphNodes for locating all connected nodes

          • lines 6-9: sorting the identified nodes by label Category and Book

          • lines 10-13: result preparation


          Results



          Easy Home Updates:



          ╒═══════════════════════════════════════════════╤═══════════════════════════════════════════════════════════════╕
          │"categoryNames" │"bookNames" │
          ╞═══════════════════════════════════════════════╪═══════════════════════════════════════════════════════════════╡
          │["Home and Garden","Family","Home Improvement"]│["Easy Home Updates","Family Home Projects","Home Improvement"]│
          └───────────────────────────────────────────────┴───────────────────────────────────────────────────────────────┘


          Gardening for Kids:



          ╒════════════════════════════════════════╤════════════════════════════════════════╕
          │"categoryNames" │"bookNames" │
          ╞════════════════════════════════════════╪════════════════════════════════════════╡
          │["Kids","Gardening","Hobbies","Outdoors"│["Gardening for Kids","Gardening Today",│
          │] │"Green Thumb Made Easy"] │
          └────────────────────────────────────────┴────────────────────────────────────────┘





          share|improve this answer























          • Thanks for taking the time to respond. Your final result set is where I am still stuck. I can get to this point but need to further reduce the results. For instance, how would one approach combining rows with related nodes such as rows 3 and 4, 2 and 5, etc. Rows 3 and 4 for example are clearly related but are still separate. I would like to combine those together along with the associated books in a single row. Hopefully that makes sense.
            – Jon McGee
            Nov 14 '18 at 14:45












          • @Jon McGee I extended my answer by an solution for your requirements.
            – ThirstForKnowledge
            Nov 14 '18 at 20:02














          0












          0








          0






          Creating your model



          For the ease of possible further answers and solutions I note my graph creating statement:



          CREATE
          (categoryHome:Category {name: 'Home and Garden'}),
          (categoryOutdoor:Category {name: 'Outdoors'}),
          (categoryHobby:Category {name: 'Hobbies'}),
          (categoryGarden:Category {name: 'Gardening'}),
          (categorySpanish:Category {name: 'Spanish'}),
          (categoryTravel:Category {name: 'Travel'}),
          (categoryLanguage:Category {name: 'Language'}),
          (categoryKids:Category {name: 'Kids'}),
          (categoryImprovement:Category {name: 'Home Improvement'}),
          (categoryFamily:Category {name: 'Family'}),
          (book1:Book {name: 'Easy Home Updates'}),
          (book2:Book {name: 'Gardening Today'}),
          (book3:Book {name: 'Gardening for Kids'}),
          (book4:Book {name: 'Green Thumb Made Easy'}),
          (book5:Book {name: 'Conversational Spanish'}),
          (book6:Book {name: 'Spanish for Travelers'}),
          (book7:Book {name: 'Advanced Spanish'}),
          (book8:Book {name: 'Home Improvement'}),
          (book9:Book {name: 'Easy Home Updates'}),
          (book10:Book {name: 'Family Home Projects'}),
          (categoryHome)-[:CONTAINS]->(book1),
          (categoryHome)-[:CONTAINS]->(book8),
          (categoryHome)-[:CONTAINS]->(book9),
          (categoryHome)-[:CONTAINS]->(book10),
          (categoryOutdoor)-[:CONTAINS]->(book2),
          (categoryOutdoor)-[:CONTAINS]->(book3),
          (categoryOutdoor)-[:CONTAINS]->(book4),
          (categoryHobby)-[:CONTAINS]->(book2),
          (categoryHobby)-[:CONTAINS]->(book3),
          (categoryHobby)-[:CONTAINS]->(book4),
          (categoryGarden)-[:CONTAINS]->(book2),
          (categoryGarden)-[:CONTAINS]->(book3),
          (categoryGarden)-[:CONTAINS]->(book4),
          (categorySpanish)-[:CONTAINS]->(book5),
          (categorySpanish)-[:CONTAINS]->(book6),
          (categorySpanish)-[:CONTAINS]->(book7),
          (categoryTravel)-[:CONTAINS]->(book5),
          (categoryTravel)-[:CONTAINS]->(book6),
          (categoryTravel)-[:CONTAINS]->(book7),
          (categoryLanguage)-[:CONTAINS]->(book5),
          (categoryLanguage)-[:CONTAINS]->(book6),
          (categoryLanguage)-[:CONTAINS]->(book7),
          (categoryKids)-[:CONTAINS]->(book2),
          (categoryKids)-[:CONTAINS]->(book3),
          (categoryImprovement)-[:CONTAINS]->(book8),
          (categoryImprovement)-[:CONTAINS]->(book9),
          (categoryImprovement)-[:CONTAINS]->(book10),
          (categoryFamily)-[:CONTAINS]->(book8),
          (categoryFamily)-[:CONTAINS]->(book9),
          (categoryFamily)-[:CONTAINS]->(book10);


          Explanation



          In my eyes, your technical implementation is right, but your requirements from a professional point of view are not consistent.
          Let us choose an example. You expect the following record:



          BOOKS:                        CATEGORIES:
          Gardening Today, Gardening,
          Gardening for Kids, Outdoors,
          Green Thumb Made Easy Hobbies,
          Kids,
          Family


          By executing the following Cypher query, the Family entry is not a valid category for the book Gardening Today.



          MATCH (book:Book {name: 'Gardening Today'})<-[:CONTAINS]-(category:Category)
          RETURN DISTINCT book.name, collect(category.name);

          ╒═════════════════╤═════════════════════════════════════════╕
          │"book.name" │"collect(category.name)" │
          ╞═════════════════╪═════════════════════════════════════════╡
          │"Gardening Today"│["Kids","Gardening","Hobbies","Outdoors"]│
          └─────────────────┴─────────────────────────────────────────┘


          Doing the cross check confirms category Family contains completly other books.



          MATCH (category:Category {name: 'Family'})-[:CONTAINS]->(book:Book)
          RETURN DISTINCT category.name, collect(book.name);
          ╒═══════════════╤═══════════════════════════════════════════════════════════════╕
          │"category.name"│"collect(book.name)" │
          ╞═══════════════╪═══════════════════════════════════════════════════════════════╡
          │"Family" │["Family Home Projects","Easy Home Updates","Home Improvement"]│
          └───────────────┴───────────────────────────────────────────────────────────────┘


          This procedure continues to propagate. That's the reason why you get differently sliced result sets as expected. So your already implemented approach is correct:



          MATCH path = (category:Category)-[:CONTAINS]->(book:Book)
          WITH collect(category.name) AS categoryGroup, book.name AS bookName
          RETURN categoryGroup, collect(bookName);

          ╒═════════════════════════════════════════════════════════════════╤═════════════════════════════════════════════════════════════════════╕
          │"categoryGroup" │"collect(bookName)" │
          ╞═════════════════════════════════════════════════════════════════╪═════════════════════════════════════════════════════════════════════╡
          │["Spanish","Travel","Language"] │["Spanish for Travelers","Advanced Spanish","Conversational Spanish"]│
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Home Improvement","Family","Home and Garden","Home and Garden"]│["Easy Home Updates"] │
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Hobbies","Gardening","Kids","Outdoors"] │["Gardening Today","Gardening for Kids"] │
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Hobbies","Gardening","Outdoors"] │["Green Thumb Made Easy"] │
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Home Improvement","Family","Home and Garden"] │["Home Improvement","Family Home Projects"] │
          └─────────────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────┘




          Extension



          Underlying Idea



          Because the requested mapping violates the assignment rules (set theory), we can not use the usual pattern matching. Instead, we can achieve our goal through a trick by finding all connected nodes for a given book and preparing them afterwards.



          Please ensure you have installed the Neo4j APOC library.



          graph



          Solution



          MATCH (selectedBook:Book)
          WHERE selectedBook.name = 'Gardening for Kids'
          CALL apoc.path.subgraphNodes(selectedBook, {uniqueness: 'NODE_GLOBAL'}) YIELD node
          WITH collect(DISTINCT node) AS subgraphNodes
          WITH
          filter (node IN subgraphNodes
          WHERE node:Category) AS categories,
          filter (node IN subgraphNodes
          WHERE node:Book) AS books
          WITH categories, books
          UNWIND categories AS category
          UNWIND books AS book
          RETURN collect(DISTINCT category.name) AS categoryNames, collect(DISTINCT book.name) AS bookNames;


          Explanation




          • lines 1-2: selecting the book under inspection

          • line 3: using the APOC procedure apoc.path.subgraphNodes for locating all connected nodes

          • lines 6-9: sorting the identified nodes by label Category and Book

          • lines 10-13: result preparation


          Results



          Easy Home Updates:



          ╒═══════════════════════════════════════════════╤═══════════════════════════════════════════════════════════════╕
          │"categoryNames" │"bookNames" │
          ╞═══════════════════════════════════════════════╪═══════════════════════════════════════════════════════════════╡
          │["Home and Garden","Family","Home Improvement"]│["Easy Home Updates","Family Home Projects","Home Improvement"]│
          └───────────────────────────────────────────────┴───────────────────────────────────────────────────────────────┘


          Gardening for Kids:



          ╒════════════════════════════════════════╤════════════════════════════════════════╕
          │"categoryNames" │"bookNames" │
          ╞════════════════════════════════════════╪════════════════════════════════════════╡
          │["Kids","Gardening","Hobbies","Outdoors"│["Gardening for Kids","Gardening Today",│
          │] │"Green Thumb Made Easy"] │
          └────────────────────────────────────────┴────────────────────────────────────────┘





          share|improve this answer














          Creating your model



          For the ease of possible further answers and solutions I note my graph creating statement:



          CREATE
          (categoryHome:Category {name: 'Home and Garden'}),
          (categoryOutdoor:Category {name: 'Outdoors'}),
          (categoryHobby:Category {name: 'Hobbies'}),
          (categoryGarden:Category {name: 'Gardening'}),
          (categorySpanish:Category {name: 'Spanish'}),
          (categoryTravel:Category {name: 'Travel'}),
          (categoryLanguage:Category {name: 'Language'}),
          (categoryKids:Category {name: 'Kids'}),
          (categoryImprovement:Category {name: 'Home Improvement'}),
          (categoryFamily:Category {name: 'Family'}),
          (book1:Book {name: 'Easy Home Updates'}),
          (book2:Book {name: 'Gardening Today'}),
          (book3:Book {name: 'Gardening for Kids'}),
          (book4:Book {name: 'Green Thumb Made Easy'}),
          (book5:Book {name: 'Conversational Spanish'}),
          (book6:Book {name: 'Spanish for Travelers'}),
          (book7:Book {name: 'Advanced Spanish'}),
          (book8:Book {name: 'Home Improvement'}),
          (book9:Book {name: 'Easy Home Updates'}),
          (book10:Book {name: 'Family Home Projects'}),
          (categoryHome)-[:CONTAINS]->(book1),
          (categoryHome)-[:CONTAINS]->(book8),
          (categoryHome)-[:CONTAINS]->(book9),
          (categoryHome)-[:CONTAINS]->(book10),
          (categoryOutdoor)-[:CONTAINS]->(book2),
          (categoryOutdoor)-[:CONTAINS]->(book3),
          (categoryOutdoor)-[:CONTAINS]->(book4),
          (categoryHobby)-[:CONTAINS]->(book2),
          (categoryHobby)-[:CONTAINS]->(book3),
          (categoryHobby)-[:CONTAINS]->(book4),
          (categoryGarden)-[:CONTAINS]->(book2),
          (categoryGarden)-[:CONTAINS]->(book3),
          (categoryGarden)-[:CONTAINS]->(book4),
          (categorySpanish)-[:CONTAINS]->(book5),
          (categorySpanish)-[:CONTAINS]->(book6),
          (categorySpanish)-[:CONTAINS]->(book7),
          (categoryTravel)-[:CONTAINS]->(book5),
          (categoryTravel)-[:CONTAINS]->(book6),
          (categoryTravel)-[:CONTAINS]->(book7),
          (categoryLanguage)-[:CONTAINS]->(book5),
          (categoryLanguage)-[:CONTAINS]->(book6),
          (categoryLanguage)-[:CONTAINS]->(book7),
          (categoryKids)-[:CONTAINS]->(book2),
          (categoryKids)-[:CONTAINS]->(book3),
          (categoryImprovement)-[:CONTAINS]->(book8),
          (categoryImprovement)-[:CONTAINS]->(book9),
          (categoryImprovement)-[:CONTAINS]->(book10),
          (categoryFamily)-[:CONTAINS]->(book8),
          (categoryFamily)-[:CONTAINS]->(book9),
          (categoryFamily)-[:CONTAINS]->(book10);


          Explanation



          In my eyes, your technical implementation is right, but your requirements from a professional point of view are not consistent.
          Let us choose an example. You expect the following record:



          BOOKS:                        CATEGORIES:
          Gardening Today, Gardening,
          Gardening for Kids, Outdoors,
          Green Thumb Made Easy Hobbies,
          Kids,
          Family


          By executing the following Cypher query, the Family entry is not a valid category for the book Gardening Today.



          MATCH (book:Book {name: 'Gardening Today'})<-[:CONTAINS]-(category:Category)
          RETURN DISTINCT book.name, collect(category.name);

          ╒═════════════════╤═════════════════════════════════════════╕
          │"book.name" │"collect(category.name)" │
          ╞═════════════════╪═════════════════════════════════════════╡
          │"Gardening Today"│["Kids","Gardening","Hobbies","Outdoors"]│
          └─────────────────┴─────────────────────────────────────────┘


          Doing the cross check confirms category Family contains completly other books.



          MATCH (category:Category {name: 'Family'})-[:CONTAINS]->(book:Book)
          RETURN DISTINCT category.name, collect(book.name);
          ╒═══════════════╤═══════════════════════════════════════════════════════════════╕
          │"category.name"│"collect(book.name)" │
          ╞═══════════════╪═══════════════════════════════════════════════════════════════╡
          │"Family" │["Family Home Projects","Easy Home Updates","Home Improvement"]│
          └───────────────┴───────────────────────────────────────────────────────────────┘


          This procedure continues to propagate. That's the reason why you get differently sliced result sets as expected. So your already implemented approach is correct:



          MATCH path = (category:Category)-[:CONTAINS]->(book:Book)
          WITH collect(category.name) AS categoryGroup, book.name AS bookName
          RETURN categoryGroup, collect(bookName);

          ╒═════════════════════════════════════════════════════════════════╤═════════════════════════════════════════════════════════════════════╕
          │"categoryGroup" │"collect(bookName)" │
          ╞═════════════════════════════════════════════════════════════════╪═════════════════════════════════════════════════════════════════════╡
          │["Spanish","Travel","Language"] │["Spanish for Travelers","Advanced Spanish","Conversational Spanish"]│
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Home Improvement","Family","Home and Garden","Home and Garden"]│["Easy Home Updates"] │
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Hobbies","Gardening","Kids","Outdoors"] │["Gardening Today","Gardening for Kids"] │
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Hobbies","Gardening","Outdoors"] │["Green Thumb Made Easy"] │
          ├─────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
          │["Home Improvement","Family","Home and Garden"] │["Home Improvement","Family Home Projects"] │
          └─────────────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────┘




          Extension



          Underlying Idea



          Because the requested mapping violates the assignment rules (set theory), we can not use the usual pattern matching. Instead, we can achieve our goal through a trick by finding all connected nodes for a given book and preparing them afterwards.



          Please ensure you have installed the Neo4j APOC library.



          graph



          Solution



          MATCH (selectedBook:Book)
          WHERE selectedBook.name = 'Gardening for Kids'
          CALL apoc.path.subgraphNodes(selectedBook, {uniqueness: 'NODE_GLOBAL'}) YIELD node
          WITH collect(DISTINCT node) AS subgraphNodes
          WITH
          filter (node IN subgraphNodes
          WHERE node:Category) AS categories,
          filter (node IN subgraphNodes
          WHERE node:Book) AS books
          WITH categories, books
          UNWIND categories AS category
          UNWIND books AS book
          RETURN collect(DISTINCT category.name) AS categoryNames, collect(DISTINCT book.name) AS bookNames;


          Explanation




          • lines 1-2: selecting the book under inspection

          • line 3: using the APOC procedure apoc.path.subgraphNodes for locating all connected nodes

          • lines 6-9: sorting the identified nodes by label Category and Book

          • lines 10-13: result preparation


          Results



          Easy Home Updates:



          ╒═══════════════════════════════════════════════╤═══════════════════════════════════════════════════════════════╕
          │"categoryNames" │"bookNames" │
          ╞═══════════════════════════════════════════════╪═══════════════════════════════════════════════════════════════╡
          │["Home and Garden","Family","Home Improvement"]│["Easy Home Updates","Family Home Projects","Home Improvement"]│
          └───────────────────────────────────────────────┴───────────────────────────────────────────────────────────────┘


          Gardening for Kids:



          ╒════════════════════════════════════════╤════════════════════════════════════════╕
          │"categoryNames" │"bookNames" │
          ╞════════════════════════════════════════╪════════════════════════════════════════╡
          │["Kids","Gardening","Hobbies","Outdoors"│["Gardening for Kids","Gardening Today",│
          │] │"Green Thumb Made Easy"] │
          └────────────────────────────────────────┴────────────────────────────────────────┘






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 14 '18 at 18:23

























          answered Nov 14 '18 at 11:10









          ThirstForKnowledge

          6351112




          6351112












          • Thanks for taking the time to respond. Your final result set is where I am still stuck. I can get to this point but need to further reduce the results. For instance, how would one approach combining rows with related nodes such as rows 3 and 4, 2 and 5, etc. Rows 3 and 4 for example are clearly related but are still separate. I would like to combine those together along with the associated books in a single row. Hopefully that makes sense.
            – Jon McGee
            Nov 14 '18 at 14:45












          • @Jon McGee I extended my answer by an solution for your requirements.
            – ThirstForKnowledge
            Nov 14 '18 at 20:02


















          • Thanks for taking the time to respond. Your final result set is where I am still stuck. I can get to this point but need to further reduce the results. For instance, how would one approach combining rows with related nodes such as rows 3 and 4, 2 and 5, etc. Rows 3 and 4 for example are clearly related but are still separate. I would like to combine those together along with the associated books in a single row. Hopefully that makes sense.
            – Jon McGee
            Nov 14 '18 at 14:45












          • @Jon McGee I extended my answer by an solution for your requirements.
            – ThirstForKnowledge
            Nov 14 '18 at 20:02
















          Thanks for taking the time to respond. Your final result set is where I am still stuck. I can get to this point but need to further reduce the results. For instance, how would one approach combining rows with related nodes such as rows 3 and 4, 2 and 5, etc. Rows 3 and 4 for example are clearly related but are still separate. I would like to combine those together along with the associated books in a single row. Hopefully that makes sense.
          – Jon McGee
          Nov 14 '18 at 14:45






          Thanks for taking the time to respond. Your final result set is where I am still stuck. I can get to this point but need to further reduce the results. For instance, how would one approach combining rows with related nodes such as rows 3 and 4, 2 and 5, etc. Rows 3 and 4 for example are clearly related but are still separate. I would like to combine those together along with the associated books in a single row. Hopefully that makes sense.
          – Jon McGee
          Nov 14 '18 at 14:45














          @Jon McGee I extended my answer by an solution for your requirements.
          – ThirstForKnowledge
          Nov 14 '18 at 20:02




          @Jon McGee I extended my answer by an solution for your requirements.
          – ThirstForKnowledge
          Nov 14 '18 at 20:02


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53294084%2faggregate-related-nodes-across-all-collections-in-neo4j-cypher%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Guess what letter conforming each word

          Port of Spain

          Run scheduled task as local user group (not BUILTIN)