/* * * * Copyright 2010-2014 Orient Technologies LTD (info(at)orientechnologies.com) * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * http://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package com.orientechnologies.orient.etl.transformer; import com.orientechnologies.common.collection.OMultiValue; import com.orientechnologies.orient.core.command.OCommandContext; import com.orientechnologies.orient.core.db.record.OIdentifiable; import com.orientechnologies.orient.core.exception.OConfigurationException; import com.orientechnologies.orient.core.metadata.schema.OType; import com.orientechnologies.orient.core.record.impl.ODocument; import com.orientechnologies.orient.etl.OETLProcessHaltedException; import com.orientechnologies.orient.etl.OETLProcessor; import java.util.*; /** * Converts a JOIN in LINK */ public class OLinkTransformer extends OAbstractLookupTransformer { private String joinValue; private String linkFieldName; private OType linkFieldType; @Override public ODocument getConfiguration() { return new ODocument() .fromJSON("{parameters:[" + getCommonConfigurationParameters() + "," + "{joinFieldName:{optional:true,description:'field name containing the value to join'}}," + "{joinValue:{optional:true,description:'value to use in lookup query'}}," + "{linkFieldName:{optional:false,description:'field name containing the link to set'}}," + "{linkFieldType:{optional:true,description:'field type containing the link to set. Use LINK for single link and LINKSET or LINKLIST for many'}}," + "{lookup:{optional:false,description:'<Class>.<property> or Query to execute'}}," + "{unresolvedLinkAction:{optional:true,description:'action when a unresolved link is found',values:" + stringArray2Json(ACTION.values()) + "}}]," + "input:['ODocument'],output:'ODocument'}"); } @Override public void configure(OETLProcessor iProcessor, final ODocument iConfiguration, OCommandContext iContext) { super.configure(iProcessor, iConfiguration, iContext); joinValue = iConfiguration.field("joinValue"); linkFieldName = iConfiguration.field("linkFieldName"); if (iConfiguration.containsField("linkFieldType")) linkFieldType = OType.valueOf((String) iConfiguration.field("linkFieldType")); } @Override public String getName() { return "link"; } @Override public Object executeTransform(final Object input) { if (!(input instanceof OIdentifiable)) { log(OETLProcessor.LOG_LEVELS.DEBUG, "skip because input value is not a record, but rather an instance of class: %s", input.getClass()); return null; } final ODocument doc = ((OIdentifiable) input).getRecord(); final Object joinRuntimeValue; if (joinFieldName != null) joinRuntimeValue = doc.field(joinFieldName); else if (joinValue != null) joinRuntimeValue = resolve(joinValue); else joinRuntimeValue = null; Object result; if (OMultiValue.isMultiValue(joinRuntimeValue)) { // RESOLVE SINGLE JOINS final Collection<Object> singleJoinsResult = new ArrayList<Object>(); for (Object o : OMultiValue.getMultiValueIterable(joinRuntimeValue)) { singleJoinsResult.add(lookup(o, true)); } result = singleJoinsResult; } else result = lookup(joinRuntimeValue, true); log(OETLProcessor.LOG_LEVELS.DEBUG, "joinRuntimeValue=%s, lookupResult=%s", joinRuntimeValue, result); if (result != null) { if (linkFieldType != null) { // CONVERT IT if (linkFieldType == OType.LINK) { if (result instanceof Collection<?>) { if (!((Collection) result).isEmpty()) result = ((Collection) result).iterator().next(); else result = null; } } else if (linkFieldType == OType.LINKSET) { if (!(result instanceof Collection)) { final Set<OIdentifiable> res = new HashSet<OIdentifiable>(); res.add((OIdentifiable) result); result = res; } } else if (linkFieldType == OType.LINKLIST) { if (!(result instanceof Collection)) { final List<OIdentifiable> res = new ArrayList<OIdentifiable>(); res.add((OIdentifiable) result); result = res; } } } if (result == null) { // APPLY THE STRATEGY DEFINED IN unresolvedLinkAction switch (unresolvedLinkAction) { case CREATE: if (lookup != null) { final String[] lookupParts = lookup.split("\\."); final ODocument linkedDoc = new ODocument(lookupParts[0]); linkedDoc.field(lookupParts[1], joinRuntimeValue); linkedDoc.save(); log(OETLProcessor.LOG_LEVELS.DEBUG, "created new document=%s", linkedDoc.getRecord()); result = linkedDoc; } else throw new OConfigurationException("Cannot create linked document because target class is unknown. Use 'lookup' field"); break; case ERROR: processor.getStats().incrementErrors(); log(OETLProcessor.LOG_LEVELS.ERROR, "%s: ERROR Cannot resolve join for value '%s'", getName(), joinRuntimeValue); break; case WARNING: processor.getStats().incrementWarnings(); log(OETLProcessor.LOG_LEVELS.INFO, "%s: WARN Cannot resolve join for value '%s'", getName(), joinRuntimeValue); break; case SKIP: return null; case HALT: throw new OETLProcessHaltedException("[Link transformer] Cannot resolve join for value '" + joinRuntimeValue + "'"); } } } // SET THE TRANSFORMED FIELD BACK doc.field(linkFieldName, result); log(OETLProcessor.LOG_LEVELS.DEBUG, "set %s=%s in document=%s", linkFieldName, result, input); return input; } }