blob: 06c2d91a1a34f68313a8740c4010e1cc6015b7ca [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You 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.apache.hugegraph.backend.store.rocksdb;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Collection;
import java.util.List;
import org.apache.hugegraph.backend.id.Id;
import org.apache.hugegraph.backend.query.Condition;
import org.apache.hugegraph.backend.query.Condition.Relation;
import org.apache.hugegraph.backend.query.ConditionQuery;
import org.apache.hugegraph.backend.serializer.BinarySerializer;
import org.apache.hugegraph.backend.store.BackendEntry;
import org.apache.hugegraph.backend.store.BackendEntry.BackendColumnIterator;
import org.apache.hugegraph.type.HugeType;
import org.apache.hugegraph.type.define.HugeKeys;
import org.apache.hugegraph.util.E;
import org.apache.hugegraph.util.StringEncoding;
public class RocksDBTables {
public static class Meta extends RocksDBTable {
private static final String TABLE = HugeType.META.string();
public Meta(String database) {
super(database, TABLE);
}
public void writeVersion(RocksDBSessions.Session session, String version) {
byte[] key = new byte[]{HugeKeys.VERSION.code()};
byte[] value = StringEncoding.encode(version);
session.put(this.table(), key, value);
try {
session.commit();
} catch (Exception e) {
session.rollback();
throw e;
}
}
public String readVersion(RocksDBSessions.Session session) {
byte[] key = new byte[]{HugeKeys.VERSION.code()};
byte[] value = session.get(this.table(), key);
if (value == null) {
return null;
}
return StringEncoding.decode(value);
}
}
public static class Counters extends RocksDBTable {
private static final String TABLE = HugeType.COUNTER.string();
public Counters(String database) {
super(database, TABLE);
}
public long getCounter(RocksDBSessions.Session session, HugeType type) {
byte[] key = new byte[]{type.code()};
byte[] value = session.get(this.table(), key);
if (value != null) {
return toLong(value);
} else {
return 0L;
}
}
public void increaseCounter(RocksDBSessions.Session session, HugeType type,
long increment) {
byte[] key = new byte[]{type.code()};
session.increase(this.table(), key, toBytes(increment));
}
private static byte[] toBytes(long value) {
return ByteBuffer.allocate(Long.BYTES)
.order(ByteOrder.nativeOrder())
.putLong(value).array();
}
private static long toLong(byte[] bytes) {
assert bytes.length == Long.BYTES;
return ByteBuffer.wrap(bytes)
.order(ByteOrder.nativeOrder())
.getLong();
}
}
public static class SchemaTable extends RocksDBTable {
public SchemaTable(String database, String table) {
super(database, table);
}
@Override
public void delete(RocksDBSessions.Session session, BackendEntry entry) {
assert entry.columns().isEmpty();
/*
* Use `scanPrefix + delete` instead of `deletePrefix` due to
* the bug that reused iterator can't see deleteRange: #9255
* `deletePrefix`: session.deletePrefix(prefix)
* `scanPrefix + delete`: session.delete(scanPrefix(prefix))
*/
byte[] prefix = entry.id().asBytes();
try (BackendColumnIterator results = session.scan(this.table(),
prefix)) {
while (results.hasNext()) {
byte[] column = results.next().name;
session.delete(this.table(), column);
}
session.commit();
}
}
}
public static class VertexLabel extends SchemaTable {
public static final String TABLE = HugeType.VERTEX_LABEL.string();
public VertexLabel(String database) {
super(database, TABLE);
}
}
public static class EdgeLabel extends SchemaTable {
public static final String TABLE = HugeType.EDGE_LABEL.string();
public EdgeLabel(String database) {
super(database, TABLE);
}
}
public static class PropertyKey extends SchemaTable {
public static final String TABLE = HugeType.PROPERTY_KEY.string();
public PropertyKey(String database) {
super(database, TABLE);
}
}
public static class IndexLabel extends SchemaTable {
public static final String TABLE = HugeType.INDEX_LABEL.string();
public IndexLabel(String database) {
super(database, TABLE);
}
}
public static class Vertex extends RocksDBTable {
public static final String TABLE = HugeType.VERTEX.string();
public Vertex(String database) {
super(database, TABLE);
}
@Override
protected BackendColumnIterator queryById(RocksDBSessions.Session session, Id id) {
return this.getById(session, id);
}
@Override
protected BackendColumnIterator queryByIds(RocksDBSessions.Session session,
Collection<Id> ids) {
// TODO: use getByIds() after batch version multi-get is ready
return super.queryByIds(session, ids);
}
}
public static class Edge extends RocksDBTable {
public static final String TABLE_SUFFIX = HugeType.EDGE.string();
public Edge(boolean out, String database) {
// Edge out/in table
super(database, (out ? 'o' : 'i') + TABLE_SUFFIX);
}
public static Edge out(String database) {
return new Edge(true, database);
}
public static Edge in(String database) {
return new Edge(false, database);
}
@Override
protected BackendColumnIterator queryById(RocksDBSessions.Session session, Id id) {
return this.getById(session, id);
}
}
public static class IndexTable extends RocksDBTable {
public IndexTable(String database, String table) {
super(database, table);
}
@Override
public void eliminate(RocksDBSessions.Session session, BackendEntry entry) {
assert entry.columns().size() == 1;
super.delete(session, entry);
}
@Override
public void delete(RocksDBSessions.Session session, BackendEntry entry) {
/*
* Only delete index by label will come here
* Regular index delete will call eliminate()
*/
for (BackendEntry.BackendColumn column : entry.columns()) {
// Don't assert entry.belongToMe(column), length-prefix is 1*
session.deletePrefix(this.table(), column.name);
}
}
}
public static class SecondaryIndex extends IndexTable {
public static final String TABLE = HugeType.SECONDARY_INDEX.string();
public SecondaryIndex(String database) {
super(database, TABLE);
}
}
public static class VertexLabelIndex extends IndexTable {
public static final String TABLE = HugeType.VERTEX_LABEL_INDEX.string();
public VertexLabelIndex(String database) {
super(database, TABLE);
}
}
public static class EdgeLabelIndex extends IndexTable {
public static final String TABLE = HugeType.EDGE_LABEL_INDEX.string();
public EdgeLabelIndex(String database) {
super(database, TABLE);
}
}
public static class SearchIndex extends IndexTable {
public static final String TABLE = HugeType.SEARCH_INDEX.string();
public SearchIndex(String database) {
super(database, TABLE);
}
}
public static class UniqueIndex extends IndexTable {
public static final String TABLE = HugeType.UNIQUE_INDEX.string();
public UniqueIndex(String database) {
super(database, TABLE);
}
}
public static class RangeIndex extends IndexTable {
public RangeIndex(String database, String table) {
super(database, table);
}
@Override
protected BackendColumnIterator queryByCond(RocksDBSessions.Session session,
ConditionQuery query) {
assert query.conditionsSize() > 0;
List<Condition> conds = query.syspropConditions(HugeKeys.ID);
E.checkArgument(!conds.isEmpty(),
"Please specify the index conditions");
Id prefix = null;
Id min = null;
boolean minEq = false;
Id max = null;
boolean maxEq = false;
for (Condition c : conds) {
Relation r = (Relation) c;
switch (r.relation()) {
case PREFIX:
prefix = (Id) r.value();
break;
case GTE:
minEq = true;
min = (Id) r.value();
break;
case GT:
min = (Id) r.value();
break;
case LTE:
maxEq = true;
max = (Id) r.value();
break;
case LT:
max = (Id) r.value();
break;
default:
E.checkArgument(false, "Unsupported relation '%s'",
r.relation());
}
}
E.checkArgumentNotNull(min, "Range index begin key is missing");
byte[] begin = min.asBytes();
if (!minEq) {
BinarySerializer.increaseOne(begin);
}
if (max == null) {
E.checkArgumentNotNull(prefix, "Range index prefix is missing");
return session.scan(this.table(), begin, prefix.asBytes(),
RocksDBSessions.Session.SCAN_PREFIX_END);
} else {
byte[] end = max.asBytes();
int type = maxEq ? RocksDBSessions.Session.SCAN_LTE_END : RocksDBSessions.Session.SCAN_LT_END;
return session.scan(this.table(), begin, end, type);
}
}
}
public static class RangeIntIndex extends RangeIndex {
public static final String TABLE = HugeType.RANGE_INT_INDEX.string();
public RangeIntIndex(String store) {
super(store, TABLE);
}
}
public static class RangeFloatIndex extends RangeIndex {
public static final String TABLE = HugeType.RANGE_FLOAT_INDEX.string();
public RangeFloatIndex(String store) {
super(store, TABLE);
}
}
public static class RangeLongIndex extends RangeIndex {
public static final String TABLE = HugeType.RANGE_LONG_INDEX.string();
public RangeLongIndex(String store) {
super(store, TABLE);
}
}
public static class RangeDoubleIndex extends RangeIndex {
public static final String TABLE = HugeType.RANGE_DOUBLE_INDEX.string();
public RangeDoubleIndex(String store) {
super(store, TABLE);
}
}
public static class ShardIndex extends RangeIndex {
public static final String TABLE = HugeType.SHARD_INDEX.string();
public ShardIndex(String database) {
super(database, TABLE);
}
}
public static class OlapTable extends RocksDBTable {
public static final String TABLE = HugeType.OLAP.string();
public OlapTable(String database, Id id) {
super(database, joinTableName(TABLE, id.asString()));
}
@Override
protected BackendColumnIterator queryById(RocksDBSessions.Session session, Id id) {
return this.getById(session, id);
}
@Override
public boolean isOlap() {
return true;
}
}
public static class OlapSecondaryIndex extends SecondaryIndex {
public static final String TABLE = HugeType.OLAP.string();
public OlapSecondaryIndex(String store) {
this(store, TABLE);
}
protected OlapSecondaryIndex(String store, String table) {
super(joinTableName(store, table));
}
}
public static class OlapRangeIntIndex extends RangeIntIndex {
public static final String TABLE = HugeType.OLAP.string();
public OlapRangeIntIndex(String store) {
this(store, TABLE);
}
protected OlapRangeIntIndex(String store, String table) {
super(joinTableName(store, table));
}
}
public static class OlapRangeLongIndex extends RangeLongIndex {
public static final String TABLE = HugeType.OLAP.string();
public OlapRangeLongIndex(String store) {
this(store, TABLE);
}
protected OlapRangeLongIndex(String store, String table) {
super(joinTableName(store, table));
}
}
public static class OlapRangeFloatIndex extends RangeFloatIndex {
public static final String TABLE = HugeType.OLAP.string();
public OlapRangeFloatIndex(String store) {
this(store, TABLE);
}
protected OlapRangeFloatIndex(String store, String table) {
super(joinTableName(store, table));
}
}
public static class OlapRangeDoubleIndex extends RangeDoubleIndex {
public static final String TABLE = HugeType.OLAP.string();
public OlapRangeDoubleIndex(String store) {
this(store, TABLE);
}
protected OlapRangeDoubleIndex(String store, String table) {
super(joinTableName(store, table));
}
}
}