Additional attributes in N-N mongoid (Like in pivot table) - ruby-on-rails

I have a has_many_and_belongs_to relationship between central and coordinators.
So my mongo documents are represented as below:
central = {
_id: 1,
title: 'First Name',
coordinators: [
BSON[1],
BSON[2],
BSON[3]
]
}
coordinators = [
{
_id: 1,
name: 'Me',
centrals: [BSON[1], BSON[2]]
},
{
_id: 1,
name: 'Mateus'
centrals: [BSON[1]]
},
{
_id: 1,
name: 'Gomes'
centrals: [BSON[1]]
},
]
If I do this:
#central = Central.find(1)
#coordinator = #central.coordinators.find(1)
#coordinator.can_edit = false
It will apply to the coordinators document resulting in this:
coordinator = {
_id: 1,
name: 'Me',
centrals: [BSON[1], BSON[2]],
can_edit: false
}
But what I really want to do is apply this can_edit attribute in the relationship, like in pivot table in RDBMS:
central = {
_id: 1,
titulo: 'First Name',
coordinators: [
{
_id: 1,
name: 'Me',
can_edit: false
},
BSON[2],
BSON[3]
]
}
Only for the central with id 1 I want to aply the can_edit to false.
I have to keep the relation between a Central and Coordinator, but in some situation, I want to have an additional information about that relation, like if I would not allow a coordinator to edit some data only in central with id 1.
How can I do this using mongoid?

The solution for this was create another relation N-N:
Add on central.rb
has_and_belongs_to_many :blocked_coordenadors,
class_name: "Central",
inverse_of: :blocked_centrals
And in coordinator.rb:
has_and_belongs_to_many :blocked_centrals,
class_name: "Central",
inverse_of: :blocked_coordenadors
And to check I do this:
central.blocked_coordenadors.include? coordinator

Related

Typeorm get nested relations without intermediate table

I have Many-to-many relations with custom properties like here
I try to get a post with all category like
PostRepository.findOne({
relations: ['postToCategories.category'],
where: { id: 1},
})
the results like this
{
id: 1,
name: 'post1',
postToCategories: [{id: 1, category: {id: 1, name: "cate1"}}]
}
But i don't want id of intermediate table. I need something like this
{
id: 1,
name: 'post1',
postToCategories: [{category: {id: 1, name: "cate1"}}]
}
or this
{
id: 1,
name: 'post1',
postToCategories: [{id: 1, name: "cate1"}]
}
I don't need id of postToCategories. How to do that thank.

How to load relations with tree entity with typeorm?

I have a Role Entity and a Route Entity which is tree structure, and they're ManyToMany relation.
Now I want to retrive all the roles via RoleRepository.find({relations: ['routes']}), which will load all the roles data as expected, however the routes prop won't automaticlly load it's children data, which looks like:
[{
id: 1,
name: 'route1',
routes: [{
id: 1,
path: '/home'
}]
}]
I've checked all documentation and had no clue to make it.
#Entity()
export class Role {
#PrimaryGeneratedColumn()
public id: number
... some other columns
#ManyToMany(type => Route, route => route.roles)
public routes: Route[]
}
#Entity()
#Tree('materialized-path')
export class Route {
#PrimaryGeneratedColumn()
public id: number
#TreeParent()
public parent: Route
#TreeChildren({ cascade: true })
public children: Route[]
#ManyToMany(type => Role, role => role.routes)
#JoinTable()
public roles: Role[]
}
I think the correct way to get the relations when is a tree entity is making:
await getManager().getTreeRepository(Route).findTrees({ relations: ["roles"] });
To load the children of a relation you can just add the property you want within the relations array like this:
RoleRepository.find({relations: ['routes', 'routes.roles']})
it should give you something like that:
"route": {
"id": 1,
"name": "route1",
"roles": [{
"id": 1,
"name": "role1",
"routes": [{
"id": 1,
"name": "route1"
}, {
"id": 2,
"name": "route2"
}
]
}
]
}

Seeding in Ruby on Rails

