How are nested set models traversed in MySQL? - sql

I'm reading this article
and on the part with the query:
SELECT node.name
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND parent.name = 'ELECTRONICS'
ORDER BY node.lft;
I'm wondering how is it travered? What happens step by step? I'm confused, please help.

Well, the database system could execute the query several different ways with the same result, but here's one way to understand what's going on:
Take 2 copies of the nested_category table, one named parent and the other named node. Find the row in parent named ELECTRONICS (the article you link to implies there's only one). The range parent.lft to parent.rgt gives any nodes with ELECTRONICS as an ancestor in the tree, at any depth.
Sorting by node.lft means you'll get the sub-nodes of ELECTRONICS along the left side of the sub-tree first, in an pre-order traversal.
It might be easier to walk through a simpler example to understand this: what if we choose TELEVISIONS instead of ELECTRONICS as the parent:
The 'parent' set has only 1 row, because of [parent.name = 'TELEVISIONS']:
{ name: "TELEVISIONS", lft: 2, rgt: 9 }
The 'node' set has the 4 rows that satisfy [node.lft between 2 and 9] because we can substitute the single lft/rgt values from parent:
{ name: "TELEVISIONS", lft: 2, rgt: 9 }
{ name: "TUBE", lft: 3, rgt: 4 }
{ name: "LCD", lft: 5, rgt: 6 }
{ name: "PLASMA", lft: 7, rgt: 8 }
And, as you can see, the above 4 rows are alread sorted by "lft" values, so to satisfy the query, we just take the name values and we're done.

Related

How to retrieve recursive childs on TypeORM

I've been experiencing some troubles lately trying to figure out a way to load recursively the childs of an entity on TypeORM (v0.3.10 on the moment I asked this question).
Right now, my objective is finding recursively all the childs from a given parentId (itself included).
For example:
Id
ParentId
1
NULL
2
1
3
2
4
NULL
Calling ID=1 should return [1, 2, 3], since the ID itself shall be included, 2 is child of 1 and 3 is child of 2.
Calling ID=2 should return [2, 3], since the ID itself shall be included and 3 is child of 2.
The entity itself looks like this at the moment:
#Entity(TableNamesConstants.GROUPS)
export class Group {
#PrimaryGeneratedColumn()
id: number;
#ManyToOne(() => Group, { nullable: true })
#JoinColumn({ name: 'parentId' })
parent?: Group;
#Column('int', { nullable: true })
parentId?: number;
}
I've tried several ways (Tree entities, trying to translate it from SQL to a TypeORM query following others suggestions, etc.) to no luck to achieve a N-depth query.
Can I have some guidance regarding this? Thanks in advance.

neo4j Parent child relationship of n-levels

I have Parent child relationships of say 10-11 levels like shown below and I need to create a relationship between them as parent child
data format
id,parentid
1,0
2,1
3,2
4,3
5,4
6,5
what I tried so far?
I have used the below code to relate them as parent child
LOAD CSV WITH HEADERS FROM 'file:///parent_child.csv' AS line
MERGE (thisThingHere:employee {id: line.id })
MERGE (parent:Element { id: line.parentid })
MERGE (thisThingHere)-[:PARENT]->(parent)
the result of above code is creating parent child relations but they are relating up to just one level, like shown in the below image (available as a link), I need a way to relate them and display it like a tree eg. 3 is a parent of 4 and 2 is a grand parent of 4 and 1 is great grand parent of 1, can anyone please help me on how can I achieve it?
result of my query above
You need to use the same node label for both parent and child node to construct a tree
LOAD CSV WITH HEADERS FROM 'file:///parent_child.csv'
AS line
MERGE (thisThingHere:Element {id: line.id })
MERGE (parent:Element { id: line.parentid })
MERGE (thisThingHere)-[:PARENT]->(parent)

Mongodb query join optimization

Lets say I have 3 collections:
Grandparents
{
_id:1,
name:old foo
}
Parents
{
_id:2,
grandparentId:1,
name: foo
}
Person
{
_id: 3,
parentId:2,
name: youngfoo
}
How do i optimize a query to find all person where person's grandparent's name = x?
Name is not unique in this case.
What i can think of so far:
1. Query all grandparents with name = x
2. Query all parents where grandparent ids == grandparent ids gotten from step 1
3. Query all persons where parent ids == parent ids gotten from step 2.
Doesn't feel very efficient. Can mongodb experts out there help me? Thanks!
I think you want to do something like this. I did not test this query, but this is what i would try in your place. This is only possible on 3.6 mongodb, because it supports multiple joins. The idea is to join all 3 collections. First join is Parents and Person by Parents id, and Persons "parentsId". Second join is Parents and Grandparents. Then you filter out by grandparent name and you will get a document that contains that grandparent, his son (parent), and his grandson(person). Then you just project the person.
db.Parents.aggregate([
{
$lookup:{
from:"Person",
localField:"_id",
foreignField:"parentId",
as:"Person"
}
},
{
$unwind:"$Person"
},
{
$lookup:{
from:"Grandparents",
localField:"grandparentId",
foreignField:"_id",
as:"Grandparents"
}
},
{
$unwind:"$Grandparents"
},
{$match:{Grandparents.name:"x"}},
{$project:{Person.name:1,Person._id:1}}
}])
I think this will do the trick

