var _ = require('underscore');
var joi = require('joi');
var Foxx = require('org/arangodb/foxx');
var ArangoError = require('org/arangodb').ArangoError;
var db = require('org/arangodb').db;
var console = require('console');
var Data = require('../repositories/data');
var Datum = require('../models/datum');
var controller = new Foxx.Controller(applicationContext);
var datumIdSchema = joi.string().required()
.description('The id of the datum')
.meta({allowMultiple: false});
var data = new Data(
applicationContext.collection('data'),
{model: Datum}
controller.get('/neighbors', function(req, res) {
var fullId = req.parameters.fullId;
var id = req.parameters.id;
var originVertexName = req.parameters.originVertexName;
var neighborVertexRestriction = JSON.parse(req.parameters.neighborVertexRestriction); //this is an array of collection names
var maxDepth = Number(req.parameters.maxDepth) + 1;
var limitOffset = Number(req.parameters.limitOffset) || 0;
var limit = Number(req.parameters.limit) || 30;
var graphName = req.parameters.graphName;
var progress = {}
var traversal = require("org/arangodb/graph/traversal");
var result = {
edges: [],
vertices: {}
};
var myVisitor = function (config, result, vertex, path, connected) {
var currentDepth = path.edges.length;
if(currentDepth === 0) { //if we are on the origin vertex
if (!result.vertices.hasOwnProperty(vertex._id)) {
// If we visit a vertex, we store its data and prepare out/in
result.vertices[vertex._id] = {
vertexData: vertex,
tempVertexData: {
evidence: [],
argument: {supporting: [], supported: [], contradicting: [], contradicted: []},
tags: [],
distFromQueried: currentDepth
},
outEdges: [],
inEdges: []
};
}
} else { //for all vertices in addition to the origin vertex
var e = path.edges[currentDepth-1]; //get the edge data for the edge immediately leading to this node en route from the original node
e._relativeDepth = currentDepth;
if(currentDepth < maxDepth) {
result.edges.push(e);
if (!result.vertices.hasOwnProperty(vertex._id) && _.contains(neighborVertexRestriction, vertex._id.split('/')[0])) {
// If we visit a vertex, we store its data and prepare out/in
result.vertices[vertex._id] = {
vertexData: vertex,
tempVertexData: {
evidence: [],
argument: {supporting: [], supported: [], contradicting: [], contradicted: []},
tags: [],
distFromQueried: currentDepth
},
outEdges: [],
inEdges: []
};
}
}
// add the edge data to the vertices' tempVertexData property
if (e._id.split('/')[0] === "edgeType1" && vertex._id.split('/')[0] === 'DocumentA') {
result.vertices[e._to].tempVertexData.evidence.push({name: vertex.name, _id: vertex._id})
}
if (e._id.split('/')[0] === "edgeType1" && vertex._id.split('/')[0] === 'DocumentB' && e.type === "supports") {
result.vertices[e._to].tempVertexData.argument.supporting.push({name: vertex.name, _id: vertex._id});
result.vertices[e._from].tempVertexData.argument.supported.push({name: vertex.name, _id: vertex._id})
}
if (e._id.split('/')[0] === "edgeType1" && vertex._id.split('/')[0] === 'DocumentB' && e.type === "contradicts") {
result.vertices[e._to].tempVertexData.argument.contradicting.push({name: vertex.name, _id: vertex._id})
result.vertices[e._from].tempVertexData.argument.contradicted.push({
name: vertex.name,
_id: vertex._id
})
}
if (e._id.split('/')[0] === "edgeType2" && vertex._id.split('/')[0] === 'DocumentA') {
result.vertices[e._to].tempVertexData.tags.push({name: vertex.name, _id: vertex._id})
}
// push in and out edges to lists on each vertex
if (result.vertices.hasOwnProperty(e._from)) {
result.vertices[e._from].outEdges.push(e._to);
}
if (result.vertices.hasOwnProperty(e._to)) {
result.vertices[e._to].inEdges.push(e._from);
}
}
};
var config = {
datasource: traversal.generalGraphDatasourceFactory('combinedGraph'),
strategy: "breadthfirst",
order: "preorder",
visitor: myVisitor,
expander: traversal.anyExpander,
minDepth: 0,
maxDepth: maxDepth
};
var traverser = new traversal.Traverser(config);
var slicedVertices = [],
extraVertices1 = [],
extraVertices2 = [],
filteredEdges = [],
filterList = [];
traverser.traverse(result, {_id: fullId});
progress.totalVertices = Object.keys(result.vertices).length;
result.vertices[fullId].vertexData = db._document(fullId);
//sort by inEdges length and then distance from the queried node, so that the queried node is always first
var sortedVertices = _.chain(result.vertices).sortBy(function(v) {return v.inEdges.length}).reverse().sortBy(function(v) {return v.tempVertexData.distFromQueried}).value();
if(limitOffset===0) {
result.vertices = sortedVertices.slice(limitOffset, limit + limitOffset);
filterList = result.vertices.map(function(v){return v.vertexData._id}); //get list of the vertices to be returned in order to filter edges with
result.edges = _.filter(result.edges, function(e){return (_.contains(filterList, e._to) && _.contains(filterList, e._from))})
} else {
slicedVertices = sortedVertices.slice(limitOffset, limit + limitOffset);
filterList = slicedVertices.map(function(v){return v.vertexData._id}); //get list of new vertices to be returned
filteredEdges = _.filter(result.edges, function(e){return (_.contains(filterList, e._to) || _.contains(filterList, e._from)) && e._relativeDepth < maxDepth}) //get edges that border the new vertices
extraVertices1 = filteredEdges.map(function(e){return result.vertices[e._from]});
extraVertices2 = filteredEdges.map(function(e){return result.vertices[e._to]});
slicedVertices = _.uniq(slicedVertices.concat(extraVertices1, extraVertices2));
filterList = slicedVertices.map(function(v){return v.vertexData._id}); //get list of the vertices to be returned in order to filter edges
filteredEdges = _.filter(result.edges, function(e){return (_.contains(filterList, e._to) && _.contains(filterList, e._from))});
result.vertices = slicedVertices;
result.edges = filteredEdges;
}
progress.currentVertex = limitOffset + limit;
progress.limit = limit;
progress.limitOffset = limitOffset;
progress.currentQuantityReturned = Math.min(limit, progress.totalVertices);
result.progress = progress;
res.json(result);
}).errorResponse(ArangoError, 404, 'The datum could not be found');