Risk querying

Sometimes, in calculations, we need to perform some complex operations, such as making decisions by looking into all child risks, returning the sum of bodilyInjury premium across all child risks, or discovering how many child risks exist for a given risk.

In calculations, you can query risks in many different ways:

bc.risk

The rater provides bc.risk utility, which can be used to query information from either the risk currently being rated or all of its child risks.

bc.risk.number (Copy link)

Returns the risk number and can be used to identify whether it’s a first risk, second risk, third risk, and so on.

bc.risk.number

bc.risk.term_premium (Copy link)

Returns the term premium from current risk.

bc.risk.term_premium

bc.risk.pro_rata_premium (Copy link)

Returns the pro-rata premium from current risk.

bc.risk.pro_rata_premium

bc.risk.get({lookup}, {default: optional}) (Copy link)

Query information from the current risk. An optional default value can be provided, which, by default, returns None when the query isn’t resolved.

bc.risk.get('fields.someField')

Returns the value of someField but returns None if the query isn’t resolved.

bc.risk.{descendants-risks} (Copy link)

This is the most powerful feature of risk querying, giving you access to perform aggregated operations on descendants risks.

bc.risk.descendants({depth}) (Copy link)

Returns specific descendants risks of provided depth, which is a number that tells BriteCore to grab risks from only that particular level.

Example:

Risk Type hierarchy:

- Policy
  - Vehicle
    - Driver
      - Violation

Querying at the policy level based on the hierarchy above:

bc.risk.descendants(1)  # Returns risks at first level so only `Vehicle` risks will be returned
bc.risk.descendants(2)  # Returns risks at second level so only `Driver` risks will be returned
bc.risk.descendants(3)  # Returns risks at third level so only `Violation` risks will be returned

bc.risk.descendants_up_to({depth}) (Copy link)

Returns descendants risks up to the provided depth, which is a number that tells BriteCore to grab risks up to only that particular level.

Example:

Risk Type hierarchy:

- Policy
  - Vehicle
    - Driver
      - Violation

Querying at the policy level based on the hierarchy above:

bc.risk.descendants_up_to(1)  # Returns risks up to first level so only `Vehicle` risks will be returned
bc.risk.descendants_up_to(2)  # Returns risks up to second level so `Vehicle` and `Driver` risks will be returned
bc.risk.descendants_up_to(3)  # Returns risks up to third level so `Vehicle`, `Driver` and `Violation` risks will be returned

bc.risk.all_descendants (Copy link)

Returns all descendants risks.

Example:

Risk Type hierarchy:

- Policy
  - Vehicle
    - Driver
      - Violation

Querying at the policy level based on the hierarchy above:

bc.risk.all_descendants

Note: The query above will return all descendants risks: VehicleDriver and Violation.

bc.risk.children (Copy link)

Convenience method to get specific descendants risks of the first level, which is similar to bc.risk.descendants(1).

bc.risk.grandchildren (Copy link)

Convenience method to get specific descendants risks of the second level, which is similar to bc.risk.descendants(2).

bc.risk.great_grandchildren (Copy link)

Convenience method to get specific descendants risks of the third level, which is similar to bc.risk.descendants(3).

Aggregated Operations (Copy link)

Aggregated operations can be performed on descendants risks.

All of the examples hereon can be used with the following:

  • bc.risk.descendants({depth})
  • bc.risk.descendants_up_to({depth})
  • bc.risk.all_descendants
  • bc.risk.children
  • bc.risk.grandchildren
  • bc.risk.great_grandchildren

Note: This isn’t limited to bc.risk.children. For simplicity, we will use only bc.risk.children for aggregation in examples hereon.

Lookups

The aggregated operations are based on a lookup expression used to define which field to aggregate.

Supported lookups (Copy link)

The example aggregation operation is sum, and it can be any supported aggregation operation defined in the following subsections.

bc.items (Copy link)

bc.risk.children.sum(bc.items.bodilyInjury.premium.term.value)

bc.fields (Copy link)

bc.risk.children.sum(bc.fields.some_field)

bc.premium (Copy link)

bc.risk.children.sum(bc.premium.term.value)

bc.calculations (Copy link)

bc.risk.children.sum(bc.calculations.some_calculation)

bc.rate_tables (Copy link)

bc.risk.children.sum(bc.rate_tables.some_rate_table)

bc.risk.children.min({lookup}) (Copy link)

