I have user and photo documents in Mongodb. Each photo belongs to user and a photo maybe shared among users. Lets say user1 has p1,p2,p3 photos and user2 has p3,p4,p5 photos. If I delete user1 (manually using tools like Compass), p1 and p2 should also be deleted but not p3. How to achieve this and what kind of database structure I need to define?
Currently if I delete user1, no photos are deleted and remain in databse which now makes the database corrupted from the point of view of the application using the database.
Its Spring Boot app and User and Photo are declared as:
import lombok.Builder;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
#Document
#Data
#Builder
public class User {
#Id
private String id;
#DBRef
private Set<Photo> photos;
private String name;
}
#Document
#Data
#Builder
public class Photo {
#Id
private String id;
private String fileName;
}
As mentioned by m4gic and in the questions he linked (here and here), MongoDB doesn't support cascading deletes. In your situation you should probably create an array in the User object, and put the complete child documents into that array instead of keeping them in their own collection. That way they will be deleted together with the parent, because they are a part of it.
MongoDB doesn't support for cascade delete as of now. As you are already storing the ref photos in the User model, you can get the photo ids from the reference list and delete the photos together. Or instead of storing the photos in the separate collection you can have the array of Photos embedded into the user object.
You can refer to this link as well:
What is the recommended equivalent of cascaded delete in MongoDB for N:M relationships?
#mindcraft is right, but if you wanted to keep photos in separate collection then you can add access property to the photo document like
{
ref: 'https://....',
access:[user1._id, user2._id]
}
You can then query like -
db.photos.find({access:{$in:[user1._id]}})
Although separate collection specifically for photos will not help a lot. Instead try putting photo urls in array
Related
I have the following entities with relation one to many - one folder can have many files.
folder
{
string id;
string name;
string owner;
}
file
{
string id;
string name;
}
How can I create this relation by taking into consideration the following rules?
The folder is a separate entity, which can be updated or displayed on its own, so it can't be part of the file document, it must remain in separate collection.
When I query all the the files, I want them to be sorted by the folder.name. This query must have pagination.
I want to display only files that are in folders owned by the caller.
As I don't want to use join in document database, and I want to get all the data in one query, I was thinking about copying the folder document in the file:
file
{
string id;
string title;
Folder folder;
}
This way, I can query by including all the listed rules. And I think this would be perfect solution, if the folder name was static, but the folder.name is something that the user can update. And if the folder.name is updated, the relation in the file will remain with invalid/old data.
So, what is the correct approach in this situation? Should I implement a mechanism for updating the relation ones the folder.name is updated?
Update:
I am expecting, a lot of queries for listing of the files, and less updates of the folde.names. The most important think for me, is that I need a design that will allow me to query, sort and use pagination on the files collection. So, at this point I am researching what are the options with their pros and cons.
I am working with 2 MongoDB collections, Recipe, and Menu. A single Menu is a combination of Recipe. Refer the below code segment for more information
#Document
public class Recipe {
private String id;
private String name;
private String description;
// getter and setter
}
#Document
public class Menu {
private String id;
private String name;
private List<RecipeItem> recipeItem;
// getter and setter
}
public class RecipeItem {
private String id;
private String name;
private String description;
// getter and setter
}
RecipeItem is just a copy of the Recipe object which is referred within the Menu collection
When the Menu collection is saved, you can add recipes to the menu and therefore a list of Recipe objects will also be saved within the Menu collection in the name of RecipeItem. When any of the Recipe is updated, the corresponding RecipeItem which is in the Menu is also required to be updated. Otherwise, the recipe within the Menu becomes outdated compared to the current Recipe after updating. So I have to iterate Menu collection which contains the updated Recipe by Id and needs to update the recipe information within the Menu collection.
So the update Menu function will initiate multiple transactions within the single execution and therefore we are in a need of a rollback mechanism as well. So I am not very fond of this approach.
I am new to MongoDB and I want to verify whether the current database design of Menu and Recipe is correct or incorrect? If yes what will be the optimal way of doing it? I know that use a DB ref between collections can be used, but there is a performance impact on it.
The Menu document should store a list of Recipe s IDs rather than the recipes themselves. Then you can dispense with RecipeItem and use Recipe directly.
It would seem more sensible that a Recipe consists of RecipeItems (Apple tart consists of flour, sugar, eggs, apples etc.).
In any case a reference would remove the need to keep two lists in sync.
Suppose I have a CRUD application that lets a user manage their album collections:
class Collection
{
int Id;
string Name;
List<Album> Albums //EF navigation property
}
class Album
{
int Id;
List<CdCase> Cases; //EF navigation property
string Name;
string Artist;
}
In this application, I let users add, edit, and delete albums to their collections. The collections and albums are stored in a SQL Server database using Entity Framework.
Naively implemented, there are going to be a lot of duplicate albums, so I'd like to do the following at collection-save time:
Deduplication: If an album already exists (as determined by Name/Artist equality), the collection uses that album instead of creating a new one
Immutability: If an album is edited, the edits are not persisted to the server. Instead, the album is removed from the case and a new one is created/linked.
Garbage Collection: If an edit/delete operation results in an album no longer being in any collection, it is deleted from the database entirely.
Is there a way to implement this logic at the DBContext level (i.e. changing the Set<Collection> behavior), rather than manually cleaning up the albums before submission?
EDIT I am using code-first EF 6.1.
I annotate a document with #Index(unique = true) like so:
public class ADocumentWithUniqueIndex {
private static final long serialVersionUID = 1L;
#Indexed(unique = true)
private String iAmUnique;
public String getiAmUnique() {
return iAmUnique;
}
public void setiAmUnique(String iAmUnique) {
this.iAmUnique = iAmUnique;
}
}
When saving the object, I specify a custom collection:
MongoOperations mongoDb = ...
mongoDb.save(document, "MyCollection");
As a result I get:
A new document in "MyCollection"
An index in the collection "ADocumentWithUniqueIndex"
How can I create the index in "MyCollection" instead without having to explicitly specify it in the annotation?
BACKGROUND:
The default collection name is too ambiguous in our use case. We cannot guarantee, that there wouldn't be two documents with the same name but in different packages. So we added the package name to the collection.
Mapping a document to a collection is dealt with in an infrastructure component.
The implementation details like collection name etc. shouldn't leak into the individual documents.
I understand this is a bit of an "abstraction on top of an abstraction" smell but required since we had to support MongoDb and Windows Azure blob storage. Not anymore though...
This seemed like a fairly standard approach to hide the persistence details in a infrastructure component. Any comments on the approach appreciated as well.
It's kind of unusual to define the collection for an object to be stored and then expect the index annotations to work. There's a few options you have here:
Use #Document on ADocumentWithUniqueIndex and configure the collection name manually. This will cause all objects of that class to be persisted into that collection of course.
Manually create indexes via MongoOperations.indexOps() into the collections you'd like to use. This would be more consistent to your approach of manually determining the collection name during persistence operations.
I'm still trying to get my hands around mongodb and how best Entities can be mapped. if you take for example: the entity user and the entity addresses. there could be one-to-many when someone is coming from jpa background. Here in mongo i don't want to use dbref. So addresses are in a Set collection in user.
Supposing i was using spring-data-mongo:
Question 1 : should both User and Address have the #Document annotation?Or just User?
Question 2 : what is the best way to query for addresses of a user. It is possible at first place? Because right now I query to get the User by username or Id and then get the addresses of the user.Can I query directly for sub-document? if yes how is it done using spring-data-mongo Criteria Query:
#Document
public class User{
#Id
private Long ID;
private String username;
private Set<Address> addresses = new HashSet<Address>();
...
}
#Document
public class Address {
#Id
private Long ID;
private String city;
private String line1;
...
}
Question 1: No, #Document is not strictly necessary at all. We just leverage this on application startup if you activate classpath-scanning for document classes. If you don't the persistence metadata scanning will be done on the first persistence operation. We traverse properties of domain objects then, so Address will be discovered.
Question 2: You'll have to read the User objects entirely as MongoDB currently does not allow returning sub-documents. So you'll have to query for the entire Userdocument but can restrict the fields being returned to the addresses field using a fieldSpec on the Query object or the repository abstraction's #Query annotation (see ref docs).