Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.activeviam.com/llms.txt

Use this file to discover all available pages before exploring further.

This guide explains how to register update-where procedures to a store in the Atoti Datastore. For information about the different types of update-where procedures, see the Register update where procedures documentation.

Context

Let’s define a datastore made of 2 stores joined together. One sales store:
final IStoreDescription salesStore =
    StoreDescription.builder()
        .withStoreName("Sales")
        .withField("Id", ILiteralType.LONG)
        .asKeyField()
        .withField("Product", ILiteralType.STRING)
        .withField("Amount", ILiteralType.LONG, 0L)
        .withField("Is Meaningful", ILiteralType.BOOLEAN, false)
        .build();
One product store:
final IStoreDescription productsStore =
    StoreDescription.builder()
        .withStoreName("Products")
        .withField("ProductId", ILiteralType.STRING)
        .asKeyField()
        .withNullableField("Country", ILiteralType.STRING)
        .withField("Unit Price", ILiteralType.DOUBLE, 0.0)
        .build();

Register an insertion-time update-where procedure

Let’s start by registering an insertion-time trigger on the sales store which updates the inserted rows where the product is “Product1” to double the Amount. For insertion-time triggers it is only possible to select fields in the updated table.
final ISelection selection =
    new Selection("Sales", List.of(AliasedField.fromFieldName("Amount")));
final ICondition condition = BaseConditions.equal(FieldPath.of("Product"), "Product1");
final IUpdateWhereProcedure procedure =
    new IUpdateWhereProcedure() {
      int inputAmountIndex;
      int targetAmountIndex;
      @Override
      public void init(final IRecordFormat selectionFormat, final IRecordFormat recordFormat) {
        this.inputAmountIndex = selectionFormat.getFieldIndex("Amount");
        this.targetAmountIndex = recordFormat.getFieldIndex("Amount");
      }
      @Override
      public void execute(final IArrayReader selectedRecord, final IArrayWriter recordWriter) {
        recordWriter.writeLong(
            this.targetAmountIndex, selectedRecord.readLong(this.inputAmountIndex) * 2);
      }
    };
final ITransactionManager tm = datastore.getTransactionManager();
tm.startTransaction();
tm.registerInsertionTimeUpdateWhereTrigger("Trigger1", 1, selection, condition, procedure);
tm.commitTransaction();
Note that the procedure will only take effect in the next transaction
Let’s now insert some data into the sales store, this will trigger the procedure we just registered:
final Object[] row1 = new Object[] {1L, "Product1", 100L, null};
final Object[] row2 = new Object[] {2L, "Product2", 50L, null};
final Object[] row3 = new Object[] {3L, "Product1", 200L, null};
datastore.edit(t -> t.addAll("Sales", List.of(row1, row2, row3)));
The content of the store has been doubled for Product1:
IdProductAmountIs Meaningful
1Product1200false
2Product250false
3Product1400false

Register a commit-time update-where procedure

Let’s now register a commit-time trigger on the sales store which update the “Is Meaningful” field to true for all the rows where the Amount multiplied by the Unit Price is greater than 1000. In order to do that, we need the value from 2 different stores at the same time, which is not possible with insertion-time triggers.
final ISelection selection =
    new Selection(
        "Sales",
        List.of(
            AliasedField.fromFieldName("Amount"),
            AliasedField.create("Unit Price", FieldPath.of("SalesToProduct", "Unit Price"))));
final IUpdateWhereProcedure procedure =
    new IUpdateWhereProcedure() {
      int inputAmountIndex;
      int inputPriceIndex;
      int targetFieldIndex;
      @Override
      public void init(final IRecordFormat selectionFormat, final IRecordFormat recordFormat) {
        this.inputAmountIndex = selectionFormat.getFieldIndex("Amount");
        this.inputPriceIndex = selectionFormat.getFieldIndex("Unit Price");
        this.targetFieldIndex = recordFormat.getFieldIndex("Is Meaningful");
      }
      @Override
      public void execute(final IArrayReader selectedRecord, final IArrayWriter recordWriter) {
        final long amount = selectedRecord.readLong(this.inputAmountIndex);
        final double price = selectedRecord.readDouble(this.inputPriceIndex);
        recordWriter.writeBoolean(this.targetFieldIndex, amount * price > 1000);
      }
    };
final ITransactionManager tm = datastore.getTransactionManager();
tm.startTransaction();
tm.registerCommitTimeUpdateWhereTrigger(
    "TriggerA", 1, selection, BaseConditions.TRUE, procedure);
tm.commitTransaction();
Note that the procedure will take effect immediately in the current transaction.
If we insert rows in both stores, the procedure will be triggered and the “Is Meaningful” field will be updated:
final Object[] row1 = new Object[] {1L, "Product1", 100L, null};
final Object[] row2 = new Object[] {2L, "Product2", 50L, null};
final Object[] row3 = new Object[] {3L, "Product1", 200L, null};
final Object[] product1 = new Object[] {"Product1", "France", 8.0};
final Object[] product2 = new Object[] {"Product2", "USA", 10.0};
datastore.edit(
    t -> {
      t.addAll("Sales", List.of(row1, row2, row3));
      t.addAll("Products", List.of(product1, product2));
    });
IdProductAmountIs Meaningful
1Product1100false
2Product250false
3Product1200true
And even if we only update the content of the target store the procedure will be triggered as well:
final Object[] product2Update = new Object[] {"Product2", "USA", 100.0};
datastore.edit(t -> t.add("Products", product2Update));
The Product2 line of the Sales store has been updated, even if the update is on the Products store:
IdProductAmountIs Meaningful
1Product1100false
2Product250true
3Product1200true