cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.

How to use consecutive UNWIND without nesting each other

Guys, I am new to Neo4j platform and pretty much noob in writing cypher queries. I have a query which creates a brand, branches and checkoutTypes associated with it.

          CREATE (brand:Brand {
                        id: apoc.create.uuid(),
                        name: $name,
                        logoUrl: $logoUrl,
                        coverUrl: $coverUrl,
                        tax: $tax,
                        deliveryCharges: $deliveryCharges,
                        category: $category,
                        isAvailable: $isAvailable,
                        color: $color
                    })
                    WITH brand
                    UNWIND $checkoutTypes as type
                    CREATE (checkoutType: FoodCheckoutType {
                        id: apoc.create.uuid(),
                        name: type.name
                    })
                    CREATE (coupon:FoodCoupon {
                        id: apoc.create.uuid(),
                        name: type.couponName,
                        percentage: type.percentage
                    }) - [:IN_CHECKOUT_TYPE] -> (checkoutType)
                    CREATE (checkoutType)-[:IN_BRAND]->(brand)
                    
                    WITH brand
                    UNWIND $branches as branch
                    CREATE (foodBranch:FoodBranch {
                        id: apoc.create.uuid(),
                        name: branch.name,
                        phoneNumber: branch.phoneNumber,
                        latitude: branch.latitude,
                        longitude: branch.longitude,
                        preparationTime: branch.preparationTime,
                        isAvailable: branch.isAvailable
                    })
                    CREATE (foodBranch) -[:BRANCH_OF] -> (brand)
                    RETURN brand

and variables are

     name: "Dominos",
      logoUrl: "https://cdn.worldvectorlogo.com/logos/domino-s-pizza-3.svg",
      coverUrl: "https://scontent.fbom3-1.fna.fbcdn.net/v/t1.0-",
    

 branch: [
      {
        name: "Dominos Panchpakhadi",
        phoneNumber: "2225437858",
        latitude: 19.1796965,
        longitude: 72.9562069,
        isAvailable: true,
        preparationTime: 2000
      },
      {
        name: "Domino's , Mulund East",
        phoneNumber: "18602100000",
        latitude: 19.1724069,
        longitude: 72.9580359,
        isAvailable: true,
        preparationTime: 2000
      },
      {
        name: "Domino's ,Vasant Oscar ,Mulund West",
        phoneNumber: "02225907250"
        latitude: 19.1758063,
        longitude: 72.9329858,
        isAvailable: true,
        preparationTime: 2000
      }
    ]
    checkoutTypes: [
      { 
        name: "FastFood",
        couponName: "25% off on all pizza",
        percentage: 25 
      },
      { 
        name: "Delivery", 
        couponName: "25% off on all pizza", 
        percentage: 25 
      },
      { name: "Takeaway", 
        couponName: "25% off on all pizza", 
        percentage: 25 
      }
    ]

But when I execute the query it creates multiple instances of the same branch which I don't want .
Is there any other way to refractor this Cypher query so its gives a desired results

Query Results are

{
        "id": "dbb3414b-2928-4cd2-a708-c4ed1de7fc0c",
        "name": "Dominos",
        "checkoutType": [
          {
            "name": "FastFood",
            "coupon": [
              {
                "name": "25% off on all pizza"
              }
            ]
          },
          {
            "name": "Takeaway",
            "coupon": [
              {
                "name": "25% off on all pizza"
              }
            ]
          },
          {
            "name": "Delivery",
            "coupon": [
              {
                "name": "25% off on all pizza"
              }
            ]
          }
        ],
        "foodBranch": [
          {
            "name": "Domino's ,Vasant Oscar ,Mulund West",
            "id": "3e88460d-c8ca-43c1-bb41-d0fbe44c40bd"
          },
          {
            "name": "Domino's ,Vasant Oscar ,Mulund West",
            "id": "abce0608-f49f-49d9-9acc-83c3c1bc9f6f"
          },
          {
            "name": "Dominos Panchpakhadi",
            "id": "f60ac181-10d8-4a62-9ddc-a076c91dbdd0"
          },
          {
            "name": "Domino's , Mulund East",
            "id": "aa2d8e36-b9bb-4b0d-9597-8c25c83df90f"
          },
          {
            "name": "Domino's ,Vasant Oscar ,Mulund West",
            "id": "76462ce9-f380-44ed-b6ca-6020e953c30e"
          },
          {
            "name": "Domino's , Mulund East",
            "id": "ec8022f9-0e61-4ac0-87f3-9e88a8570c65"
          },
          {
            "name": "Domino's , Mulund East",
            "id": "0b0075d5-5358-496d-8036-867ebce070f3"
          },
          {
            "name": "Dominos Panchpakhadi",
            "id": "f7fd7767-55f4-4a35-9780-cc1c0ab78f1b"
          },
          {
            "name": "Dominos Panchpakhadi",
            "id": "db50c866-6f54-418c-884a-54784b20a399"
          }
        ]
      }
1 ACCEPTED SOLUTION

This was mentioned earlier in my solution post, but here's the relevant section:

In cases where you need to use UNWIND, but later want to collapse the cardinality, you'll need to use either the DISTINCT keyword or usage of aggregation functions to get back to the state you want.

For example, in your query, once you're done with the operations for that particular UNWIND, you want to get back to distinct brands (and not multiple rows with the same brand).

So instead of WITH brand, you need either WITH DISTINCT brand or WITH brand, count(brand) as count (an aggregation function that would make brand distinct).

