Programmatically Determine Automatically Included Fields

Is there a way to programmatically determine this? My current use case is that I want to fetch a parent type, but I need to include all the fields from a child type as well, as if I were to fetch the child type on its own.

Here is a little function I’ve been working on that I think meets your requirements or can be further extended to meet your requirements. For my scenario I wanted to get all primitive and reference fields from the base type and all primitive fields from the referenced types.

/**
 * Returns primitive fields and fields from related reference types.
 * typeName: name of the type
 * refOnly: used to indicate if the type is a reference field
 * refFieldName: name of the reference field from the parent type.
 *
 * example: lookupFieldSchemaMap("SmartBulb");
 * returns: currentPrediction.prediction,currentPrediction.timestamp,id, name,latitude,longitude, ...
 **/
function lookupFieldSchemaMap(typeName, refOnly, refFieldName) {
  var fieldSchemaMap = [];
  var typ = c3Type(typeName);
  var j;
  var fullFieldName;
  var depFields;
  var inclFields = [];

  // get list of fields, field value types, and target type from the user provided type
  _.each(typ.fieldTypes(), function(ft) { 
    var fmo = {};

    if (!ft.valueType().isMethod())  {
      fmo.fieldName = ft.name();
      fmo.fieldType = ft.valueType().type().typeName();
      
      if (ft.valueType().type().typeName() == 'ReferenceType') {
        fmo.targetType = ft.valueType().name();
      }

      if (ft.valueType().type().typeName() == 'PrimitiveType') {
        fmo.fieldValueType = ft.valueType().shortName();
      }

      fieldSchemaMap.push(fmo);
    }
  })

  // extract primitive and and reference fields.  Ignore Meta.
  fieldSchemaMap.forEach(function(typ) {
    if (refOnly == true) {
      if (typ.fieldType == 'PrimitiveType') {
        fullFieldName = refFieldName + "." + typ.fieldName;
        inclFields.push(fullFieldName);
      }
    } else {
      if (typ.fieldType == 'PrimitiveType') {
        inclFields.push(typ.fieldName);
      } else if (typ.fieldType == 'ReferenceType') {
        if (typ.targetType != "Meta") {
          depFields = lookupFieldSchemaMap(typ.targetType, true, typ.fieldName)
          inclFields = inclFields.concat(depFields);
        }
      }
    }

  } )

  return inclFields.join(",");

}
1 Like

Not sure I fully understand the question, but it sounds similar to something I am doing. I have a base type with several types that mix it. My code receives an instance of the base type but needs to act on it with the fields from the type that mixes it. I use the following and it works in my case.

In this example, header starts out as an instance of the base type. At the end, it is an instance of the type that mixes it, with the additional fields from that type.

if ( !header.meta ) { header.meta = {}; }
header.meta.include = 'this';
header = header.getSpecific();

If you need fields from referenced types those can be added to the include, header.meta.include += ',referenceField.columnName', assuming you know them ahead of time or can figure them out in some other way, such as scottk’s post (in my case I know them ahead of time).

Unfortunately I don’t know the child type fields ahead of time. For example, I have MyType.child.fieldX. Ideally my include could be “this, child.this”, but that is not supported; if an automatically included field is added to child in the future, my function will no longer work.

When you say you receive the parent type PT, do you know how to find the child you’re looking for, starting from PT? If it is a specific field on the parent, say myChildN, you can use PT.fieldTypes() to get a list of objects in which you can find your myChildN field matching _init.name (at least in the console version), and its type is in _init.valueType.name. Then you can use that type name as the first argument to the above lookupFieldSchemaMap to get a list of fields included by default (if refOnly is false and refFieldName is "myChildN").

1 Like

Yep! That’s exactly what I’m doing for now :slight_smile:

@AlexBakic Don’t use _init—that is an implementation detail and subject to change in any version without notice. Use the APIs provided instead:

  • fieldType.name() for the field name.
  • fieldType.valueType().name() for the value type name if it’s a reference field or primitive.
2 Likes