Adding or updating a reference field in a list
-
21-12-2019 - |
Question
In my data model, a Layer
keeps a list of other related layers. I want to be able to sort the related layers by some score, so the linked
field refers to a Link
class that keeps the score and a reference to the other layer:
class Layer(Document):
name = StringField()
linked = ListField(ReferenceField('Link'))
class Link(Document):
doc = ReferenceField('Layer')
modified = DateTimeField()
score = LongField()
How can I add-or-update a Link
? I want to do something like:
def add_link(layer1, layer2):
if layer2 in layer1.linked:
link = layer1.linked[layer2]
link.score += 1
link.modified = now()
link.save()
else:
link = Link()
link.score = 1
link.modified = now()
link.save()
Solution
What I really have here is a many-to-many relationship. I solved it by getting rid of the Layer.linked
field and using update(upsert=True)
when updating the links:
class Layer(Document):
name = StringField()
class Link(Document):
layer1 = ReferenceField('Layer')
layer2 = ReferenceField('Layer')
modified = DateTimeField()
score = LongField()
def add_link(layer1, layer2):
# Refer to layers in a consistent (but arbitrary) order
if layer1.id < layer2.id:
a = layer1
b = layer2
else:
a = layer2
b = layer1
# Create or update
Link.objects(layer1=a, layer2=b).update_one(
set__modified=datetime.datetime.utcnow(),
inc__score=1,
upsert=True)
Apparently upsert
can cause trouble if two updates are run at the same time when the link doesn't exist yet. A complete solution would probably need a unique, compound index on the layer1
and layer2
fields, and error checking in add_link
in case creation fails.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow