Convert query using Spring Mongodb Aggregation apis - spring

db.flm_conversation.aggregate(
[
{$match: {"conversationRecords.isPrimary":true,"conversationRecords.commentTime":{'$gte': new Date('2016-12-18 00:00:00'), '$lte': new Date('2016-12-18 23:59:59')} } },
{$unwind:"$conversationRecords"},
{ $group: {
_id: {
"commentLevel": "$commentLevel",
"time":{"$add": [
{
"$subtract": [
{ "$subtract": [
"$conversationRecords.commentTime",
new Date(0)
]}
,
{ "$mod": [
{ "$subtract": [
"$conversationRecords.commentTime",
new Date(0)
]},
1000 * 60 * 30
]}
]
},
new Date(0)
]}
},
count: { "$sum": 1 }
}},
{ $group: {_id: "$_id.commentLevel",count: { "$sum": 1 },pointrecord:{$push: {time:"$_id.time",count:"$count"} } }},
{ $project: { _id: 1,count:1,pointrecord:1 } }
])
How to convert this query using Spring Mongodb Aggregation apis?
AggregationOperation match = Aggregation.match(Criteria.where("conversationRecords.isPrimary").is(true)
.and("conversationRecords.commentTime").gte(DateUtils.stringToDate("2016-12-18 00:00:00","yyyy-MM-dd HH:mm:ss")).lte(DateUtils.stringToDate("2016-12-18 23:59:59","yyyy-MM-dd HH:mm:ss")));
AggregationOperation unwind = Aggregation.unwind("conversationRecords");
AggregationOperation group = Aggregation.group("commentLevel");
Aggregation agg = newAggregation(match,unwind,group);
AggregationResults<SummaryRecordAggre> groupResults
= mongoTemplate.aggregate(agg, COLLECTION_NAME, SummaryRecordAggre.class);
I don't know group "$add" how to convert?I find by
http://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mongo.aggregation

You can try something like this. I removed the extra operators which I felt was not needed but we can easily put them back should you need.
The first group which is something you need to build using MongoDB objects as currently all aggregation operators are not supported out of box in spring mongo.
Similar way for push operator in second group as you cant push multiple values.
AggregationOperation match = Aggregation.match(Criteria.where("conversationRecords.isPrimary").is(true)
.and("conversationRecords.commentTime").gte(DateUtils.stringToDate("2016-12-18 00:00:00", "yyyy-MM-dd HH:mm:ss")).lte(DateUtils.stringToDate("2016-12-18 23:59:59", "yyyy-MM-dd HH:mm:ss")));
AggregationOperation unwind = Aggregation.unwind("conversationRecords");
AggregationOperation firstGroup = context -> context.getMappedObject(new BasicDBObject(
"$group", new BasicDBObject(
"_id", new BasicDBObject("commentLevel", "$commentLevel")
.append(
"time", new BasicDBObject(
"$subtract", new Object[]{"$conversationRecords.commentTime", new BasicDBObject(
"$mod", new Object[]{"$conversationRecords.commentTime", 1000 * 60 * 30})}))).append("count", new BasicDBObject("$sum", 1))));
AggregationOperation secondGroup = Aggregation.group("_id.commentLevel").count().as("count").push(new BasicDBObject("time", "$_id.time").append("count", "$count")).as("pointrecord");
AggregationOperation project = Aggregation.project("_id", "count", "pointrecord");
Aggregation agg = newAggregation(match, unwind, firstGroup, secondGroup, project);
This is equivalent of below query
[{
"$match": {
"conversationRecords.isPrimary": true,
"conversationRecords.commentTime": {
"$gte": {
"$date": "2016-12-27T14:46:50.896Z"
},
"$lte": {
"$date": "2016-12-27T14:46:50.896Z"
}
}
}
}, {
"$unwind": "$conversationRecords"
}, {
"$group": {
"_id": {
"commentLevel": "$commentLevel",
"time": {
"$subtract": ["$conversationRecords.commentTime", {
"$mod": ["$conversationRecords.commentTime", 1800000]
}]
}
},
"count": {
"$sum": 1
}
}
}, {
"$group": {
"_id": "$_id.commentLevel",
"count": {
"$sum": 1
},
"pointrecord": {
"$push": {
"time": "$_id.time",
"count": "$count"
}
}
}
}, {
"$project": {
"_id": 1,
"count": 1,
"pointrecord": 1
}
}]
}

Related

filtering out zero total from nested result in graphql query