MATCH(brand: Brand { id: $brandId })
CREATE(product: FoodProduct {
    id: apoc.create.uuid(),
    name: $name,
    isAvailable: $isAvailable,
    coverUrl: $coverUrl,
    category: $category,
    isVeg: $isVeg,
    description: $description
}) - [: PRODUCT_OF] - (brand)
WITH product
UNWIND $checkoutTypes as checkoutType
MATCH(checkout: CheckoutType { id: checkoutType })
CREATE(product) - [: HAS_CHECKOUT_TYPE] - (checkout)
WITH DISTINCT product // this resets the cardinality to 1 row per product
UNWIND $branches as branchId
MATCH(branch: FoodBranch { id: branchId })
CREATE(branch) - [: HAS_PRODUCT] - (product)
RETURN DISTINCT product // reset cardinality again

View solution in original post

8 REPLIES 8

If the operations you want to use based on the list elements are only for writing (CREATE, MERGE, DELETE, SET, REMOVE), then use a FOREACH instead of an UNWIND. FOREACH won't alter the cardinality, so you can do two FOREACHes back to back.

Thanks for the reply, just a doubt when should I use UNWIND, is there any particular scenario ?

Either when you're faced with a limitation where you can't use FOREACH, or when you want to continue working with the resulting rows further in the query.

FOREACH does not allow non-writing clauses, so MATCH, OPTIONAL MATCH, and WITH are not allowed.

In cases where you need to use UNWIND, but later want to collapse the cardinality, you'll need to use either the DISTINCT keyword or usage of aggregation functions to get back to the state you want.

For example, in your query, once you're done with the operations for that particular UNWIND, you want to get back to distinct brands (and not multiple rows with the same brand).

So instead of WITH brand, you need either WITH DISTINCT brand or WITH brand, count(brand) as count (an aggregation function that would make brand distinct).

@andrew.bowman, Now I have this use case where I have to match IDs in an array and return it, but
now second UNWIND executes inside the first UNWIND which is not required.
Is there any way to stop the first UNWIND ? Or is there any other way to perform this query ?

MATCH (brand:Brand {id: $brandId})
WITH brand
UNWIND $checkoutTypes as checkoutType
MATCH (checkout: FoodCheckoutType {id: checkoutType})-[:IN_BRAND]-(brand)
WITH  checkout, brand
UNWIND $branches as branchId
MATCH (branch: FoodBranch {id: branchId})
RETURN brand,  checkout, branch

I'm a little confused with this query. You're not doing anything with the $braches or the resulting matched branches except matching and returning, it has nothing to do with the operations earlier in the query.

Can you describe what the intent of the query is, and what the desired results and format is supposed to be?

Purpose of the query is to validate IDs which are provide as variables.
Variables are as follows:

checkout = [1, 2 , 3],
branches = [1, 2, 3],
brand = 1

My purpose of the creating this query is check if this variables exists in the graph and return it.
But I have many other use cases where I have to MATCH and CREATE relationship and nodes inside the UNWIND. In that case I can't use FOREACH because it throws error for MATCH For eg:

MATCH(brand: Brand { id: $brandId })
CREATE(product: FoodProduct {
    id: apoc.create.uuid(),
    name: $name,
    isAvailable: $isAvailable,
    coverUrl: $coverUrl,
    category: $category,
    isVeg: $isVeg,
    description: $description
}) - [: PRODUCT_OF] - (brand)
WITH product
UNWIND $checkoutTypes as checkoutType
MATCH(checkout: CheckoutType { id: checkoutType })
CREATE(product) - [: HAS_CHECKOUT_TYPE] - (checkout)
UNWIND $branches as branchId
MATCH(branch: FoodBranch { id: branchId })
CREATE(branch) - [: HAS_PRODUCT] - (product)
RETURN product

This query will create many duplicates branches relationship with products. I can't use MERGE because that would create relationship even if the branchId is not available and it will run several times.

Problem of the both query is same I can't use Consecutive Unwind statements with MATCH and CREATE in it because it will be create duplicates because of the nesting of the UNWIND, which is not desired. So how can I loop or UNWIND over two arrays consecutively but not nesting each other.

This was mentioned earlier in my solution post, but here's the relevant section:

In cases where you need to use UNWIND, but later want to collapse the cardinality, you'll need to use either the DISTINCT keyword or usage of aggregation functions to get back to the state you want.

For example, in your query, once you're done with the operations for that particular UNWIND, you want to get back to distinct brands (and not multiple rows with the same brand).

So instead of WITH brand, you need either WITH DISTINCT brand or WITH brand, count(brand) as count (an aggregation function that would make brand distinct).

MATCH(brand: Brand { id: $brandId })
CREATE(product: FoodProduct {
    id: apoc.create.uuid(),
    name: $name,
    isAvailable: $isAvailable,
    coverUrl: $coverUrl,
    category: $category,
    isVeg: $isVeg,
    description: $description
}) - [: PRODUCT_OF] - (brand)
WITH product
UNWIND $checkoutTypes as checkoutType
MATCH(checkout: CheckoutType { id: checkoutType })
CREATE(product) - [: HAS_CHECKOUT_TYPE] - (checkout)
WITH DISTINCT product // this resets the cardinality to 1 row per product
UNWIND $branches as branchId
MATCH(branch: FoodBranch { id: branchId })
CREATE(branch) - [: HAS_PRODUCT] - (product)
RETURN DISTINCT product // reset cardinality again

@andrew.bowman Thank you so much .. you saved my day