Spring MongoDB: Auditing one-to-many relation subdocument - mongodb

I am currently working on a self-taught project, thus I am a beginner in MongoDB and I am struggling to audit a subdocument in an one-to-many relation. For some reason none of the new records inserted in this collection is being audited, although audit is working fine for all the other collections.
Below is the structure of the collections in my project:
User document is the main Document - auditing ok
Provider is an embedded subdocument in User (One-to-One) - auditing ok
Address is an embedded set of documents in Provider (One-to-Many) auditing fail
public class User extends Audit<String>{
#Id
private String id;
private String email;
private String firstName;
private String lastName;
private String salt;
private String password;
private String role;
private Boolean isVerified;
private String userTempCode;
private LocalDateTime deactivationDate;
private Provider provider; // This subdocument gets audited no problem
...
public class Provider extends Audit<ObjectId>{
private ObjectId id = new ObjectId();
private LocalDate dob;
private String phone;
private Double price;
private Object geoLocation;
private Set<WeekDays> days;
private Set<TimeRange> hours;
private Set<Address> addresses; // Here is where I am having trouble, the createdBy, createdDate and so on, are not working
private String userId;
private LocalDateTime deactivationDate;
private Set<Reviews> ratings;
....
public class Address extends Audit<ObjectId>{
#Id
private ObjectId id = new ObjectId();
private String street;
private String street2;
private String city;
private String province;
private String country;
private String postalCode;
...
//Below My Audit class inherited by all documents
#Data
public abstract class Audit<T> implements Persistable<T> {
#CreatedBy
private String createdBy;
#CreatedDate
private LocalDateTime createdDate;
#LastModifiedBy
private String lastModifiedBy;
#LastModifiedDate
private LocalDateTime lastModifiedDate;
#Version
public Integer version;
}
So, why is my Set< Address> not being audited whereas the other documents are ok, am I missing something here?
Thank you!

Related

Unable to override Auditable fields - createdAt

I've access_tokens collection and here is the corresponding class:
public class AccessToken extends Auditable {
#Id
private String id;
#NotBlank
private String token;
#DBRef
#NotBlank
private User user;
#NotBlank
private String origin;
public void setOrigin(String origin) {
this.origin = JwtUtils.getOrigin(origin);
}
#Indexed(expireAfter = "1d")
private LocalDateTime createdAt;
}
And here is the Auditable class:
public class Auditable {
#CreatedDate
private LocalDateTime createdAt;
#LastModifiedDate
private LocalDateTime updatedAt;
#CreatedBy
private ObjectId createdBy;
#LastModifiedBy
private ObjectId updatedBy;
}
The reason I'm defining createdAt in AccessToken is, I want to create a TTL index (basically I want to delete all access tokens after one day). But I'm getting following error:
Caused by: org.springframework.data.mapping.MappingException: Ambiguous field mapping detected! Both #org.springframework.data.annotation.CreatedDate()private java.time.LocalDateTime com.iof.models.Auditable.createdAt and private java.time.LocalDateTime com.iof.models.AccessToken.createdAt map to the same field name createdAt! Disambiguate using #Field annotation!
How can I solve this?

How to update few fields in mongodb by using MongoRepository?

I have a User POJO having fields:
#Id
private String _id;
private String phone;
private String email;
private String password;
private String userName;
private String dob;
private String gender;
private String city;
private String pincode;
private String status;
private String validUpto;
private List<String> userRole;
private String persona;
I saved all the fields in MongoDB (document).
Now I want to update only few fields like city, Pincode.
I also refer this question, but it is not giving the answer via MongoRepository.
is there any way we can update only few fields via MongoRepository instead of MongoTemplate.
The repository doesn't provide an 'update' operation only .save(object);
But you can update it by retrieving the Object from the repository, change the relevant fields. Afterwards, you save the updated object to the repository.
Which will get you the desired result of 'updating'.
Spring-boot/SpringRepository example.
#Autowired
UserRepository userRepository;
#Test
public void testUpdateUser() throws Exception {
User foundUser = userRepository.findById("1");
foundUser.setCity("Helsinki");
// foundUser.setOtherFields("new values");
userRepository.save(foundUser); // Will 'update' but it essentially replaces the entity in database
}

Spring data MongoDB match, lookup and projection to select only required field from looked-up document

I have below two Document structures. In the structure CRMContact.orgGroupId == OrganizationGroup.id. I would like to fetch all the CRMContact document that matches with sharedGroupIds and also select only a few fields from CRMContact and only OrganizationGroup.groupownername from OrganizationGroup and match/populate groupId (with only one field [groupownername] populated). I have used below custom implementation but didn't work.
I have included aggregarionsNotWorking which is not working and aggregarions returning entire OrganizationGroup populated. How to achieve this i.e. just to populate groupownername field, using spring data mongodb?
#Document(collection = "ww_crm_contact")
public class CRMContact{
#Id
protected String id;
private String displayName;
private String firstName;
private String middleName;
private String lastName;
private OrganizationGroup groupId; //Ignore //Modified field name orgGroupId
#Indexed(name = "CRMCONTACT_SHAREDGROUPID_IDX",background = true)
private List<String> sharedGroupIds = new LinkedList<>();
#Indexed(name = "CRMCONTACT_ORGGROUPID_IDX",background = true)
private String orgGroupId;
}
#Document(collection = "ww_organization_groups")
public class OrganizationGroup {
private static final long serialVersionUID = 600049975643062552L;
#Id
protected String id;
private String groupName;
private int riaId;
private Boolean isPrivate;
private String description;
private Boolean deleted;
#Transient
private int count;
private String groupownerid;
private String groupownername;
}
#Repository
public class CustomCRMContactDAO {
#Autowired
MongoTemplate mongoTemplate;
public List<CRMContact> getContactsPresentInGroup(List<ObjectId> objectIds){
LookupOperation lookupOperation = LookupOperation.newLookup().from("ww_organization_groups").localField("orgGroupId").foreignField("_id").as("groupId");
ProjectionOperation fields = project("firstName","lastName", "primaryId","displayName","groupId.groupownername");
Aggregation aggregarionsNotWorking = Aggregation.newAggregation(Aggregation.match(Criteria.where("sharedGroupIds").in(objectIds)),lookupOperation,unwind("groupId"),fields); //Not Working even if I change the field only to groupownername
Aggregation aggregarions = Aggregation.newAggregation(Aggregation.match(Criteria.where("sharedGroupIds").in(objectIds)),lookupOperation,fields); //
List<CRMContact> crmContacts = mongoTemplate.aggregate(aggregarions, "ww_crm_contact",CRMContact.class).getMappedResults();
return crmContacts;
}
}

How to map existing JPA entities to PicketLink

I am trying to migrate a Seam 2 app to CDI and use PicketLink for security. After all the reading and researching, it seems like all the examples are having one to one mapping between PicketLink model and the backend entity. e.g. Account to AccountEntity, Partition to PartitionEntity. Since I already have entities in place representing identity model, I am stuck on trying to map them to PicketLink. Here is what I have:
#MappedSuperClass
public class ModelEntityBase implement Serializable {
#Id #Generated
Long id;
Date creationDate;
}
#Entity
public Account extends ModelEntityBase {
String username;
String passwordHash;
#OneToOne(mappedBy = "account")
Person person;
}
#Entity
public Person extends ModelEntityBase {
String name;
String email;
#OneToOne
#JoinColumn(name = "account_id")
Account account;
}
Two entities (plus a super class) representing a single identity model in PicketLink, e.g. stereo type User.
Based on this why IdentityType id is String not Long, I tried to add a new Entity in:
#Entity
#IdentityManaged(BaseIdentityType.class);
public class IdentityTypeEntity implement Serializble {
#Id #Identifier
private String id;
#OneToOne(optional = false, mappedBy = "identityType")
#OwnerReference
private Account account;
#IdentityClass
private String typeName;
#ManyToOne #OwnerReference
private PartitionEntity partition;
}
I've tried a few different ways with the annotation and model classes. But when using IdentityManager.add(myUserModel), I just can't get it to populate all the entities. Is this even possible?
Got help from Pedro (PicketLink Dev). Post the answer here to help others.
This is the model class I ended up using.
#IdentityStereotype(USER)
public class User extends AbstractAttributedType implements Account {
#AttributeProperty
private Account accountEntity;
#AttributeProperty
#StereotypeProperty(IDENTITY_USER_NAME)
#Unique
private String username;
#AttributeProperty
private boolean enabled;
#AttributeProperty
private Date createdDate;
#AttributeProperty
private Date expiryDate;
#AttributeProperty
private Partition partition;
// getter and setter omitted
}
And created a new entity to map to this model:
public class IdentityTypeEntity implements Serializable {
#Id
#Identifier
private String id;
#OneToOne(optional = false, mappedBy = "identityType",
cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#AttributeValue
// #NotNull
private HAccount accountEntity;
#IdentityClass
private String typeName;
#ManyToOne
#OwnerReference
private PartitionEntity partition;
#AttributeValue
private String username;
#AttributeValue
// #Transient
private boolean enabled;
#AttributeValue
private Date createdDate;
#AttributeValue
private Date expiryDate;
}
PL can map property with #AttributeProperty to entity property with #AttributeValue. But it can only map to one entity. Therefore there is no way to map, say User and its properties over to Account and Person. But you can have the entity (in my case accountEntity) in the model. I also have to duplicate a few fields in the new IdentityTypeEntity and my existing Account entity (username, eanbled, createdDate) because PL requires these. Use a #PrePersist and similar to sync them.

Mapping & fetching entities with composite keys using spring data JPA

I have Spring MVC and Data JPA. I am trying to do mapping tables I have. The structure is like below:
Device
--------------
PK deviceId
deviceName
Setting
--------------
PK deviceId
PK packageName
PK name
value
And I have classes for those tables:
#Entity
public class DeviceSetting implements Serializable {
#EmbeddedId
private String deviceId
private String deviceName;
#ManyToOne
#JoinColumn(name="deviceId", referencedColumnName="deviceId", insertable=false, updatable=false)
private Device device;
//Setters and Getters
}
#Embeddable
public class DeviceSettingPk implements Serializable {
private String deviceId;
private String packageName;
private String name;
public DeviceSettingPk(){}
public DeviceSettingPk(String deviceId, String packageName, String name) {
super();
this.deviceId = deviceId;
this.packageName = packageName;
this.name = name;
}
//Setters and Getters
}
#Entity
public class Device implements Serializable {
private static final long serialVersionUID = 1L;
#NotEmpty
#Id
#Column(name="deviceId")
private String deviceId;
private String deviceName;
//Getters and Setters
}
But I did not get device data when I put device and setting data has same deviceId and queried DeviceSetting by using repository.findOne(deviceId);
What else do I need to do to get the device data? Any advice would be helpful. Thanks.
Change the mapping as follows.
#Entity
public class DeviceSetting implements Serializable {
#EmbeddedId
private DeviceSettingPk deviceSettingId;
#ManyToOne
#JoinColumn(name="deviceId", referencedColumnName="deviceId", insertable=false, updatable=false)
private Device device;
//Setters and Getters
}
And then use the following code to lookup.
DeviceSettingPk id = new DeviceSettingPk(deviceId, packageName, name);
// use the deviceSettingRespository to lookup
repository.findOne(id);