java MongoTemplate aggregate with project nested document - javascript

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()])));

Related

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.

Looping though all documents in the collection and an array in each document to match array value to the project

I have a MongoDB collection with the following structure:
/* 1 */
{
"_id" : ObjectId("5cdb24b41a40ae58e6d690fd"),
"versions" : [
ObjectId("5cdb24b41a40ae58e6d690fe")
],
"releases" : [],
"monetization" : [],
"owner" : "testuser",
"name" : "test-repo-2",
"repoAddress" : "/testuser/test-repo-2",
"repoKey" : null,
"__v" : 0
}
/* 2 */
{
"_id" : ObjectId("5cdb23cb1a40ae58e6d690fa"),
"versions" : [
ObjectId("5cdb23cb1a40ae58e6d690fb"),
ObjectId("5cdda9c54e6d0b795a007960")
],
"releases" : [
ObjectId("5cdda9c54e6d0b795a00795c")
],
"monetization" : [],
"owner" : "testuser",
"name" : "test-repo-1",
"repoAddress" : "/testuser/test-repo-1",
"repoKey" : null,
"__v" : 2,
"createdAt" : ISODate("2019-05-16T18:19:49.159Z"),
"updatedAt" : ISODate("2019-05-16T18:19:49.252Z")
}
I need to loop though all the documents in the collection as well as they array of versions to look for a specific to match it to the project. I need to do this with NodeJS, but for now I'm trying it from mongoshell. I'm trying to use forEach() and $in operator to do this.
db.projects.find().forEach(
function () {
{
versions: {
$in: ['5cdb24b41a40ae58e6d690fe']
}
}
}
);
But each time I get the following response: Script executed successfully, but there are no results to show. Am I doing this correctly?
There are various solutions you could try, for example:
1) You could add a filter to your find-query:
db.getCollection('debug').find({"versions":{"$in":[ObjectId("5cdb24b41a40ae58e6d690fe")]}})
This would directly return the object you're looking for.
2) If you don't want to pass a filter and really query all documents and filter them yourself, you could try the following:
db.getCollection('debug').find({}).forEach(doc => {
doc.versions.forEach(v => {
if(v.toString() === "ObjectId(\"5cdb24b41a40ae58e6d690fe\")") {
print("match");
}
});
});

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.

manage neo4j cross relationship in d3.js

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!

Categories

Resources