I'm new to mongoDB, and I wonder how I can translate tables relations in MongoDB.
For example in my MySQL base, I have two tables : Manager and Employee.
Manager {
name: String,
ID: Int Primary Key
}
Employee {
name: String,
ID: Int Primary Key,
Manager: int Foreign key (Manager.ID)
}
I would like to know what is the correct way to implement this with MongoDB.
MongoDB is a non-relational document database.
'Document' is a record in a collection and each document can have any number of fields, including arrays, maps, etc. The strength and weakness of this are that two documents in the same collection may hold completely different fields. Normal forms of relational databases do not apply. Also, each document has a unique id.
How to link documents/collections together is completely up to the app logic.
Few options:
Keep everything together in once collection.
//
{
name: "Jon Appleseed",
manager: "Jonh Bosch"
},
{
name:"Another employee",
manager: "Jonh Bosch"
}
Use two collections as suggested by #iprakashv
Use a mixed approach:
// Employees
{
_id: "_234234",
name: "Jon Appleseed",
manager:{
name:"John Bosh",
id: _12321
}
},
{
_id: "_233234",
name: "Another employee",
manager:{
name:"John Bosh",
id: "_12321"
}
}
// Managers
{
_id: _123213,
name:"John Bosh",
subordinates:
[
{name:"Jon Appleseed", id: "_234234" },
{name:"Another employee", id: "_12321" },
]
}
As you see, you gain much more flexibility at the expense of normalization.
As mongodb is a NoSQL database for schema-less and relation-less collections (though you can put validations over a collection), join-less schema and nested objects make more sense here.
Now as per our requirement, each Manager object is linked to its employee so, should ideally have an array containing all its employee objects, but, it is not practical when we need all the employees irrespective of the Manager.
So, we may want to have just the array of employee id's instead of an array of employee objects and employee collection (table) to be maintained separately to fetch employee objects.
The samples documents for the above schema would look like:
//employee collection document
{
"ID": "E12334",
"name": "John Doe"
}
//manager collection document
{
"ID": "M453",
"name": "Jane Doe",
"employeeIDs": ["E12334", "E12343"]
}
Related
I have come to postgraphile yesterday from a couple of years with Hasura. I was stunned at what PG does out of the box. My main sense of loss is around the GQL api that PG offers, relative to Hasura. If I have Customer with a foreign key to an Address, and I add the nested mutations plugin, I can say this:
mutation MyMutation {
createCustomer(
input: {customer: {addressToAddressId: {create: {line1: "5", line2: "The Street"}}, firstName: "Breda", lastName: "Smith"}}
) {
address {
id
}
customer {
id
}
}
}
But with Hasura I would be able to say something like :
mutation MyMutation {
createCustomer(input:{firstName: "Breda", lastName: "Smith", address:{line1: "5", line2: "The Street"}}) {
address {
id
}
customer {
id
}
}
}
How can I configure PG so it can do this kind of mutation?
I asked on the postgraphile discord channel, and Benjie replied:
It’d be possible to write a plugin to do this, but I’m not a big fan of CRUD mutations in general. I’d do it this way:
The first way to consider adding a mutation to your GraphQL schema is with a Custom Mutations. These create individual highly specialised mutations from the VOLATILE functions in your PostgreSQL database. There's a checklist of requirements they must meet defined in their documentation .
If this doesn't suit your needs, you may also using makeExtendSchemaPlugin or have a look through the community pluginsto see if one suits your needs. Relevant reading on GraphQL mutation design
I have an database full of objects that contain information, as well as other arrays of objects. I would like to change the inner arrays to only be arrays with each index as an ObjectId type with their respective ObjectId
I am using the mongoose populate function to retrieve this information later in the program. So only the ObjectId is needed for the reference.
job {
_id: 1,
name: "name",
parts: [
{ _id: ObjectId("5c790ce7d3dc8d00ccc2688c"), name: "name"},
{ _id: ObjectId("5c790ce7d3dc8d00ccc2688b"), name: "name"},
{ _id: ObjectId("5c790ce7d3dc8d00ccc2688a"), name: "name"},
]
}
Desired Result
job {
_id: 1,
name: "name",
parts: [
ObjectId("5c790ce7d3dc8d00ccc2688c"),
ObjectId("5c790ce7d3dc8d00ccc2688b"),
ObjectId("5c790ce7d3dc8d00ccc2688a")
]
}
I tried a few mongoDB queries from the command line but none of them are quite giving the result I need. This one has no errors, but it doesn't seem to change anything.
db.jobs.update(
{},
{
$set: {"parts.$[element]": "element._id"}
},
{
multi: true,
arrayFilters: [{ "element": {} }]
}
)
I'm not sure if this is possible or not using only the mongo shell.
Mongo v4.2 introduced pipelined updates this allows the use of aggregation operators within the update and more importantly updating a document using it's own values. which is what you want to achieve.
db.jobs.updateMany(
{},
[
{
'$set': {
'parts': {
'$map': {
'input': '$parts',
'as': 'part',
'in': '$$part._id'
}
}
}
}
]);
Unfortunately this is not possible for earlier Mongo versions. Specifically $arrayFilters allows you to filter an array but again prior to the new update syntax accessing own values within the update is not possible.
You'll have to iterate over all documents and do the update one by one in code.
As #Tom Slabbaert mentioned in the other answer you will have to use updates with aggregation pipelines available from v4.2 if you want to update the documents in bulk in one operation.
As an alternative to using $map, if you want only a single value at the top level and the value is accessible using the dot notation. You can simply use $set with the dot notation.
db.jobs.updateMany({}, [{
$set: { parts: "$parts._id" }
}])
I have a table with an one-to-one self relationship to represent an hierarchical structure. Below are the diagram of the Table and a few sample data:
This is the Location model code for the relationship:
class Location extends Model
{
public function location()
{
return $this->hasOne('App\Location');
}
}
I would like to query the Locations table and send a JSON response like the example below but I'm not sure how to approach the Query:
{
"id": 1,
"name": "Country",
"location_id": null
"location": {
"id": 2,
"name": "State",
"location_id": 1
"location": {
"id": 3,
"name": "City",
"location_id": 2
}
}
}
To always eager load the location relation and not worry about depth, you could add the $with property to your location model:
protected $with = ['location'];
Now when you return a location, it will have all nested sub locations loaded up:
return App\Location::find(1);
Laravel also handles returning this as JSON if it's returned from a controller method.
as pointed out by Kyslik, this presents a potential N+1 problem, potential depth along with query size and the rest of the environment should be considered when deciding to use a solution like this in production, for something like locations, it's probably not an issue and well worth the simlpicity
I am mapping my database using gorm.
I have two tables (service and resource) with a many-to-many relationship. I am modelling them in code as such:
type Service struct {
BaseModel
Name string `gorm:"not null;unique_index"`
Resources []Resource `gorm:"many2many:service_resource"`
}
type Resource struct {
BaseModel
Name string `gorm:"not null;unique_index"`
}
Using gorm's AutoMigrate the following tables are created:
(I also executed a raw SQL query to add the id primary key in the mapping table.)
To create a new service, I use the following code:
service := Service{
Name: "core",
Resources: []Resource{
{Name: "ec2"},
{Name: "dynamo"},
},
}
db.Create(&service)
This creates all the resources along with the service and fills in the relationship between them in the service_resource table just fine, as expected.
However, my problem is when I'm querying for the services. I use the following code to retrieve all services:
services := []model.Service{}
db.Find(&services)
This returns successfully with the services array populated, but the Resources array of each service is empty:
"services": [
{
"ID": 1,
"Name": "core",
"Resources": null
},
...
]
I was under the assumption that gorm would populate it automatically. Is there some step that I'm missing?
You need to Preload the resources field before querying for services:
services := []model.Service{}
db.Preload("Resources").Find(&services) // error checking ommited
This correctly populates the Resources field of every service.
I'm designing REST API for many resources I need to maintain at least 2 representations of the same format (e.g. JSON) which only differ in the amount of data they expose.
For example, I have books, publishers and authors which I'm exposing via REST. So I have 3 collection of resources:books, publishers and authors.
When requesting a book I want it to include minimum information about its authors and publishers - just names, ids and hyperlinks to their full representation.
GET /books/12345
200 OK
{
id: "12345",
title: "My Book",
authors: [
{id:"111", name:"J. Smith", _href:"/authors/111"},
{id:"222", name:"J.R.R. Tolkien", _href:"/authors/222"}
],
publisher: {id:"333", name:"HarperCollins", _href:"/publishers/333"},
}
When user requests, e.g. /publishers/333 it should get more info about it, not just name and id:
{
id: "333",
name: "HarperCollins",
address : { ... },
contact : { ... },
logo : { ... }
}
In my application I have 3 classes Book, Publisher and Author, where Book contain publisher and collection of authors. And I'm using Jackson and Spring MVC to expose all the entities as JSON via REST API.
#RequestMapping("/books/{id}")
#ResponseBody
public Book getBook(String id) {
return getBookById(id);
}
#RequestMapping("/authors/{id}")
#ResponseBody
public Book getAuthor(String id) {
return getAuthorById(id);
}
Now the questions:
How would I tell the view which fields of which object should be rendered in which case?
Should I instead split each my entity into two - one for short and another for full representation (e.g. AuthorReference and Author, PublisherReference and Publisher).
Should I prefer composition or generalization if I go with option #2?
Is there any best practice how to deal with different detalizations of the same entity representation in REST applications?
Have you considered using a more standard hypermedia format, such as HAL?
Doing so, it becomes pretty clear that you would have a difference between resources, and links to other resources. For example, it might end up as:
GET /books/123
200 OK
{
"id": "123"
"title": "My Book"
"_links": {
"author": [
{ "href": "/authors/111", "title": "J. Smith" },
{ "href": "/authors/321", "title": "Jules Verne" }
],
"publisher": { "href": "/publishers/123", "title": "Harper Collins" }
}
}
In terms of implementation, I would say you can either:
Create custom DTO classes for each resource representation, along the lines of:
public class BookDto {
#JsonProperty(value="_links")
public LinkCollection getLinks() { }
public String getTitle() { }
}
Use the same classes, but use filters to modify the objects before Jackson serializes, and use some combination of #JsonProperty/#JsonIgnore.
1/ According to Spring MVC documentation, to serialize your object over HTTP you have to define your own class implementing HttpMessageConverter<T> with T either Book, Publisher or Author.
http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/remoting.html#rest-message-conversion
2/ It is not necessary to split your objects in two. The logic to select what fields you pick up from an Author is contained in the AuthorToHttp implement HttpMessageConverter<Author>. Just need to develop a AuthorReferenceToHttp implement HttpMessageConverter<Author> to use when you want less data to be send over HTTP.
Another solution could be either AuthorReference extend Author or Author extend AuthorReference on one hand and AuthorToHttp implement HttpMessageConverter<Author> with AuthorReferenceToHttp implement HttpMessageConverter<AuthorReference> on the other hand to serialize your Author.
3/ Inheritance/Generalization is enough. Not necessarily a need for a pattern.
Composite is a wrong choice, you don't need a tree structure. According to circumstances you want either all data about an author - default behavior -, or the main data about an author - "proxied" behavior -. Proxy would be a better choice.
4/ No idea.