package de.nava.mlsample.service;
import com.fasterxml.jackson.databind.JsonNode;
import com.marklogic.client.document.JSONDocumentManager;
import com.marklogic.client.extra.jackson.JacksonHandle;
import com.marklogic.client.io.DocumentMetadataHandle;
import com.marklogic.client.io.SearchHandle;
import com.marklogic.client.io.StringHandle;
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 java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Sample implementation of the {@link ProductRepository}
* making use of MarkLogic's {@link JSONDocumentManager}.
*
* @author Niko Schmuck
*/
@Component
public class ProductRepositoryJSON implements ProductRepository {
private static final Logger logger = LoggerFactory.getLogger(ProductRepositoryJSON.class);
public static final String COLLECTION_REF = "/products.json";
public static final String OPTIONS_NAME = "price-year-bucketed";
public static final int PAGE_SIZE = 10;
@Autowired
protected QueryManager queryManager;
@Autowired
protected JSONDocumentManager jsonDocumentManager;
@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);
JacksonHandle writeHandle = new JacksonHandle();
JsonNode writeDocument = writeHandle.getMapper().convertValue(product, JsonNode.class);
writeHandle.set(writeDocument);
// TODO: writing JacksonHandle with metadata throws: java.io.IOException: Attempted write to closed stream.
StringHandle stringHandle = new StringHandle(writeDocument.toString());
jsonDocumentManager.write(getDocId(product.getSku()), metadata, stringHandle);
}
@Override
public void remove(Long sku) {
jsonDocumentManager.delete(getDocId(sku));
}
@Override
public Product findBySku(Long sku) {
JacksonHandle jacksonHandle = new JacksonHandle();
logger.info("Search for product with SKU {} ...", sku);
jsonDocumentManager.read(getDocId(sku), jacksonHandle);
return fetchProduct(jacksonHandle);
}
/**
* Demonstrates End-to-End JSON direct access.
*/
public JsonNode rawfindBySku(Long sku) {
JacksonHandle jacksonHandle = new JacksonHandle();
jsonDocumentManager.read(getDocId(sku), jacksonHandle);
return jacksonHandle.get();
}
@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() {
StringQueryDefinition queryDef = queryManager.newStringDefinition(OPTIONS_NAME);
queryDef.setCollections(COLLECTION_REF);
SearchHandle resultsHandle = new SearchHandle();
queryManager.setPageLength(PAGE_SIZE);
queryManager.search(queryDef, resultsHandle, 0);
return toSearchResult(resultsHandle);
}
@Override
public ProductSearchResult findByName(String name) {
//KeyValueQueryDefinition query = queryManager.newKeyValueDefinition();
//query.put(queryManager.newKeyLocator("name"), name); // exact match
// Alternatively use:
StringQueryDefinition query = queryManager.newStringDefinition();
query.setCriteria(name); // example: "index OR Cassel NEAR Hare"
query.setCollections(COLLECTION_REF);
queryManager.setPageLength(PAGE_SIZE);
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 String getDocId(Long sku) {
return String.format("/products/%d.json", sku);
}
private ProductSearchResult toSearchResult(SearchHandle resultsHandle) {
List<Product> products = new ArrayList<>();
for (MatchDocumentSummary summary : resultsHandle.getMatchResults()) {
logger.info(" * found {}", summary.getUri());
// Assumption: summary URI refers to JSON document
JacksonHandle jacksonHandle = new JacksonHandle();
jsonDocumentManager.read(summary.getUri(), jacksonHandle);
products.add(fetchProduct(jacksonHandle));
}
return new ProductSearchResult(products, resultsHandle.getFacetResult("price"),
resultsHandle.getFacetResult("year"));
}
private Product fetchProduct(JacksonHandle jacksonHandle) {
try {
JsonNode jsonNode = jacksonHandle.get();
return jacksonHandle.getMapper().readValue(jsonNode.toString(), Product.class);
} catch (IOException e) {
throw new RuntimeException("Unable to cast to product", e);
}
}
}