MongoDb + Spring boot + intersectsArrays
I want to fetch randomly 15 users and know there common interest. Document structure is something like this
{
"_id": {
"$oid": "593f773202338a47584b351e"
},
"interests": [
{
"_id": {
"$oid": "5957933cf3c5f5253ec9476c"
},
"name": "abc1",
"facebookId": "123"
}]
]
}
Need to fetch common interest for each user returned by mongo.
Below is my Spring boot code
Criteria criteria = new Criteria().andOperator(Criteria.where("lastLoginDate").gte(Utility.getDateBeforeDays(lastLoginNewerThan)));
Aggregation agg = Aggregation.newAggregation(new CustomAggregationOperation(maxRecords),
Aggregation.match(criteria),Aggregation.project("id","name")
);
AggregationResults<Users> userResults = operations.aggregate(agg, Users.class, Users.class);
List<Users> users = userResults.getMappedResults();
Looks like Spring Mongo Data api doesn't provide a way to pass a list of values as input to the set intersection operator.
You can use AggressionExpression to provide custom implementation.Include below projection in project stage.
.and(new AggregationExpression() {
#Override
public DBObject toDbObject(AggregationOperationContext context) {
return new BasicDBObject("$setIntersection",
Arrays.<Object> asList(
Arrays.<Object> asList("reading","movies"),
"$interests"));
}
}).as("commonInterests"));
You can wrap the expression as a lambda for Java 8
and(context -> new BasicDBObject("$setIntersection",
Arrays.<Object> asList(
Arrays.<Object> asList("reading","movies"),
"$interests")))
.as("commonInterests");
Related
I'm new to springboot and mongodb as well. I have the following json document in mongodb
Note: *Name of database is Friends and name of collection is Friend. It has around 118k documents.
One sample document:
[{
"_id":"abhj",
"id":"abhj",
"Main_array":[
{
"number":12345,
"pincode":247800,
"address": [
"vasant"
"vihar"
"kota"
]
}
],
}]
There is Main_array inside which there is an object inside which we have address which is an array.
I want to fetch the size of this address array.
I tried this but it didn't work.
Note: I have to use MongoClient.
MongoDatabase database = mongoClient.getDatabase("Friends");
MongoCollection<Document> collection = database.getCollection("Friend");
BasicDBObject filter = new BasicDBObject("Main_Array.0.address", new BasicDBObject("$exists", "true"));
collection.find(filter).forEach((Consumer<Document>) doc -> {
Object obj = doc.get("Main_array.address")
}
But I got null value in obj.
You can use following aggregation to find out the size.
here is the code
db.collection.aggregate([
{
$addFields: {
Main_array: {
"$map": {
"input": "$Main_array",
"in": {
number: "$$this.number",
pincode: "$$this.pincode",
addressSize: {
$size: "$$this.address"
}
}
}
}
}
}
])
Working Mongo playground
There is a TRICK to convert aggregations... The java code might be
#Autowired
private MongoTemplate mongoTemplate;
public List<YOUR_CONVERTER_CLASS> test() {
Aggregation aggregation = Aggregation.newAggregation(
l-> new Document("$addFields",
new Document("Main_array",
new Document("$map",
new Document("input","$Main_array")
.append("in",
new Document("number","$$this.number")
.append("pincode","$$this.pincode")
.append("addressSize",
new Document("$size","$$this.address")
)
)
)
)
)
).withOptions(AggregationOptions.builder().allowDiskUse(Boolean.TRUE).build());
return mongoTemplate.aggregate(aggregation, mongoTemplate.getCollectionName(YOUR_COLLECTION_CLASS.class), YOUR_CONVERTER_CLASS.class).getMappedResults();
}
Below is the sample of my Mongo-collection data-structure
{
"id": "5d91fe25da1917111182ce5a",
"customName": "Chess Application",
"status":"not_ready",
"environments": [
{
"environmentId": "6bbbbda6-b01a-4b9e-99d5-a1d0f696449a",
"environmentName": "Dev",
"environmentType": "dev",
},
{
"environmentId": "3b958d27-8fb7-4edd-bbb0-1dd86437d313",
"environmentName": "qa",
"environmentType": "qa",
}
]
}
Am using spring-JPA to get the data.. I will get only the environmentId as input and i will scan all the collections and get the collection that has this environmentId
Note: the Environment-id here is not mongo-created ID. It is the UUID generated by my Java app during insertion
i used findByEnvironmentsIsIn() method and it is not helpful . Any idea on how to get only one object from the list-of-sub-documents ?
#Query("{'environments' : { $elemMatch: { 'environmentId': { $in: ?0 }}}}")
List<Object> findByEnvironmentsIsIn( Set<String> environmentIds);
I guess this should work for you
How do I find an element in Meteor.user() and return other element in that same array in mongodb with meteor. How do I find course_id and get classes_id array?
this is in meteor.user
{
"_id": "RoFFcaAfXBeR2napZ",
"emails": [
{
"address": "tong#gmail.com",
"verified": false
}
],
"classes": [
{
"course_id": "svesvinfdsgrvnekuktndvsk",
"classes_id": ["myclass1", "myclass2"]
},
{
"course_id": "cegtrtcrtvw5cgekrgecec",
"classes_id": ["myclass3", "myclass4"]
},
{
"course_id": "rvxwefdsrvyjvyccrhnkik",
"classes_id": ["myclass5", "myclass6"]
},
],
"courses": [
"qwmZdgQbrZ3rmHdN8"
]
}
You can do it on the server using the Mongo's $ projection operator, but you cannot get the same functionality using MiniMongo out of the box, as it does not support this operator.
Let courseId hold the desired course id.
On the server, using:
c.find({"classes.course_id": courseId},{fields:{"courses.$": 1}})
should yield the desired result.
You will have to refactor your collection or use other tricks in order to get the same result on the client side.
One trick that comes to mind is using a mapping to leave only the desired courses:
function getClasses(courseId){
return myCollection.find({"classes.course_id": courseId}).map((student) => {
student.classes = _.filter(student.classes, (el) => el.course_id === courseId)
return student;
});
}
which also keeps your query reactive.
By default Meteor subscribes only the _id and profile object to the client. so you'll not be able to fetch your class records. you need to make a custom publish which returns your desired records and subscribe to that publication.
This question already has answers here:
How to use $cond operation in Spring-MongoDb aggregation framework
(2 answers)
Closed 5 years ago.
I am looking to aggregate the following data
{
"user": "user1",
"error": true
}
{
"user": "user2",
"error": false
}
{
"user": "user1",
"error": false
}
Into
{
"_id": "user1",
"errorCount": 1,
"totalCount": 2
},
{
"_id": "user2",
"errorCount": 0,
"totalCount": 1
}
With $cond operator, this can be achieved using:
$group: {
_id: "$user",
errorCount : { "$sum" : {"$cond" : ["$error", 1, 0]}},
totalCount : { "$sum" : 1 }
}
However, since I am using Spring-data-mongodb which does not yet support $cond (as of 1.3.4-RELEASE), I couldn't do this.
Is there a way to do the same aggregation without $cond?
Thanks to Neil Lunn's suggestion, I have managed to get $cond to using aggregation support from spring-data. To do this, you have to implement the AggregationOperation interface to take in a DBObject.
public class DBObjectAggregationOperation implements AggregationOperation {
private DBObject operation;
public DBObjectAggregationOperation (DBObject operation) {
this.operation = operation;
}
#Override
public DBObject toDBObject(AggregationOperationContext context) {
return context.getMappedObject(operation);
}
}
Then you will be able to use it in TypeAggregation normally:
DBObject operation = (DBObject)JSON.parse ("your pipleline here...");
TypedAggregation<UserStats> aggregation = newAggregation(User.class,
new DBObjectAggregationOperation(operation)
);
AggregationResults<UserStats> result = mongoTemplate.aggregate(aggregation, UserStats.class);
This approach will allow you to use any aggregation operator not yet defined in the framework. However, it also bypassed the validation put in place by spring-data and should be used with caution.
Please vote if you would like to have $cond operator supported properly through the framework: https://jira.springsource.org/browse/DATAMONGO-861
You are not bound to this even if there is no "functional interface" in Spring data yet. (BTW, raise a JIRA)
Just get the native form and use BasicDBObject types in the pipeline. So in principle:
DBCollection myCollection = mongoOperation.getCollection("collection");
<result cast> = myCollection.aggregate(<pipeline here>);
Spring data gives you abstractions, but it does not prohibit the use of the native driver functions. It actually gives you accessors to use them, as I demonstrated above.
I'm just starting to learn mongoDB using JAVA. I have the the following document in mongo
{
"_id": {
"$oid": "513fa587c5d0cf174eb9b1f8"
},
"suitename": "test_suite_name",
"testname": "test_name]",
"milestones": [
{
"milestone_id": 45
}
]
}
I have a compound key on suitename, testname, and milestone_id. I have to process a file which has these three fields. I create a simple DBObject query and check if count != 0
BasicDBObject query = new BasicDBObject();
query.put("suitename", testsuite);
query.put("testname", testcase);
query.put("milestones.milestone_id", SampleProgram.milestone_id);
If count == 0 --> add document in mongo -> this seems to work fine
What I am trying to figure out is:
If I have a new value of milestone_id in my file, I just need to add a milestone to some existing document's milestone array. Existing document is determined based on suitename AND testname.
So if milestone_id = 10, the document should look like
{
"_id": {
"$oid": "513fa587c5d0cf174eb9b1f8"
},
"suitename": "test_suite_name",
"testname": "test_name]",
"milestones": [
{
"milestone_id": 45
},
{
"milestone_id": 10
}
]
}
How can I accomplish this?
Thanks
This can be accomplished with the $push operator which appends values to an array and the update(…) method.
BasicDBObject query = new BasicDBObject();
query.put("suitename", testsuite);
query.put("testname", testcase);
BasicDBObject push = new BasicDBObject();
push.put("$push",
new BasicDBObject("milestones",
new BasicDBObject("milestone_id", SampleProgram.milestone_id)));
yourCollection.update(query, push);
BasicDBObject newDocument = new BasicDBObject();
newDocument.append("$set", new BasicDBObject().append("clients", 110));
BasicDBObject searchQuery = new BasicDBObject().append("hosting", "hostB");
collection.update(searchQuery, newDocument);