manage neo4j cross relationship in d3.js - javascript

I start recently to work with Neo4j database and i'm trying to display a graph using d3js (i'm new with it too!).
For my test i used the movie example data include in neo4j.
Using JQuery ajax and neo4j REST API, i find a way to make it more or less work.
But currently i can't make an actor link to several movie (or i duplicate them!).
here is currently my Cypher request:
match (a)-[r]->(m) RETURN {id: id(m), name: m.title}, collect({name : a.name, id: id(a)})
and the answer i get look like this:
{
"columns" : [ "{id: id(m), name: m.title}", "collect({name : a.name, id: id(a)})" ],
"data" :
[
[
{ "id" : 3, "name" : "The Matrix Reloaded" },
[
{ "id" : 5, "name" : "Keanu Reeves" },
{ "id" : 6, "name" : "Laurence Fishburne" },
{ "id" : 7, "name" : "Carrie-Anne Moss" }
]
],
[
{ "id" : 4, "name" : "The Matrix Revolutions" },
[
{ "id" : 5, "name" : "Keanu Reeves" },
{ "id" : 6, "name" : "Laurence Fishburne" },
{ "id" : 7, "name" : "Carrie-Anne Moss" }
]
],
[
{ "id" : 2, "name" : "The Matrix" },
[
{ "id" : 5, "name" : "Keanu Reeves" },
{ "id" : 6, "name" : "Laurence Fishburne" },
{ "id" : 7, "name" : "Carrie-Anne Moss" }
]
]
]
}
So i'm thinking of 2 workarounds : either i find a way in d3 to do it (but i have a real hard time to understand d3js for this) or i change my cypher request to get the relationship in a different way.
i can of course make a huge workaround in javascript but my objective is to keep the code as much effective as possible (at the end i want to use it to get a lot of data).
i use the example here : http://bl.ocks.org/mbostock/1093130
where i modify the flatten function like this (and some other minor change) :
// Returns a list of all nodes under the root.
function flatten(root) {
var nodes = [], i = 0;
root.data.forEach(function(l) {
var childrenlist = [];
l[1].forEach(function(actor) {
var ac = { name: actor.name, id: actor.id };
childrenlist.push(ac);
nodes.push(ac);
});
var movie = { id: l[0].id, name: l[0].name, children: childrenlist };
nodes.push(movie);
});
return nodes;
}
/*! end of flatten */
So ! since i have a hard time with d3js, i'm looking a way to make my cypher request to give me the relationship info in a different way. more like this example : http://bl.ocks.org/mbostock/4062045
i'm really stuck because i don't know how to build my cypher request!
i'm looking for a way to get the relationship source and destination node id and the node list but i don't know how to do both!
i want to mix this 2 requests:
match (a)-[r]->(m) RETURN {id: id(m), name: m.title}}
and something like this (but i don't know how to get relationship source and dest):
match (a)-[r]->(m) RETURN {src: id(r.src), dst: id(r.dst)}
if you could help me it would be awesome ! i hope i'm clear enough.
of course i'm open to any other suggestions or leads!
Thanks in advance!

Related

java MongoTemplate aggregate with project nested document

I have a document.
{
"errors" : [
{
"priority" : 3,
"category" : "aaa"
"other":"nothing"
},
{
"priority" : 4,
"category" : "bbb"
"other":"nothing"
},
{
"priority" : 2,
"category" : "ccc"
"other":"nothing"
},
{
"priority" : 3,
"category" : "ddd"
"other":"nothing"
},
{
"priority" : 2,
"category" : "eee"
"other":"nothing"
}
],
"file_name" : "xxx.json",
"vehicle_id" : "esdf",
"day" : "2022-03-08"
}
I execute a command with js client.
db.wty_test.aggregate({
$project: {
'_id': 0, 'errors.priority': 1, 'errors.category': 1, 'file_name': 1, 'vehicle_id': 1,
}
})
I get the result I want.
The errors is an array containing objects.
Now I need to overwrite this command with java client(springboot-data-mongo). This is my java code.
import org.springframework.data.mongodb.core.MongoTemplate;
...
Aggregation aggregation = Aggregation.newAggregation(Aggregation.project("errors.priority", "errors.category", "file_name", "vehicle_id"));
mongoTemplate.aggregate(aggregation, "wty_test", HashMap.class).getMappedResults();
The priority and category is not in errors.
How to use java to get the same result as js?
I try the nested.But it's not what I want.
Here is a way to get the desired result.
Document projectn = new Document("$project",
new Document("_id", 0L)
.append("file_name", 1L)
.append("vehicle_id", 1L)
.append("errors",
new Document("$map",
new Document("input", "$errors")
.append("in",
new Document("priority", "$$this.priority")
.append("category", "$$this.category")
)
)
)
);
List<Document> pipeline = Arrays.asList(projectn);
List<Document> results = mongoOps.getCollection("collection_name")
.aggregate(pipeline)
.into(new ArrayList<>());
Note that this uses MongoDB Java Driver API query syntax, and Document is org.bson.Document. The conversion of the native query to Java Driver uses $map aggregation array operator (and it looks like thats the (maybe only) way).
With MongoTemplate.aggregate the code is:
Aggregation agg = newAggregation(
project()
.and(
VariableOperators.Map.itemsOf("errors")
.as("e")
.andApply(ctx -> new Document("priority", "$$e.priority").append("category", "$$e.category") ))
.as("errors")
.andExclude("_id")
.andInclude("file_name", "vehicle_id")
);
AggregationResults<Document> results = mongoOps.aggregate(agg, "collection_name", Document.class);
Alternate Method:
In case your query is just about the projection, you can use the following query using MongoTemplate#find method. This is much simpler to construct and understand:
db.collection.find(
{}, // your query filter
{ _id: 0, 'errors.category': 1, 'errors.priority': 1, file_name: 1, vehicle_id: 1 }
)
The MongoTemplate version of it:
Query query = new Query();
query.fields()
.include("errors.priority", "errors.category", "file_name", "vehicle_id")
.exclude("_id");
List<Document> results = mongoOps.find(query, Document.class, "collection_name");
You want to be using the nested() function, like so:
AggregationOperation project = Aggregation.project("file_name", "vehicle_id").
and("errors").nested(Fields.fields("priority","category"))
Aggregation aggregation = Aggregation.newAggregation(project);
List<String> projectFieldSet = Arrays.asList("errors.category","errors.priority");
List<Field> fieldList = projectFields.stream().map(f->{return Fields.field(f,f);}).collect(Collectors.toList());
ProjectionOperation projectAggOp = Aggregation.project(Fields.from(fieldList.toArray(new Field[fieldList.size()])));

Mongodb native driver chain queries

I am new to queries in mongodb. I have a document like this -
{
"_id" : ObjectId("5eb0f70f88cd051e7839325c"),
"id" : "1",
"arrayInfo" : [ {"color":"red"}, {"color":"black"}, {"color":"cyan"} ]
}
There are many documents in this format with changing ids and colors inside arrayInfo. I want to do something like -
Find record with id "1" -> Display object inside array info with {"color" : "cyan"}
I believe I have to chain queries after finding like this -
db.collection('Records').findOne({id:"1"}).**something**
Any help will be appreciated thanks.
if(id===1){
res.arrayInfo.map(item => console.log(item.color))
}
db.inventory.find( { "instock": { $elemMatch: { qty: 5, warehouse: "A" } } } )
enter link description here
If no operator is specified, MongoDB by default performs array element matching when the document stores an array. Thus you can simply do:
MongoDB Enterprise ruby-driver-rs:PRIMARY> db.foo.findOne({id:'1',arrayInfo:{color:'cyan'}})
{
"_id" : ObjectId("5eb0f70f88cd051e7839325c"),
"id" : "1",
"arrayInfo" : [
{
"color" : "red"
},
{
"color" : "black"
},
{
"color" : "cyan"
}
]
}
To match one field in the array instead of the complete array element, use $elemMatch.

Mongoose find returns document instead of specific object in array

When I try to find specific object in array using find({query}) I always get all elements from array.
Activities array stores activities (it would be a thousands of them) as you can see in the following snippet:
This is my collection:
{
"_id" : ObjectId("58407140755324d04db2ce95"),
"owner" : 103429326776572,
"activities" : [
{
"name" : "test1",
"startTime" : ISODate("2016-08-11T17:41:54Z"),
"type" : "te1",
"lat" : 1,
"lon" : 1,
"creator" : 126212904493088,
"coverPhoto" : {
"name" : "test1",
"path" : "c:\\Users\\Francis\\Desktop\\dusk\\public\\coverPhotos\\SJ9tpP6Mx.jpg"
},
"identifier" : "H1g9F6vpGl",
"users" : [
1,
2,
3
],
"hashTags" : [
"some",
"hashtags"
]
},
{
"name" : "test2",
"startTime" : ISODate("2016-08-11T17:41:53Z"),
"type" : "te2",
"lat" : 1,
"lon" : 1,
"creator" : 103312904493090,
"coverPhoto" : {
"name" : "test2",
"path" : "c:\\Users\\Francis\\Desktop\\dusk\\public\\coverPhotos\\Hy8qpvafe.jpg"
},
"identifier" : "rJlU5TvpMx",
"users" : [
1,
2,
3
],
"hashTags" : [
"some",
"hashtags"
]
}
]
}
I need to get for example an activity that has specific identifier.
I tried to use queries like:
1) db.myCollection.find({'activities.identifier' : "rJlU5TvpMx"})
2) db.myCollection.find({'activities' : { $elemMatch : { "identifier" : "rJlU5TvpMx", "creator" : 103312904493090 } })
And all combinations with '' or "" signs
I found above queries at mongodb docs in equal documents schema as mine is.
Can you tell me what am I doing wrong ?
You can try either use single match or multiple match based on your need. This makes use of $elemMatch(projection)
db.myCollection.find({"_id" : ObjectId("58407140755324d04db2ce95")},
{activities: {$elemMatch: { identifier: "rJlU5TvpMx"}}})
db.myCollection.find( {"_id" : ObjectId("58407140755324d04db2ce95")},
{activities: {$elemMatch: {creator : 103312904493090, identifier: "rJlU5TvpMx" }}})
You are looking for the projection object which gets passed as an argument in your query. It allows the return of specific fields from your search rather than the entire document. http://mongoosejs.com/docs/api.html#model_Model.find
I would also suggest looking at the response to this question here: Mongoose Query: Find an element inside an array which makes use of the unwind operator to enter the array as it seems to be relevant to your needs.
In the collection you are searching in, you have just one Document(Object). If you apply method find() to your collection and the query inside matches the value in activities.identifier it will return the only Document(object).
To have a better understanding of what I am talking about check example on mongoose API doc
And query result here.
Try check this out https://docs.mongodb.com/v3.0/reference/operator/projection/elemMatch/#proj._S_elemMatch instead

Get all values of a key in a nested array of objects using ramda.js

I am a little confused in how to proceed with this scenario using ramda. Here is the JSON that I am working with.
{
"type" : "CartWsDTO",
"Cartentries" : [ {
"entryNumber" : 1,
"flightGroup" : {
"PAXDetails" : [ {
"paxID" : "1",
"paxPrice" : "770.82",
"paxType" : "ADT"
}, {
"paxID" : "2",
"paxPrice" : "770.82",
"paxType" : "ADT"
} ]
}
}, {
"entryNumber" : 2,
"flightGroup" : {
"PAXDetails" : [ {
"paxID" : "1",
"paxName" : "Vinitha",
"paxPrice" : "770.82",
"paxSurname" : "Vinitha",
"paxType" : "ADT"
}, {
"paxID" : "2",
"paxName" : "Prahal",
"paxPrice" : "770.82",
"paxSurname" : "Prahal",
"paxType" : "ADT"
} ]
}
} ]
}
There are 2 CartEnteries in the above JSON. There is an array named paxDetails in flightGroup of each entry. From this paxDetails array I want to pick the paxPrice and make a sum of all the pax prices for that cart entry. In traditional for loop and if conditions I am able to achieve it. But using Ramda I couldn't understand how to start with. Kindly provide me a solution.
Thanks in advance.
It's not entirely clear to me what you're looking for as output. Here's a solution that simply returns the sum of the two sets of prices and returns an array with those values:
var calcTotals = R.pipe(
R.prop('Cartentries'),
R.map(R.pipe(
R.path(['flightGroup', 'PAXDetails']),
R.map(R.pipe(R.prop('paxPrice'), Number)),
R.sum
))
);
calcTotals(cart); //=> [1541.64, 1541.64]
But if you wanted a different sort of output, such as
{1: 1541.64, 2: 1541.64}
or
[{entryNumber : 1, total: 1541.64}, {entryNumber: 2. total: 1541.64}]
or whatever, you'd have to make some changes.

MongoDB : Map Reduce : Create one sub-document from another one

I have a mongodb collection which has documents like this :
{
"_id" : ObjectId("safdsd435tdg54trgds"),
"startDate" : ISODate("2013-07-02T17:35:01.000Z"),
"endDate" : ISODate("2013-08-02T17:35:01.000Z"),
"active" : true,
"channels" : [
1, 2, 3, 4
],
}
I want to convert this to something like this :
{
"_id" : ObjectId("safdsd435tdg54trgds"),
"startDate" : ISODate("2013-07-02T17:35:01.000Z"),
"endDate" : ISODate("2013-08-02T17:35:01.000Z"),
"active" : true,
"channels" : [
1, 2, 3, 4
],
"tags" :[
{
"name": one
"type": channel
},
{
"name": two
"type": channel
},
{
"name": three
"type": channel
},
{
"name": four
"type": channel
}
]
}
I already have a mapping of what 1,2,3,4 mean. Just for the sake of simplicity I put them as their alphabetical format. the values could be different, but they're static mappings.
You seem to be trying to do this update without a big iteration of your collection, So you "could" do this with mapReduce, albeit in a very "mapReduce way" as it has it's own way of doing things.
So first you want to define a mapper that encapsulates your current document :
var mapFunction = function (){
var key = this._id;
var value = {
startDate: this.startDate,
endDate: this.endDate,
active: this.active,
channels: this.channels
};
emit( key, value );
};
Now here the reducer is actually not going to be called as all the keys from the mapper will be unique, being of course the _id values from the original document. But to make the call happy:
var reduceFunction = function(){};
As this is a one to one thing this will go to finalize. It could be in the mapper, but for cleanliness sake
var finalizeFunction = function (key, reducedValue) {
var tags = [
{ name: "one", type: "channel" },
{ name: "two", type: "channel" },
{ name: "three", type: "channel" },
{ name: "four", type: "channel" }
];
reducedValue.tags = [];
reducedValue.channels.forEach(function(channel) {
reducedValue.tags.push( tags[ channel -1 ] );
});
return reducedValue;
};
Then call the mapReduce:
db.docs.mapReduce(
mapFunction,
reduceFunction,
{
out: { replace: "newdocs" },
finalize: finalizeFunction
}
)
So that will output to a new collection, but in the way that mapReduce does it so you have this:
{
"_id" : ObjectId("53112b2d0ceb66905ae41259"),
"value" : {
"startDate" : ISODate("2013-07-02T17:35:01Z"),
"endDate" : ISODate("2013-08-02T17:35:01Z"),
"active" : true,
"channels" : [ 1, 2, 3, 4 ],
"tags" : [
{
"name" : "one",
"type" : "channel"
},
{
"name" : "two",
"type" : "channel"
},
{
"name" : "three",
"type" : "channel"
},
{
"name" : "four",
"type" : "channel"
}
]
}
}
So all your document fields other than _id are stuck under that value field, so that's not the document that you want. But that is how mapReduce works.
If you really need to get out of jail from this and are willing to wait a bit, the upcoming 2.6 release has added an $out pipeline stage. So you "could" transform the documents in your new collection with $project like this:
db.newdocs.aggregate([
// Transform the document
{"$project": {
"startDate": "$value.startDate",
"endDate": "$value.endDate",
"active": "$value.active",
"channels": "$value.channels",
"tags": "$value.tags"
}},
// Output to new collection
{"$out": "fixeddocs" }
])
So that will be right. But of course this is not your original collection. So to back to that state you are going to have to .drop() collections and use .renameCollection() :
db.newdocs.drop();
db.docs.drop();
db.fixeddocs.renameCollection("docs");
Now please READ the documentation carefully on this, there are several limitations, and of course you would have to re-create indexes as well.
All of this, and in particular the last stage is going to result in a lot of disk thrashing and also keep in mind that you are dropping collections here. It almost certainly is a case for taking access to your database off-line while this is performed.
And even as such the dangers here are real enough that perhaps you can just live with running an iterative loop to update the documents, using arbitrary JavaScript. And if you really must have to do so, you could always do that using db.eval() to have that all execute on the server. But if you do, then please read the documentation for that very carefully as well.
But for completeness even if I'm not advocating this:
db.eval(function(){
db.docs.find().forEach(function(document) {
var tags = [
{ name: "one", type: "channel" },
{ name: "two", type: "channel" },
{ name: "three", type: "channel" },
{ name: "four", type: "channel" }
];
document.tags = [];
document.channels.forEach(function(channel) {
document.tags.push( tags[ channel -1 ] );
});
var id = document._id;
delete document._id;
db.docs.update({ "_id": id },document);
});
})

Categories

Resources