Returns the minimum value from all child risks for a given lookup.

bc.risk.children.min(bc.items.bodilyInjury.premium.term.value)

Returns the minimum bodilyInjury premium across all child risks. So, if bodilyInjury.premium.term.value (child risk 1) resolved to 1.0 and bodilyInjury.premium.term.value (child risk 2) resolved to 2.0, then it will return 1.0.

Note: If there aren’t any child risks or the query isn’t resolved, then min will return None.

bc.risk.children.max({lookup}) (Copy link)

Returns the maximum value from all child risks for a given lookup.

bc.risk.children.max(bc.items.bodilyInjury.premium.term.value)

Returns the minimum bodilyInjury premium across all child risks. So, if bodilyInjury.premium.term.value (child risk 1) resolved to 1.0 and bodilyInjury.premium.term.value (child risk 2) resolved to 2.0, then it will return 2.0.

Note: If there aren’t any child risks or the query isn’t resolved, then max will return None.

bc.risk.children.sum({lookup}) (Copy link)

Returns the sum of all resolved values from all child risks for a given lookup.

bc.risk.children.sum(bc.items.bodilyInjury.premium.term.value)

Returns the minimum bodilyInjury premium across all child risks. So, if bodilyInjury.premium.term.value (child risk 1) resolved to 1.0 and bodilyInjury.premium.term.value (child risk 2) resolved to 2.0, then it will return 3.0.

bc.risk.children.avg({lookup}) (Copy link)

Returns the average of all resolved values from all child risks for a given lookup.

bc.risk.children.avg(bc.items.bodilyInjury.premium.term.value)

Returns the minimum bodilyInjury premium across all child risks. So, if bodilyInjury.premium.term.value (child risk 1) resolved to 100.0 and bodilyInjury.premium.term.value (child risk 2) resolved to 300.0, then it will return 200.0.

bc.risk.children.count({lookup: optional}) (Copy link)

Returns the count of all child risks. Optionally, you can pass lookup, which checks its existence.

bc.risk.children.count()

If a risk has two sub-child risks, then the answer will be 2.

bc.risk.children.count(bc.items.bodilyInjury)

If a risk has two sub-child risks but only one of them has bodilyInjury, then the answer will be 1.

bc.risk.children.exists({lookup}) (Copy link)

If the provided lookup successfully resolved in any child risks, then it returns True or False.

bc.risk.children.exists(bc.items.bodilyInjury)

If any of the child risks has bodilyInjury coverage enabled, then it returns True; otherwise, it will return False.

bc.risk.children.get({lookup}, {default: optional}) (Copy link)

Returns the value of a given lookup.

Note: This operation should be accessed only after filtering all child risks to the point only one risk is found.

If there are multiple risks that meet the criteria, this operation will fail. You can provide an optional default value, which, by default, returns None if the query isn’t resolved.

bc.risk.children.filter(type__name='drivers').get(bc.fields.goodStudent)

Returns one of the following values:

  • The goodStudent field for the drivers risk type.
  • None if the query isn’t resolved.

Note: If there are no drivers risks, the above query will fail unless a default value is provided.

Children operations (Copy link)

bc.risk.children.order_by({lookup1}, {lookup2}, …) (Copy link)

Sorts the child risks based on fields defined by one or more lookups. By default, the results are sorted in ascending order.

Note: To sort the results in descending order, add the - sign to the corresponding lookup as a prefix.

bc.risk.children.order_by(bc.items.bodilyInjury.premium.term.value)

Returns the child risks and orders them by their bodilyInjury coverage term premium, starting with the smallest.

bc.risk.children.order_by(-bc.items.bodilyInjury.premium.term.value)

Returns the child risks and orders them by their bodilyInjury coverage term premium, starting with the largest.

bc.risk.children.order_by(-bc.items.bodilyInjury.premium.term.value, bc.calculations.driverFactor)

Returns the child risks:

  • First, in descending order by their bodilyInjury coverage term premium.
  • Next, in ascending order by their driverFactor.

bc.risk.children.limit({count}) (Copy link)

Return a subset of the child risks, which is limited to the number defined by count.

bc.risk.children.limit(2)

Returns only the first two child risks. If the original set is empty, an empty set is returned. If the original set contains only one child, then this child is returned.

bc.risk.children.get_values({lookup}) (Copy link)

Finds value for a given lookup in all child risk states.

