question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Virtual Fields (aka; Calculated Fields)

See original GitHub issue

Virtual field allow adding a field to the GraphQL end point, as well as the Admin UI, which are not backed by a direct database field, but instead are calculated at request time based on either the result of the other requested fields, related fields, or external data.

Examples of virtual fields:

  • _label; a human readable short summary of an item
  • approvedPosts; A shorthand for:
    query {
      User {
        approvedPosts: _postsMeta(where: { status: 'Approved' }) {
          count
        }
      }
    }
    

Could be implemented as a new Field Type:

import { Virtual, Integer } from '@keystone-alpha/fields';

keystone.createList('User', {
  fields: {
    approvedPosts: {
      // Use Integer's views, outputType etc.
      // But override all the functionality with the `Virtual` type.
      type: { ...Integer, Virtual },
      // Specify any data required to calculate this field.
      // Will be mixed into the graphQL query for getting this item's data.
      queryFragment: `
        _postsMeta(where: { status: 'Approved' }) {
          count
        }
      `,
      resolver: item => item._postsMeta.count,
    }
  }
});

Or, could be more explicit in setting views/outputType so it becomes the Virtual field’s responsibility to take those options and mix them into the type correctly.

import { Virtual, Integer } from '@keystone-alpha/fields';

keystone.createList('User', {
  fields: {
    approvedPosts: {
      type: Virtual,
      views: Integer.views,
      outputType: Integer.outputType,
      // Specify any data required to calculate this field.
      // Will be mixed into the graphQL query for getting this item's data.
      queryFragment: `
        _postsMeta(where: { status: 'Approved' }) {
          count
        }
      `,
      resolver: item => item._postsMeta.count,
    }
  }
});

To describe a nested object type, may need to also allow overwriting getGqlAuxTypes().

Notes

  • To ensure the correct typing information for the GraphQL API, and also for the display aspect, we need to allow setting the GraphQL return type.
  • The field would be displayed in the AdminUI’s list & details views
    • This display info is specified like any other field type; via views on the type (but set by options).
  • The field would not be displayed in the AdminUI’s edit views
  • The field would show up in the GraphQL API’s queries
    • The output type would be specified as an option.
  • The field would not be available as input to the GraphQL API’s mutations

Todo

  • Implement #220: Optimize database queries to only pull out fields actually requested
  • Implement query dependencies (#1116 Enable _label field to specify its GraphQL dependencies)
  • Fix #487: encodeSortBy not working with non-virtual fields

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:7
  • Comments:5 (5 by maintainers)

github_iconTop GitHub Comments

3reactions
jesstelfordcommented, May 13, 2019

No reason these can’t both be types 🎉

3reactions
molombycommented, May 13, 2019

Another approach for this would be to use read-only lists backed by DB views. This doesn’t help at all for the _label use case but might be a much better solution in some cases.

A fairly trivial example:

keystone.createList('User', {
  fields: {
    name: { type: Text },
    dob: { type: Date },
    virtuals: { type: Relationship, ref: 'UserVirtual', many: false }
  }
});

// This is backed by a view, created by the app developer, that contains the required fields
keystone.createList('UserVirtual', {
  options: { isReadOnly: true },
  fields: {
    daysTillBirthday: { type: Integer },
    postCount: { type: Integer },
    quoteOfTheDay: { type: String }
  }
});

Callers can then query the calculated values by joining through to them in graphQL:

query {
  User {
    name
    virtuals: { daysTillBirthday }
}

And the underlying view can contain whatever kind of logic the DB supports including things like materialised views, when available.

Pros:

  • Should work on most (almost all) DBs (inc. Mongo which has views since v3.4)
  • Doesn’t resolve/calculate the virtual fields unless they (any of them) are accessed
  • The “virtual” values are available at a DB level so can be reused in related systems (eg. BI dashboards, etc.)

Cons:

  • Requires DB support (although support is very common)
  • Requires the dev to create the underlying views manually (and keep the KS list definition in sync)
  • Requires “read-only” lists which we don’t currently have (but should be pretty trivial to implement?)
  • As is, if any field on the related list is accessed it will cause all fields to be calculated (until we address #220)

This is kind of solution is complementary to other approaches. You could use a related, read-only list and the Virtual field type from Jess’ example on the same list.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Virtual/Calculated Column | phpGrid - PHP Datagrid
It's a calculated field created from other columns. The virtual columns are added to the END of the existing datagrid. phpGrid only adds...
Read more >
Computed columns and virtual fields in data entities
This article provides information about computed and virtual fields, which are the two types of unmapped fields that a data entity can have....
Read more >
Postgres: Computed fields | Hasura GraphQL Docs
Computed fields are virtual values or objects that are dynamically computed and can be queried along with a table/view's columns. Computed fields are ......
Read more >
Calculated Fields – REDCap How-To Guide - UF CTSI
On the Online Designer page, you would select Calculated Field as the field type. Type the calculation into the Calculation Equation box.
Read more >
Generated (Virtual and Persistent/Stored) Columns - MariaDB
"A virtual column is a column in a table that has its value automatically calculated using either a deterministic expression, or the values...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found