Saving a POJO in MongoDB with null fields using Jongo - mongodb

so I have a POJO object that I am creating and saving to a MongoDB collection using Jongo:
import java.util.Map;
public class MyObject {
private String name;
private Map<String, String> mappings;
public MyObject() {
}
public MyObject(String name, Map mappings) {
this.name = name;
this.mappings = mappings;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<String, String> getMappings() {
return mappings;
}
public void setMappings(Map<String, String> mappings) {
this.mappings = mappings;
}
}
Test class for saving objects to mongo:
import com.mongodb.DB;
import com.mongodb.MongoClient;
import org.jongo.Jongo;
import org.jongo.MongoCollection;
import java.net.UnknownHostException;
import java.util.HashMap;
public class NullFieldTest {
public static void main(String[] args) throws UnknownHostException {
DB db = new MongoClient("localhost", 27017).getDB("testDB") ;
Jongo jongo = new Jongo(db);
MongoCollection testCollection = jongo.getCollection("testCollection");
MyObject objectA = new MyObject("objectA", new HashMap());
MyObject objectB = new MyObject("objectB", null);
testCollection.save(objectA);
testCollection.save(objectB);
}
}
This test saves the objects fine:
{
"_id" : ObjectId("543cf1a6cd8936deafcf66cf"),
"name" : "objectA",
"mappings" : {}
}
{
"_id" : ObjectId("543cf1a6cd8936deafcf66d0"),
"name" : "objectB"
}
but what I really want is for the null mappings in objectB to appear as
"mappings" : null
I know that a field within a collection can have a null value, but I dont know how to do this with the jongo driver, any ideas?
FYI, I'm using jongo V1.1

problem solved, my issue was in the serialization of the objects, added an annotation from Jackson to include even null fields:
#JsonInclude(JsonInclude.Include.ALWAYS)
public class MyObject {
Found more details on this post:
How to tell Jackson to ignore a field during serialization if its value is null?

Jongo comes with a pre-configured Jackson which ignore null fields : https://github.com/bguerout/jongo/blob/443b461e47acdcffb9f51efafb291b7e0c805c26/src/main/java/org/jongo/marshall/jackson/configuration/VisibilityModifier.java
You can change this configuration by creating a custom Mapper using JacksonMapper.Builder :
Mapper mapper = new JacksonMapper.Builder().addModifier(new MapperModifier() {
public void modify(ObjectMapper mapper) {
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
}
}).build();
Jongo jongo = new Jongo(db, mapper);

Related

Spring Data MongoDB Converter not getting registered

I have a setup of multiple MongoDB configuration. Here is the configuration class
#Configuration
#RequiredArgsConstructor
#EnableConfigurationProperties(MongoConfigProperties.class)
public class MultipleMongoConfig {
private static final Logger logger = LoggerFactory.getLogger(MultipleMongoConfig.class);
private final MongoConfigProperties mongoProperties;
#Primary
#Bean(name = "sysdiagMongoTemplate")
public MongoOperations sysdiagMongoTemplate() {
MappingMongoConverter mappingMongoConverter = new MappingMongoConverter(new DefaultDbRefResolver(sysdiagFactory(mongoProperties.getSysdiag())),
new MongoMappingContext());
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(new AggregationResultReadConverter());
mappingMongoConverter.setCustomConversions(new CustomConversions(CustomConversions.StoreConversions.NONE, converters));
mappingMongoConverter.afterPropertiesSet();
boolean canConvert = mappingMongoConverter.getConversionService().canConvert(Document.class, AggregationResult.class);
mappingMongoConverter.afterPropertiesSet();
logger.info("canConvertFromDocumentToAggResult:: " + canConvert); //gives TRUE
return new MongoTemplate(sysdiagFactory(this.mongoProperties.getSysdiag()), mappingMongoConverter);
}
#Bean(name = "monitoringMongoTemplate")
public MongoOperations monitoringMongoTemplate() {
return new MongoTemplate(monitoringFactory(this.mongoProperties.getMonitoring()));
}
public MongoDbFactory sysdiagFactory(final MongoProperties mongo) {
return new SimpleMongoDbFactory(new MongoClient(mongo.getHost(), mongo.getPort()),
mongo.getDatabase());
}
public MongoDbFactory monitoringFactory(final MongoProperties mongo) {
return new SimpleMongoDbFactory(new MongoClient(mongo.getHost(), mongo.getPort()),
mongo.getDatabase());
}
}
Here is the read converter class (I only require reading from MongoDB). We have dynamic keys in the document due to which I need to convert them into a Map
public class AggregationResultReadConverter implements Converter<Document, AggregationResult> {
#Override
public AggregationResult convert(Document source) {
AggregationResult aggregationResult = new AggregationResult();
aggregationResult.setData(new HashMap());
for(Map.Entry<String,Object> entry : source.entrySet()){
if(entry.getKey().matches("[A-Z][A-Z][A-Z]")){
aggregationResult.getData().put(entry.getKey(), entry.getValue());
}
}
return aggregationResult;
}
}
Here is the mapping configuration for one of the MongoDB database
#Configuration
#EnableMongoRepositories(basePackages = {"com.hns.services.restapi.db.mongo.sysdiag.entity", "com.hns.services.restapi.db.mongo.sysdiag.repo"}, mongoTemplateRef = "sysdiagMongoTemplate")
public class SysdiagMongoConfig {
}
And here is the repository interface
#Repository
public interface AggregationResultRepository extends MongoRepository<AggregationResult, ObjectId> {
#Query("{ TIME: {$gte : ?0, $lt: ?1}}")
List<AggregationResult> findInTimeRange(Long startTime, Long endTime);
}
When I query using AggregationResultRepository, I expect the converter code to be executed so that I can convert the fields and put them in the Entity (Document) class object as per the logic. The query is going fine as I saw in the debug logs and I see an output but the converter is not getting called.
The converted is getting registered with the mongo template as the canConvertFromDocumentToAggResult logger gives TRUE. I tried changing the converted from Document -> AggregationResult to DBObject -> AggregationResult but no luck. Not sure what am I missing here.

Drools : Getting the catched word in a list in THEN

Below is my pojo class
-----------------------------------pojo_Classes2.RootDoc.java-----------------------------------
package pojo_Classes2;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"content",
"person"
})
public class RootDoc {
#JsonProperty("content")
private String content;
#JsonProperty("person")
private List<String> person = null;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("content")
public String getContent() {
return content;
}
#JsonProperty("content")
public void setContent(String content) {
this.content = content;
}
#JsonProperty("person")
public List<String> getPerson() {
return person;
}
#JsonProperty("person")
public void setPerson(List<String> person) {
this.person = person;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
Here is the type of rule which i want to apply
$list1=[gaurav,gagan,anshu....]
...................................................................................................................
Rule1
If
content contains any of the above $list1
Then
Retrieve which name was captured in content and set person the person name in then
............................................................................................................
For eg. gaurav and gagan were captured in content then set get that gaurav and gagan were matched in content and get them back in then part.
Is it possible in drools
Yes, but create object of your class like:
when
$rd : Rootdoc(****your query****);
then
rd.setPerson(query);
end

Unable to insert collection in mongo database: Can't find a codec

Mongo runtime throws following error when trying to insert a new document in the collection. Note that the database and collection does NOT exist yet (and my assumption is that mongo runtime will create the database, followed by collection and then insert my first document by converting my POJO to BSON using a default codec). Any suggestions?
Error: 2015-12-13 18:46:46,384 - application-akka.actor.default-dispatcher-3 - [error] - application - Can't find a codec for class models.User.
/* Model Class */
package models;
import javax.persistence.Id;
import org.mongojack.ObjectId;
public class User {
#ObjectId #Id public String _id;
public String firstname;
public String lastname;
public String email;
public String phone;
public String address;
}
/* Controller Class */
public class Users extends Controller {
#BodyParser.Of(BodyParser.Json.class)
public Result create() {
Logger.info("Enter - Users::create()");
try {
Form<User> user = Form.form(User.class).bindFromRequest();
if (user.hasErrors()) {
Logger.info("User: " + user.toString());
Logger.info(user.errorsAsJson().toString());
return badRequest(user.errorsAsJson());
}
else {
User oneUser = user.get();
MongoClient mongoClient= new MongoClient();
MongoDatabase db = mongoClient.getDatabase("marketplace");
MongoCollection<User> col = db.getCollection("users", User.class);
col.insertOne(oneUser);
mongoClient.close();
return ok();
}
}
catch (Exception e) {
Logger.error(e.getLocalizedMessage());
return internalServerError(e.getMessage());
}
finally {
Logger.info("Exit - Users::create()");
}
}
}
You will have to wrap the MongoCollection with a JacksonDBCollection to enable all the MongoJack features.
Something like this:
JacksonDBCollection<User, String> userColl = JacksonDBCollection.wrap(col, User.class, String.class);
And then use userColl to insert your new object.
It seems that this the only way supported at this time. This requires using the getDB() method, which is deprecated since the mongo driver 3.0.
See the relevant issue on github here:
https://github.com/mongojack/mongojack/issues/105
Yup - this works. However I had to use deprecated getDB since it has a getter that returns collection of type DBCollection which "wrap" function expects. I was hoping to use getDatabase instead but gets me MongoCollection which Jackson's "Wrap" won't accept. Any suggestions?
User oneUser = user.get();
MongoClient mongoClient= new MongoClient();
DB db = mongoClient.getDB("marketplace");
DBCollection col = db.getCollection("marketplace");
JacksonDBCollection<User, String> userCol = JacksonDBCollection.wrap(col,User.class, String.class);
userCol.insert(oneUser);

Spring boot data rest findby traverson to java object

my URI is
http://localhost:8080/context/my-objects/search/findByCode?code=foo
JSON response:
{
"_embedded" : {
"my-objects" : [ {
"code" : "foo",
"description" : "foo description",
"_links" : {
"self" : {
"href" : "http://localhost:8080/context/my-objects/34"
}
}
} ]
}
}
How can I get a java MyObject with Traverson or RestTemplate?
import org.springframework.hateoas.ResourceSupport;
public class MyObject extends ResourceSupport{
private String code;
private String description;
public String getDescription() {
return description;
}
public void setDescription(final String description) {
this.description = description;
}
public String getCode() {
return code;
}
public void setCode(final String code) {
this.code = code;
}
}
This is my Template. I've also tried with a default one.
{
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.registerModule(new Jackson2HalModule());
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(MediaType.parseMediaTypes("application/hal+json"));
converter.setObjectMapper(mapper);
RestTemplate template = new RestTemplate(Collections.<HttpMessageConverter<?>> singletonList(converter));
}
Thx in advance.
I've found the solution. First, create a Resources class:
import org.springframework.hateoas.Resources;
public class MyObjects extends Resources<MyObject> { }
Then it is straightforward:
MyObjects myObjects = template.getForObject("http://localhost:8080/context/my-objects/search/findByCode?code=foo", MyObjects.class);
Attention: the template should support hal+json Media Type.
Or with Traverson:
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.client.Traverson;
Traverson traverson;
try{
traverson = new Traverson(new URI("http://localhost:8080/context"), MediaTypes.HAL_JSON);
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("code", "foo");
MyObjects myObjects = traverson.follow("my-objects", "search", "findByCode").withTemplateParameters(
parameters).toObject(MyObjects.class);
} catch (URISyntaxException e) {}
If you don't want to extend your POJO MyObject with ResourceSupport class, your Resources class should be typed with Resource:
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.Resources;
public class MyObjects extends Resources<Resource<MyObject>> { }
(If you don't need links the type parameter may again be MyObject).

how to insert object in mongodb

I have a class as follows:
package mongo;
import com.mongodb.BasicDBObject;
public class tweet extends BasicDBObject{
private String name;
private Integer id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
Now I am trying to insert the object of this class into mongodb:
public void connect() throws UnknownHostException
{
MongoClient mongoClient = new MongoClient( "localhost" , 27017 );
DB db = mongoClient.getDB( "test" );
tweet t=new tweet();
t.setId(100);
t.setName("Gggggg");
DBCollection Collection = null ;
Collection = db.getCollection("test");
DBObject doc = new BasicDBObject();
doc.put("first", t);
Collection.save( doc);
System.err.println(Collection.findOne());
}
But when I run this code, the object t is not inserted to the db and the system.err.println return the following:
{ "_id" : { "$oid" : "546c00efbadcd42088c8fee3"}}
How can I add the object into mongodb? Is it possible to do that?
BasicDBObject is actually a LinkedHashMap. So when you extend it, you need to put and retrieve values as you do it in a map.
So simply setting the values as attributes doesn't make sense, those attributes need to be put inside the Tweet map.
You need to modify your Tweet Class as follows:
class Tweet extends BasicDBObject{
public String getName() {
return (String)this.get("name");
}
public void setName(String name) {
this.put("name", name);
}
public Integer getId() {
return (Integer)this.get("_id");
}
public void setId(Integer id) {
this.put("_id", id);
}
}
and your main method as:
MongoClient mongoClient = new MongoClient( "localhost" , 27017 );
DB db = mongoClient.getDB( "test" );
Tweet t=new Tweet();
t.setId(100);
t.setName("Ghorbani");
DBCollection collection = null ;
collection = db.getCollection("test");
collection.save(t);
System.err.println(collection.findOne());
If you notice,collection.save(t);, the Tweet object is being directly saved. That is how it should work.
o/p:
{ "_id" : 100 , "name" : "Ghorbani"}
You seem to be trying to set an ID for your object. Usually that is something that is done automatically from Mongo. Try removing t.setId(100); and then run your code again.
Tip - try using Mongoose to manage your connection to Mongo.