MongoDB with Spring Data find object in array by id - java

I am using Spring Data with a Mongo DB embedded database and have the following document structure:
{
id : 111
messaage : abcd
commentsList: [
{
id : 123
text: test text
numberOfLikes : 5
numberOfDislikes: 2
}
]
}
I am trying to get a comment by id and update the numberOfLikes field by one and can't seem to get it to work, here is what I've tried and the java class structure:
public class Post {
private String id;
private String message;
private List<Comment> commentsList = new ArrayList<>();
...
}
public class Comment {
private String id;
private String text;
private int numberOfLikes;
private int numberOfDislikes;
...
}
Query query = new Query();
query.addCriteria(Criteria.where("commentsList._id").is("123"));
List<MongoComment> commentList = mongoTemplate.find(query, MongoComment.class);
Currently the returned list is always null.

Since you are trying to get a comment by id and update the numberOfLikes field by one, you essentially want to replicate this mongo shell update operation:
db.post.update(
{ "commentsList.id": "123" },
{
"$inc": { "commentsList.$.numberOfLikes": 1 }
}
)
The equivalent Spring Data MongoDB code follows:
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query;
import static org.springframework.data.mongodb.core.query.Update;
...
WriteResult wr = mongoTemplate.updateMulti(
new Query(where("commentsList.id").is("123")),
new Update().inc("commentsList.$.numberOfLikes", 1),
MongoPost.class
);

The id of the elements in the array "commentsList" is "id" (without the '_').
query.addCriteria(Criteria.where("commentsList.id").is("123"))
That should work.
The "_id" you're trying to query is the identifier Mongodb automatically generates for each document. Your document in the database looks like this:
{
_id: ObjectId("56b46e1d1d1353e38886dcc34f"),
id : 111,
messaage : "abcd",
commentsList: [
{
id : 123,
text: "test text",
numberOfLikes : 5,
numberOfDislikes: 2
}
]
}

Related

Write custom document to Cosmos DB with Java API

I have a Cosmos DB and want to write different kind of documents to it. The structure of the documents is dynamic and can change.
I tried the following. Let's say I have the following class:
class CosmosDbItem implements Serializable {
private final String _id;
private final String _payload;
public CosmosDbItem(String id, String payload) {
_id = id;
_payload = payload;
}
public String getId() {
return _id;
}
public String getPayload() {
return _payload;
}
}
I can create then the document with some JSON as follows:
CosmosContainer _container = ...
CosmosDbItem dataToWrite = new CosmosDbItem("what-ever-id-18357", "{\"name\":\"Jane Doe\", \"age\":42}")
item = _cosmosContainer.createItem(dataToWrite, partitionKey, cosmosItemRequestOptions);
This results in a document like that:
{
"id": "what-ever-id-18357",
"payload": "{\"name\":\"Jane Doe\", \"age\":42}",
"_rid": "aaaaaaDaaAAAAAAAAAA==",
"_self": "dbs/aaaaAA==/colls/aaaaAaaaDI=/docs/aaaaapaaaaaAAAAAAAAAA==/",
"_etag": "\"6e00c443-0000-0700-0000-5f8499a70000\"",
"_attachments": "attachments/",
"_ts": 1602525607
}
Is there a way in generating the payload as real JSON object in that document? What do I need to change in my CosmosDbItem class? Like this:
{
"id": "what-ever-id-18357",
"payload": {
"name":"Jane Doe",
"age":42
},
"_rid": "aaaaaaDaaAAAAAAAAAA==",
"_self": "dbs/aaaaAA==/colls/aaaaAaaaDI=/docs/aaaaapaaaaaAAAAAAAAAA==/",
"_etag": "\"6e00c443-0000-0700-0000-5f8499a70000\"",
"_attachments": "attachments/",
"_ts": 1602525607
}
Here is my solution that I ended up. Actually it is pretty simple once I got behind it. Instead of using CosmosDbItem I use a simple HashMap<String, Object>.
public void writeData() {
...
Map<String, Object> stringObjectMap = buildDocumentMap("the-id-", "{\"key\":\"vale\"}");
_cosmosContainer.createItem(stringObjectMap, partitionKey, cosmosItemRequestOptions);
...
}
public Map<String, Object> buildDocumentMap(String id, String jsonToUse) {
JSONObject jsonObject = new JSONObject(jsonToUse);
jsonObject.put("id", id);
return jsonObject.toMap();
}
This can produce the following document:
{
"key": "value",
"id": "the-id-",
"_rid": "eaaaaaaaaaaaAAAAAAAAAA==",
"_self": "dbs/eaaaAA==/colls/eaaaaaaaaaM=/docs/eaaaaaaaaaaaaaAAAAAAAA==/",
"_etag": "\"3b0063ea-0000-0700-0000-5f804b3d0000\"",
"_attachments": "attachments/",
"_ts": 1602243389
}
One remark: it is important to set the id key in the HashMap. Otherwise one will get the error
"The input content is invalid because the required properties - 'id; ' - are missing"

How get JSON Object with Retrofit 2 and Android Studio?

I am making an application in Android Studio and I want to consume API for cooking recipes, I have the following response from the API that I am consuming with Android Studio and Java:
API Response
"q" : "pollo",
"from" : 0,
"to" : 10,
"params" : {
"sane" : [ ],
"q" : [ "pollo" ],
"app_id" : [ "02" ],
"app_key" : [ "\n66b" ]
},
"more" : true,
"count" : 1000,
"hits" : [ {
"recipe" : {
"uri" : "http://www.edamam.com/ontologies/edamam.owl#recipe_d56f75c72ab67a45174441af1efe4473",
"label" : "Pollo con Crema a las Hierbas",
"image" : "http://cdn.kiwilimon.com/recetaimagen/23127/thumb120x90-15802.jpg",
"source" : "KiwiLimon",
"url" : "http://www.kiwilimon.com/receta/carnes-y-aves/pollo-con-crema-a-las-hierbas",
"shareAs" : "http://www.edamam.com/recipe/pollo-con-crema-a-las-hierbas-d56f75c72ab67a45174441af1efe4473/pollo",
"yield" : 42.0,
And continue with more 'recipe', what I want is to get only the array of hits that all the recipes have to be able to show in my application, the problem is that I get the following error:
Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
I understand that it is because it expects an array and it obtains a JSON object, but I do not know how to parse it, I have my Recipe model class and the RecipeService service and I manage everything in MainActivity, I have seen in some answers that I would have to do an intermediate response, but I do not understand how I could implement it in my code, then I show the classes that handle all this.
Recipe Class (Model):
public class Recipe {
private String label;
private String image;
private String source;
private String shareAs;
private List<String> dietLabels;
private List<String> healthLabels;
private List<String> cautions;
private List<String> ingredientLines;
private List<String> ingredients;
private double calories;
private double totalWeight;
private List<String> totalNutrients;
private List<String> totalDaily;
public String getLabel() {
return label;
}
.
.
.
RecipeService Class:
public interface RecipeService {
String API_ROUTE = "/search";
String API_KEY = "&app_key=" + Credentials.API_KEY;
String APP_ID = "&app_id=" + Credentials.APP_ID;
//String query = "";
#GET(API_ROUTE)
Call< List<Recipe> > getRecipe(#Query("q") String q);
}
MainActivity:
private void getRecipes() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://test-es.edamam.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
RecipeService recipeService = retrofit.create(RecipeService.class);
Call<List<Recipe>> call = recipeService.getRecipe("pollo");
System.out.println("GET RECIPES");
System.out.println("HEADERS: "+ call.request().headers());
call.enqueue(new Callback<List<Recipe>>() {
#Override
public void onResponse(Call<List<Recipe>> call, Response<List<Recipe>> response) {
System.out.println("RESPONSE CODE: " + response.code());
for(Recipe recipe : response.body()){
System.out.println("AÑADIENDO: " + recipe.getLabel());
recipes.add(recipe.getLabel());
}
//System.out.println(recipes.toArray().toString());
}
#Override
public void onFailure(Call<List<Recipe>> call, Throwable t) {
System.out.println("HA OCURRIDO UN FALLO");
System.out.println(t.getMessage());
}
});
}
If API response is exactly what you post then the response is INVALID JSON format. You may check your JSON validity by jsonlint.
Secondly, your error says,
Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
Which means, POJO is designed for a JSON array, but from the API you are getting a JSON object.
The solution is really simple.
There are tons of plugins in Android Studio like this, or go this online converter tool. Then hopefully you will not get the same error,

How to store mongodb aggregate query result in spring mongotemplate?

I am using an aggregate query in mongodb to find the sum of an attribute in all the documents present in a collection.
Query:
db.conversation.aggregate( [
{
$match:{
$and:[{"mailBoxId":"1","isHidden":false}]
}
},
{
$group:
{
_id: {"mailBoxId":"$mailBoxId","isHidden":"$isHidden"} ,
messageCount: { $sum:"$messageCount" }
}
}
]);
The result returned by Mongodb is fine and is in this format.
{
"result" : [
{
"_id" : {
"mailBoxId" : "2",
"isHidden" : false
},
"messageCount" : 2
}
],
"ok" : 1
}
I just want the messageCount field. I am using MongoTemplate(Spring) class to query the database.
Query retrievalQuery = new Query();
retrievalQuery.addCriteria(Criteria.where("mailBoxId").is(userId).and("isHidden").is(false));
return mongoTemplate.find(retrievalQuery, );
I am confused how to store the resultant object returned by Mongodb and extract a specific field from it.
Pls help.
The way you are trying to use aggregate in mongoTemplate is wrong . Try this i am sure it will help.
Aggregation agg = Aggregation.newAggregation(
Aggregation.match(
Criteria.where("mailBoxId").is("1").and("isHidden").is(false)),
Aggregation.group("$mailBoxId").sum("$unReadMessagesCount").as("unReadMessagesCount")
);
System.out.println("Query ==>>["+agg.toString()+"]");
AggregationResults<AggResultObj> data = mongoTemplate.aggregate(agg, "collectionName", AggResultObj.class);
System.out.println("UnReadMesasgeCode :"+data.getUniqueMappedResult().getUnReadMessagesCount());
The AggResultObj will be looks like
public class AggResultObj{
String _id;
int unReadMessagesCount;
public String get_id() {
return _id;
}
public void set_id(String _id) {
this._id = _id;
}
public int getUnReadMessagesCount() {
return unReadMessagesCount;
}
public void setUnReadMessagesCount(int unReadMessagesCount) {
this.unReadMessagesCount = unReadMessagesCount;
}
}
For more information you can see my blog where i have created a example for the same for your scenario. please click https://satishkumardangi.blogspot.in/2016/09/using-mongo-db-aggregation-with-spring.html
Try this
Query retrievalQuery = new Query();
retrievalQuery.addCriteria(Criteria.where("mailBoxId").is(userId).and("isHidden").is(false));
var result = mongoTemplate.find(retrievalQuery);
var final = result[1].messageCount;
return final;

Upsert nested objects in mongodb 3.2 using Java driver

I'm trying to use the upsert feature of mongodb v3.2 using java,
so every solution not including a java response would not be accepted.
My problem is that the upsert command override nested objects instead of adding new ones, I have tried to use '$addToSet' and 'push', but without success and I get an error message indicating that the storage engine does not support this command.
I want to update the client's document as well as their inner objects such as checks and checks's values.
the global structure of the client doc is as below.
Client
|
|__Checks // array of checks , update or insert operation
|
|__values // array of values, every check has its own values (20 max)
// update using index(id)
link of the: Example's source code
My intention is to use only one query to update client's document without using many queries.
I'm not specialist in mongodb, so every advice or critics would be appreciated.
Even if I'm doing this all wrong, feel free to notify me, and please using java for mongo 3.2.
Here is the source code used to generate the last result.
package org.egale.core;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.UpdateOptions;
import java.util.ArrayList;
import java.util.List;
import org.bson.Document;
/**
*
* #author Zied
*/
public class MongoTest {
/**
* Pojo used to populate data
*/
static class CheckModel {
public String client;
public String checkId;
public String name;
public String command;
public String description;
public String topic;
public int refresh = 60;
public int status;
public String output;
}
static MongoClient mongoClient = new MongoClient();
static String dbName = "eagle";
private static List<Document> getCheckValues(CheckModel checkModel, int index) {
final List<Document> checkValues = new ArrayList<>();
final Document val = new Document()
.append("id", index)
.append("output", checkModel.output)
.append("status", checkModel.status);
checkValues.add(val); // second execution should not ovveride the content of value but a new
return checkValues;
}
private static void insertCheck(MongoDatabase db, CheckModel checkModel) {
int idx =++index % 20;
final List<Document> checks = new ArrayList<>();
final Document check = new Document()
.append("name", checkModel.name)
.append("command", checkModel.command)
.append("id", checkModel.checkId)
.append("description", checkModel.description)
.append("topic", checkModel.topic)
.append("last_output", checkModel.output)
.append("index", index)
.append("last_status", checkModel.status)
.append("values", getCheckValues(checkModel,idx))
.append("refresh", checkModel.refresh);
checks.add(check);
Document client = new Document()
.append("name", checkModel.client)
.append("checks", checks);
//.append("$addToSet" , new Document("checks", checks)); // <<- error here '$addToSet' is not recocnized
db.getCollection("clients") // execute client insert or update
.updateOne(
new Document().append("_id", checkModel.client), new Document("$set", client), new UpdateOptions().upsert(true)
);
}
static int index = 0;
// Name of the topic from which we will receive messages from = " testt"
public static void main(String[] args) {
MongoDatabase db = mongoClient.getDatabase(dbName);
CheckModel checkModel = new CheckModel();
checkModel.command = "ls -lA";
checkModel.client = "client_001";
checkModel.description = "ls -l command";
checkModel.checkId = "lsl_command";
checkModel.name = "client 001";
checkModel.output = "result of ls -l";
checkModel.status = 0;
checkModel.topic = "basic_checks";
checkModel.refresh = 5000;
initDB(db);
// insert the first check
insertCheck(db, checkModel);
// insert the second check after some modification
// insertCheck(db, modifyData(checkModel));
}
// mdofiy data to test the check
private static CheckModel modifyData(CheckModel checkModel){
checkModel.status = 1;
checkModel.output = "ls commadn not found";
return checkModel;
}
private static void initDB(MongoDatabase db) {
MongoCollection<Document> collection = db.getCollection("configuration");
if (collection.count() == 0) {
Document b = new Document()
.append("_id", "app_config")
.append("historical_data", 20)
.append("current_index", 0);
collection.insertOne(b);
}
Document b = new Document().append("none", "none");
MongoCollection<Document> clients = db.getCollection("clients");
clients.insertOne(b);
clients.deleteOne(b);
MongoCollection<Document> topics = db.getCollection("topics");
topics.insertOne(b);
topics.deleteOne(b);
}
}
You may use $push, $each, $slice to solve your problem, see alse https://docs.mongodb.org/manual/reference/operator/update/slice/.
db.students has following documents
{ "_id" : 10, "scores" : [ 1, 2, 3 ] }
db.students.update(
{ _id: 10 },
{
$push: {
scores: {
$each: [ 4 ],
$slice: -3
}
}
}
)
result is:
{ "_id" : 10, "scores" : [ 2, 3, 4] }

Spring Mongo Upsert nested document

I am new bie to spring framework. I have my mongo document like
{
"_id" : ObjectId("527242d584ae917d8bd75c7b"),
"postTitle" : "Car",
"postDesc" : "rent",
"owner" : ObjectId("526a588f84aed6f41cca10bd"),
"intrest" : []
}
What I want is to search document having id
"_id" : ObjectId("527242d584ae917d8bd75c7b")
and update it to
{
"_id" : ObjectId("527242d584ae917d8bd75c7b"),
"postTitle" : "Car",
"postDesc" : "rent",
"owner" : ObjectId("526a588f84aed6f41cca10bd"),
"intrest" : [
{
"userId" : ObjectId("526a587d84aed6f41cca10bc"),
"timestamp" : ISODate("2013-10-31T11:45:25.256Z")
},
{
"userId" : ObjectId("526a587d84aed6f41cca10bc"),
"timestamp" : ISODate("2013-11-31T11:55:25.256a")
}
]
}
my domain is
#Document
public class Post {
#Id
private ObjectId _id;
private String postTitle;
private String postDesc;
private ObjectId owner=Global.getCurruser();
private List<Intrest> intrest = new ArrayList<Intrest>();
// Getters and setters
}
#Document
public class Intrest {
private ObjectId userId;
private Date timestamp;
// Getters and setters
}
What upsert should I write to add or modify entries in intrest array[].
Please Help.
I am using spring-mongodb .. Here is what I do
Intrest insertObj = new Insert();
//initilize insert obj here ..
Update args = new Update();
args.addToSet("intrest",insertObj);
Query query = new Query(Criteria.where("id").is("527242d584ae917d8bd75c7b"));
// if u want to do upsert
mongoOperation.findAndModify(query, args, FindAndModifyOptions.options().upsert(true), Post.class);
//if u want to just update
mongoOperation.findAndModify(query, args, Post.class);
I think what you intend to do is an update. Upsert will modify your document matching the given query if not it will create a new document , where as update will only modify your document if found. here is the reference
I do not know about java, but all you need to do is $pushAll operator (I really hope you can find how to do this with java driver).
db.collection.update(
{"_id" : ObjectId("527242d584ae917d8bd75c7b")},
{ $pushAll: { intrest: [ {
"userId" : ObjectId("526a587d84aed6f41cca10bc"),
"timestamp" : ISODate("2013-10-31T11:45:25.256Z")
},
{
"userId" : ObjectId("526a587d84aed6f41cca10bc"),
"timestamp" : ISODate("2013-11-31T11:55:25.256a")
}] } }
);

Categories