I have several deeply nested models and I would like to seed my database. The models are as following: Each restaurant has many menus. Each menu has many categories. Each category has many meals. Currently, my seed looks like this:
restaurant_seed = [
{
name: "KFC",
address: "Sofia",
description: "Fast food.",
phone_number: "88888888"
}
]
menu_seed = [
{
name: 'Spring menu.',
active: true
},
}
name: 'Winter menu.',
active: false
}
]
category_seed = [
{
name: "Dessert",
available_all_day: false,
age_restriction: false,
category_avatar: File.open(File.join(Rails.root, "app/assets/images/desserts.jpg"))
},
{
name: "Salad",
available_all_day: true,
age_restriction: false,
category_avatar: File.open(File.join(Rails.root, "app/assets/images/salads.jpeg"))
}
]
meal_seed = [
{
name: "Shopska salata",
meal_avatar: File.open(File.join(Rails.root, "app/assets/images/shopska_salad.jpg"))
},
{
name: "Shisha",
meal_avatar: File.open(File.join(Rails.root, "app/assets/images/shisha.jpg"))
}
]
However, I do not know how to actually seed the database with that info. The idea is that each restaurant will have all of the menu seeds, each of the menus in each restaurant will have each category from the category seed and so on. Thank you for any suggestions!
Write a method to iterate all seeds and create corresponding records.
def setup_restaurants(restaurant_seed, menu_seed, category_seed, meal_seed)
restaurant_seed.each do |r_seed|
restaurant = Restaurant.create(r_seed)
menu_seed.each do |m_seed|
menu = restaurant.menus.create(m_seed)
category_seed.each do |c_seed|
category = menu.categories.create(c_seed)
meal_seed.each do |mm_seed|
category.meals.create(mm_seed)
end
end
end
end
end

Is there a way to control loading relations in gorm with mongodb?

I'm building a REST api with grails 3 and mongo. I have encountered a problem when i need to marshal an object graph with a bigger depth.
I have the following domains:
class Category extends Resource {
/* other fields */
Category parent
}
class Product extends Resource {
/* other fields */
List<Category> categories
static hasMany = [categories: Category]
}
I have in the database the following structure(simplified for the sake of understanding):
categories:
{name: 'cat1'}
{name: 'cat2', parent: 'cat3'}
{name: 'cat3', parent: 'cat4'}
{name: 'cat4', parent: 'cat5'}
{name: 'cat5'}
product:
{categories: ['cat1', 'cat2']}
I am extending from RestfullController when creating my controllers. I want to be able to get a product and have the categories with parents in the returned json.
I get the following results:
/product/${id}
{
id: '...',
categories: [{
id: '...',
name: 'cat1'
}, {
id: '...',
name: 'cat2',
parent: { id: '...' }
}]
}
/category/cat2id
{
id: '...',
name: 'cat2',
parent: { id: '...' }
}
/category
[{
id: '...',
name: 'cat1'
},{
id: '...',
name: 'cat5'
},{
id: '...',
name: 'cat4',
parent: {
id: '...',
name: 'cat5'
}
},{
id: '...',
name: 'cat3',
parent: {
id: '...',
name: 'cat4',
parent: {
id: '...',
name: 'cat5'
}
}
},{
id: '...',
name: 'cat2',
parent: {
id: '...',
name: 'cat3',
parent: {
id: '...',
name: 'cat4',
parent: {
id: '...',
name: 'cat5'
}
}
}
}]
Why would Category.list() load the whole category object graph, and Category.get(), Product.get() and Product.list() would not load it? Is there a way to control this behaviour ?
The way Grails works is that it will only render associations that have already been loaded from the database, hence why you get some associations rendered and others not.
There is no built in way to control this behaviour other than writing your own marshaller. See http://grails.github.io/grails-doc/latest/guide/webServices.html#renderers

How to compose queries in Neo4j?

