use of com.rbmhtechnology.vind.api.query.update.Update in project vind by RBMHTechnology.
the class SolrSearchServer method execute.
@Override
public boolean execute(Update update, DocumentFactory factory) {
// Check if document is updatable and all its fields are stored.
final boolean isUpdatable = factory.isUpdatable() && factory.getFields().values().stream().allMatch(descriptor -> descriptor.isUpdate());
if (isUpdatable) {
final SolrInputDocument sdoc = new SolrInputDocument();
sdoc.addField(ID, update.getId());
sdoc.addField(TYPE, factory.getType());
HashMap<FieldDescriptor<?>, HashMap<String, SortedSet<UpdateOperation>>> updateOptions = update.getOptions();
updateOptions.keySet().forEach(fieldDescriptor -> Stream.of(UseCase.values()).forEach(useCase -> updateOptions.get(fieldDescriptor).keySet().stream().forEach(context -> {
// NOTE: Backwards compatibility
final String updateContext = Objects.isNull(context) ? update.getUpdateContext() : context;
final String fieldName = getFieldname(fieldDescriptor, useCase, updateContext);
if (fieldName != null) {
final Map<String, Object> fieldModifiers = new HashMap<>();
updateOptions.get(fieldDescriptor).get(context).stream().forEach(entry -> {
UpdateOperations opType = entry.getType();
if (fieldName.startsWith("dynamic_single_") && useCase.equals(UseCase.Sort) && opType.equals(UpdateOperations.add)) {
opType = set;
}
fieldModifiers.put(opType.name(), toSolrJType(SolrUtils.FieldValue.getFieldCaseValue(entry.getValue(), fieldDescriptor, useCase)));
});
sdoc.addField(fieldName, fieldModifiers);
}
})));
try {
if (solrClientLogger.isTraceEnabled()) {
solrClientLogger.debug(">>> add({}): {}", update.getId(), sdoc);
} else {
solrClientLogger.debug(">>> add({})", update.getId());
}
SolrInputDocument finalDoc = sdoc;
// Get the original document
final SolrDocument updatedDoc = solrClient.getById(update.getId());
// Setting the document version for optimistic concurrency
final Object version = updatedDoc.getFieldValue("_version_");
if (Objects.nonNull(version)) {
finalDoc.setField("_version_", version);
} else {
log.warn("Error updating document '{}': " + "Atomic updates in nested documents are not supported by Solr", updatedDoc.get(ID));
return false;
}
// Get the nested docs of the document if existing
final NamedList<Object> paramList = new NamedList<>();
paramList.add(CommonParams.Q, "!( _id_:" + update.getId() + ")&(_root_:" + update.getId() + ")");
final QueryResponse query = solrClient.query(SolrParams.toSolrParams(paramList), SolrRequest.METHOD.POST);
// if the document has nested docs solr does not support atomic updates
if (CollectionUtils.isNotEmpty(query.getResults())) {
log.info("Update document `{}`: doc has {} nested documents, changing from partial update to full index.", finalDoc.getFieldValue(SolrUtils.Fieldname.ID), query.getResults().size());
// Get the list of nested documents
final List<SolrInputDocument> childDocs = query.getResults().stream().map(nestedDoc -> ClientUtils.toSolrInputDocument(nestedDoc)).collect(Collectors.toList());
finalDoc = this.getUpdatedSolrDocument(sdoc, updatedDoc, childDocs);
}
try {
log.info("Updating document `{}`: current version `{}`", finalDoc.getFieldValue(SolrUtils.Fieldname.ID), version);
solrClient.add(finalDoc);
return true;
} catch (HttpSolrClient.RemoteSolrException e) {
log.warn("Error updating document {}: {}", finalDoc.getFieldValue(ID), e.getMessage(), e);
return false;
}
} catch (SolrServerException | IOException e) {
log.error("Unable to perform solr partial update on document with id [{}]", update.getId(), e);
throw new SearchServerException("Can not execute solr partial update.", e);
}
} else {
Exception e = new SearchServerException("It is not safe to execute solr partial update: Document contains non stored fields");
log.error("Unable to perform solr partial update on document with id [{}]", update.getId(), e);
throw new RuntimeException("Can not execute solr partial update.", e);
}
}
use of com.rbmhtechnology.vind.api.query.update.Update in project vind by RBMHTechnology.
the class ParentChildrenTest method testParentDuplicationOnAtomicUpdate.
// MBDN-579
@Test
public void testParentDuplicationOnAtomicUpdate() {
// Safe check: ensure the orChildren search works
FulltextSearch search = Search.fulltext().filter(eq(shared_value, "red")).orChildrenSearch(child);
SearchResult result = server.execute(search, parent);
assertEquals(2, result.getNumOfResults());
assertEquals(Integer.valueOf(1), result.getResults().get(1).getChildCount());
// Update parent document to pink
final Update updateToPink = Search.update("P2").set(parent_value, "pink");
server.execute(updateToPink, parent);
server.commit();
search = Search.fulltext().filter(eq(parent_value, "pink"));
result = server.execute(search, parent);
assertEquals(1, result.getNumOfResults());
// Safe check: ensure the orChildren search still works
search = Search.fulltext().filter(eq(shared_value, "red")).orChildrenSearch(child);
result = server.execute(search, parent);
assertEquals(2, result.getNumOfResults());
assertEquals(Integer.valueOf(1), result.getResults().get(1).getChildCount());
// ////////////////////////////////////
server.index(parent.createDoc("P2").setValue(parent_value, "blue").setValue(shared_value, "purple").addChild(child.createDoc("C1").setValue(child_value, "red").setValue(shared_value, "red"), child.createDoc("C2").setValue(child_value, "goblin-green").setValue(shared_value, "yellow")));
server.commit();
// ///////////////////////////////////
// Update parent document to neon-orange
final Update updateToNeonOrange = Search.update("P2").set(parent_value, "neon-orange");
server.execute(updateToNeonOrange, parent);
server.commit();
search = Search.fulltext().filter(eq(parent_value, "neon-orange"));
result = server.execute(search, parent);
assertEquals(1, result.getNumOfResults());
// Safe check: ensure the orChildren search still works
search = Search.fulltext().filter(eq(shared_value, "red")).orChildrenSearch(child);
result = server.execute(search, parent);
assertEquals(2, result.getNumOfResults());
assertEquals(Integer.valueOf(1), result.getResults().get(1).getChildCount());
server.index(parent.createDoc("P2").setValue(parent_value, "neon-yellow").setValue(shared_value, "purple").addChild(child.createDoc("C1").setValue(child_value, "red").setValue(shared_value, "red"), child.createDoc("C2").setValue(child_value, "blue").setValue(shared_value, "yellow")));
server.commit();
search = Search.fulltext().filter(eq(parent_value, "neon-yellow"));
result = server.execute(search, parent);
assertEquals(1, result.getNumOfResults());
// Safe check: ensure the orChildren search still works
search = Search.fulltext().filter(eq(shared_value, "red")).orChildrenSearch(child);
result = server.execute(search, parent);
assertEquals(2, result.getNumOfResults());
assertEquals(Integer.valueOf(1), result.getResults().get(1).getChildCount());
server.index(parent.createDoc("P2").setValue(parent_value, "neon-yellow").setValue(shared_value, "purple").addChild(child.createDoc("C0").setValue(child_value, "blue").setValue(shared_value, "yellow")));
server.commit();
assertEquals(1, 1);
}
use of com.rbmhtechnology.vind.api.query.update.Update in project vind by RBMHTechnology.
the class SolrSearchServer method getUpdatedSolrDocument.
private SolrInputDocument getUpdatedSolrDocument(SolrInputDocument sdoc, SolrDocument updatedDoc, List<SolrInputDocument> nestedDocs) {
// TODO:find a better way - non deprecated way
// Create an input document from the original doc to be updated
final SolrInputDocument inputDoc = ClientUtils.toSolrInputDocument(updatedDoc);
// Add nested documents to the doc
inputDoc.addChildDocuments(nestedDocs);
// TODO: think about a cleaner solution
// Update the original document
sdoc.getFieldNames().stream().filter(// TODO: Add all the special fields or do the oposite check, whether it fits a dynamic Vind field
fn -> !fn.equals(ID) && !fn.equals(TYPE) && !fn.equals("_version_")).forEach(fn -> {
final ArrayList fieldOp = (ArrayList) sdoc.getFieldValues(fn);
fieldOp.stream().forEach(op -> {
final Set<String> keys = ((HashMap<String, String>) op).keySet();
keys.stream().forEach(k -> {
switch(UpdateOperations.valueOf(k)) {
case set:
inputDoc.setField(fn, ((HashMap<String, String>) op).get(k));
break;
case add:
inputDoc.addField(fn, ((HashMap<String, String>) op).get(k));
break;
case inc:
final Number fieldValue;
try {
fieldValue = NumberFormat.getInstance().parse((String) inputDoc.getFieldValue(fn));
} catch (ParseException e) {
throw new RuntimeException();
}
inputDoc.setField(fn, String.valueOf(fieldValue.floatValue() + 1));
break;
case remove:
inputDoc.removeField(fn);
break;
case removeregex:
final String fieldStringValue = (String) inputDoc.getFieldValue(fn);
final String regex = ((HashMap<String, String>) op).get(k);
if (regex.matches(fieldStringValue)) {
inputDoc.removeField(fn);
}
break;
}
});
});
});
return inputDoc;
}
Aggregations