Computed Fields Scripts API in Siren Investigate

A computed fields script allows you to supplement records with additional data, for example, from a web service or another Elasticsearch index.

context.registerComputations

The context global variable provides a registerComputations function to register computed fields scripts in the Siren Platform.

Simple Example

  context.registerComputations({
    mainComputation: {
      compute: async (records, computeOptions) => {
        const firstFieldName = computeOptions.computedFields[0].name; // 'computed-id'
        for (const record of records) {
          const uuid = await record.getFirstRawFieldValue('generatedUUID');
          await context.setComputedField(record, firstFieldName, `${firstFieldName}:${uuid}`);
        }
      },
      outputFields: ['computed-id'],
      dependentFields: ['generatedUUID'] // computed field from a previously executed script
    }
  });

Join with Projection Example

  const OUTPUT_FIELDS = ['my_field'];

  const getRelatedArticlesByCompanyId = async (companyIds: string[]) => {
    const alias = 'company_id';
    const scriptSource = `if (doc.containsKey('${alias}') && !doc['${alias}'].empty) { return doc['${alias}'].value; }`

    const scriptFields = {
      projected_company: {
        script: {
          source: scriptSource
        }
      }
    };

    const joinQuery = {
      join: {
        indices: ['company'],
        on: ['companies', 'id'],
        request: {
          project: [
            {
              field: {
                name: 'id',
                alias
              }
            }
          ],
          query: {
            terms: {
              id: companyIds
            }
          }
        }
      }
    };

    const response = await sirenapi.elasticsearch.post('/article/_search', {
      size: 10000,
      _source: false,
      script_fields: scriptFields,
      query: joinQuery
    });

    const groupedByCompanyId = response.hits.hits.reduce((acc, hit) => {
      const companyId = hit.fields?.projected_company?.[0];
      if (acc[companyId]) {
        acc[companyId].push(hit);
      } else {
        acc[companyId] = [hit];
      }
      return acc;
    }, {});

    return groupedByCompanyId;
  };

  context.registerComputations({
    mainComputation: {
      compute: async (records, options) => {
        const companyIds = await Promise.all(records.map(record => record.getFirstRawFieldValue('id')));
        const articlesGroupedByCompanyId = await getRelatedArticlesByCompanyId(companyIds);

        for (const record of records) {
          for (const { name } of options.computedFields) {
            const companyId = await record.getFirstRawFieldValue('id');
            const relatedArticles = articlesGroupedByCompanyId[companyId] || [];
            const relatedArticleIds = relatedArticles.map(hit => `${hit._index}/_doc/${hit._id}`);
            await context.setComputedField(record, name, relatedArticleIds);
          }
        }
      },
      outputFields: OUTPUT_FIELDS
    }
  });

Properties

Name Type Description

[computation-name]

Record<string, ComputationDefinition>

Computed fields script definition.

Script Sub-type

The script sub-type for computed fields scripts is compute-fields.

ComputationDefinition

An object that defines a computed fields script to register with context.registerComputations.

Properties

Name Type Description

compute

Function

Required. The function definition to be invoked for the computation.

outputFields

Array<string>

Required. List of computed field names. Must be a non-empty array. Cannot be a function call (e.g., getOutputFields()).

dependentFields

Array<string>

Optional. List of computed field names this computation depends on. Must be an array if provided.


ComputationDefinition.compute(records, computeOptions)

The compute function is called with an array of DataRecord objects. Use context.setComputedField(record, fieldName, value) inside the body of compute to set computed field values on a given record. The compute function must be async.

context.setComputedField must be called and awaited in order to ensure the field’s value is correctly set on a record instance.

Parameter Type Description

records

Array<DataRecord>

Array of DataRecord objects to compute fields for.

computeOptions

ComputeOptions

Additional compute options which can be read by the compute function definition.

Returns: Promise<any> | any.

ComputeOptions

Field compute options to determine the relevant operations for the passed records.

Name Type Description

origin

ScriptOrigin

A ScriptOrigin instance used by Investigate to keep track of the running script context. Some SirenAPI functions require this information to infer how to behave.

computedFields

{ name: string; type: string }[]

An array of computed field objects which are writable in the current compute function’s context. Populating a field with the wrong type will result in an error.

Error Handling

Errors thrown in the compute function are caught and reported in the UI. For best results, handle errors within your script and provide meaningful error messages.

Supported Date Types

For computed fields of type date, only the following value types are supported:

  • JavaScript Date objects

  • ISO strings (without offset), for example: 1969-07-21T02:56:15.000Z

  • Epoch milliseconds (number)

When to use computed fields scripts?

Use these scripts when you want to supplement a record in the graph with additional data that is only accessible outside of the given record’s definition, for example, via a web service or another Elasticsearch index.

These scripts are executed at a low level, so performance is critical. Inefficient scripts can have a noticeable impact on performance.