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);
}
Related
I am trying to get count of likes and if user is liked this post in Mongo.
I managed to get this via native query with facets, but problems is how can i map this two fields on my custom java class (LikeStatus.class)?
thanks in advance!
please code below:
POJO:
public class LikeStatus {
String entityId;
LikedEntityType entityType;
long likesCount;
boolean isLikedByUser;
}
Document class:
public class Like {
#Id
private String id;
#EqualsAndHashCode.Include
private String entityId;
#EqualsAndHashCode.Include
private String profileId;
#EqualsAndHashCode.Include
private LikedEntityType entityType;
private LocalDateTime createdAt = LocalDateTime.now();
}
Query i used in Mongo:
> db.likes.aggregate({$facet:
{count:[
{$match:{entityId:"entityId"},
$match:{entityType:"OFFER"}}, {$count:"count"}],
isliked:[{$match:{profileId:"profileId4"}}, {$count:"isliked"}]}}).pretty();
and gives me result:
{
"count" : [
{
"count" : 3
}
],
"isliked" : [
{
"isliked" : 1
}
]
}
I managed to find solution which is suited my needs, hope it will be useful who faced with the same kind of queries in Mongodb and it will give some idea how it can be solved)
Java solution: i used Facet object to collect two aggregation request in one query like this:
In repository layer i created query:
default Aggregation getLikeStatus(String entityId, String entityType, String profileId){
FacetOperation facet = Aggregation.facet(match(where(ENTITY_ID_FIELD).is(entityId).and(ENTITY_TYPE_FIELD).is(entityType)),
Aggregation.count().as(LIKES_COUNT_FIELD)).as(LIKES_COUNT_FIELD)
.and(match(where(ENTITY_ID_FIELD).is(entityId)
.and(ENTITY_TYPE_FIELD).is(entityType)
.and(PROFILE_ID_FIELD).is(profileId)),
Aggregation.count().as(IS_LIKED_BY_USER_FIELD)).as(IS_LIKED_BY_USER_FIELD);
ProjectionOperation project = project()
.and(ConditionalOperators.ifNull(ArrayOperators.ArrayElemAt.arrayOf(LIKES_COUNT_FIELD).elementAt(0)).then(0)).as(LIKES_COUNT_FIELD)
.and(ConditionalOperators.ifNull(ArrayOperators.ArrayElemAt.arrayOf(IS_LIKED_BY_USER_FIELD).elementAt(0)).then(0)).as(IS_LIKED_BY_USER_FIELD)
.andExclude("_id");
return newAggregation(facet, project);
}
then in service layer it returns Document object which is mapped on my custom class LikeStatus fields:
Document status = template.aggregate(likeRepo.getLikeStatus(entityId, entityType, profileId), Like.class, Document.class).getUniqueMappedResult();
my custom POJO:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class LikeStatus {
String entityId;
LikedEntityType entityType;
long likesCount;
boolean isLikedByUser;
}
Also i post native query solution in Mongo for reference:
db.likes.aggregate([
{$facet:
{"likesCountGroup":[
{$match:{entityId:"entityId", entityType:"TYPE"}},{$count:"likesCount"}
],
"isUserLikedGroup":[
{$match:{entityId:"entityId", entityType:"TYPE", profileId:"604cd12c-1633-4661-a773-792a6ec22187"}},
{$count:"isUserLiked"}
]}},
{$addFields:{}},
{$project:{"likes":{"$ifNull":[
{$arrayElemAt:["$likesCountGroup.likesCount", 0]},0]},
"isUser":{"$ifNull:[{$arrayElemAt["$isUserLikedGroup.isUserLiked",0]},0]}}}]);
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.
Let us take the following JSON response which I want to return from my REST service,
{
"id" : 123,
"name" : "ABC",
}
For the above JSON response, I can create a POJO class like,
public class Student{
private long id;
private String name;
//getters and setters
}
So, I can write a GET service to return the Student object which will be then transformed as JSON.
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response get(){
Student student = new Student();
student.setId(123);
student.setName("ABC");
return Response.ok(student).build();
}
It works fine. Now I want to introduce optional parameters to my JSON response as follows,
{
"id" : 123,
"name" : "ABC",
"params" : {"param1":"xxx","param2":342}
}
Here the params in the JSON response is an Object type and the attributes of that object are not fixed. It will vary for every request like sometime it can have 3 attributes and sometime it will have none. I don't know how to create my POJO class for this requirement. Can anybody suggest me a way how to do it?
Unless you don't need anything special, you should design it as like:
public class Student{
private long id;
private String name;
//getters and setters
private Map<String, String> parameters = new HashMap<>();
public void add(String key, String value) {
parameters.put(key, value);
}
public void addAll(Map<String, String> map) {
parameters.putAll(map);
}
}
If you need type safety then the design is little bit complicated a consider using something like:
class StudentParameters {
long param1;
String param2;
}
and Student:
public class Student{
private long id;
private String name;
//getters and setters
private StudentParameters studentParameters;
public setStudentParameters(final StudentParameters studentParameters) {
this.studentParameters = studentParameters;
}
}
Do not create complex hierarchies e.g Map<List<List>, List<List>> it will complicate whole structure.
How do I find all the person which are having city ="XYZ" in Address collection
public class Person {
#Id
private String id;
private String description
#DBRef
private Address address;
// Getters and Setters
}
public class Address
{
#Id
private String id;
private String area
private String city
// Getters and Setters
}
Mongo understands #DBRef as a reference to another document, in this case, an Address document and ultimately when the object is loaded from MongoDB, those references will be eagerly resolved and this will get populated to the user as a HATEOAS friendly link. You will get back a mapped object that looks the same as if it had been stored embedded within your master document.
You can define your repository, which will map the endpoints to your database, for the given object, like PersonRepository defined below as an example:
import com.mycompany.domain.Person;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface PersonRepository extends MongoRepository<Person, String> {
List<Person> findByCity(#Param("city") String city);
}
Another way you could go around this using the query criteria methods is executing two queries.
First query would be to fetch the address documents which have the city = "XYZ". Resolve the ids from the list returned.
Generate another query on the Person entity using the ids from the previous operation.
The following demonstrates this approach
Query addressQuery = new Query(where("city").is("XYZ"));
addressQuery.fields().include("_id");
List<Address> addressList = mongoTemplate.find(addressQuery, Address.class, "address"); // get the addresses list that satisfy the given city criteria
// Resolve the ids for the addresses
final List<ObjectId> addressIds = new ArrayList<ObjectId>(addressList.length);
for(final Address address : addressList) {
addressIds.add(new ObjectId(address.getId()));
}
// Get the Person list using the ids from the previous operation
Query personQuery = new Query(where("address.$id").in(addressIds));
List<Person> list = mongoTemplate.find(personQuery, Person.class, "person");
If you knew the address id before hand you can then use a custom query:
public interface PersonRepository extends MongoRepository<Person, String> {
#Query("{ 'address': {'$ref': 'address', '$id': { '$oid': ?0 } } }")
List<Person> findByAddres(String addressIdAsString);
}
I have the following Play Framework entity (using Morphia for persistence) as part of a generic blogging app:
#Entity
public class Comment extends Model {
...
#Reference
#Indexed
public SiteUser commenter;
public static List<Comment> getLastCommentsByUsers(final List<SiteUser> users) {
final Query<Comment> query ds().createQuery(Comment.class);
query.field(commenter).hasAnyOf(users);
return query.asList();
}
}
SiteUser:
#Entity(noClassnameStored=true)
public class SiteUser extends AbstractUser {
public String realName;
}
AbstractUser:
public class AbstractUser extends Model {
#Indexed(value= IndexDirection.DESC, unique = true)
public String emailAddress;
#Required
public String password;
}
The method getLastCommentsByUsers() is supposed to return all comments by the users in the users parameter, but I always get an empty List back. The reason that Commment is a separate collection is to be able to retrieve last X Comments by certain users across their associated Posts, which isn't possible if the Comment is embedded in the Post collection.
Is there something wrong with my query (should I be using something other than hasAnyOf), or is it a problem with the relationship mapping - should I be using ObjectId instead?
I use the in() method with a list or set and its working perfectly. Here's a snippet:
List<String> keywordList;
List<Product> products = Product.find().field("keywords").in(keywordList).asList();
This should work for collection of embedded or references too.
You should use List<Key<SiteUser>> to query:
public static List<Comment> getLastCommentsByUsers(final List<SiteUser> users) {
final Query<Comment> query ds().createQuery(Comment.class);
query.field(commenter).hasAnyOf(toKeys(users)); // convert to keys
return query.asList();
}
public static List<Key<SiteUser>> toKeys(List<SiteUser> users) {
List<Key<SiteUser>> keys = new ArrayList<Key<SiteUser>>();
for(SiteUser user: users) {
keys.add(ds().getMapper().getKey(user));
}
return keys;
}
Or you can just get the keys by:
List<Key<SiteUser>> keys = ds().createQuery(SiteUser.class).query().filter(...).asKeyList();