Skip to content

Commit

Permalink
Merge pull request #64 from timatbw/add-concat-upsert-action
Browse files Browse the repository at this point in the history
CAPI-654 Add new concat/concat_lc actions to UpsertCondition used by …
  • Loading branch information
timatbw authored Mar 8, 2024
2 parents d06ab17 + dc7d295 commit 1a316c8
Show file tree
Hide file tree
Showing 3 changed files with 465 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.lucene.util.BytesRef;
import org.apache.solr.common.SolrException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.regex.Pattern;
import java.util.stream.Stream;

import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.BooleanClause;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrInputDocument;
Expand All @@ -44,7 +45,9 @@
class UpsertCondition {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

private static final Pattern ACTION_PATTERN = Pattern.compile("^(skip|insert)|(upsert|retain):(\\*|[\\w,]+)|(nullify):([\\w,]+)$");
private static final Pattern ACTION_PATTERN = Pattern.compile(
"^(skip|insert)|(upsert|retain):(\\*|[\\w,]+)|(nullify):([\\w,]+)|(concat|concat_lc):([\\w]+):([\\w,|?]+)$"
);
private static final List<String> ALL_FIELDS = Collections.singletonList("*");

private final String name;
Expand Down Expand Up @@ -137,6 +140,7 @@ ActionType run(SolrInputDocument oldDoc, SolrInputDocument newDoc) {
boolean matches(SolrInputDocument oldDoc, SolrInputDocument newDoc) {
Docs docs = new Docs(oldDoc, newDoc);
boolean atLeastOneMatched = false;
boolean hasPositive = false;
for (FieldRule rule: rules) {
boolean ruleMatched = rule.matches(docs);
switch(rule.getOccur()) {
Expand All @@ -145,25 +149,28 @@ boolean matches(SolrInputDocument oldDoc, SolrInputDocument newDoc) {
return false;
}
atLeastOneMatched = true;
hasPositive = true;
break;
case MUST_NOT:
if (ruleMatched) {
return false;
}
atLeastOneMatched = true;
break;
default:
atLeastOneMatched = ruleMatched || atLeastOneMatched;
hasPositive = true;
break;
}
}
return atLeastOneMatched;
return atLeastOneMatched || !hasPositive;
}

enum ActionType {
UPSERT, // copy some/all fields from the OLD doc (when they don't exist on the new doc)
RETAIN, // copy some/all fields from the OLD doc always
NULLIFY, // make sure specific fields are null before doc written
CONCAT, // attempt to set a field to be the concatenation of other fields from NEW or OLD doc
CONCAT_LC, // attempt to set a field to be the lowercase concatenation of other fields from NEW or OLD doc
INSERT, // just do a regular insert as normal
SKIP; // entirely skip inserting the doc
}
Expand Down Expand Up @@ -289,10 +296,12 @@ private static Predicate<SolrInputDocument> forField(String field, Predicate<Obj
private static class Action {
private final ActionType type;
private final List<String> fields;
private final String target;

Action(ActionType type, List<String> fields) {
Action(ActionType type, List<String> fields, String target) {
this.type = type;
this.fields = fields;
this.target = target;
}

static Action parse(String actionValue) {
Expand All @@ -302,6 +311,7 @@ static Action parse(String actionValue) {
}
ActionType type;
List<String> fields;
String target = null;
if (m.group(1) != null) {
if ("skip".equals(m.group(1))) {
type = ActionType.SKIP;
Expand All @@ -317,12 +327,21 @@ static Action parse(String actionValue) {
}
String fieldsConfig = m.group(3);
fields = Arrays.asList(fieldsConfig.split(","));
} else {
} else if (m.group(4) != null) {
type = ActionType.NULLIFY;
String fieldsConfig = m.group(5);
fields = Arrays.asList(fieldsConfig.split(","));
} else {
if ("concat".equals(m.group(6))) {
type = ActionType.CONCAT;
} else {
type = ActionType.CONCAT_LC;
}
target = m.group(7);
String fieldsConfig = m.group(8);
fields = Arrays.asList(fieldsConfig.split(","));
}
return new Action(type, fields);
return new Action(type, fields, target);
}

void run(SolrInputDocument oldDoc, SolrInputDocument newDoc) {
Expand All @@ -346,7 +365,51 @@ void run(SolrInputDocument oldDoc, SolrInputDocument newDoc) {
fields.forEach(field -> {
newDoc.setField(field, null);
});
} else if (type == ActionType.CONCAT || type == ActionType.CONCAT_LC) {
final StringBuilder builder = new StringBuilder();
for (String field : fields) {
final String fieldValue = getFieldValue(field, oldDoc, newDoc);
if (fieldValue == null) {
// One of the required fields is not present, so we can't set the target field
return;
}
builder.append(type == ActionType.CONCAT_LC ? fieldValue.toLowerCase(Locale.ROOT) : fieldValue);
}
newDoc.setField(target, builder.toString());
}
}

private static String getFieldValue(String field, SolrInputDocument oldDoc, SolrInputDocument newDoc) {
boolean optional = field.endsWith("?");
for (String fieldName : StringUtils.removeEnd(field, "?").split("\\|")) {
String value = getFieldFromDoc(fieldName, newDoc);
if (value != null) {
return value;
}
value = getFieldFromDoc(fieldName, oldDoc);
if (value != null) {
return value;
}
}
return optional ? "" : null;
}

private static String getFieldFromDoc(String fieldName, SolrInputDocument doc) {
if (doc == null) {
return null;
}
Object fieldValue = doc.getFieldValue(fieldName);
if (fieldValue instanceof String) {
return (String)fieldValue;
}
if (fieldValue instanceof Map) {
final Object setValue = ((Map)fieldValue).get("set");
if (setValue instanceof String) {
return (String)setValue;
}
}
// Cannot support non-String types or collection (multi-valued field) types
return null;
}
}
}
Loading

0 comments on commit 1a316c8

Please sign in to comment.