com.qwazr.graph.GraphInstance Maven / Gradle / Ivy
/**
* Copyright 2015-2016 Emmanuel Keller / QWAZR
*
* 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.qwazr.graph;
import com.qwazr.database.model.ColumnDefinition;
import com.qwazr.database.store.CollectorInterface.LongCounter;
import com.qwazr.database.store.*;
import com.qwazr.database.store.Query.OrGroup;
import com.qwazr.database.store.Query.QueryHook;
import com.qwazr.database.store.Query.TermQuery;
import com.qwazr.graph.model.GraphDefinition;
import com.qwazr.graph.model.GraphDefinition.PropertyTypeEnum;
import com.qwazr.graph.model.GraphNode;
import com.qwazr.graph.model.GraphNodeResult;
import com.qwazr.graph.model.GraphRequest;
import com.qwazr.utils.StringUtils;
import com.qwazr.utils.concurrent.ThreadUtils;
import com.qwazr.server.ServerException;
import org.apache.commons.collections4.trie.PatriciaTrie;
import org.roaringbitmap.RoaringBitmap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.core.Response.Status;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.*;
public class GraphInstance {
private static final Logger logger = LoggerFactory.getLogger(GraphInstance.class);
static final String FIELD_PREFIX_PROPERTY = "prop.";
static final String FIELD_PREFIX_EDGE = "edge.";
static String getPropertyField(String name) {
return StringUtils.fastConcat(FIELD_PREFIX_PROPERTY, name);
}
static String getEdgeField(String name) {
return StringUtils.fastConcat(FIELD_PREFIX_EDGE, name);
}
private final Table table;
private final GraphDefinition graphDef;
GraphInstance(String name, Table table, GraphDefinition graphDef) {
this.table = table;
this.graphDef = graphDef;
}
/**
* The required fields are create or deleted.
*
* @throws IOException if any I/O error occurs
* @throws ServerException if any server exception occurs
*/
void checkFields() throws ServerException, IOException {
Map existingColumns = table.getColumns();
Map columnDefinitions = new LinkedHashMap();
// Build the property fields
if (graphDef.node_properties != null) {
for (Map.Entry entry : graphDef.node_properties.entrySet()) {
String fieldName = getPropertyField(entry.getKey());
switch (entry.getValue()) {
case indexed:
columnDefinitions.put(fieldName,
new ColumnDefinition(ColumnDefinition.Type.STRING, ColumnDefinition.Mode.INDEXED));
break;
case stored:
columnDefinitions.put(fieldName,
new ColumnDefinition(ColumnDefinition.Type.STRING, ColumnDefinition.Mode.STORED));
break;
case boost:
columnDefinitions.put(fieldName,
new ColumnDefinition(ColumnDefinition.Type.DOUBLE, ColumnDefinition.Mode.INDEXED));
break;
}
}
}
// Create the edge fields
if (graphDef.edge_types != null) {
for (String type : graphDef.edge_types) {
String fieldName = getEdgeField(type);
columnDefinitions.put(fieldName,
new ColumnDefinition(ColumnDefinition.Type.STRING, ColumnDefinition.Mode.INDEXED));
}
}
try {
for (Map.Entry entry : columnDefinitions.entrySet())
if (!existingColumns.containsKey(entry.getKey()))
table.setColumn(entry.getKey(), entry.getValue());
} catch (Exception e) {
throw ServerException.getServerException(e);
}
}
private static void createUpdate(Table table, GraphDefinition graphDef, String node_id, GraphNode node)
throws ServerException, IOException {
Map row = new HashMap<>();
// Populate the property fields
if (node.properties != null && !node.properties.isEmpty()) {
if (graphDef.node_properties == null)
throw new ServerException(Status.BAD_REQUEST, "This graph database does not define any property.");
for (Map.Entry entry : node.properties.entrySet()) {
String field = entry.getKey();
if (!graphDef.node_properties.containsKey(field))
throw new ServerException(Status.BAD_REQUEST, "Unknown property name: " + field);
row.put(getPropertyField(field), entry.getValue());
}
}
// Populate the edge fields
if (node.edges != null && !node.edges.isEmpty()) {
if (graphDef.edge_types == null)
throw new ServerException(Status.BAD_REQUEST, "This graph database does not define any edge.");
for (Map.Entry> entry : node.edges.entrySet()) {
String type = entry.getKey();
if (!graphDef.edge_types.contains(type))
throw new ServerException(Status.BAD_REQUEST, "Unknown edge type: " + type);
if (entry.getValue() == null)
continue;
row.put(getEdgeField(type), entry.getValue());
}
}
table.upsertRow(node_id, row);
}
/**
* Create a new node. If the node does not exists, it is created. If the
* node exist, it is updated.
*
* @param node_id the ID of the node
* @param node the content of the node
* @param upsert set to true to add the values, false (or null) to replace the
* node
* @throws URISyntaxException if the server parameters are wrong
* @throws IOException if any I/O error occurs
* @throws ServerException if any server exception occurs
*/
void createUpdateNode(String node_id, GraphNode node, Boolean upsert) throws ServerException, IOException {
if (node == null)
return;
if (upsert != null && upsert) {
// If the node already exists, we merge it
try {
node.add(getNode(node_id));
} catch (ServerException e) {
if (e.getStatusCode() != Status.NOT_FOUND.getStatusCode())
throw e;
}
}
createUpdate(table, graphDef, node_id, node);
}
/**
* Create a list of node. If a node does not exists, it is created.
* Otherwise it is updated.
*
* @param nodes a map of nodes
* @param upsert set to true to add the values, false (or null) to replace the
* node
* @throws URISyntaxException if the server parameters are wrong
* @throws IOException if any I/O error occurs
* @throws ServerException if any server exception occurs
*/
void createUpdateNodes(Map nodes, Boolean upsert) throws IOException, URISyntaxException {
if (nodes == null || nodes.isEmpty())
return;
if (logger.isInfoEnabled())
logger.info("Update " + nodes.size() + " node(s)");
if (upsert != null && upsert) {
// If the nodes already exists, we merge them
Map dbNodes = getNodes(nodes.keySet());
if (dbNodes != null) {
for (Map.Entry entry : nodes.entrySet()) {
GraphNode dbNode = dbNodes.get(entry.getKey());
if (dbNode != null)
entry.getValue().add(dbNode);
}
}
}
for (Map.Entry entry : nodes.entrySet())
createUpdate(table, graphDef, entry.getKey(), entry.getValue());
}
private void populateReturnedFields(Collection returnedFields) {
if (graphDef.node_properties != null)
for (String name : graphDef.node_properties.keySet())
returnedFields.add(getPropertyField(name));
if (graphDef.edge_types != null)
for (String type : graphDef.edge_types)
returnedFields.add(getEdgeField(type));
}
private void populateGraphNode(Map document, GraphNode node) {
if (document == null)
return;
for (Map.Entry entry : document.entrySet()) {
String field = entry.getKey();
Object value = entry.getValue();
if (value == null)
continue;
if (value instanceof List>) {
List> values = (List>) value;
if (values.isEmpty())
continue;
if (field.startsWith(FIELD_PREFIX_PROPERTY)) {
node.addProperty(field.substring(FIELD_PREFIX_PROPERTY.length()), values.get(0));
} else if (field.startsWith(FIELD_PREFIX_EDGE)) {
for (Object val : values)
node.addEdge(field.substring(FIELD_PREFIX_EDGE.length()), val);
}
}
if (field.startsWith(FIELD_PREFIX_PROPERTY))
node.addProperty(field.substring(FIELD_PREFIX_PROPERTY.length()), value);
else if (field.startsWith(FIELD_PREFIX_EDGE)) {
node.addEdge(field.substring(FIELD_PREFIX_EDGE.length()), value);
}
}
}
/**
* Retrieve a node. If the node does not exists, an IOException is thrown
* thrown.
*
* @param node_id the id of the node
* @return a node instance
* @throws URISyntaxException if the server parameters are wrong
* @throws IOException if any I/O error occurs
* @throws ServerException if any server exception occurs
*/
GraphNode getNode(String node_id) throws IOException {
Set returnedFields = new LinkedHashSet<>();
populateReturnedFields(returnedFields);
Map document = table.getRow(node_id, returnedFields);
if (document == null)
throw new ServerException(Status.NOT_FOUND, "Node not found: " + node_id);
GraphNode node = new GraphNode();
populateGraphNode(document, node);
return node;
}
/**
* Retrieve a list of nodes.
*
* @param node_ids a collection of node id
* @return a map with the nodes
* @throws URISyntaxException if the server parameters are wrong
* @throws IOException if any I/O error occurs
* @throws ServerException if any server exception occurs
*/
Map getNodes(Set node_ids) throws IOException, URISyntaxException {
final Set returnedFields = new LinkedHashSet<>();
populateReturnedFields(returnedFields);
final List