How to limit subnodes from each nodes Neo4j Cypher

I am new to Neo4j,I have the following situation
In the above diagram represented a node with label user with sub-nodes having label shops. Each of these sub-nodes have sub-nodes with label items. Each node items has attribute size and the items node is in descending order by size attribute for each node shops as represented in the figure.
Question
I want to get two items node whose size is less than or equal to 17 from each shops .
How to do that? I tried, but its not working the way I need
Here is what I have tried
match (a:user{id:20000})-[:follows]-(b:shops)
with b
match (b)-[:next*]->(c:items)
where c.size<=17
return b
limit 2
Note- These shops node can have thousands of items nodes. So how to find the desired nodes without traversing all thousands of items nodes.
Please help , thanks in advance.
Right now Cypher does not handle this case well enough, I would probably do a java based unmanaged extension for this.
It would look like this:
public List<Node> findItems(Node shop, int size, int count) {
List<Node> results=new ArrayList<>(count);
Node item = shop.getSingleRelationship(OUTGOING, "next").getEndNode();
while (item.getProperty("size") > size && results.size() < count) {
if (item.getProperty("size") <= size) result.add(item);
item = item.getSingleRelationship(OUTGOING, "next").getEndNode();
}
return result;
}
List<Node> results=new ArrayList<>(count*10);
for (Relationship rel = user.getRelationships(OUTGOING,"follows")) {
Node shop = rel.getEndNode();
results.addAll(findItems(shop,size,count));
}
You can avoid having to traverse all items of each shop by grouping them according to size. In this approach, your graph looks like this
(:User)-[:follows]-(:Shop)-[:sells]-(:Category {size: 17})-[:next]-(:Item)
You could then find two items per shop using
match (a:User {id: 20000})-[:follows]-(s:Shop)-[:sells]-(c:Category)
where c.size <= 17
with *
match p = (c)-[:next*0..2]-()
with s, collect(tail(nodes(p))) AS allCatItems
return s, reduce(shopItems=allCatItems[..0], catItems in allCatItems | shopItems + catItems)[..2]
shopItems=allCatItems[..0] is a workaround for a type checking problem, this essentially initializes shopItems to be an empty Collection of nodes.

rails ancestry pagination

I've just followed the Railscast tutorial:
http://railscasts.com/episodes/262-trees-with-ancestry
Is it possible to paginate results from Ancestry which have been arranged?
eg: Given I have the following in my Message controller:
def index
#messages = Message.arrange(:order => :name)
end
Then how would I paginate this as it's going to result in a hash?
Update
I found that if I use .keys then it will paginate, but only the top level not the children.
Message.scoped.arrange(:order => :name).keys
Update
Each message has a code and some content. I can have nested messages
Suppose I have
code - name
1 - Test1
1 - test1 sub1
2 - test1 sub2
2 - Test2
1 - test2 sub1
2 - test2 sub2
3 - test2 sub3
This is how I want to display the listing, but I also want to paginate this sorted tree.
It is possible but I've only managed to do it using two database trips.
The main issue stems from not being able to set limits on a node's children, which leads to either a node's children being truncated or children being orphaned on subsequent pages.
An example:
id: 105, Ancestry: Null
id: 117, Ancestry: 105
id: 118, Ancestry: 105/117
id: 119, Ancestry: 105/117/118
A LIMIT 0,3 (for the sake of the example above) would return the first three records, which will render all but id:119. The subsequent LIMIT 3,3 will return id: 119 which will not render correctly as its parents are not present.
One solution I've employed is using two queries:
The first returns root nodes only. These can be sorted and it is this query that is paginated.
A second query is issued, based on the first, which returns all children of the paginated parents. You should be able to sort children per level.
In my case, I have a Post model (which has_ancestry) . Each post can have any level of replies. Also a post object has a replies count which is a cache counter for its immediate children.
In the controller:
roots = #topic.posts.roots_only.paginate :page => params[:page]
#posts = Post.fetch_children_for_roots(#topic, roots)
In the Post model:
named_scope :roots_only, :conditions => 'posts.ancestry is null'
def self.fetch_children_for_roots(postable, roots)
unless roots.blank?
condition = roots.select{|r|r.replies_count > 0}.collect{|r| "(ancestry like '#{r.id}%')"}.join(' or ')
unless condition.blank?
children = postable.posts.scoped(:from => 'posts FORCE INDEX (index_posts_on_ancestry)', :conditions => condition).all
roots.concat children
end
end
roots
end
Some notes:
MySQL will stop using the ancestry column index if multiple LIKE statements are used. The FORCE INDEX forces mySQL to use the index and prevents a full table scan
LIKE statements are only built for nodes with direct children, so that replies_count column came in handy
What the class method does is appends children to root, which is a WillPaginate::Collection
Finally, these can be managed in your view:
=will_paginate #posts
-Post.arrange_nodes(#posts).each do |post, replies|
=do stuff here
The key method here is arrange_nodes which is mixed in from the ancestry plugin and into your model. This basically takes a sorted Array of nodes and returns a sorted and hierarchical Hash.
I appreciate that this method does not directly address your question but I hope that the same method, with tweaks, can be applied for your case.
There is probably a more elegant way of doing this but overall I'm happy with the solution (until a better one comes along).