How do I pass an array to a MongoRepository #Query? - mongodb

Using Spring Data MongoDB. And I have defined this query on my MongoRepository:
#Query("{'type':?0, 'tags.type':?1, 'tags.softSkill.name': {$all: ?2}}")
List<Foo> findByTypeAndTags_TypeAndTags_SoftSkills(
FooType type, TagType tagType, String[] softSkills);
As I want to use $all operator I can't build the query through the method-naming way so I use the #Query annotation.
The issue is that it doesn't like the String[] that I try to pass for ?2 argument. I've tried with a List but the the error remains.
value expected to be of type java.util.List but is class java.lang.String toString:fooStr
On a mongo-shell would be like: $all: ['foo1','foo2']
Foo abstract entity is like:
#Data
#Document(collection="foes")
public abstract class Foo {
/*...*/
protected List<Tag> tags = new ArrayList<>();
}
Tag asbtract pojo:
#Data
public abstract class Tag {
protected final TagType type;
protected TagLevel level;
}
TagSoftSkill pojo:
#Data
#EqualsAndHashCode(callSuper=true)
public class TagSoftSkill extends Tag{
private SoftSkill softSkill; //another pojo that has a "name" field
public TagSoftSkill() {
super(TagType.SOFT_SKILL);
}
}
I already debugged and softSkills prints [foo]. I modified the value to be like ['foo'] but there are no changes.

I didn't mention that I'm using fongo. Switched to real mongo and everything works as it should. So it's a fongo issue.

Related

MongoDB with Spring Boot and document containing similar fields

I'm current trying to access an object in my MongoDB database.
My object is stored this format in db:
{
"_id" : ObjectId("some object id"), // mongodb gives this id
"my_id" : "Given id by myself",
"url" : "Some string data"
}
Myobj class:
#Document(collection = "MYOBJ")
public class Myobj {
#Id
private ObjectId _id;
private String my_id;
private String url;
// getters and setters and other methods
}
I want to fetch this object using my_id field. In my repository I have these:
public interface MyobjRepository extends MongoRepository<Myobj, String> {
Myobj findBy_id(ObjectId _id);
Myobj findByMy_id(String my_id);
}
But it fails to build, it gives me this error:
No property my found for type Myobj! Did you mean 'url'?
I suppose it cannot differantiate between my_id and _id. How can I solve this issue without changing my object in the database?
Without findByMy_id method it was working. That method causes compile error.
Problem is that you inside interface MyobjRepository set String as Id of that template MongoRepository<Myobj, String>, and you need to set ObjectId like MongoRepository<Myobj, ObjectId> because you said in your Myobj class that you will be use ObjectId _id for #Id
Am I right ?
#Document(collection = "MYOBJ")
public class Myobj {
private ObjectId _id;
#Id
private String my_id;
private String url;
}
public interface MyobjRepository extends MongoRepository<Myobj, String> {
Myobj findBy_id(ObjectId _id);
Myobj findByMy_id(String my_id);
}

how to disambiguate using #DocumentField in spring data mongoDB

While trying to save Object into mongo DB using Spring-Data MongoDB, I am getting this exception.
Ambiguous field mapping detected! Both <child class field> and <parent class field> map to the same field name <field name>! Disambiguate using #DocumentField annotation!
I am hiding a field in child class and this is causing the issue. But this is essential for me to hide the field declared in super class. I cannot find #DocumentField annotation anywhere. How can I proceed here? Is there any other solution?
For those of you (like me) who arrived here due to another form of Ambiguous field mapping detected! error that is not quite the same as OP, consider that your child class may not even need a field at all and the answer provided by #sparm could be changed to:
public class Parent {
private String myField;
public getMyField() {
return myField;
}
}
#Document(collection = "children")
public class Child extends Parent {
public getMyField() {
super.getMyField();
}
}
I believe its an error in the exception. You need to use #Field instead and specify a different name for the parameter so:
public class Parent {
private String myField;
}
#Document(collection = "children")
public class Child extends Parent {
#Field("childField")
private String myField;
}

