fons / cl-mongo

lisp interface to mongo db
fons.github.com/cl-mongo
MIT License
143 stars 31 forks source link

db.update Statement Does Not Change Records #14

Closed Shinmera closed 11 years ago

Shinmera commented 11 years ago

If I try to execute a db.update statement and supply a document or a hash-map as the argument, rather than a simple kv, the update statement does not seem to get executed. More specifically, (db.update "foo" (kv "field" "foobar") (kv "field" "baz")) works and changes the record to be simply {"field": "baz"}. However, if I do

(let ((document (make-document)))
  (add-element "field" "baz" document)
  (db.update "foo" (kv "field" "foobar") document))

the record remains the same in the database and doesn't seem to change at all. No errors are being thrown either as far as I can tell. I also tried simply passing the hash-table to the db.update function as I assumed it might have to do with the ID field, but that didn't help either. I know that both the document construction and the query are right, as executing a db.find with the query or a db.insert with the document work correctly.

I tried digging into the source code of the library a bit, but I couldn't find anything as to why this doesn't work. Am I just missing something or is this really a bug?

My setup is running SBCL 1.1.8 and Quicklisp reports:

< SYSTEM cl-mongo / cl-mongo-20120208-git / quicklisp 2013-06-15>

< SYSTEM cl-mongo-id / cl-mongo-id-20130128-git / quicklisp 2013-06-15>

Any help would be greatly appreciated.

fons commented 11 years ago
  1. take a look in the mongodb log files. Does it perhaps throw an error ?
  2. if you're convinced this is a bug, put a small test together of exactly what you attempt to do and I'll take a look at it.
Shinmera commented 11 years ago

I restarted mongodb and created a fresh logfile. After that I executed the following statements on the REPL:

(db.use "test")
(pp (iter (db.find "updatetest" :all)))

(db.update "updatetest" (kv "field" "foobar") (kv "field" "baz"))
(pp (iter (db.find "updatetest" :all)))

(db.update "updatetest" (kv "field" "baz") (kv "field" "foobar"))
(pp (iter (db.find "updatetest" :all)))

(let ((document (make-document)))
  (add-element "field" "baz" document)
  (db.update "updatetest" (kv "field" "foobar") document))
(pp (iter (db.find "updatetest" :all)))

Here's the output: image

And finally, here's the mongodb log file from /var/log/mongodb/mongod.log

***** SERVER RESTARTED *****
Fri Jul  5 19:23:55.354 [initandlisten] MongoDB starting : pid=15385 port=27017 dbpath=/var/lib/mongodb 64-bit host=linuz-com
Fri Jul  5 19:23:55.355 [initandlisten] db version v2.4.4
Fri Jul  5 19:23:55.355 [initandlisten] git version: nogitversion
Fri Jul  5 19:23:55.355 [initandlisten] build info: Linux -var-lib-archbuild-extra-x86_64-fyan 3.9.2-1-ARCH #1 SMP PREEMPT Sat May 11 20:31:08 CEST 2013 x86_64 BOOST_LIB_VERSION=1_53
Fri Jul  5 19:23:55.355 [initandlisten] allocator: tcmalloc
Fri Jul  5 19:23:55.355 [initandlisten] options: { bind_ip: "127.0.0.1", config: "/etc/mongodb.conf", dbpath: "/var/lib/mongodb", logappend: "true", logpath: "/var/log/mongodb/mongod.log", quiet: true }
Fri Jul  5 19:23:55.356 [initandlisten] journal dir=/var/lib/mongodb/journal
Fri Jul  5 19:23:55.356 [initandlisten] recover : no journal files present, no recovery needed
Fri Jul  5 19:23:55.363 [initandlisten] waiting for connections on port 27017
Fri Jul  5 19:23:55.363 [websvr] admin web console waiting for connections on port 28017
Fri Jul  5 19:25:37.104 [conn2] getMore: cursorid not found test.updatetest 0
Fri Jul  5 19:26:07.278 [conn2] getMore: cursorid not found test.updatetest 0

Is there anything else I could provide you with to make debugging this easier?

fons commented 11 years ago

not sure what you're trying to do; (kv "field" "foobar") => key id field; (kv "field" "baz") => key is "field" also. so it looks like you're trying to create a document with two values for the same key..

In addition in the log file you should see an update message. You may need to turn the log level up a bit though..

Lastly, look at the unit tests and see if those fail.

Shinmera commented 11 years ago

The document I initially have in the database is {"field":"foobar"}. I want to change it to be {"field":"baz"}. This works with db.update if I use kv to construct the new values, but not if I use a document.

Running (quick-test :collection "units") gives the following output: http://pastebin.com/57USruw3 None of the update tests seem to use a document for new values though.

I've checked the mongodb logs with verbose on. It looks like if you try to issue an update statement with a document, it fails to update because the document ID is sent along. MongoDB refuses to update documents to a new ID and ignores the request.

I did some more testing and it seems that if I do

(let ((document (make-document)))
  (add-element "field" "baz" document)
  (db.update "updatetest" (kv "field" "foobar") (cl-mongo::elements document)))

instead, it works fine. I'm not sure why this didn't work before, but I'm glad it does now. I probably did something wrong back there, my apologies for that.

I think to make db.update usable with documents as data arguments as well a new method should be added though. Something like this maybe?

(defmethod db.update ((collection string) (selector t) (new-document document)
              &key (mongo (mongo)) (upsert nil) (multi nil))
  (db.update collection selector (elements new-document) :mongo mongo :upsert upsert :multi multi))

Thanks a lot for the swift responses and the otherwise excellent library! I'll close this as I found a workaround for now.