Aggregating Values


(James) #1

First off, I’m super new to DGraph and graphing databases in general, but so far it’s been great to play around with!

I’m having trouble figuring out how to do a query/aggregation for a set of data. Basically, what I want is to have a “component” that has weight that’s an int. I want to also allow for a component to “contain” other components. If it contains other component, then its weight is derived from the components that belong to it. This can go on and on down the tree. Ultimately, I want the top level component to have a derived weight that’s based on the weights of all the components associated with it—be it from components that have their own inherent weight, or from components that are made up of other components.

So far I have been able to come up with the following query, but it only works for one level deep.

{
  component(func: uid(0x3)) {
    name
    component {
      name
    	weight: w as weight
    }
    weight : sum(val(w))
  }
}

(Michel Conrado) #2

Hum, Try out Recurse Query https://docs.dgraph.io/query-language/#recurse-query


(James) #3

Thanks for the link. I checked that out and am able to recursively retrieve all of my components and nested components. However, the tricky part seems to be getting the actual proper weights. Some components have an actual weight attribute, while others do not and only reference additional sub-components that have actual weights. Does that make sense? I’ve been struggling to be able to clearly word this.

I’ve attached an image of my graph. The node labeled “Everything” would ultimately have a weight of all of the other nodes. Basically only the leaf nodes have actual weights, anything else has no actual weight as it’s just a set of leaf nodes.

So to really summarize what I’m wanting to do… I want to take the weight attribute of each leaf node and aggregate it up into its parent node, and then take that aggregated weight and aggregate it up into that node’s parent node until I get to the main node.


(Michel Conrado) #4

I do not know if I completely understood. But maybe Recursive does not work for this.
In this case you would have to build a Query shaped for your structure. As in the example below.

You start to use “math functions” for this.

{
  component(func: uid(0x3)) {
    name
    component {
      name
    	weight: w as weight
      sub-component {
          name
          weight: w_sub as weight
        }
        weight : sum(val(w_sub))
    }
    Total_weight : math(w + w_sub)
  }
}

Anyway, please if you can provide a sample (JSON or RDF) so I can study the case directly.
And also a sample in JSON of the desired result. Would be nice.

Cheers.


(James) #5

Here’s what the data currently looks like:

{
	"data": {
		"components": [{
			"name": "Component A",
			"component": [{
					"name": "Component B",
					"component": [{
							"name": "Component C",
							"component": [{
									"name": "Component D",
									"weight": 100,
									"uid": "0x6a"
								},
								{
									"name": "Component E",
									"weight": 50,
									"uid": "0x6b"
								}
							],
							"uid": "0x64"
						},
						{
							"name": "Component F",
							"weight": 10,
							"uid": "0x65"
						}
					],
					"uid": "0x66"
				},
				{
					"name": "Component G",
					"weight": 20,
					"uid": "0x69"
				}
			],
			"uid": "0x68"
		}]
	}
}

Here’s what I want to end up with:

{
	"data": {
		"components": [{
			"name": "Component A",
			"total_weight": 180,
			"component": [{
					"name": "Component B",
					"total_weight": 160,
					"component": [{
							"name": "Component C",
							"total_weight": 150,
							"component": [{
									"name": "Component D",
									"weight": 100,
									"uid": "0x6a"
								},
								{
									"name": "Component E",
									"weight": 50,
									"uid": "0x6b"
								}
							],
							"uid": "0x64"
						},
						{
							"name": "Component F",
							"weight": 10,
							"uid": "0x65"
						}
					],
					"uid": "0x66"
				},
				{
					"name": "Component G",
					"weight": 20,
					"uid": "0x69"
				}
			],
			"uid": "0x68"
		}]
	}
}

You can see that the components D and E make each weigh 100 and 50 respectively. They both belong to component C, so therefore component C has a weight of 150. You can then see that component C and component F have weights of 150 and 10. Both of those components belong to component B, so component B has a weight of 160. The same goes for component B and G belonging to component A.

Does that help clarify at all?


(Michel Conrado) #6

So I did something that corroborates with your desired result. I do not know if it’s exactly what you want.

Query

{
  rootQ(func: has(components)) {
    components {
      component {#LV1
        uid
        name
        weight: LV1 as weight
          
        component { #LV2
          name
          weight: LV2 as weight
          
          component { #LV3
            name
            weight: LV3 as weight
          } #END_LV3
          
          lv3 as sum(val(LV3))
          #lv3_lv2 as math(LV2 + lv3)
          } #END_LV2

	    weight_from_LV2 : smlv2 as sum(val(LV2))
	    weight_from_lv3 : smlv3 as sum(val(lv3))
	    lv3_lv2 as math(smlv2 + smlv3)

        } #END_LV1

   		weight_from_LV1 : sm1 as sum(val(LV1))
   		weight_otherlvels : sm2 as sum(val(lv3_lv2))
   		Total : math(sm1 + sm2)
          }
    }
}

Mutation

{
  "set": [
    {
		"components": [{
			"name": "Component A",
			"component": [{
					"name": "Component B",
					"component": [{
							"name": "Component C",
							"component": [{
									"name": "Component D",
									"weight": 100,
									"uid": "0x6a"
								},
								{
									"name": "Component E",
									"weight": 50,
									"uid": "0x6b"
								}
							],
							"uid": "0x64"
						},
						{
							"name": "Component F",
							"weight": 10,
							"uid": "0x65"
						}
					],
					"uid": "0x66"
				},
				{
					"name": "Component G",
					"weight": 20,
					"uid": "0x69"
				}
			],
			"uid": "0x68"
		}]
	}
  ]
}

Result

{
  "data": {
    "rootQ": [
      {
        "components": [
          {
            "component": [
              {
                "uid": "0x66",
                "name": "Component B",
                "component": [
                  {
                    "name": "Component C",
                    "component": [
                      {
                        "name": "Component D",
                        "weight": 100
                      },
                      {
                        "name": "Component E",
                        "weight": 50
                      }
                    ],
                    "sum(val(LV3))": 150
                  },
                  {
                    "name": "Component F",
                    "weight": 10
                  }
                ],
                "weight_from_LV2": 10,
                "weight_from_lv3": 150,
                "val(lv3_lv2 )": 160
              },
              {
                "uid": "0x69",
                "name": "Component G",
                "weight": 20
              }
            ],
            "weight_from_LV1": 20,
            "weight_otherlvels": 160,
            "Total": 180
          }
        ]
      }
    ]
  },
  "extensions": {
    "server_latency": {
      "processing_ns": 999900
    },
    "txn": {
      "start_ts": 10495
    }
  }
}

(James) #7

This is working great and makes sense. However, after some playing around with various data sets, I receive an error similar to Wrong use of var() with [{lvl_3_component_weight 2}]. I’m assuming this is because a component has neither a weight or additional components linked to it. Is there a way around this?


(Michel Conrado) #8

I can not see why this error occurs. What modification did you make?

Remembering that aggregation works only one level above the block.