Exploring op.fromView()

Turn TDE and relational views into stable row plans

personClever Llamas
CleverLlamasMinimum Llamaverse Version: 2
databaseMinimum MarkLogic Version: 11

op.fromView() is the entry point for row-based Optic plans when your data already has a view behind it. Instead of walking documents directly, start from the TDE-backed or relational view and treat it as a row source ready for projection, filtering, and joins.

Use it when the data shape is already clear and the next step is row logic rather than document traversal. The examples below show how a simple view read becomes easier to maintain once you add explicit qualifiers and column scoping.

Llamaverse Context for This API Call

When you run op.fromView('llamaverse', 'llamas'), you are not querying arbitrary JSON directly. You are querying a TDE view that has already mapped source document content into rows and columns.

Context ItemWhat this article uses
Data setllamaverse v2.0+ sample data
Source documents/cleverllamas/llamaverse/raw/wild-llamas/llamas/{uuid}.json
Schemallamaverse
Viewllamas
Columns used in this articlename, placeOfBirth (and later id, secretPowerId in follow-on examples)
Field usageNone in this specific example; this is view-column projection, not a field query
Index contextTDE view extraction and column metadata must be available in the target database for op.fromView() to resolve rows

If op.fromView() returns no rows or throws a view/schema error, first confirm that llamaverse is loaded and that the TDE templates for the llamaverse.llamas view are installed in the same database your Optic query is running against.

Source Docs and Configuration Used

ItemValue used in this article
MarkLogic API docsop:from-view, op.fromView
Llamaverse source collectionwild-llamas
Llamaverse source URI pattern/cleverllamas/llamaverse/raw/wild-llamas/llamas/{uuid}.json
Required database configTDE templates must expose view llamaverse.llamas with columns name and placeOfBirth

Parameters

NameDatatypeRequiredNotes
schemaStringYesThe name of the schema that contains the view.
viewStringYesThe name of the view to source data from.
qualifierStringNoOptional runtime alias for the view name, useful when multiple sources would otherwise have ambiguous column names.
systemColsStringNoOptional system columns (for example fragment ID) used in join and diagnostics workflows.

Default Usage

In this example, no qualifier or system column is used, so the plan resolves with the default view and schema naming. The simplest op.select() shape keeps the code short but becomes fragile once column names are not unique across joins.

{
  "name": "Aaron",
  "breed": "Huacaya",
  "placeOfBirth": "Cusco, Peru",
  "secretPowerId": "d8839ba6-2b77-4bcc-9927-b86cdfecb9fb"
}
xquery version "1.0-ml";
import module namespace op = "http://marklogic.com/optic" at "/MarkLogic/optic.xqy";

op:from-view("llamaverse", "llamas")
  => op:select(("name", "placeOfBirth"))
  => op:order-by("name")
  => op:limit(3)
  => op:result()
'use strict';

const op = require('/MarkLogic/optic');

const llamaPlan = op.fromView('llamaverse', 'llamas')
  .select(['name', 'placeOfBirth'])
  .orderBy('name')
  .limit(3);

const results = llamaPlan.result();

({
  sample: 'optic/op-fromView/assets/default-usage.sjs',
  kind: Array.isArray(results) ? (results.every((item) => typeof item === 'object' && 'subject' in item && 'predicate' in item && 'object' in item) ? 'triples' : 'rows') : ((results !== null && typeof results === 'object') ? 'object' : 'scalar'),
  count: Array.isArray(results) ? results.length : 0,
  data: results
});

You can see in the results that data is scoped by schema and view name, similar to schema.table scoping in relational systems.

Explicit View and Column References

Building on the previous example, op.viewCol() lets you scope column references to a specific view. In simple plans this looks optional, but in multi-join plans it prevents ambiguity and keeps maintenance sane.

There is also op.col(), which is more generic. Prefer explicit op.viewCol() references when the plan is expected to grow.

{
  "name": "Aaron",
  "breed": "Huacaya",
  "placeOfBirth": "Cusco, Peru",
  "secretPowerId": "d8839ba6-2b77-4bcc-9927-b86cdfecb9fb"
}
xquery version "1.0-ml";
import module namespace op = "http://marklogic.com/optic" at "/MarkLogic/optic.xqy";

op:from-view("llamaverse", "llamas")
  => op:select((
       op:view-col("llamas", "name"),
       op:view-col("llamas", "placeOfBirth")
     ))
  => op:order-by("name")
  => op:limit(3)
  => op:result()
'use strict';

const op = require('/MarkLogic/optic');

const llamaPlan = op.fromView('llamaverse', 'llamas')
  .select([op.viewCol('llamas', 'name'), op.viewCol('llamas', 'placeOfBirth')])
  .orderBy('name')
  .limit(3);

const results = llamaPlan.result();

({
  sample: 'optic/op-fromView/assets/explicit-view-col.sjs',
  kind: Array.isArray(results) ? (results.every((item) => typeof item === 'object' && 'subject' in item && 'predicate' in item && 'object' in item) ? 'triples' : 'rows') : ((results !== null && typeof results === 'object') ? 'object' : 'scalar'),
  count: Array.isArray(results) ? results.length : 0,
  data: results
});

The results are identical.

Change the View Name at runtime

If you want SQL-style aliases, use the qualifier parameter. It lets you rename the source at runtime and keeps later column references clear.

{
  "name": "Aaron",
  "breed": "Huacaya",
  "placeOfBirth": "Cusco, Peru",
  "secretPowerId": "d8839ba6-2b77-4bcc-9927-b86cdfecb9fb"
}
xquery version "1.0-ml";
import module namespace op = "http://marklogic.com/optic" at "/MarkLogic/optic.xqy";

op:from-view("llamaverse", "llamas", "llamaList")
  => op:select((
       op:view-col("llamaList", "name"),
       op:view-col("llamaList", "placeOfBirth")
     ))
  => op:order-by("name")
  => op:limit(3)
  => op:result()
'use strict';

const op = require('/MarkLogic/optic');

const llamaPlan = op.fromView('llamaverse', 'llamas', 'llamaList')
  .select([op.viewCol('llamaList', 'name'), op.viewCol('llamaList', 'placeOfBirth')])
  .orderBy('name')
  .limit(3);

const results = llamaPlan.result();

({
  sample: 'optic/op-fromView/assets/runtime-qualifier.sjs',
  kind: Array.isArray(results) ? (results.every((item) => typeof item === 'object' && 'subject' in item && 'predicate' in item && 'object' in item) ? 'triples' : 'rows') : ((results !== null && typeof results === 'object') ? 'object' : 'scalar'),
  count: Array.isArray(results) ? results.length : 0,
  data: results
});

Now the difference is explicit: llamaList is the runtime qualifier, not llamaverse.llamas.

The next step is op.select(), where qualifier handling combines with projection patterns.

When to Choose op.fromView()

Use op.fromView() when your data is already modelled as a view and the next step is row logic, not raw document traversal.

Choose explicit qualifiers and column scoping early. That small discipline prevents ambiguous-column failures once joins and reporting logic become more complex.

Need Some Help?


Looking for more information on this subject or any other topic related to MarkLogic? Contact Us (info@cleverllamas.com) to find out how we can assist you with consulting or training!