Alternatively, if you just want a sorted set, look at zsets.
- Josiah
On Sat, Jul 10, 2010 at 2:57 PM, markc <mcons...@gmail.com> wrote:
> Can general hierarchical trees be modelled with Redis?
>
> If so, are there any examples on how to do so?
>
> --
> You received this message because you are subscribed to the Google Groups "Redis DB" group.
> To post to this group, send email to redi...@googlegroups.com.
> To unsubscribe from this group, send email to redis-db+u...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/redis-db?hl=en.
>
>
You can do that exact thing the "hash" way, or the "set + hash" way,
both of which are very easy. These aren't the only ways to do it,
just the couple ways that made the most sense to me without thinking
too hard about them (and having an idea about what Redis offers).
The hash way:
Each node in your tree represents a level of a menu. It has a
reference to it's parent menu, has references to child names, and
child sub-menus if any. These would be modeled using hashes, and to
pull a single level of the menu, would be a single HGETALL command.
Using Python syntax...
'root': {'name': 'Car Manufacturers', 'label_0':'Acura',
'link_0':'acura', 'label_1':'Audi', 'link_1':'audi', ...}
'acura': {'name': 'Acura Models', 'parent':'root', 'label_0':'MDX',
'link_0':'mdx', 'label_1':'RDX', 'link_1':'rdx', ...}
This method includes everything you want in a single hash for any
level of the menu. No additional queries required. It includes both
links and metadata in hashes.
The set + hash way:
On the database side of things, you are going to be querying for the
items whose parent is given in the parent column. You can do the same
here using sets...
'parent_root': set(['acura', 'audi', ...])
'parent_acura': set(['mdx', 'rdx', ...])
Then you keep your metadata in hashes...
'root': {'name': 'Car Manufacturers', ...}
'acura': {'name': 'Acura Models', 'parent': 'root', ...}
That is... you define your link structure using sets, but your
metadata using hashes. To get a particular level, you pull the set
for that level, then use a pipelined set of HGETALL to pull down all
of the labeling information.
An alternative to set+hash is list+hash, where you replace the sets
with lists, and the ordering of items within the list determines menu
ordering.
Regards,
- Josiah
Cheers,
Pieter
If I want to change the ordering of the child, how to achieve them?
Taking your hash example and assume I want to move 'Audi' before
'Acura' to give it a preference (not respecting lexicographic
ordering). Then I need to rename 'label_n' for all subsequently listed
children here which will be costly and proportional to count of child.
Also, this feature of ordering the children will not be possible with
the sets approach and sets will break the ordering of inserts anyway.
How do you approach this feature?
Ethan.
On Jul 11, 11:12 pm, Josiah Carlson <josiah.carl...@gmail.com> wrote:
Also, remember that these are computers. While *we* may have issues
dealing with reordering a couple hundred entries, computers can and
will do such operations in a few milliseconds (especially when using
pipelines in Redis with a single round-trip). If you have trees that
people are navigating, and there are more than a few dozen items in
any level, then your usability may be lacking. So even if this is an
O(n) operation to reorder stuff, n is probably going to be pretty
small, so it might not matter.
Regards,
- Josiah
However, I am currently trying to solve a problem that can have items
as high as 4-6K and need to move an element from one position to
another. If I need to renumber all of the following entries, it's not
actually efficient in this case. Would have been great if there had
been an 'insert' commend in List. Do you see other efficient ways to
solve this?
Ethan.
The slightly more complicated answer is: break up each level into
things like audi0, audi1, ... but then you have to deal with
merging/splitting as you insert and move entries around.
I do recall someone writing a list insertion method on the client
side. It involved using pushes and pops to rotate the list (pop off
of the left and append to the right, or the reverse), insert an item,
then rotating it back. On the other hand, adding the ability to
insert into the middle of a list wouldn't be that bad. It's only
benefit is that the method of performing insertions is simple, but
it's an O(n) operation to insert in the middle.
Other options:
As an alternative to a list, sorting, etc., one could always use a
zset with the same member name as would be an entry in a set/list,
with the score being the index. To insert an item at position k,
you'd only need to pull the scores for k-1 and k, average them, then
re-insert the item. This will work pretty well until you run out of
precision, but that takes at least 53 insertions per position with
IEEE 754 floating point doubles.
For a completely insane option, you can emulate similar features of a
zset with a set and key/values (sorting the set by the values
referenced by the keys), allowing you to avoid the IEEE 754 precision
issue, but you end up implementing infinite precision floating point
numbers. Renumbering on occasion is not necessary, but probably
desired for memory reclamation on tree levels with high entry churn.
Regards,
- Josiah
Keeping this feature out of design for some more time -- going with
addition/deletion features for the time being.
Ethan.