New ACM paper, free-tier cloud, and open-source license

Explanation objects

In this guide, you’ll learn how to use driver API methods and stateful objects to programmatically work with explanations. This page assumes you’ve already followed instructions from the Inference page.

Understanding explanations

TypeDB has explainable rule-based data inference feature: explanations represent chains of reasoning that yield a conclusions. The Driver API provides methods to programmatically work with explanations as objects. To learn how to use explanations in TypeDB Studio, see the explanation section of the TypeDB Studio Manual.

Explainables

To get an explanation on infered data, you need to infer some data with a Get query. If the explain transaction option is enabled, the response to the query includes an Explainables object as a part of every ConceptMap. We will use the Explainables object later to get explanations.

  • Rust

  • Python

  • Java

  • Node.js

  • C++

Example
let response = tx.query().get(get_query)?;
for (i, cmap) in response.enumerate() {
    let explainable_relations = cmap.clone()?.explainables.relations;
}

For more information, see the Explainables and Explainable classes.

Example
response = tx.query.get(get_query)
for i, ConceptMap in enumerate(response, start=1):
    explainable_relations = ConceptMap.explainables().relations()

For more information, see the Explainables and Explainable classes.

Example
Stream<ConceptMap> response = tx.query().get(getQuery);
response.forEach(result -> {
    Stream<Pair<String, ConceptMap.Explainable>> explainable_relations = result.explainables().relations();
});

For more information, see the Explainables and Explainable classes.

Example
let response = await tx.query.get(get_query).collect();
for(let i = 0; i < response.length; i++) {
    let explainable_relations = await response[i].explainables.relations;
}

For more information, see the Explainables and Explainable classes.

Example
TypeDB::ConceptMapIterable results = tx.query.get(getQuery);
for (TypeDB::ConceptMap& cm : results) {
    TypeDB::StringIterable explainableRelations = cm.explainables().relations();
}

For more information, see the Explainables and Explainable classes.

Explain query

An Explain query is a special type of query that can be sent with driver API to get explanations. It is the only type of query that doesn’t use TypeQL. Instead, it uses an explainable object as an argument. See an example below.

  • Rust

  • Python

  • Java

  • Node.js

  • C++

Example
for (var, explainable) in explainable_relations {
    let explain_iterator = tx.query().explain(&explainable)?;
}

For more information, see the Explain query and Explainable class methods.

Example
for var, explainable in explainable_relations:
    explain_iterator = tx.query.explain(explainable)

For more information, see the Explain query and Explainable class methods.

Example
explainable_relations.forEach(explainable -> {
    Stream<Explanation> explain_iterator = tx.query().explain(explainable.second());
});

For more information, see the Explain query and Explainable class methods.

Example
for await (const explainable of explainable_relations) {
    explain_iterator = tx.query.explain(explainable);
}

For more information, see the Explain query and Explainable class methods.

Example
for (std::string& explainable : explainableRelations) {
    TypeDB::ExplanationIterable explainIterator = tx.query.explain(cm.explainables().relation(explainable));
}

For more information, see the Explain query and Explainable class methods.

Explanation

An explanation object has information on which rule was used for inferring the explained concept, the condition of the rule, the conclusion of the rule, and the mapping of variables between the query and the rule’s conclusion.

  • Rust

  • Python

  • Java

  • Node.js

  • C++

Example
for explanation in explain_iterator {
    let exp = explanation?;
    println!("Rule: {}", exp.rule.label);
    println!("Condition: {}", exp.rule.when.to_string());
    println!("Conclusion: {}", exp.rule.then.to_string());
    println!("Variable mapping:");
    for qvar in exp.variable_mapping.keys() {
        println!(
            "Query variable {} maps to the rule variable {}",
            *qvar,
            exp.variable_mapping.get(qvar).unwrap().concat().to_string()
        );
    }
}

For more information, see the Explanation class methods.

Example
for explanation in explain_iterator:
    print("\nRule: ", explanation.rule().label)
    print("Condition: ", explanation.condition())
    print("Conclusion: ", explanation.conclusion())
    print("Variable mapping: ")
    for qvar in explanation.query_variables():
        print(
            f"  Query variable {qvar} maps to the rule variable {explanation.query_variable_mapping(var)}")

For more information, see the Explanation class methods.

Example
explain_iterator.forEach(explanation -> {
    System.out.println("Rule: " + explanation.rule().getLabel());
    System.out.println("  Condition: " + explanation.rule().getWhen().toString());
    System.out.println("  Conclusion: " + explanation.rule().getThen().toString());
    explanation.queryVariables().forEach(var ->
        System.out.println("Query variable " + var + "maps to the rule variable " + explanation.queryVariableMapping(var)));
});

For more information, see the Explanation class methods.

