package de.nava.mlsample.service;
import com.marklogic.client.document.XMLDocumentManager;
import com.marklogic.client.io.DocumentMetadataHandle;
import com.marklogic.client.io.JAXBHandle;
import com.marklogic.client.io.SearchHandle;
import com.marklogic.client.query.*;
import de.nava.mlsample.domain.Product;
import de.nava.mlsample.domain.ProductSearchResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.List;
/**
* Sample implementation of the {@link ProductRepository}
* making use of MarkLogic's {@link XMLDocumentManager}.
*
* @author Niko Schmuck
*/
@Component
public class ProductRepositoryXML implements ProductRepository {
private static final Logger logger = LoggerFactory.getLogger(ProductRepositoryXML.class);
public static final String COLLECTION_REF = "/products.xml";
public static final int PAGE_SIZE = 10;
@Autowired
protected QueryManager queryManager;
@Autowired
protected XMLDocumentManager xmlDocumentManager;
@Override
public void add(Product product) {
// Add this document to a dedicated collection for later retrieval
DocumentMetadataHandle metadata = new DocumentMetadataHandle();
metadata.getCollections().add(COLLECTION_REF);
JAXBHandle contentHandle = getProductHandle();
contentHandle.set(product);
xmlDocumentManager.write(getDocId(product.getSku()), metadata, contentHandle);
}
@Override
public void remove(Long sku) {
xmlDocumentManager.delete(getDocId(sku));
}
@Override
public Product findBySku(Long sku) {
JAXBHandle contentHandle = getProductHandle();
JAXBHandle result = xmlDocumentManager.read(getDocId(sku), contentHandle);
return (Product) result.get(Product.class);
}
@Override
public Long count() {
StructuredQueryBuilder sb = queryManager.newStructuredQueryBuilder();
StructuredQueryDefinition criteria = sb.collection(COLLECTION_REF);
SearchHandle resultsHandle = new SearchHandle();
queryManager.search(criteria, resultsHandle);
return resultsHandle.getTotalResults();
}
@Override
public ProductSearchResult findAll() {
StructuredQueryBuilder sb = queryManager.newStructuredQueryBuilder();
StructuredQueryDefinition criteria = sb.collection(COLLECTION_REF);
SearchHandle resultsHandle = new SearchHandle();
queryManager.search(criteria, resultsHandle);
return toSearchResult(resultsHandle);
}
@Override
public ProductSearchResult findByName(String name) {
KeyValueQueryDefinition query = queryManager.newKeyValueDefinition();
queryManager.setPageLength(PAGE_SIZE);
query.put(queryManager.newElementLocator(new QName("name")), name);
// TODO: How to restrict either to XML or JSON document types?
SearchHandle resultsHandle = new SearchHandle();
queryManager.search(query, resultsHandle);
return toSearchResult(resultsHandle);
}
@Override
public ProductSearchResult findByYear(int year) {
throw new UnsupportedOperationException("findByYear: not yet implemented");
}
// ~~
private JAXBHandle getProductHandle() {
try {
JAXBContext context = JAXBContext.newInstance(Product.class);
return new JAXBHandle(context);
} catch (JAXBException e) {
throw new RuntimeException("Unable to create product JAXB context", e);
}
}
private String getDocId(Long sku) {
return String.format("/products/%d.xml", sku);
}
private ProductSearchResult toSearchResult(SearchHandle resultsHandle) {
List<Product> products = new ArrayList<>();
for (MatchDocumentSummary summary : resultsHandle.getMatchResults()) {
JAXBHandle contentHandle = getProductHandle();
logger.info(" * found {}", summary.getUri());
xmlDocumentManager.read(summary.getUri(), contentHandle);
products.add((Product) contentHandle.get(Product.class));
}
return new ProductSearchResult(products, resultsHandle.getFacetResult("price"),
resultsHandle.getFacetResult("year"));
}
}