Reputation: 99
We are building a enterprise application with ML as back-end. Many screens in the application have drop downs which require distinct values of elements inside documents . So we have created element range index for these elements and have the code ready to get the distinct values using range index.
Now we are segregating documents into different collections logically - like "basedata" collection which holds all the application base data documents and "transactiondata" collection which holds all incoming/outgoing transactions to our application.
Now the requirement is to restrict the range index to only the base data collection documents and not the transactional data documents.
For example: if we have a element range index for localname="entityName" we dont want the range index to be built for the entityName values present in documents in transactional data collection. We want the distinct values of "entityName" occuring only in documents attached to basedata collection.
How can we achieve this?
Edit:
Thanks David and grtjn.
This is what we are currently doing in the java layer: The below code is properly returning the Distinct names for the field entityName which has a range index created.
String valueOptionString =
" <search:options xmlns:search="http://marklogic.com/appservices/search">
<search:values name="entityName">
<search:range type="xs:string">
<search:element name="entityName">
</search:element>
</search:range>
</search:values>
</search:options> ";
QueryManager queryMgr = client.newQueryManager();
QueryOptionsManager optionsMgr = client.newServerConfigManager().newQueryOptionsManager();
optionsMgr.writeOptions("DistinctValues", new StringHandle(valueOptionString));
ValuesDefinition vdef = queryMgr.newValuesDefinition("entityName", "DistinctValues");
ValuesHandle vh = queryMgr.values(vdef, new ValuesHandle());
for (CountedDistinctValue value : vh.getValues()) {
System.out.println("Distinct value is :: " +
value.get("xs:string", String.class));
}
As you can see I am not setting the collection anywhere on the queryMgr.values();Based on the suggestion provided I tried to add the collection constraint int the valueOptionString but it is not working. Is this the right approach ? Is there any other way out
<search:options xmlns:search="http://marklogic.com/appservices/search">
<search:values name="createdBy">
<search:range type="xs:string">
<search:element name="createdBy"/>
</search:range>
<search:collection>
<search:uri>basedaata</search:uri>
<search:uri>tansactiondata</search:uri>
</search:collection>
</search:values>
</search:options>
Error encountered:
Local message: /config/query write failed: Bad Request. Server Message: RESTAPI-INVALIDCONTENT: (err:FOER0000) Invalid content: Op
eration results in invalid Options: XDMP-VALIDATEUNEXPECTED: (err:XQDY0027) validate strict { $opt } -- Invalid node: Found text{"basedaata..."} but expected () at fn:doc("")
EDIT 2:
Based on the below answers I have updated my code to include "additional-query" section in the optional query to contain the distinct values returned. However it looks the additional query part is being ignored and is not working as expected
Updated optional query:
<search:options xmlns:search="http://marklogic.com/appservices/search">
<search:values name="entityName">
<search:range type="xs:string">
<search:element name="entityName"/>
</search:range>
</search:values>
<search:additional-query>
<cts:collection-query xmlns:cts="http://marklogic.com/cts">
<cts:uri>basedata</cts:uri>
<cts:uri>DistinctValueTest</cts:uri>
</cts:collection-query>
</search:additional-query>
</search:options>
Below are the documents I have. Also i have element range index on field entityName..
Document in basedata collection:
<?xml version="1.0" encoding="UTF-8"?>
<entity>
<entityName>Company</entityName>
<createdBy>CompanyOwner</createdBy>
<createdDate>2017-01-01T05:56:35.360Z</createdDate>
<status>Active</status>
<entityattributes>
<entityattribute>
</entityattribute>
</entityattributes>
</entity>
Now created a test collection called DistinctValueTest and added a document for testing purpose in that collection.
Document in DistinctValueTest collection:
<?xml version="1.0" encoding="UTF-8"?>
<entity>
<entityName>DistinctValueTestEntity</entityName>
<createdBy>DistinctValuteSystemNew</createdBy>
<createdDate>2017-01-03T05:56:35.360Z</createdDate>
<status>Active</status>
<entityattributes>
<entityattribute>
</entityattribute>
</entityattributes>
</entity>
The output of the program is : Company,DistinctValueteSystemNew when both the collections are supplied in the additional query section
<cts:collection-query xmlns:cts="http://marklogic.com/cts">
<cts:uri>basedata</cts:uri>
<cts:uri>DistinctValueTest</cts:uri>
</cts:collection-query
However if i only provide "basedata" collection in the cts:uri it still shows output as Company,DistinctValueteSystemNew , instead of just Company
<cts:collection-query xmlns:cts="http://marklogic.com/cts">
<cts:uri>basedata</cts:uri>
</cts:collection-query
Upvotes: 1
Views: 221
Reputation: 8422
Building on grtjn's answer and your updated question, a straightforward way to restrict the values you get back to a particular collection is with an additional-query added to your options.
<search:options xmlns:search="http://marklogic.com/appservices/search">
<search:values name="createdBy">
<search:range type="xs:string">
<search:element name="createdBy"/>
</search:range>
</search:values>
<search:additional-query>
<cts:collection-query xmlns:cts="http://marklogic.com/cts">
<cts:uri>basedata</cts:uri>
<cts:uri>transactiondata</cts:uri>
</cts:collection-query>
</search:additional-query>
</search:options>
An additional-query
is added to every query run with that set of options.
Upvotes: 1
Reputation: 20414
Rather than building multiple indexes for each combination of criteria, you combine indexes upon read. The cts:values
function takes a 4th parameter which represents a query that will trim down the fragments from which values will be derived. In your case it would look something like:
cts:values(
cts:element-reference(xs:QName("entityName")),
(),
(),
cts:collection-query("basedata")
)
Similar effects can be achieved with the slightly more abstract search:values
function, and the /v1/values
REST endpoints.
HTH!
Upvotes: 3