How Does Optic Work?
Note
The Optic API Processing Model article provides a deeper dive into how Optic works.
Optic accesses any MarkLogic data model—table, document, or graph—and represents it as one common model for processing the data: a row sequence, which you can think of as a table. From there, according to your query, Optic manipulates the data as if it were rows and columns with familiar concepts like joins, filters, sorts, and selects. Finally, it produces the results as a row sequence for your app to consume.
Here is a SQL query followed by its Optic equivalent:
SELECT ContributorUserName, UserReputation, UserLocation FROM Samplestack.Contributors WHERE UserReputation > 5000 ORDER BY UserReputation LIMIT 25 OFFSET 0;
op.fromView('Samplestack', 'Contributors') // FROM .select(['ContributorUserName', 'UserReputation', 'UserLocation']) // SELECT .where(op.gt(op.col('UserReputation'), 5000)) // WHERE .orderBy('UserReputation') // ORDER BY .offsetLimit(0,25) // LIMIT 25 OFFSET 0 .result();
When you create an Optic query or update, you create a pipeline:
Data Accessor Function-->Operator Function(s)--> Executor Function (JavaScript & XQuery)
With Data Accessor (or Data Access) Functions like
fromView()
, analogous to SQL’sFROM
statement, you tell Optic where to look for data. You use only one data accessor function per query or update (except in ones that by nature require two data sources: joins, unions, intersections, and excepts).With the Operator (or ModifyPlan) Function
select()
, analogous to SQL’sSELECT
statement, you select the columns you need from the data source.With operator functions like
orderBy()
,where()
, andoffsetLimit()
, analogous to SQL’sORDER BY
,WHERE
, andLIMIT
+OFFSET
statements, you tell Optic how you want that data manipulated. You can have many operator functions in a query or update.For JavaScript and XQuery, with an Executor (or IteratePlan) Function like
result()
, you execute the pipeline and receive its results. You use only one executor function per query or update.
When the executor function executes the query or update, Optic optimizes the pipeline to minimize memory usage and maximize performance.
So, to satisfy the example query, upon hitting result()
, Optic uses fromView()
to project the required data from the correct documents into a row sequence. It then uses the most time- and memory-efficient way to manipulate the row sequence with select()
reducing the number of columns, orderBy()
determining the order of rows, where()
reducing the rows to those containing specified values in specified columns, and offsetLimit()
restricting the number of resulting rows. Finally, it uses result()
to produce the processed rows. These rows are ready to be consumed by your application.
More details on how Optic works will unfold as you see how we have built the following queries and updates to explore and change some sample data.
Note
If your query or update requires calling built-in MarkLogic functions against every row in a column, turn those functions into Optic Expression Functions (formerly called Value Processing Functions) by using the op.
prefix.