I want to define my Neo4j queries in terms of a generic structure (much like GraphQL) and compose them.
For example, here's a simple query with no composition:
chatrooms: {
query: {limit: 10}
fields: {
id: true,
name: true }}
My Neo4j interpreter may look like this:
chatrooms = ({query, fields}) ->
"""
MATCH (c:CHATROOMS)
RETURN #{R.join(' ', R.map(R.concat('c.'), R.keys(fields)))}
LIMIT #{query.limit}
"""
But I run into trouble when I want to compose more deeply nested queries.
For example, what if I want some information about the owner of each chatroom as well? Rather than spend two round-trip HTTP requests, I should be able to query this all at once.
chatrooms: {
query: {limit: 10}
fields: {
id: true,
name: true
owner: {
fields: {
id: true,
name: true}}}}
At this point, I get a little hung up.
I don't know how to make the interpreter generic enough to handle these cases.
I know the query should look something like this.
MATCH (c:CHATROOM)
MATCH (c)<-[:OWNS]-(u:USER)
RETURN c.id, c.name, u.id, u.name
LIMIT 10
Ideally, this query would return something with a similar structure like this:
[
{id: 1, name:'neo4j', owner: {id: 99, name: 'michael'}}
{id: 2, name:'meteor', owner: {id: 100, name: 'chet'}}
]
That would make composition and interpretation of the results much easier, but thats a detail for later.
Lastly, I'm having trouble nesting these queries even deeper.
For example, what if I want some messages for each chatroom as well?
And what if I want some information about the owner of each chatroom?
chatrooms: {
query: {limit: 10}
fields: {
id: true,
name: true
owner: {
fields: {
id: true,
name: true}}
messages: {
query: {limit: 20}
fields: {
id: true,
text: true,
createdAt: true,
owner: {
fields: {
id: true,
name: true }}}}}}
Here's my go at it -- although I'm quite sure its totally wrong.
MATCH (c:CHATROOM)
MATCH (c)<-[:OWNS]-(u:USER)
UNWIND c as room
MATCH (room)-[:OWNS]->(m:MESSAGE)
MATCH (m)<-[:OWNS]-(x:USER)
RETURN collect(m.id, m.text, m.creatdAt, x.id, x.name)
LIMIT 20
RETURN c.id, c.name, u.id, u.name,
LIMIT 10
Anyways, the goal is to be able to specify one giant nested query and be able to run it all in a single HTTP request.
Maybe some parsing of the output will be necessary, but ideally, I will get a comparable data-structure as output as well.
Any ideas?
First it's agains the GraphQL philosophy to have nested structures.
You should use references instead.
e.g.
chatrooms: {
query: {limit: 10}
fields: {
id: true,
name: true
owner: ${owner.id}
messages: {
query: {limit: 20}
fields: {
id: true,
text: true,
createdAt: true,
owner: ${owner.id}
}
}
}
owners: [{id: 1, fields: { id: true, name: true}}]
Why are you using fields property instead of fields at root object?
You can use a sequence and combination of collect({key:value}) or {key:collect(value).
MATCH (c:CHATROOM)<-[:OWNS]-(u:USER)
RETURN {id: c.id, name: c.name, owner: collect({id: u.id, name: u.name})} AS data
LIMIT 10
then with this data:
create (c:CHATROOM {id:1, name:"neo4j"})<-[:OWNS]-(u:USER {id:99,name:"Michael")
create (c:CHATROOM {id:2, name:"meteor"})<-[:OWNS]-(u:USER {id:100,name:"Chet")
running the query you get
+--------------------------------------------------------------------+
| data |
+--------------------------------------------------------------------+
| {id=2, name=meteor, owner=[{id=100, name=Chet}]} |
| {id=1, name=neo4j, owner=[{id=99, name=Michael}]} |
+--------------------------------------------------------------------+
live here: http://console.neo4j.org/r/d41luc
To combine both rows into one you would use WITH and then RETURN but then you wouldn't get the benefit of streaming your data back.
MATCH (c:CHATROOM)<-[:OWNS]-(u:USER)
WITH {id: c.id, name: c.name, owner: collect({id: u.id, name: u.name})} AS row
RETURN collect(row) as data
LIMIT 10
Update
Please use more descriptive relationship-types instead of just :OWNS everywhere.
MATCH (c:CHATROOM)<-[:OWNS]-(u:USER)
MATCH (c)-[:OWNS]->(m:MESSAGE)<-[:OWNS]-(x:USER)
WITH u,c,collect({id: m.id, text: m.text, created_at: m.createdAt, author_id: x.id, author: x.name}) as messages[0..20]
RETURN collect({ id: c.id, room: c.name,
owner_id: u.id, owner: u.name,
messages: messages}) as rooms
LIMIT 10

Resources