Powered by Zoomin Software. For more details please contactZoomin

Get Started with Optic

Building Inline Views

  • Last Updated: September 10, 2026
  • 3 minute read
    • MarkLogic Server
    • Version 12.0
    • Documentation

Virtual views are useful for supporting ad hoc queries and data exploration on large data sets where query needs are not known up front or are frequently changing. For more information about virtual views and how to access them, see Template Driven Extraction.

Inline views expand that feature by allowing the view structure to be defined at query time. So, there is no need to configure a TDE template before running the search.

fromDocs Data Access Function

Rows from TDE views are generally accessed using the fromView() data access function. Inline views are configured using the fromDocs() data access function, which has this signature:

JavaScript XQuery
op.fromDocs(
  query as cts.query,
  contextPath as String,
  columns as columnDef,
  [viewName as String?],
  [systemColumn as String?],
  [namespaces as objectLiteral?]
)
op:from-docs(
  $query as cts:query,
  $context-path as xs:string,
  $columns as $columnDef,
  [$view-name as xs:string?],
  [$system-column as xs:string?],
  [$namespaces as map:map?]
)

The op.fromDocs() function documentation provides a more in-depth description of each parameter.

The rest of this section focuses on the helper object in constructing the columnDef parameter.

Column Builder

A helper function (XQuery) or object (JavaScript) is available to build the column definition parameter. The primary method is through addColumn or op:add-column, which can be chained with configuration methods such as type(), xpath(), and so on. See Sample Query for sample usage.

These column configurations, described in Columns in Relational Data Modeling with MarkLogic Server, may be adjusted using the equivalent column builder function:

TDE column config Column Builder Function Column Builder default
scalar-type type() string
val xpath() or expr() value of input to columnBuilder.addColumn()
collation collation() http://marklogic.com/collation/
dimension dimension() none
coordinate-system coordinateSystem() none
units units() miles

Those functions may be chained after the call to addColumn().

This table shows how to convert a TDE column configuration into the equivalent columnDef using op:column-builder() or op.columnBuilder():

TDE column XQuery Javascript
<column>
  <name>basic</name>
  <scalar-type>string</scalar-type>
  <nullable>true</nullable>
  <val>basic</val>
</column>
op:column-builder()
  => op:add-column("basic")
op.columnBuilder()
  .addColumn("basic")

TDE templates always use a string value for default. For column definitions defined using op:column-builder() or op.columnBuilder(), your default can be typed:

TDE column XQuery Javascript
<column>
  <name>count</name>
  <scalar-type>int</scalar-type>
  <default>-99</default>
</column>
op:column-builder()
  => op:add-column("count")
    => op:type("int")
    => op:nullable(fn:false())
    => op:default(-99)
op.columnBuilder()
  .addColumn("count")
    .type("int")
    .nullable(false)
    .default(-99)

.expr() lets you use data transformation functions:

TDE column XQuery Javascript
<column>
  <name>category</name>
  <scalar-type>string</scalar-type>
  <val>fn:substring(label, 1, 10)</val>
</column>
op:column-builder()
  => op:add-column("category")
    => op:nullable(fn:false())
    => op:expr(ofn:substring(label, 1, 10))
op.columnBuilder()
  .addColumn("category")
    .nullable(false)
    .expr(op.fn.substring(label, 1, 10))

When using op:xpath() inside an op:expr() column call, make op:context() the first parameter:

XQuery Javascript
op:column-builder()
  => op:add-column("label")
    => op:expr(op.xpath(op.context(), 'label'))
op.columnBuilder()
  .addColumn("label")
    .expr(op.xpath(op.context(), 'label'))

Subsequent columns do not have access to preceding column definitions. So, these column definitions result in columns named copyCount and doubleCount with values of null:

XQuery Javascript
op:column-builder()
  => op:add-column("count")
    => op:xpath("quantity")
  => op:add-column("copyCount")
    => op:expr(op:xpath(op:context(), 'count'))
  => op:add-column("doubleCount")
    => op:val('count * 2')
op.columnBuilder()
  .addColumn("count")
    .xpath("quantity")
  .addColumn("copyCount")
    .expr(op.xpath(op.context(), 'count'))
  .addColumn("doubleCount")
    .val('count * 2')

op:expr() and op:xpath() cannot be defined for the same column. So, this column configuration results in an XDMP-ARG-DETAIL error:

XQuery Javascript
op:column-builder()
  => op:add-column("label")
    => op:expr(op:xpath(op:context(), 'label'))
    => op:xpath("label")
op.columnBuilder()
  .addColumn("label")
    .expr(op.xpath(op.context(), 'label'))
    .xpath('label')

Scenario Setup

This section provides a scenario to better understand the various concepts and features related to inline views.

Sample Document

Create this sample document using Query Console:

'use strict';

declareUpdate();

xdmp.documentInsert(
  '/products/flipflops.json',
  {
    'product': {
      'label': 'flip flops',
      'stock': 20,
      'purchase': {
        'cost': 1.99,
      },
      'retail':{
        'price': 2.49,
      }
    }
  }
)

Sample TDE Template

If you were using TDE views, you would need a template like this to query the document:

'use strict';
declareUpdate();

const tde = require("/MarkLogic/tde.xqy");

let template = xdmp.toJSON({
  "template": {
    "description": "test column access",
    "context": "/product",
    "rows": [
      {
        "schemaName": "acme",
        "viewName": "products",
        "viewVirtual": true,
        "columns": [
          {
            "name": "label",
            "scalarType": "string",
            "val": "label"
          },
          {
            "name": "count",
            "scalarType": "int",
            "val": "stock"
          },
          {
            "name": "cost",
            "scalarType": "decimal",
            "val": "purchase/cost"
          },
          {
            "name": "price",
            "scalarType": "decimal",
            "val": "retail/price",
            "nullable": true
          }
        ]
      }
    ]
  }
})

// review the generated row data
tde.nodeDataExtract(fn.doc().toArray(),[template]);

Sample Query

But instead, run this query in Query Console to access the sample document through an inline view:

'use strict';

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

op.fromDocs(
    cts.trueQuery(), 
    "product", 
    op.columnBuilder()
      .addColumn("label")
      .addColumn("count")
        .xpath("stock")
        .type("int")
      .addColumn("cost")
        .xpath("purchase/cost")
        .type("decimal")
      .addColumn("price")
        .xpath("retail/price")
        .type("decimal")
        .nullable(true)
  )
  .result()

Concerns and Considerations

In-line views have the same limitations as Virtual Views. These are listed as part of Concerns and Considerations in the Creating Template Views section of the Relational Data Modeling with MarkLogic Server.

Alert