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()
Was it helpful?

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
scroll top