/* * * * Copyright 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. * * * * For more information: http://www.orientechnologies.com * */ package com.orientechnologies.orient.core.index; import com.orientechnologies.orient.core.db.record.OMultiValueChangeEvent; import com.orientechnologies.orient.core.metadata.schema.OType; import com.orientechnologies.orient.core.record.impl.ODocument; import java.util.*; /** * Index implementation bound to one schema class property that presents * {@link com.orientechnologies.orient.core.metadata.schema.OType#EMBEDDEDMAP or * * @link com.orientechnologies.orient.core.metadata.schema.OType#LINKMAP} property. */ public class OPropertyMapIndexDefinition extends OAbstractIndexDefinitionMultiValue { private static final long serialVersionUID = 275241019910834466L; /** * Indicates whether Map will be indexed using its keys or values. */ public static enum INDEX_BY { KEY, VALUE } private INDEX_BY indexBy = INDEX_BY.KEY; public OPropertyMapIndexDefinition() { } public OPropertyMapIndexDefinition(final String iClassName, final String iField, final OType iType, final INDEX_BY indexBy) { super(iClassName, iField, iType); if (indexBy == null) throw new NullPointerException("You have to provide way by which map entries should be mapped"); this.indexBy = indexBy; } @Override public Object getDocumentValueToIndex(ODocument iDocument) { return createValue(iDocument.field(field)); } @Override public Object createValue(List<?> params) { if (!(params.get(0) instanceof Map)) return null; final Collection<?> mapParams = extractMapParams((Map<?, ?>) params.get(0)); final List<Object> result = new ArrayList<Object>(mapParams.size()); for (final Object mapParam : mapParams) { result.add(createSingleValue(mapParam)); } return result; } @Override public Object createValue(Object... params) { if (!(params[0] instanceof Map)) return null; final Collection<?> mapParams = extractMapParams((Map<?, ?>) params[0]); final List<Object> result = new ArrayList<Object>(mapParams.size()); for (final Object mapParam : mapParams) { result.add(createSingleValue(mapParam)); } return result; } public INDEX_BY getIndexBy() { return indexBy; } @Override protected void serializeToStream() { super.serializeToStream(); document.field("mapIndexBy", indexBy.toString()); } @Override protected void serializeFromStream() { super.serializeFromStream(); indexBy = INDEX_BY.valueOf(document.<String> field("mapIndexBy")); } private Collection<?> extractMapParams(Map<?, ?> map) { if (indexBy == INDEX_BY.KEY) return map.keySet(); return map.values(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; OPropertyMapIndexDefinition that = (OPropertyMapIndexDefinition) o; if (indexBy != that.indexBy) return false; return true; } public Object createSingleValue(final Object... param) { return OType.convert(param[0], keyType.getDefaultJavaType()); } public void processChangeEvent(final OMultiValueChangeEvent<?, ?> changeEvent, final Map<Object, Integer> keysToAdd, final Map<Object, Integer> keysToRemove) { final boolean result; if (indexBy.equals(INDEX_BY.KEY)) result = processKeyChangeEvent(changeEvent, keysToAdd, keysToRemove); else result = processValueChangeEvent(changeEvent, keysToAdd, keysToRemove); if (!result) throw new IllegalArgumentException("Invalid change type :" + changeEvent.getChangeType()); } private boolean processKeyChangeEvent(final OMultiValueChangeEvent<?, ?> changeEvent, final Map<Object, Integer> keysToAdd, final Map<Object, Integer> keysToRemove) { switch (changeEvent.getChangeType()) { case ADD: processAdd(createSingleValue(changeEvent.getKey()), keysToAdd, keysToRemove); return true; case REMOVE: processRemoval(createSingleValue(changeEvent.getKey()), keysToAdd, keysToRemove); return true; case UPDATE: return true; } return false; } private boolean processValueChangeEvent(final OMultiValueChangeEvent<?, ?> changeEvent, final Map<Object, Integer> keysToAdd, final Map<Object, Integer> keysToRemove) { switch (changeEvent.getChangeType()) { case ADD: processAdd(createSingleValue(changeEvent.getValue()), keysToAdd, keysToRemove); return true; case REMOVE: processRemoval(createSingleValue(changeEvent.getOldValue()), keysToAdd, keysToRemove); return true; case UPDATE: processRemoval(createSingleValue(changeEvent.getOldValue()), keysToAdd, keysToRemove); processAdd(createSingleValue(changeEvent.getValue()), keysToAdd, keysToRemove); return true; } return false; } @Override public List<String> getFieldsToIndex() { if (indexBy == INDEX_BY.KEY) return Collections.singletonList(field + " by key"); return Collections.singletonList(field + " by value"); } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + indexBy.hashCode(); return result; } @Override public String toString() { return "OPropertyMapIndexDefinition{" + "indexBy=" + indexBy + "} " + super.toString(); } @Override public String toCreateIndexDDL(String indexName, String indexType,String engine) { final StringBuilder ddl = new StringBuilder("create index `"); ddl.append(indexName).append("` on `"); ddl.append(className).append("` ( `").append(field).append("`"); if (indexBy == INDEX_BY.KEY) ddl.append(" by key"); else ddl.append(" by value"); ddl.append(" ) "); ddl.append(indexType); return ddl.toString(); } }