Why is Spring Data MongoDB unable to instantiate this nested type structure?

My document structure is like:
{
_id: "A",
groups:[{
groupId: "someId",
groupName: "someName",
params: {
type1: ["a", "b"],
type2: ["c", d]
}
}],
config: {
person: {}
dataDetails: {
dataTypeDetails: {},
dataList: ["dt1", "dt2"]
}
}
}
My Spring Data MongoDB model types look like this:
// Imports etc.
#Document
public class Entity {
#Id
private String _id;
private List<Group> groups;
private Config config;
// setters and getters
public class Group {
private String groupId;
private String groupName;
private ParamData params;
// setter and getters
}
public class ParamData {
private List<String> type1;
private List<String> type2;
}
public class Config {
private Map person;
private DataConfig dataDetails;
}
public class DataConfig {
private Map dataTypeDetails;
private List<String> dataList;
}
}
Stacktrace:
org.springframework.data.mapping.model.MappingInstantiationException: Failed to instantiate com.****.common.models.Entity$ParamData using constructor public com.****.common.models.Entity$ParamData(com.****.common.models.Entity) with arguments com.****.common.models.Entity$Group#2eb61a7b
at org.springframework.data.convert.ReflectionEntityInstantiator.createInstance(ReflectionEntityInstantiator.java:78)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:257)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:237)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readValue(MappingMongoConverter.java:1136)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.access$100(MappingMongoConverter.java:78)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:1085)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.getValueInternal(MappingMongoConverter.java:816)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$1.doWithPersistentProperty(MappingMongoConverter.java:270)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$1.doWithPersistentProperty(MappingMongoConverter.java:263)
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:261)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:263)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:237)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readCollectionOrArray(MappingMongoConverter.java:861)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readValue(MappingMongoConverter.java:1134)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.access$100(MappingMongoConverter.java:78)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:1085)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.getValueInternal(MappingMongoConverter.java:816)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$1.doWithPersistentProperty(MappingMongoConverter.java:270)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter$1.doWithPersistentProperty(MappingMongoConverter.java:263)
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:261)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:263)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:237)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:201)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:197)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:78)
at org.springframework.data.mongodb.core.MongoTemplate$ReadDbObjectCallback.doWith(MongoTemplate.java:2016)
at org.springframework.data.mongodb.core.MongoTemplate.executeFindMultiInternal(MongoTemplate.java:1700)
at org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:1523)
at org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:1507)
at org.springframework.data.mongodb.core.MongoTemplate.find(MongoTemplate.java:532)
at org.springframework.data.mongodb.core.MongoTemplate.findOne(MongoTemplate.java:497)
at org.springframework.data.mongodb.core.MongoTemplate.findOne(MongoTemplate.java:489)
My in DAO I am trying to fetch the document by identifier, but it is failing only for the values at dataDetails and params. If I comment out the ParamData param, I get error for DataConfig. The data was added using command line/node scripts and was not added using this code. But it is getting retrieved properly by node/mongoose as well as from command line.
This seems to be an issue with doubly nested inner classes and the synthetically generated constructors created by the compiler. I could reproduce that issue locally and see if we can provide a fix. In the meantime you have two options:
Turn the inner class into static ones as this will remove the synthetic constructors and instantiation will work correctly.
Nest the type declarations in the same way you nest the properties. I.e. move the ParamData class into the Group class, DataConfig into Config as that will cause the synthetic constructors created in a way they match instantiation order Spring Data currently relies on.
I'd suggest the former approach as it doesn't artificially bind the classes to instances of the outer class.
Failed to instantiate ... using constructor public ... ReflectionEntityInstantiator
says it cannot create the objects using reflection.
Do you have getters and setters for all the fields in all your classes? Your code above does not have them for ParamData, Config and DataConfig.
Also, if you happen to have non-default constructors in any of your classes make sure you have an empty argument constructor, else reflection will fail.