Example
for (explanation of explain_iterator) {
    console.log("Rule: " + explanation.rule.label)
    console.log("Condition: " + explanation.condition.toString())
    console.log("Conclusion " + explanation.conclusion.toString())
    for (qvar of explanation.variableMapping.keys()) {
        console.log("Query variable " + qvar + " maps to the rule variable " + explanation.variableMapping.get(qvar))
    }
}

For more information, see the Explanation class methods.

Example
for (TypeDB::Explanation& explanation : explainIterator) {
    std::cout << "Rule: " << explanation.rule().label() << std::endl;
    std::cout << "Condition: " << explanation.rule().when() << std::endl;
    std::cout << "Conclusion: " << explanation.rule().then() << std::endl;
    std::cout << "Variable mapping: " << std::endl;
    for (std::string& var : explanation.queryVariables()) {
        std::cout << "Query variable " << var << " maps to the rule variable " << explanation.queryVariableMapping(var)[1] << std::endl;
    }
}

For more information, see the Explanation class methods.

Full example

See below a comprehensive example of using explanation of inferred results. In this example we get all names of a user, matched by its email containing substring alice. There should be only one such user in the database so far, and it should have only one name, but according to the users rule, it should have an additional infered name: 'User`. So, we turn on inference, retrieve both names, and proceed to retrieve explainables for both names. Since only one name is infered, we can only have one explainable. So we iterate through explainables and send an Explain query to retrieve an explanation. We then print the information from the explanation object, including rule label, condition, conclusion, and variable mapping between the query and the rule.

  • Rust

  • Python

  • Java

  • Node.js

  • C++

Example
let databases = DatabaseManager::new(driver);
let db = databases.get(DB_NAME)?;
let options = Options::new().infer(true).explain(true);
{
    let session = Session::new(db, SessionType::Data)?;
    {
        let tx = session.transaction_with_options(TransactionType::Read, options)?;
        let get_query = "
                        match
                        $u isa user, has email $e, has name $n;
                        $e contains 'Alice';
                        get
                        $u, $n;
                        ";
        let response = tx.query().get(get_query)?;
        for (i, cmap) in response.enumerate() {
            let ncmap = cmap.clone();
            let name_concept = ncmap?.get("n").unwrap().clone();
            let name = match name_concept {
                Concept::Attribute(Attribute { value: Value::String(value), .. }) => value,
                _ => unreachable!(),
            };
            println!("Name #{}: {}", (i + 1).to_string(), name);
            let explainable_relations = cmap?.explainables.relations;
            for (var, explainable) in explainable_relations {
                println!("{}", var);
                println!("{}", explainable.conjunction);
                let explain_iterator = tx.query().explain(&explainable)?;
                for explanation in explain_iterator {
                    let exp = explanation?;
                    println!("Rule: {}", exp.rule.label);
                    println!("Condition: {}", exp.rule.when.to_string());
                    println!("Conclusion: {}", exp.rule.then.to_string());
                    println!("Variable mapping:");
                    for qvar in exp.variable_mapping.keys() {
                        println!(
                            "Query variable {} maps to the rule variable {}",
                            *qvar,
                            exp.variable_mapping.get(qvar).unwrap().concat().to_string()
                        );
                    }
                }
            }
        }
    }
}

For more information, see the Explanation class methods.

Example
with driver.session(DB_NAME, SessionType.DATA) as session:
    with session.transaction(TransactionType.READ, TypeDBOptions(infer=True, explain=True)) as tx:
        get_query = """
                    match
                    $u isa user, has email $e, has name $n;
                    $e contains 'alice';
                    get
                    $u, $n;
                    """
        response = tx.query.get(get_query)
        for i, ConceptMap in enumerate(response, start=1):
            name = ConceptMap.get("n").as_attribute().get_value()
            print(f"Name #{i}: {name}")
            explainable_relations = ConceptMap.explainables().relations()
            for var, explainable in explainable_relations:
                print("Explained variable:", explainable)
                print("Explainable object:", explainable_relations[explainable])
                print("Explainable part of query:", explainable_relations[explainable].conjunction())
                explain_iterator = tx.query.explain(explainable)
                for explanation in explain_iterator:
                    print("\nRule: ", explanation.rule().label)
                    print("Condition: ", explanation.condition())
                    print("Conclusion: ", explanation.conclusion())
                    print("Variable mapping: ")
                    for qvar in explanation.query_variables():
                        print(
                            f"  Query variable {qvar} maps to the rule variable {explanation.query_variable_mapping(var)}")
                    print("----------------------------------------------------------")

For more information, see the Explanation class methods.

