Search code examples
marklogic

Retrieve distinct list of attribute given another attribute value, via REST


I'm trying to come up with a performant way of getting a distinct list of attribute values on an element, given another attribute on that element, and do that across the REST API. I'm using Semaphore with MarkLogic and have documents with elements like:

<META name="fruit" value="apple" />
<META name="fruit" value="banana" />
<META name="fruit" value="cherry" />
<META name="vegetable" value="artichoke" />
<META name="vegetable" value="beet" />
<META name="vegetable" value="carrot" />

So, given "fruit", I'd like to come back with the list (apple, banana, cherry) and given "vegetable", I'd like to come back with (artichoke, beet, carrot).

Lexicons on META/@name and META/@value and hitting the /v1/values endpoint are no problem, but not on sort of the combination. element-attribute-value-co-occurences and value-tuples seem to give all possible combinations, so I get fruit/carrot, which I don't want.

Ultimately, I'd like to use this to drive two select lists on a UI, the first containing the distinct name attributes, then once the user makes a selection, the second would contain the value attributes appropriate to it.


Solution

  • If you put a TDE in place that pulls the values into a view you can use the Optic API with the /v1/rows endpoint to pull the values. Each row here will align with a Meta pair of name and value. Therefore, you can use the groupBy modifier to get the unique combinations.

    TDE:

    <tde:template xml:lang="zxx"
        xmlns:tde="http://marklogic.com/xdmp/tde">
        <tde:description></tde:description>
        <tde:context>/es:envelope/es:instance[es:info/es:version = "0.0.1"][Article]</tde:context>
        <tde:path-namespaces>
            <tde:path-namespace>
                <tde:prefix>es</tde:prefix>
                <tde:namespace-uri>http://marklogic.com/entity-services</tde:namespace-uri>
            </tde:path-namespace>
        </tde:path-namespaces>
    
        <tde:templates>
            <tde:template>
                <tde:context>ancestor::es:envelope/es:headers//es:META</tde:context>
                <tde:rows>
                    <tde:row>
                        <tde:schema-name>Article</tde:schema-name>
                        <tde:view-name>Tags</tde:view-name>
                        <tde:view-layout>sparse</tde:view-layout>
                        <tde:columns>
                            <tde:column>
                                <tde:name>uri</tde:name>
                                <tde:scalar-type>string</tde:scalar-type>
                                <tde:val>xdmp:node-uri(.)</tde:val>
                                <tde:nullable>false</tde:nullable>
                                <tde:invalid-values>ignore</tde:invalid-values>
                            </tde:column>
                            <tde:column>
                                <tde:name>name</tde:name>
                                <tde:scalar-type>string</tde:scalar-type>
                                <tde:val>@name</tde:val>
                                <tde:nullable>true</tde:nullable>
                                <tde:invalid-values>ignore</tde:invalid-values>
                            </tde:column>
                            <tde:column>
                                <tde:name>value</tde:name>
                                <tde:scalar-type>string</tde:scalar-type>
                                <tde:val>@value</tde:val>
                                <tde:nullable>true</tde:nullable>
                                <tde:invalid-values>ignore</tde:invalid-values>
                            </tde:column>
                        </tde:columns>
                    </tde:row>
                </tde:rows>
            </tde:template>
        </tde:templates>
    </tde:template>
    

    Optic Query:

    'use strict';
    
    const op = require('/MarkLogic/optic');
    
    op.fromView('Article', 'Tags')
      .groupBy(op.col('name'), [op.col('value')])
      .result();
    

    /v1/rows (https://docs.marklogic.com/REST/POST/v1/rows)

    curl --location --request POST 'http://localhost:8011/v1/rows?column-types=header' 
    --header 'Content-Type: application/vnd.marklogic.querydsl+javascript' 
    --data-binary '@query.dsl'