TypeDB.Answer (TypeDB v0.7.0)

Copy Markdown View Source

The result of a TypeQL query.

TypeDB answers come in three shapes, and the driver gives each its own struct so you can pattern match on the one you expect:

Every answer carries query_type (:read, :write or :schema), which is what TypeDB determined the query to be — not what you asked for, and truncated?/1, which is how you find out that a read was capped before you build on half of it.

query = "match $p isa person; select $p;"

case TypeDB.query!(conn, "social", query, transaction_type: :read) do
  %TypeDB.Answer.ConceptRows{rows: rows} -> length(rows)
  %TypeDB.Answer.Ok{} -> 0
end

ConceptRows and ConceptDocuments are Enumerable, so they can be piped straight into Enum and Stream. Select an attribute if you want values — typed_value/2 on an entity answers nil, because an entity is a thing with an identity rather than a value:

conn
|> TypeDB.query!("social", "match $p isa person, has name $name; select $name;",
     transaction_type: :read)
|> Enum.map(&TypeDB.ConceptRow.typed_value(&1, "name"))
#=> ["Alice", "Bob"]

Summary

Types

What TypeDB classified the query as.

t()

Functions

Returns the documents of a ConceptDocuments answer, or [] for any other.

Returns the query type TypeDB classified this query as.

Returns the rows of a ConceptRows answer, or [] for any other answer.

Returns true when TypeDB did not give you the whole answer.

Returns the server-side warning attached to an answer, if any.

Types

query_type()

@type query_type() :: :read | :write | :schema

What TypeDB classified the query as.

t()

Functions

documents(arg1)

@spec documents(t()) :: [term()]

Returns the documents of a ConceptDocuments answer, or [] for any other.

query_type(arg1)

@spec query_type(t()) :: query_type()

Returns the query type TypeDB classified this query as.

rows(arg1)

@spec rows(t()) :: [TypeDB.ConceptRow.t()]

Returns the rows of a ConceptRows answer, or [] for any other answer.

truncated?(answer)

@spec truncated?(t()) :: boolean()

Returns true when TypeDB did not give you the whole answer.

A read over the HTTP API is capped — at 10,000 rows by default, or at whatever :answer_count_limit says — and exceeding the cap is not an error. The answer arrives with the rows it did produce, and code that counts what came back is quietly wrong.

{:ok, answer} = TypeDB.query(conn, "social", "match $p isa person;", transaction_type: :read)

if TypeDB.Answer.truncated?(answer) do
  {:error, :partial}      # page it, raise the cap, or aggregate server-side
else
  {:ok, TypeDB.Answer.rows(answer)}
end

Why the row count cannot answer this

length(rows) == limit is neither necessary nor sufficient, and both halves were measured against TypeDB 3.12.1. With eight rows in the database:

requestrowstruncated?
answer_count_limit: 22true
answer_count_limit: 88false

The server warns only when it really had more, which is the right behaviour and exactly what makes the count useless: a complete answer can be the size of the cap, and a truncated one can be smaller than it when the query carries its own limit.

What this is, precisely

true when TypeDB attached a warning to the answer. It is deliberately not a match against the notice's text — that text is the server's, this driver's versioning does not cover it, and a rephrasing would silently turn a guard back into false, which is the failure this function exists to prevent. A warning of some other kind, if TypeDB ever attaches one, therefore reads as truncated here: the error is in the direction of refusing an answer rather than building on half of one.

TypeDB.TruncationIntegrationTest pins the server's actual behaviour against every TypeDB in the CI matrix, so a change in what the server warns about is a red build here rather than a short answer in your application.

Use warning/1 for the text, which names which limit was hit.

warning(arg1)

@spec warning(t()) :: String.t() | nil

Returns the server-side warning attached to an answer, if any.

The text names which limit was hit:

"Read query results limit (10000) exceeded. Not all answers are returned."

It is prose, and prose from the server — branch on truncated?/1 rather than on this. Read it, log it, put it in front of a person.

case TypeDB.Answer.warning(answer) do
  nil -> :ok
  warning -> Logger.warning("partial answer: " <> warning)
end

The driver already logs it for you at :warning level, so a truncated answer is never silent — but a log line is not a decision, and only you know whether a partial answer is one your code can use.