Java tutorial
// Copyright 2017 JanusGraph Authors // // 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 org.janusgraph.graphdb.database; import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.collect.*; import org.janusgraph.core.*; import org.janusgraph.core.Cardinality; import org.janusgraph.core.schema.Parameter; import org.janusgraph.core.schema.SchemaStatus; import org.janusgraph.diskstorage.configuration.Configuration; import org.janusgraph.graphdb.idmanagement.IDManager; import org.janusgraph.graphdb.query.graph.GraphCentricQueryBuilder; import org.janusgraph.graphdb.query.graph.MultiKeySliceQuery; import org.janusgraph.graphdb.types.ParameterType; import org.janusgraph.diskstorage.*; import org.janusgraph.diskstorage.indexing.*; import org.janusgraph.diskstorage.Entry; import org.janusgraph.diskstorage.keycolumnvalue.KeySliceQuery; import org.janusgraph.diskstorage.util.BufferUtil; import org.janusgraph.diskstorage.util.HashingUtil; import org.janusgraph.diskstorage.util.StaticArrayEntry; import org.janusgraph.graphdb.database.idhandling.VariableLong; import org.janusgraph.graphdb.database.management.ManagementSystem; import org.janusgraph.graphdb.database.serialize.AttributeUtil; import org.janusgraph.graphdb.database.serialize.DataOutput; import org.janusgraph.graphdb.database.serialize.Serializer; import org.janusgraph.graphdb.internal.*; import org.janusgraph.graphdb.query.graph.IndexQueryBuilder; import org.janusgraph.graphdb.query.JanusGraphPredicate; import org.janusgraph.graphdb.query.condition.*; import org.janusgraph.graphdb.query.graph.JointIndexQuery; import org.janusgraph.graphdb.query.vertex.VertexCentricQueryBuilder; import org.janusgraph.graphdb.relations.RelationIdentifier; import org.janusgraph.graphdb.transaction.StandardJanusGraphTx; import org.janusgraph.graphdb.types.*; import org.janusgraph.util.encoding.LongEncoding; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.HashCodeBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nullable; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.INDEX_NAME_MAPPING; /** * @author Matthias Broecheler (me@matthiasb.com) */ public class IndexSerializer { private static final Logger log = LoggerFactory.getLogger(IndexSerializer.class); private static final int DEFAULT_OBJECT_BYTELEN = 30; private static final byte FIRST_INDEX_COLUMN_BYTE = 0; private final Serializer serializer; private final Configuration configuration; private final Map<String, ? extends IndexInformation> mixedIndexes; private final boolean hashKeys; private final HashingUtil.HashLength hashLength = HashingUtil.HashLength.SHORT; public IndexSerializer(Configuration config, Serializer serializer, Map<String, ? extends IndexInformation> indexes, final boolean hashKeys) { this.serializer = serializer; this.configuration = config; this.mixedIndexes = indexes; this.hashKeys = hashKeys; if (hashKeys) log.info("Hashing index keys"); } /* ################################################ Index Information ################################################### */ public boolean containsIndex(final String indexName) { return mixedIndexes.containsKey(indexName); } public String getDefaultFieldName(final PropertyKey key, final Parameter[] parameters, final String indexName) { Preconditions.checkArgument(!ParameterType.MAPPED_NAME.hasParameter(parameters), "A field name mapping has been specified for key: %s", key); Preconditions.checkArgument(containsIndex(indexName), "Unknown backing index: %s", indexName); String fieldname = configuration.get(INDEX_NAME_MAPPING, indexName) ? key.name() : keyID2Name(key); return mixedIndexes.get(indexName).mapKey2Field(fieldname, new StandardKeyInformation(key, parameters)); } public static void register(final MixedIndexType index, final PropertyKey key, final BackendTransaction tx) throws BackendException { tx.getIndexTransaction(index.getBackingIndexName()).register(index.getStoreName(), key2Field(index, key), getKeyInformation(index.getField(key))); } // public boolean supports(final String indexName, final Class<?> dataType, final Parameter[] parameters) { // IndexInformation indexinfo = indexes.get(indexName); // Preconditions.checkArgument(indexinfo != null, "Index is unknown or not configured: %s", indexName); // return indexinfo.supports(new StandardKeyInformation(dataType,parameters)); // } public boolean supports(final MixedIndexType index, final ParameterIndexField field) { IndexInformation indexinfo = mixedIndexes.get(index.getBackingIndexName()); Preconditions.checkArgument(indexinfo != null, "Index is unknown or not configured: %s", index.getBackingIndexName()); return indexinfo.supports(getKeyInformation(field)); } public boolean supports(final MixedIndexType index, final ParameterIndexField field, final JanusGraphPredicate predicate) { IndexInformation indexinfo = mixedIndexes.get(index.getBackingIndexName()); Preconditions.checkArgument(indexinfo != null, "Index is unknown or not configured: %s", index.getBackingIndexName()); return indexinfo.supports(getKeyInformation(field), predicate); } private static StandardKeyInformation getKeyInformation(final ParameterIndexField field) { return new StandardKeyInformation(field.getFieldKey(), field.getParameters()); } public IndexInfoRetriever getIndexInfoRetriever(StandardJanusGraphTx tx) { return new IndexInfoRetriever(tx); } public static class IndexInfoRetriever implements KeyInformation.Retriever { private final StandardJanusGraphTx transaction; private IndexInfoRetriever(StandardJanusGraphTx tx) { Preconditions.checkNotNull(tx); transaction = tx; } @Override public KeyInformation.IndexRetriever get(final String index) { return new KeyInformation.IndexRetriever() { Map<String, KeyInformation.StoreRetriever> indexes = new ConcurrentHashMap<String, KeyInformation.StoreRetriever>(); @Override public KeyInformation get(String store, String key) { return get(store).get(key); } @Override public KeyInformation.StoreRetriever get(final String store) { if (indexes.get(store) == null) { Preconditions.checkState(transaction != null, "Retriever has not been initialized"); final MixedIndexType extIndex = getMixedIndex(store, transaction); assert extIndex.getBackingIndexName().equals(index); ImmutableMap.Builder<String, KeyInformation> b = ImmutableMap.builder(); for (ParameterIndexField field : extIndex.getFieldKeys()) b.put(key2Field(field), getKeyInformation(field)); final ImmutableMap<String, KeyInformation> infoMap = b.build(); KeyInformation.StoreRetriever storeRetriever = new KeyInformation.StoreRetriever() { @Override public KeyInformation get(String key) { return infoMap.get(key); } }; indexes.put(store, storeRetriever); } return indexes.get(store); } }; } } /* ################################################ Index Updates ################################################### */ public static class IndexUpdate<K, E> { private enum Type { ADD, DELETE }; private final IndexType index; private final Type mutationType; private final K key; private final E entry; private final JanusGraphElement element; private IndexUpdate(IndexType index, Type mutationType, K key, E entry, JanusGraphElement element) { assert index != null && mutationType != null && key != null && entry != null && element != null; assert !index.isCompositeIndex() || (key instanceof StaticBuffer && entry instanceof Entry); assert !index.isMixedIndex() || (key instanceof String && entry instanceof IndexEntry); this.index = index; this.mutationType = mutationType; this.key = key; this.entry = entry; this.element = element; } public JanusGraphElement getElement() { return element; } public IndexType getIndex() { return index; } public Type getType() { return mutationType; } public K getKey() { return key; } public E getEntry() { return entry; } public boolean isAddition() { return mutationType == Type.ADD; } public boolean isDeletion() { return mutationType == Type.DELETE; } public boolean isCompositeIndex() { return index.isCompositeIndex(); } public boolean isMixedIndex() { return index.isMixedIndex(); } public void setTTL(int ttl) { Preconditions.checkArgument(ttl > 0 && mutationType == Type.ADD); ((MetaAnnotatable) entry).setMetaData(EntryMetaData.TTL, ttl); } @Override public int hashCode() { return new HashCodeBuilder().append(index).append(mutationType).append(key).append(entry).toHashCode(); } @Override public boolean equals(Object other) { if (this == other) return true; else if (other == null || !(other instanceof IndexUpdate)) return false; IndexUpdate oth = (IndexUpdate) other; return index.equals(oth.index) && mutationType == oth.mutationType && key.equals(oth.key) && entry.equals(oth.entry); } } private static final IndexUpdate.Type getUpateType(InternalRelation relation) { assert relation.isNew() || relation.isRemoved(); return (relation.isNew() ? IndexUpdate.Type.ADD : IndexUpdate.Type.DELETE); } private static boolean indexAppliesTo(IndexType index, JanusGraphElement element) { return index.getElement().isInstance(element) && (!(index instanceof CompositeIndexType) || ((CompositeIndexType) index).getStatus() != SchemaStatus.DISABLED) && (!index.hasSchemaTypeConstraint() || index.getElement().matchesConstraint(index.getSchemaTypeConstraint(), element)); } public Collection<IndexUpdate> getIndexUpdates(InternalRelation relation) { assert relation.isNew() || relation.isRemoved(); Set<IndexUpdate> updates = Sets.newHashSet(); IndexUpdate.Type updateType = getUpateType(relation); int ttl = updateType == IndexUpdate.Type.ADD ? StandardJanusGraph.getTTL(relation) : 0; for (RelationType type : relation.getPropertyKeysDirect()) { if (!(type instanceof PropertyKey)) continue; PropertyKey key = (PropertyKey) type; for (IndexType index : ((InternalRelationType) key).getKeyIndexes()) { if (!indexAppliesTo(index, relation)) continue; IndexUpdate update; if (index instanceof CompositeIndexType) { CompositeIndexType iIndex = (CompositeIndexType) index; RecordEntry[] record = indexMatch(relation, iIndex); if (record == null) continue; update = new IndexUpdate<StaticBuffer, Entry>(iIndex, updateType, getIndexKey(iIndex, record), getIndexEntry(iIndex, record, relation), relation); } else { assert relation.valueOrNull(key) != null; if (((MixedIndexType) index).getField(key).getStatus() == SchemaStatus.DISABLED) continue; update = getMixedIndexUpdate(relation, key, relation.valueOrNull(key), (MixedIndexType) index, updateType); } if (ttl > 0) update.setTTL(ttl); updates.add(update); } } return updates; } private static PropertyKey[] getKeysOfRecords(RecordEntry[] record) { PropertyKey[] keys = new PropertyKey[record.length]; for (int i = 0; i < record.length; i++) keys[i] = record[i].key; return keys; } private static int getIndexTTL(InternalVertex vertex, PropertyKey... keys) { int ttl = StandardJanusGraph.getTTL(vertex); for (int i = 0; i < keys.length; i++) { PropertyKey key = keys[i]; int kttl = ((InternalRelationType) key).getTTL(); if (kttl > 0 && (kttl < ttl || ttl <= 0)) ttl = kttl; } return ttl; } public Collection<IndexUpdate> getIndexUpdates(InternalVertex vertex, Collection<InternalRelation> updatedProperties) { if (updatedProperties.isEmpty()) return Collections.EMPTY_LIST; Set<IndexUpdate> updates = Sets.newHashSet(); for (InternalRelation rel : updatedProperties) { assert rel.isProperty(); JanusGraphVertexProperty p = (JanusGraphVertexProperty) rel; assert rel.isNew() || rel.isRemoved(); assert rel.getVertex(0).equals(vertex); IndexUpdate.Type updateType = getUpateType(rel); for (IndexType index : ((InternalRelationType) p.propertyKey()).getKeyIndexes()) { if (!indexAppliesTo(index, vertex)) continue; if (index.isCompositeIndex()) { //Gather composite indexes CompositeIndexType cIndex = (CompositeIndexType) index; IndexRecords updateRecords = indexMatches(vertex, cIndex, updateType == IndexUpdate.Type.DELETE, p.propertyKey(), new RecordEntry(p)); for (RecordEntry[] record : updateRecords) { IndexUpdate update = new IndexUpdate<StaticBuffer, Entry>(cIndex, updateType, getIndexKey(cIndex, record), getIndexEntry(cIndex, record, vertex), vertex); int ttl = getIndexTTL(vertex, getKeysOfRecords(record)); if (ttl > 0 && updateType == IndexUpdate.Type.ADD) update.setTTL(ttl); updates.add(update); } } else { //Update mixed indexes if (((MixedIndexType) index).getField(p.propertyKey()).getStatus() == SchemaStatus.DISABLED) continue; IndexUpdate update = getMixedIndexUpdate(vertex, p.propertyKey(), p.value(), (MixedIndexType) index, updateType); int ttl = getIndexTTL(vertex, p.propertyKey()); if (ttl > 0 && updateType == IndexUpdate.Type.ADD) update.setTTL(ttl); updates.add(update); } } } return updates; } private IndexUpdate<String, IndexEntry> getMixedIndexUpdate(JanusGraphElement element, PropertyKey key, Object value, MixedIndexType index, IndexUpdate.Type updateType) { return new IndexUpdate<String, IndexEntry>(index, updateType, element2String(element), new IndexEntry(key2Field(index.getField(key)), value), element); } public void reindexElement(JanusGraphElement element, MixedIndexType index, Map<String, Map<String, List<IndexEntry>>> documentsPerStore) { if (!indexAppliesTo(index, element)) return; List<IndexEntry> entries = Lists.newArrayList(); for (ParameterIndexField field : index.getFieldKeys()) { PropertyKey key = field.getFieldKey(); if (field.getStatus() == SchemaStatus.DISABLED) continue; if (element.properties(key.name()).hasNext()) { element.values(key.name()) .forEachRemaining(value -> entries.add(new IndexEntry(key2Field(field), value))); } } Map<String, List<IndexEntry>> documents = documentsPerStore.get(index.getStoreName()); if (documents == null) { documents = Maps.newHashMap(); documentsPerStore.put(index.getStoreName(), documents); } getDocuments(documentsPerStore, index).put(element2String(element), entries); } private Map<String, List<IndexEntry>> getDocuments(Map<String, Map<String, List<IndexEntry>>> documentsPerStore, MixedIndexType index) { Map<String, List<IndexEntry>> documents = documentsPerStore.get(index.getStoreName()); if (documents == null) { documents = Maps.newHashMap(); documentsPerStore.put(index.getStoreName(), documents); } return documents; } public void removeElement(Object elementId, MixedIndexType index, Map<String, Map<String, List<IndexEntry>>> documentsPerStore) { Preconditions.checkArgument( (index.getElement() == ElementCategory.VERTEX && elementId instanceof Long) || (index.getElement().isRelation() && elementId instanceof RelationIdentifier), "Invalid element id [%s] provided for index: %s", elementId, index); getDocuments(documentsPerStore, index).put(element2String(elementId), Lists.<IndexEntry>newArrayList()); } public Set<IndexUpdate<StaticBuffer, Entry>> reindexElement(JanusGraphElement element, CompositeIndexType index) { Set<IndexUpdate<StaticBuffer, Entry>> indexEntries = Sets.newHashSet(); if (!indexAppliesTo(index, element)) return indexEntries; Iterable<RecordEntry[]> records; if (element instanceof JanusGraphVertex) records = indexMatches((JanusGraphVertex) element, index); else { assert element instanceof JanusGraphRelation; records = Collections.EMPTY_LIST; RecordEntry[] record = indexMatch((JanusGraphRelation) element, index); if (record != null) records = ImmutableList.of(record); } for (RecordEntry[] record : records) { indexEntries.add(new IndexUpdate<StaticBuffer, Entry>(index, IndexUpdate.Type.ADD, getIndexKey(index, record), getIndexEntry(index, record, element), element)); } return indexEntries; } public static RecordEntry[] indexMatch(JanusGraphRelation relation, CompositeIndexType index) { IndexField[] fields = index.getFieldKeys(); RecordEntry[] match = new RecordEntry[fields.length]; for (int i = 0; i < fields.length; i++) { IndexField f = fields[i]; Object value = relation.valueOrNull(f.getFieldKey()); if (value == null) return null; //No match match[i] = new RecordEntry(relation.longId(), value, f.getFieldKey()); } return match; } public static class IndexRecords extends ArrayList<RecordEntry[]> { public boolean add(RecordEntry[] record) { return super.add(Arrays.copyOf(record, record.length)); } public Iterable<Object[]> getRecordValues() { return Iterables.transform(this, new Function<RecordEntry[], Object[]>() { @Nullable @Override public Object[] apply(@Nullable RecordEntry[] record) { return getValues(record); } }); } private static Object[] getValues(RecordEntry[] record) { Object[] values = new Object[record.length]; for (int i = 0; i < values.length; i++) { values[i] = record[i].value; } return values; } } private static class RecordEntry { final long relationId; final Object value; final PropertyKey key; private RecordEntry(long relationId, Object value, PropertyKey key) { this.relationId = relationId; this.value = value; this.key = key; } private RecordEntry(JanusGraphVertexProperty property) { this(property.longId(), property.value(), property.propertyKey()); } } public static IndexRecords indexMatches(JanusGraphVertex vertex, CompositeIndexType index) { return indexMatches(vertex, index, null, null); } public static IndexRecords indexMatches(JanusGraphVertex vertex, CompositeIndexType index, PropertyKey replaceKey, Object replaceValue) { IndexRecords matches = new IndexRecords(); IndexField[] fields = index.getFieldKeys(); if (indexAppliesTo(index, vertex)) { indexMatches(vertex, new RecordEntry[fields.length], matches, fields, 0, false, replaceKey, new RecordEntry(0, replaceValue, replaceKey)); } return matches; } private static IndexRecords indexMatches(JanusGraphVertex vertex, CompositeIndexType index, boolean onlyLoaded, PropertyKey replaceKey, RecordEntry replaceValue) { IndexRecords matches = new IndexRecords(); IndexField[] fields = index.getFieldKeys(); indexMatches(vertex, new RecordEntry[fields.length], matches, fields, 0, onlyLoaded, replaceKey, replaceValue); return matches; } private static void indexMatches(JanusGraphVertex vertex, RecordEntry[] current, IndexRecords matches, IndexField[] fields, int pos, boolean onlyLoaded, PropertyKey replaceKey, RecordEntry replaceValue) { if (pos >= fields.length) { matches.add(current); return; } PropertyKey key = fields[pos].getFieldKey(); List<RecordEntry> values; if (key.equals(replaceKey)) { values = ImmutableList.of(replaceValue); } else { values = new ArrayList<RecordEntry>(); Iterable<JanusGraphVertexProperty> props; if (onlyLoaded || (!vertex.isNew() && IDManager.VertexIDType.PartitionedVertex.is(vertex.longId()))) { //going through transaction so we can query deleted vertices VertexCentricQueryBuilder qb = ((InternalVertex) vertex).tx().query(vertex); qb.noPartitionRestriction().type(key); if (onlyLoaded) qb.queryOnlyLoaded(); props = qb.properties(); } else { props = vertex.query().keys(key.name()).properties(); } for (JanusGraphVertexProperty p : props) { assert !onlyLoaded || p.isLoaded() || p.isRemoved(); assert key.dataType().equals(p.value().getClass()) : key + " -> " + p; values.add(new RecordEntry(p)); } } for (RecordEntry value : values) { current[pos] = value; indexMatches(vertex, current, matches, fields, pos + 1, onlyLoaded, replaceKey, replaceValue); } } /* ################################################ Querying ################################################### */ public List<Object> query(final JointIndexQuery.Subquery query, final BackendTransaction tx) { IndexType index = query.getIndex(); if (index.isCompositeIndex()) { MultiKeySliceQuery sq = query.getCompositeQuery(); List<EntryList> rs = sq.execute(tx); List<Object> results = new ArrayList<Object>(rs.get(0).size()); for (EntryList r : rs) { for (java.util.Iterator<Entry> iterator = r.reuseIterator(); iterator.hasNext();) { Entry entry = iterator.next(); ReadBuffer entryValue = entry.asReadBuffer(); entryValue.movePositionTo(entry.getValuePosition()); switch (index.getElement()) { case VERTEX: results.add(VariableLong.readPositive(entryValue)); break; default: results.add(bytebuffer2RelationId(entryValue)); } } } return results; } else { List<String> r = tx.indexQuery(((MixedIndexType) index).getBackingIndexName(), query.getMixedQuery()); List<Object> result = new ArrayList<Object>(r.size()); for (String id : r) result.add(string2ElementId(id)); return result; } } public MultiKeySliceQuery getQuery(final CompositeIndexType index, List<Object[]> values) { List<KeySliceQuery> ksqs = new ArrayList<KeySliceQuery>(values.size()); for (Object[] value : values) { ksqs.add(new KeySliceQuery(getIndexKey(index, value), BufferUtil.zeroBuffer(1), BufferUtil.oneBuffer(1))); } return new MultiKeySliceQuery(ksqs); } public IndexQuery getQuery(final MixedIndexType index, final Condition condition, final OrderList orders) { Condition newCondition = ConditionUtil.literalTransformation(condition, new Function<Condition<JanusGraphElement>, Condition<JanusGraphElement>>() { @Nullable @Override public Condition<JanusGraphElement> apply(@Nullable Condition<JanusGraphElement> condition) { Preconditions.checkArgument(condition instanceof PredicateCondition); PredicateCondition pc = (PredicateCondition) condition; PropertyKey key = (PropertyKey) pc.getKey(); return new PredicateCondition<String, JanusGraphElement>(key2Field(index, key), pc.getPredicate(), pc.getValue()); } }); ImmutableList<IndexQuery.OrderEntry> newOrders = IndexQuery.NO_ORDER; if (!orders.isEmpty() && GraphCentricQueryBuilder.indexCoversOrder(index, orders)) { ImmutableList.Builder<IndexQuery.OrderEntry> lb = ImmutableList.builder(); for (int i = 0; i < orders.size(); i++) { lb.add(new IndexQuery.OrderEntry(key2Field(index, orders.getKey(i)), orders.getOrder(i), orders.getKey(i).dataType())); } newOrders = lb.build(); } return new IndexQuery(index.getStoreName(), newCondition, newOrders); } // // // // public IndexQuery getQuery(String index, final ElementCategory resultType, final Condition condition, final OrderList orders) { // if (isStandardIndex(index)) { // Preconditions.checkArgument(orders.isEmpty()); // return new IndexQuery(getStoreName(resultType), condition, IndexQuery.NO_ORDER); // } else { // Condition newCondition = ConditionUtil.literalTransformation(condition, // new Function<Condition<JanusGraphElement>, Condition<JanusGraphElement>>() { // @Nullable // @Override // public Condition<JanusGraphElement> apply(@Nullable Condition<JanusGraphElement> condition) { // Preconditions.checkArgument(condition instanceof PredicateCondition); // PredicateCondition pc = (PredicateCondition) condition; // JanusGraphKey key = (JanusGraphKey) pc.getKey(); // return new PredicateCondition<String, JanusGraphElement>(key2Field(key), pc.getPredicate(), pc.getValue()); // } // }); // ImmutableList<IndexQuery.OrderEntry> newOrders = IndexQuery.NO_ORDER; // if (!orders.isEmpty()) { // ImmutableList.Builder<IndexQuery.OrderEntry> lb = ImmutableList.builder(); // for (int i = 0; i < orders.size(); i++) { // lb.add(new IndexQuery.OrderEntry(key2Field(orders.getKey(i)), orders.getOrder(i), orders.getKey(i).getDataType())); // } // newOrders = lb.build(); // } // return new IndexQuery(getStoreName(resultType), newCondition, newOrders); // } // } public Iterable<RawQuery.Result> executeQuery(IndexQueryBuilder query, final ElementCategory resultType, final BackendTransaction backendTx, final StandardJanusGraphTx transaction) { MixedIndexType index = getMixedIndex(query.getIndex(), transaction); Preconditions.checkArgument(index.getElement() == resultType, "Index is not configured for the desired result type: %s", resultType); String backingIndexName = index.getBackingIndexName(); IndexProvider indexInformation = (IndexProvider) mixedIndexes.get(backingIndexName); StringBuffer qB = new StringBuffer(query.getQuery()); final String prefix = query.getPrefix(); Preconditions.checkNotNull(prefix); //Convert query string by replacing int replacements = 0; int pos = 0; while (pos < qB.length()) { pos = qB.indexOf(prefix, pos); if (pos < 0) break; int startPos = pos; pos += prefix.length(); StringBuilder keyBuilder = new StringBuilder(); boolean quoteTerminated = qB.charAt(pos) == '"'; if (quoteTerminated) pos++; while (pos < qB.length() && (Character.isLetterOrDigit(qB.charAt(pos)) || (quoteTerminated && qB.charAt(pos) != '"') || qB.charAt(pos) == '*')) { keyBuilder.append(qB.charAt(pos)); pos++; } if (quoteTerminated) pos++; int endPos = pos; String keyname = keyBuilder.toString(); Preconditions.checkArgument(StringUtils.isNotBlank(keyname), "Found reference to empty key at position [%s]", startPos); String replacement; if (keyname.equals("*")) { replacement = indexInformation.getFeatures().getWildcardField(); } else if (transaction.containsRelationType(keyname)) { PropertyKey key = transaction.getPropertyKey(keyname); Preconditions.checkNotNull(key); Preconditions.checkArgument(index.indexesKey(key), "The used key [%s] is not indexed in the targeted index [%s]", key.name(), query.getIndex()); replacement = key2Field(index, key); } else { Preconditions.checkArgument(query.getUnknownKeyName() != null, "Found reference to non-existant property key in query at position [%s]: %s", startPos, keyname); replacement = query.getUnknownKeyName(); } Preconditions.checkArgument(StringUtils.isNotBlank(replacement)); qB.replace(startPos, endPos, replacement); pos = startPos + replacement.length(); replacements++; } String queryStr = qB.toString(); if (replacements <= 0) log.warn("Could not convert given {} index query: [{}]", resultType, query.getQuery()); log.info("Converted query string with {} replacements: [{}] => [{}]", replacements, query.getQuery(), queryStr); RawQuery rawQuery = new RawQuery(index.getStoreName(), queryStr, query.getParameters()); if (query.hasLimit()) rawQuery.setLimit(query.getLimit()); rawQuery.setOffset(query.getOffset()); return Iterables.transform(backendTx.rawQuery(index.getBackingIndexName(), rawQuery), new Function<RawQuery.Result<String>, RawQuery.Result>() { @Nullable @Override public RawQuery.Result apply(@Nullable RawQuery.Result<String> result) { return new RawQuery.Result(string2ElementId(result.getResult()), result.getScore()); } }); } /* ################################################ Utility Functions ################################################### */ private static final MixedIndexType getMixedIndex(String indexName, StandardJanusGraphTx transaction) { IndexType index = ManagementSystem.getGraphIndexDirect(indexName, transaction); Preconditions.checkArgument(index != null, "Index with name [%s] is unknown or not configured properly", indexName); Preconditions.checkArgument(index.isMixedIndex()); return (MixedIndexType) index; } private static final String element2String(JanusGraphElement element) { return element2String(element.id()); } private static final String element2String(Object elementId) { Preconditions.checkArgument(elementId instanceof Long || elementId instanceof RelationIdentifier); if (elementId instanceof Long) return longID2Name((Long) elementId); else return ((RelationIdentifier) elementId).toString(); } private static final Object string2ElementId(String str) { if (str.contains(RelationIdentifier.TOSTRING_DELIMITER)) return RelationIdentifier.parse(str); else return name2LongID(str); } private static final String key2Field(MixedIndexType index, PropertyKey key) { return key2Field(index.getField(key)); } private static final String key2Field(ParameterIndexField field) { assert field != null; return ParameterType.MAPPED_NAME.findParameter(field.getParameters(), keyID2Name(field.getFieldKey())); } private static final String keyID2Name(PropertyKey key) { return longID2Name(key.longId()); } private static final String longID2Name(long id) { Preconditions.checkArgument(id > 0); return LongEncoding.encode(id); } private static final long name2LongID(String name) { return LongEncoding.decode(name); } private final StaticBuffer getIndexKey(CompositeIndexType index, RecordEntry[] record) { return getIndexKey(index, IndexRecords.getValues(record)); } private final StaticBuffer getIndexKey(CompositeIndexType index, Object[] values) { DataOutput out = serializer.getDataOutput(8 * DEFAULT_OBJECT_BYTELEN + 8); VariableLong.writePositive(out, index.getID()); IndexField[] fields = index.getFieldKeys(); Preconditions.checkArgument(fields.length > 0 && fields.length == values.length); for (int i = 0; i < fields.length; i++) { IndexField f = fields[i]; Object value = values[i]; Preconditions.checkNotNull(value); if (AttributeUtil.hasGenericDataType(f.getFieldKey())) { out.writeClassAndObject(value); } else { assert value.getClass().equals(f.getFieldKey().dataType()) : value.getClass() + " - " + f.getFieldKey().dataType(); out.writeObjectNotNull(value); } } StaticBuffer key = out.getStaticBuffer(); if (hashKeys) key = HashingUtil.hashPrefixKey(hashLength, key); return key; } public long getIndexIdFromKey(StaticBuffer key) { if (hashKeys) key = HashingUtil.getKey(hashLength, key); return VariableLong.readPositive(key.asReadBuffer()); } private final Entry getIndexEntry(CompositeIndexType index, RecordEntry[] record, JanusGraphElement element) { DataOutput out = serializer.getDataOutput(1 + 8 + 8 * record.length + 4 * 8); out.putByte(FIRST_INDEX_COLUMN_BYTE); if (index.getCardinality() != Cardinality.SINGLE) { VariableLong.writePositive(out, element.longId()); if (index.getCardinality() != Cardinality.SET) { for (RecordEntry re : record) { VariableLong.writePositive(out, re.relationId); } } } int valuePosition = out.getPosition(); if (element instanceof JanusGraphVertex) { VariableLong.writePositive(out, element.longId()); } else { assert element instanceof JanusGraphRelation; RelationIdentifier rid = (RelationIdentifier) element.id(); long[] longs = rid.getLongRepresentation(); Preconditions.checkArgument(longs.length == 3 || longs.length == 4); for (int i = 0; i < longs.length; i++) VariableLong.writePositive(out, longs[i]); } return new StaticArrayEntry(out.getStaticBuffer(), valuePosition); } private static final RelationIdentifier bytebuffer2RelationId(ReadBuffer b) { long[] relationId = new long[4]; for (int i = 0; i < 3; i++) relationId[i] = VariableLong.readPositive(b); if (b.hasRemaining()) relationId[3] = VariableLong.readPositive(b); else relationId = Arrays.copyOfRange(relationId, 0, 3); return RelationIdentifier.get(relationId); } }