Cannot create self link for class X. No persistent entity found

Getting the error in the title when using Spring Data REST. How to resolve?
Party.java:
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, property="#class")
#JsonSubTypes({ #JsonSubTypes.Type(value=Individual.class, name="Individual") })
public abstract class Party {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
protected Long id;
protected String name;
#Override
public String toString() {
return id + " " + name;
}
...getters, setters...
}
Individual.java:
#Entity
public class Individual extends Party {
private String gender;
#Override
public String toString() {
return gender + " " + super.toString();
}
...getters, setters...
}
PartyRepository.java:
public interface PartyRepository extends JpaRepository<Party,Long> {
}
If I POST, it saves to the db correctly:
POST /parties {"#class":"com.example.Individual", "name":"Neil", "gender":"MALE"}
But returns a 400 error:
{"cause":null,"message":"Cannot create self link for class com.example.Individual! No persistent entity found!"}
It looks like it's an Individual after retrieving from repository:
System.out.println(partyRepository.findOne(1L));
//output is MALE 1 Neil
Looks like Jackson can figure out that it's an individual:
System.out.println( new ObjectMapper().writeValueAsString( partyRepository.findOne(1L) ) );
//output is {"#class":"com.example.Individual", "id":1, "name":"Neil", "gender":"MALE"}
Why can SDR not figure it out?
How to fix? Preferably with XML config.
Versions:
SDR 2.2.0.RELEASE
SD JPA 1.7.0.RELEASE
Hibernate 4.3.6.Final
SDR repositories expect a non-abstract entity, in your case it would be Individual. You can google or search here for explaination on why SDR expects a non-abstract entity.
I tried your code myself and SDR won't even work for POST and I see below error message.
{
"cause": {
"cause": null,
"message": "Can not construct instance of com.spring.data.rest.test.Party, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information\n at [Source: org.apache.catalina.connector.CoyoteInputStream#30217e25; line: 1, column: 1]"
},
"message": "Could not read JSON: Can not construct instance of com.spring.data.rest.test.Party, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information\n at [Source: org.apache.catalina.connector.CoyoteInputStream#30217e25; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.spring.data.rest.test.Party, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information\n at [Source: org.apache.catalina.connector.CoyoteInputStream#30217e25; line: 1, column: 1]"
}
I suggest you change respository from PartyRepository to IndividualRepository
public interface IndividualRepository extends JpaRepository<Individual,Long> {
}
You are seeing that error since SDR could not find a repository refering Individual while constructing links. Simply adding Individual respository and not exporting it would solve your problem.
#RepositoryRestResource(exported = false)
public interface IndividualRepository extends JpaRepository<Individual,Long> {
}

springdata mongo repository method to return specific document property list

using spring data for mongodb, how do I specify the return type of the repository method to include a particular property from the document?
Ex:
#Document (collection = "foo")
class Foo {
String id
String name
... many more attributes
}
repository:
interface FooRepository extends MongoRepository<Foo, String> {
#Query { value = "{}", fields = "{'name' : 1}" }
List<String> findAllNames()
}
Above findAllNames works as expected and fetches only name property from the document. However spring data returned object is a string representation of Foo object which has id and name properties with values and remaining attributes as null.
Instead of Foo objects, I need to fetch List<String> which represents names.
As of now, I used a custom interface to achieve this. Moved the findAllNames() method from Spring data repository interface to my custom interface
interface FooRepositoryCustom {
List<String> findAllNames()
}
interface FooRepository extends MongoRepository<Foo, String>, FooRepositoryCustom {
}
#Component
class FooRepositoryImpl implements FooRepositoryCustom {
#Autowired
MongoOperations mongoOperations;
List<String> findAllNames() {
//using mongoOperations create the query and execute. Return the property values from document
}
}