Example
try (TypeDBSession session = driver.session(DB_NAME, TypeDBSession.Type.DATA)) {
    TypeDBOptions options = new TypeDBOptions().infer(true).explain(true);
    try (TypeDBTransaction tx = session.transaction(TypeDBTransaction.Type.READ, options)) {
        String getQuery = """
                            match
                            $u isa user, has email $e, has name $n;
                            $e contains 'Alice';
                            get
                            $u, $n;
                            """;
        int[] ctr = new int[1];
        tx.query().get(getQuery).forEach(result -> {
            String name = result.get("n").asAttribute().getValue().toString();
            System.out.println("Email #" + (++ctr[0]) + ": " + name);
            Stream<Pair<String, ConceptMap.Explainable>> explainable_relations = result.explainables().relations();
            explainable_relations.forEach(explainable -> {
                System.out.println("Explainable variable:" + explainable.first());
                System.out.println("Explainable part of the query:" + explainable.second().conjunction());
                Stream<Explanation> explain_iterator = tx.query().explain(explainable.second());
                explain_iterator.forEach(explanation -> {
                    System.out.println("Rule: " + explanation.rule().getLabel());
                    System.out.println("  Condition: " + explanation.rule().getWhen().toString());
                    System.out.println("  Conclusion: " + explanation.rule().getThen().toString());
                    explanation.queryVariables().forEach(var ->
                        System.out.println("Query variable " + var + "maps to the rule variable " + explanation.queryVariableMapping(var)));
                });
            });
        });
    }
}

For more information, see the Explanation class methods.

Example
try {
    session = await driver.session(DB_NAME, SessionType.DATA);
    try {
        let options = new TypeDBOptions();
        options.infer = true;
        options.explain = true;
        tx = await session.transaction(TransactionType.READ, options);
        const get_query = `
                            match
                            $u isa user, has email $e, has name $n;
                            $e contains 'Alice';
                            get
                            $u, $n;
                            `;
        let response = await tx.query.get(get_query).collect();
        for(let i = 0; i < response.length; i++) {
            console.log("Name #" + (i + 1) + ": " + response[i].get("n").value);
            let explainable_relations = await response[i].explainables.relations;
            for await (const explainable of explainable_relations) {
                console.log("Explainable part of the query: " + explainable.conjunction())
                explain_iterator = tx.query.explain(explainable);
                for (explanation of explain_iterator) {
                    console.log("Rule: " + explanation.rule.label)
                    console.log("Condition: " + explanation.condition.toString())
                    console.log("Conclusion " + explanation.conclusion.toString())
                    for (qvar of explanation.variableMapping.keys()) {
                        console.log("Query variable " + qvar + " maps to the rule variable " + explanation.variableMapping.get(qvar))
                    }
                }
            }
        }
    }
    finally {if (tx.isOpen()) {await tx.close()};}
}
finally {await session?.close();}

For more information, see the Explanation class methods.

Example
TypeDB::Options inferOptions;
inferOptions.infer(true);
inferOptions.explain(true);
TypeDB::Session session = driver.session(DB_NAME, TypeDB::SessionType::DATA, inferOptions);
{
    TypeDB::Transaction tx = session.transaction(TypeDB::TransactionType::READ, inferOptions);
    std::string getQuery = R"(
                            match
                            $u isa user, has email $e, has name $n;
                            $e contains 'Alice';
                            get
                            $u, $n;
                            )";
    TypeDB::ConceptMapIterable results = tx.query.get(getQuery);
    int16_t i = 0;
    for (TypeDB::ConceptMap& cm : results) {
        i += 1;
        std::cout << "Name #" << std::to_string(i) << ": " << cm.get("n")->asAttribute()->getValue()->asString() << std::endl;
        TypeDB::StringIterable explainableRelations = cm.explainables().relations();
        for (std::string& explainable : explainableRelations) {
            std::cout << "Explained variable " << explainable << std::endl;
            std::cout << "Explainable part of the query " << cm.explainables().relation(explainable).conjunction() << std::endl;
            TypeDB::ExplanationIterable explainIterator = tx.query.explain(cm.explainables().relation(explainable));
            for (TypeDB::Explanation& explanation : explainIterator) {
                std::cout << "Rule: " << explanation.rule().label() << std::endl;
                std::cout << "Condition: " << explanation.rule().when() << std::endl;
                std::cout << "Conclusion: " << explanation.rule().then() << std::endl;
                std::cout << "Variable mapping: " << std::endl;
                for (std::string& var : explanation.queryVariables()) {
                    std::cout << "Query variable " << var << " maps to the rule variable " << explanation.queryVariableMapping(var)[1] << std::endl;
                }
            }
        }
    }
}

For more information, see the Explanation class methods.

Learn more

Learn more about how to use inference in TypeDB.

Check out the Fetching inferred data page of our TypeDB Learning course.

Learn more about TypeDB drivers: list of available drivers, installation guides, API reference.

For more information about driver API methods, see the API reference of the relevant driver:

rustRust
javaJava
cppC++

Provide Feedback