Re-ordering child nodes in django-MPTT - django

I'm using Ben Firshman's fork of django-MPTT (hat tip to Daniel Roseman for the recommendation).
I've got stuck trying to re-order nodes which share a common parent. I've got a list of primary keys, like this:
ids = [5, 9, 7, 3]
All of these nodes have a parent, say with primary key 1.
At present, these nodes are ordered [5, 3, 9, 7], how can I re-order them to [5, 9, 7, 3]?
I've tried something like this:
last_m = MyModel.get(pk = ids.pop(0))
last_m.move_to(last_m.parent, position='first-child')
for id in ids:
m = MyModel.get(pk = id)
m.move_to(last_m, position='right')
Which I'd expect to do what I want, per the docs on move_to, but it doesn't seem to change anything. Sometimes it seems to move the first item in ids to be the first child of its parent, sometimes it doesn't.
Am I right in my reading of the docs for move_to that calling move_to on a node n with position=right and a target which is a sibling of n will move n to immediately after the target?
It's possible I've screwed up my models table in trying to figure this out, so maybe the code above is actually right. It's also possible there's a much more elegant way of doing this (perhaps one that doesn't involve O(n) selects and O(n) updates).
Have I misunderstood something?
Bonus question: is there a way of forcing django-MPTT to rebuild lft and rght values for all instances of a given model?

I think this is an artefact of a failure in MPTT that I've mentioned before - when you move nodes around, it correctly updates the instance of the node you're moving, but it doesn't update the instance of the target (although it does get updated in the database).
The consequence of this is that in your code, each m gets moved to the right of last_m - but the values in the last_m still reflect the position before the move, so the next move uses the original lft/right values instead of the new post-move ones.
The solution is to reload last_m each time:
for id in ids:
last_m = MyModel.objects.get(pk=last_m.id)
m = MyModel.get(pk = id)
m.move_to(last_m, position='right')

Related

how to deal with virtual index in a database table in Django + PostgreSQL

Here is my current scenario:
Need to add a new field to an existing table that will be used for ordering QuerySet.
This field will be an integer between 1 and not a very high number, I expect less than 1000. The whole reasoning behind this field is to use it for visual ordering on the front-end, thus, index 1 would be the first element to be returned, index 2 second, etc...
This is how the field is defined in model:
priority = models.PositiveSmallIntegerField(verbose_name=_(u'Priority'),
default=0,
null=True)
I will need to re-arrange (reorder) the whole set of elements in this table if a new or existing element gets this field updated. So for instance, imagine I have 3 objects it this table:
Element A
priority 1
Element B
priority 2
Element C
priority 3
If I change Element C priority to 1 I should have:
Element C
priority 1
Element A
priority 2
Element B
priority 3
Since this is not a real db index ( and have empty values), I'm gonna have to query for all elements on database each time a new element is created / updated and change priority value for each record in table. Not really worried about performance since table will always be small BUT, I'm worried this way to proceed is not the way to go or simply it generates too much overhead.
Maybe there is simpler way to do this with plain SQL stuff? If I use an index though, I will get an error every time an existing priority is used, something I don't want either.
Any pointers?
To insert at 10th position all you need is a single sql query:
MyModel.objects.filter(priority__gte=10).update(priority=models.F('priority')+1)
Then you would need a similar one for deleting an element, and swapping two elements (or whatever your use case requires). It all should be doable in a similar manner with bulk update queries, no need to manually update entry by entry.
First, you can very well index this column, just don't enforce it to contains unique values. Such standard indexes can have nulls and duplicates... they are just used to locate the row(s) matching a criteria.
Second, updating each populated* row each time you insert/update a record should be looked at based on the expected update frequency. If each user is inserting several records each time they use the system and you have thousands of concurrent users, it might not be a good idea... whereas if you have a single user updating any number of rows once in a while, it is not so much an issue. On the same vein, you need to consider if other updates are occurring to the same rows or not. You don't want to lock all rows too often if they are to be updated/locked for updating other fields.
*: to be accurate, you wouldn't update all populated rows, but only the ones having a priority lower than the inserted one. (inserting a priority 999 would only decrease the priority of items with 999 and 1000)

Quick database update based on previous value in Django

I have a QuerySet which contains Page objects, which have page_number attribute. The QuerySet is sorted by page_number, so running [i.page_number for i in pages_query_set] would return something like, for example, [1,2,3,5,6,10,11,13,16,19,21]. My task is to write a method that moves the Page objects so they would be consecutive: in this example, the page that was 5 would become 4, 6 would become 5, 10 would be 6, 11 is 7, 13 is 8, etc.
This is my initial solution, a method inside PageQuerySet class:
def validatePageNumbers(self):
prev_page = 0
for page in self:
if page.page_number > prev_page+1:
page.page_number = prev_page+1
page.save()
prev_page = page.page_number
Functionally, it works fine. But calling save() every time really slows it down (probably due to calling database queries every time). I need to find a faster approach. If there had been only one "gap" in this sequence, I would just slice the QuerySet and use something like sliced_qs.update(page_number=models.F('page_number')-gap), because I've seen a singular update() being much faster than several save(). But the gaps are multiple and pretty random.
So I'm confused. F objects don't seem to support such looping. It would be great if I could use a callable in update(), but I haven't found any information about that in docs, nor it works when I try it. Is there a way to apply update() here? Or maybe some other way to make this method faster?
The solution to this and numerous other bottlenecks was quite simple.
from django.db import transaction
with transaction.atomic():
#do stuff here
Guess it wraps it all into a single transaction and hits the database only once. This answer has helped a lot here.

How to add multiple nodes to relations

How to add multiple node to relations here is my query
MATCH (n:Customer{name:"motoM"})-[:RECENT {default:TRUE}]-(l:Location{name:"Adugodi"}) return l how to write a query to add one more "location" node to the relation "recent" if location node is not found and setting default to true to newly created realtion
What about this?
MATCH (n:Customer{name:"motoM"})-[:RECENT {default:TRUE}]-(l:Location{name:"Adugodi"})
MERGE (n)-[:RECENT]->(l2:Location)
ON CREATE SET l2.default = true
RETURN l, l2
The direction needs to be specified so I made it up, but it might need to go the other way.
Well, I don't know if I understood what you were looking for, but this might help you :)
Try with this query:
MATCH (n:Customer{name:"motoM"})-[r:RECENT {default:TRUE}]-(:Location{name:"Adugodi"})
CREATE (l2:Location{name:"Wherever You need"})
With r,n,l,l2
Set r.default = false
With n,l2
CREATE (n)-[r2:RECENT{default:TRUE}]->(l2)
I'm using Withto make the query easier to read, but you can do it in a single query.
In fact, I think your problem is your Graph model.
You should probably do something like a Customer node, related to Location nodes with a "VISITED" relation, and when you create your VISITED relation, you set date property to timestamp. Then, when you get your relations, you can simply compare timestamps to get the closest one, and you know which one is the one your need. Also, if you need a default property, set it on the node, it'll be easier to match.
Tell me if you need a code example for match, create and set data with this graph model.

Check if node exist in my database Neo4j

please i can't check and create a node plus a relationship to pre-existing node.
i need to check if node pre-existing in my Neo4j database and create a node plus relationship to this node.
Thank you
Here's how you'd merge existing data with data that you create on the fly using Cypher 2.0, the native language to Neo4j:
MERGE ({ name: "Jane" })-[r:KNOWS]->({ name: "John" })
In fact none of these data need pre-exist. It will all be created on the fly, relationships and all. However there's not much checking going on, and if you make a small mistake, it's easy to create duplicate entries this way. Thus it's good practice to check things beforehand.
Here's how you'd use Cypher to check whether a node already exists:
MATCH (n:SomeNode {some_field: "some_discrete_data"}) RETURN n;
In your case it would probably be something like:
MATCH (n {id: 1}) RETURN n;
If there is a node with a field containing the data you need (in this case I've assumed it is {id: 1}, but you might of course want something else), it will be returned. And if not, it will return 0 nodes.
Warning: If you do not describe the data you need, i.e. {id: 1}, all the nodes in the database will be returned, and you might not want that.
However you can't make a relationship with just one node. You need at least two nodes to create a relationship between them. So we'll create another:
CREATE (:OtherNode {id: 2});
To make a relationship with this new node to the pre-existing one, you need to find them with MATCH, and then create the relationship. Here's an example:
MATCH (node1 {id: 1}), (node2 {id: 2})
CREATE (node1)<-[:LIKES]-(node2);
The <- denotes that node2 will have a directional relationship to the already pre-existing node1.
However you can do this process in just one step; that is check if a node already exists, and add a new node plus relationship between the new and the pre-existing node all at the same time! You can do this with the UNIQUE keyword:
MATCH (node1 {id: 1})
CREATE UNIQUE (node1)<-[:LIKES]-(node2 {id: 2});
As long as node1 already exists, node2 and its relationship to node1 will be created if they don't already exist. However if node1 doesn't exist, nothing will happen. IMHO that's a nice safety feature if you want to avoid duplicates.
As for the Python-spesific API, you'd have to check the documentation for the language driver relevant to the programming language you use. Most drivers accept Cypher in some form or another. Here's the relevant part regarding Python: http://book.py2neo.org/en/latest/cypher/
Check out the py2neo docs for the Cypher support and use just a merge query like this:
MERGE (user:User {name:"John"}) // get-or-crate
MERGE (friend:User {name:"Jane"}) // create friend
MERGE (user)-[r:KNOWS]->(friend) // create relationship
RETURN user,r,friend
Cypher's MERGE is it: http://docs.neo4j.org/chunked/stable/query-merge.html. And http://www.catb.org/esr/faqs/smart-questions.html might also be a good read.

Emulate LEFT JOIN with mapreduce?

Emulating an INNER JOIN with mapreduce is relatively trivial, mapping common keys and joining values in the reducer the job gets done. But when it comes to a LEFT OUTER JOIN one faces the problem of combinations filling empties for the Right table when there are keys in the Left table not present in the Right table. The non matching keys would be discarded when mapped into the reducer, how can one then add these non matching keys from the Left table?
For example, let's assume we have two files:
Left = {'matches': 1}
Right = {'matches': 2,
'matches_not': 3}
One would want an output like:
Output: {'matches-matches': [1, 2],
'matches-matches_not': [1, None]}
Emitting the common key 'matches' from the mapper has no problem as both occurences from Left and Right will get to the reducer with common key, but how can one get the combination for the 'matches_not' if it never makes it to the reducer?
The reducer will get all records mapped, even if the matching record is absent. You just have to be add an indicator to each key/value pair showing which side it came from. That indicator will help your reduce() method determine the exact case you're dealing with for each key.
For more details on how to set that up, see my answer to a different question about join.