bc.risk.children.get_values(bc.fields.name)

Returns a list with the value of the name field for all the child risks. If a child doesn’t have the name field, nothing is added to the list for that child.

bc.risk.children.distinct({lookup}) (Copy link)

bc.risk.children.distinct({lookup})

Returns a subset of the child risks, with only one risk per distinct value found for the lookup path.

bc.risk.children.distinct(bc.fields.year)

For the following child risks:

  • Vehicle 1 (make: Toyota, year: 2014)
  • Vehicle 2 (make: Honda, year: 2014)
  • Vehicle 3 (make: BMW, year: 2016)
  • Vehicle 4 (make: Ford)

The calculation above returns:

  • Vehicle 1 (make: Toyota, year: 2014)
  • Vehicle 3 (make: BMW, year: 2016)

As you can see in the results above, the risks are returned in the order that they appear. If you need them in a different order, you can use distinct along with the order_by utility:

bc.risk.children.order_by(bc.fields.make).distinct(bc.fields.year)

For the same child risks, the calculation above returns:

  • Vehicle 3 (make: BMW, year: 2016)
  • Vehicle 2 (make: Honda, year: 2014)

Filtering (Copy link)

To filter before calling on aggregated operations:

bc.risk.children.filter(*args, **kwargs)

This filters child risks using the provided lookup parameters.

Lookup parameters can be passed as:

  • Keyword filtering
  • Positional filtering

Keyword filtering (Copy link)

Keyword arguments of the fields__lookuptype=value type where lookuptype specifies the filtering operation.

bc.risk.children.filter(number__gt=2)

Note: The above filter will match all child risks where the number field is greater than 2.Each lookup is treated as a conditional clause. If multiple lookups are provided, then they are combined using the logical and operator. For nested fields, use a double underscore.

Positional filtering (Copy link)

Positional arguments of the .filter(Q(...)) type, which can be used to build conditional clauses that need to be either combined using logical or or negated using not.

bc.risk.children.filter(Q(number=1) | Q(number=2))

Note: The above query will filter only the child risk states where the number is either 1 or 2.

Supported lookup types (Copy link)

The following are supported lookup types:

  • exact exact equality (default)
  • neq inequality
  • contains containment
  • icontains insensitive containment
  • in membership
  • startswith string startswith
  • istartswith insensitive startswith
  • endswith string endswith
  • iendswith insensitive endswith
  • gt greater than
  • gte greater than or equal to
  • lt less than
  • lte less than or equal to
  • regex regular expression search
  • filter nested filter

Examples:

  • bc.risk.children.filter(type__name='vehicles').min(bc.items.bodilyInjury.premium.term.value) – Returns minimum bodilyInjury premium across all child Vehicle risks.
  • bc.risk.children.filter(type__name__in=['vehicles', 'trailers']).min(bc.items.bodilyInjury.premium.term.value) – Returns minimum bodilyInjury premium across all child Vehicle or Trailer risks.
  • bc.risk.children.filter(type__name='vehicles').min(bc.calculations.driverFactor) – Returns the minimum driverFactor calculation result across all child Vehicle risks.
  • bc.risk.children.filter(type__name='vehicles').min(bc.rate_tables.propertyDamageLimitFactorTable) – Returns the minimum propertyDamageLimitFactorTable rate table result across all child Vehicle risks
  • bc.risk.children.filter(type__name='vehicles').filter(fields__mileage__gte=1000).avg(bc.fields.mileage) – Returns the average mileage across all Vehicle risks whose mileage is greater than or equal to 1000.
  • bc.risk.children.filter(type__name='vehicles').filter(~Q(fields__ratingTier__in=['ratingTier_standard', 'ratingTier_preferred']).count() – Returns the total count of all the Vehicle risks whose ratingTier is not Standard or Preferred.

Gotchas!

Be aware of the following gotchas:

  • Filtering should always come before aggregation operations (minmaxsum, or count).
  • Filtering shouldn’t yield 0 results when using on following aggregated operations minmax or sum.

Shorthand methods for filtering (Copy link)

The following is a shorthand method you can use as an alternative for bc.risk.children.filter(type__name='vehicles'):

bc.risk.{risk_type_name}

Usage:

bc.risk.vehicles.count()

bc.risk.vehicles.min(bc.items.bodilyInjury.premium.term.value)

Note: If no risks have been added to the quote for the referenced risk type, the min and max will return None.