I have the following query that can be run against the github graphql API
query userRepositories($cursor: String, $q: String!, $githubId: String!) {
search(query: $q, type: REPOSITORY, first: 100, after: $cursor) {
repositoryCount
pageInfo {
endCursor
startCursor
}
nodes {
... on Repository {
id
name
description
isArchived
isPrivate
nameWithOwner
url
defaultBranchRef {
target {
... on Commit {
history(first: 10, author: {id: $githubId}) {
totalCount
}
}
}
}
}
}
}
}
It returns results like this:
{
"data": {
"search": {
"repositoryCount": 103,
"pageInfo": {
"endCursor": "Y3Vyc29yOjEwMA==",
"startCursor": "Y3Vyc29yOjE="
},
"nodes": [
{
"id": "MDEwOlJlcG9zaXRvcnk2MTg1OTczOA==",
"name": "microstates",
"nameWithOwner": "thefrontside/microstates",
"url": "https://github.com/thefrontside/microstates",
"defaultBranchRef": {
"target": {
"history": {
"totalCount": 0
}
}
},
{
"id": "MDEwOlJlcG9zaXRvcnkxNTU5MTUyODc=",
"name": "effection",
"nameWithOwner": "thefrontside/effection",
"url": "https://github.com/thefrontside/effection",
"defaultBranchRef": {
"target": {
"history": {
"totalCount": 16
}
}
}
},
I am only interested in the nodes array that has a defaultBranchRef.target.history.totalCount that is greater than 0.
So I am not interested in element 0 of the nodes array but I am of element 1.
Can I filter this in graphql or do I have to do that in code outside of the query?
GraphQL can't filter an array so if the API support filter base on totalCount you can pass this filter otherwise you have to filter in your code. In JS it's very easy:
const filtered = {
...data,
search: {
...data.search,
nodes: data.search.nodes.filter(node => node.defaultBranchRef.target.history.totalCount > 0),
}
};

Why is my graphql query return all product?

I'm testing graphql query on Shopify GraphiQL Explorer for filtering products on Storefront API.
My query is like this:
query ProductType {
collectionByHandle(handle: "pants") {
handle
products(first: 10, filters: {
productType: "pants"
}) {
edges {
node {
handle
productType
}
}
}
}
}
And got the result like this:
{
"data": {
"collectionByHandle": {
"handle": "pants",
"products": {
"edges": [
{
"node": {
"handle": "my-test-product-pants",
"productType": "pants"
}
},
{
"node": {
"handle": "pleated-straight-pants-mens-fashion-elastic-waist-casual-pants-men-streetwear-loose-ice-silk-trousers-mens-wide-leg-pants-s-2xl",
"productType": ""
}
},
...
Basically, the result has all the products that I have for that collection. Can anybody help me with this? This is literally the code I got from shopify website
As we can see in the tutorial filters is an array
{
"product_filters": [
{
"productType": "shoes"
},
{
"productVendor": "bestshop"
},
{
"variantOption": {
"name": "color",
"value": "blue"
}
}
]
}
So, try this instead
query ProductType {
collectionByHandle(handle: "pants") {
handle
products(first:10, filters: [{ productType: "pants" ]}) {
...
}
}
}

spring mongoTemplate bulk find

I've got an employee collection that looks like:
{
_id
company_id
job_type
name
age
created_at
...
}
I've for a list of company_ids and for each of them I want to get the latest 3 employees for job_type="part_time" and the latest 3 for job_type="intern" (according to the created_at field)
How can I do such a thing using one call?
An answer with mongoTemplate.bulkOps will also be valid.
To get the latest you can use the auto created _id field it has a date embedded in it. Assuming the name of your collection is employee you can use the following aggregation:
db.employee.aggregate([
{
$facet: {
"part_time": [
{
$match: {
"job_type": "part_time"
}
},
{
$match: {
"company_id": "id_from_your_list"
}
},
{ $sort: { "_id": 1 } },
{
$limit: 3
}
],
"intern": [
{
$match: {
"job_type": "intern"
}
},
{
$match: {
"company_id": "id_from_your_list"
}
},
{ $sort: { "_id": 1 } },
{
$limit: 3
}
]
}
}])
I assume that you are using spring data mongodb you can execute this aggregation operation using MongoTemplate helper.
FacetOperation facetOperation = facet().and(match(Criteria.where("job_type").is("part_time")),match(Criteria.where("company_id").is("company_id_from_your_list")),
sort(Sort.Direction.ASC, "_id"),
limit(3)).as("part_time")
.and(match(Criteria.where("job_type").is("intern")),match(Criteria.where("company_id").is("company_id_from_your_list")),
sort(Sort.Direction.ASC, "_id"),
limit(3)).as("intern");
mongoTemplate.aggregate(Aggregation.newAggregation(facetOperation), "employee", Document.class).getUniqueMappedResult();

Spring Mongo - An aggregation to order by objects in an array

I have the following data:
{
"_id": ObjectID("5e2fa881c3a1a70006c5743c"),
"name": "Some name",
"policies": [
{
"cId": "dasefa-2738-4cf0-90e0d568",
"weight": 12
},
{
"cId": "c640ad67dasd0-92f981583568",
"weight": 50
}
]
}
I'm able to query this with Spring Mongo fine, however I want to be able to order the policies by weight
At the moment I get my results fine with:
return mongoTemplate.find(query, CArea::class.java)
However say I make the following aggregations:
val unwind = Aggregation.unwind("policies")
val sort = Aggregation.sort(Sort.Direction.DESC,"policies.weight")
How can I go and actually apply those to the returned results above? I was hoping that the dot annotation would do the job in my query however didnt do anything e.g. Query().with(Sort.by(options.sortDirection, "policies.weight"))
Any help appreciated.
Thanks.
I am not familier with Spring Mongo, but I guess you can convert the following aggregation to spring code.
db.collection.aggregate([
{
$unwind: "$policies"
},
{
$sort: {
"policies.weight": -1
}
},
{
$group: {
_id: "$_id",
"policies": {
"$push": "$policies"
},
parentFields: {
$first: "$$ROOT"
}
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
"$parentFields",
{
policies: "$policies"
}
]
}
}
}
])
This will result:
[
{
"_id": "5e2fa881c3a1a70006c5743c",
"name": "Some name",
"policies": [
{
"cId": "c640ad67dasd0-92f981583568",
"weight": 50
},
{
"cId": "dasefa-2738-4cf0-90e0d568",
"weight": 12
}
]
}
]
Playground

Spring Data MongoDB building dynamic query

Need help to build dynamic MongoDB query.
everything inside the "$or" Array is dynamic.
db.group.find({
"version" : NumberLong(0),
"$or" : [{
"$and" : [
{
"object_type" : "D"
},
{
"type" : "R"
},
{
"name" : "1"
}
]
},{
"$and" : [
{
"object_type" : "D"
},
{
"type" : "E"
},
{
"name" : "2"
}
]
]
});
Did the below spring data query but doesn't work
Criteria criteria = Criteria.where("version").is("123");
List<Criteria> docCriterias = new ArrayList<Criteria>();
groups.stream().forEach(grp -> {
docCriterias.add(Criteria.where("type").is(grp.get("type").toString())
.andOperator(Criteria.where("object_type").is(grp.get("objectType").toString()))
.andOperator(Criteria.where("name").is(grp.get("name").toString())));
});
criteria.orOperator((Criteria[]) docCriterias.toArray());
Query q = new Query(criteria);
Thanks for the help
You should pay attention to how you combine the operators.
The ff code should work for you (note this is groovy remember to change the closure into to java lambda expression):
List<Criteria> docCriterias = new ArrayList<Criteria>();
List groups = [
[
type: "type1",
object_type: "object_type1",
name: "name1"
],
[
type: "type2",
object_type: "object_type2",
name: "name2"
],
[
type: "type3",
object_type: "object_type3",
name: "name3"
],
]
groups.stream().each {grp ->
docCriterias.add(new Criteria().andOperator(
Criteria.where("type").is(grp.get("type")),
Criteria.where("object_type").is(grp.get("object_type")),
Criteria.where("name").is(grp.get("name"))
))
};
Criteria criteria = new Criteria().andOperator(
Criteria.where("version").is("123"),
new Criteria().orOperator(docCriterias.toArray(new Criteria[docCriterias.size()]))
);
Query q = new Query(criteria);
Which will give you this query:
{
"$and":[
{
"version":"123"
},
{
"$or":[
{
"$and":[
{
"type":"type1"
},
{
"object_type":"object_type1"
},
{
"name":"name1"
}
]
},
{
"$and":[
{
"type":"type2"
},
{
"object_type":"object_type2"
},
{
"name":"name2"
}
]
},
{
"$and":[
{
"type":"type3"
},
{
"object_type":"object_type3"
},
{
"name":"name3"
}
]
}
]
}
]
},
Fields:{
},
Sort:{
}
You could reach this using MongoDB Aggregation Pipeline in Json and Apache Velocity to customize more the Query, then execute this using db.runCommand using Spring MongoTemplate.
Example:
monodb_client_dynamic_query.vm
{
"aggregate": "client",
"pipeline": [
{
"$match" : {
"$and" : [
{
"is_removed" : {
"$ne" : [
true
]
}
},
{
"errors" : {
"$size" : 0.0
}
},
{
"client_id": "$velocityMap.client_id"
}
]
}
},
{
"$project" : {
"_id" : -1.0,
"account" : "$_id.account",
"person_id" : "$_id.person_id",
"begin_date": { $dateToString: { format: "%Y-%m-%d", date: "$value.begin_date" } },
"end_date": { $dateToString: { format: "%Y-%m-%d", date: "$value.end_date" } }
}
}
]
}
Then execute using MondoTemplate:
String script = ...load from file the script monodb_client_dynamic_query.vm
Map parameters = ... put all variables to replace in the mongodb script
String scriptNoSql = VelocityUtil.loadTemplateVM(script, parameters);
DBObject dbObject = (BasicDBObject) JSON.parse(scriptNoSql);
if (null == dbObject) {
return;
}
DB db = mongoTemplate.getDb();
CommandResult result = db.command(dbObject);
if(!result.ok()) {
throw result.getException();
}

Resources