msiemens / tinydb

TinyDB is a lightweight document oriented database optimized for your happiness :)
https://tinydb.readthedocs.org
MIT License
6.84k stars 550 forks source link

insert only if document not exists #464

Closed LeiYangGH closed 2 years ago

LeiYangGH commented 2 years ago

I think this is a very common use case of CRUD operations. what I want:

        doc = {'name': 'John', 'like': 'eggs'}
        db.insert(doc)
        if not db.contains(doc):
            db.insert(doc)

but the check doesn't work. I know Query syntax can solve:

  1. query like db.search(User.name == 'John') will do, but it requires defining an unique key to the object.
  2. insert method returns the Id, so can also check the Id exists, but it requires extra code to record the Ids.

What's the point of allowing inserting the same document multiple times? Can insert method automatically check for duplicates?

MrPigss commented 2 years ago

The default implementation of tinydb, without custom tables, storages, middleware's,.... automagically generates an integer as a unique key (the doc_id) so u can search specific entries even if their contents are the same. So every dictionary gets transformed into a Document on insert.

What's the point of allowing inserting the same document multiple times?

You can't. You CAN insert Documents with the same contents, but these will have different doc_id's generated for them. In other words, you can insert the same dictionary multiple times, but these dicts will get transformed into different Documents with different doc_id's.

For example: This might be useful for an eventlog where the same event can happen multiple times. (Of course it would be useful to use the date-time as a unique key in this case.)

Can insert method automatically check for duplicates?

Not without losing a lot of functionality.

It is however possible to customize this behavior see the docs for more info. but this requires a bit more work.

What you can do is use the fragment method:

doc = {'name': 'John', 'like': 'eggs'}
db.insert(doc)
if not db.contains(Query().fragment(doc)):
    db.insert(doc)

This will check for a document containing these keys.

Let me know if this answered your question.

LeiYangGH commented 2 years ago

Thanks for your explanations. I think the fragment query is generic enough and can satisfiy my requirements. Thank you!

msiemens commented 2 years ago

@MrPigss You're spot on!

TinyDB has the ability to store multiple documents with the same contents because in some cases, that's what you want to do. @MrPigss's suggestion of using a fragments query is the easiest solution to this, but one could also use the upsert method to skip the condition block:

doc = {'name': 'John', 'like': 'eggs'}
db.upsert(doc, Query().fragment(doc))

See Upserting Data in the documentation for details on how the upsert operation works.

LeiYangGH commented 2 years ago

Thanks, this method can reduce one line of if else block.