#lang scribble/doc @define[mongodb-plt (list "jaymccarthy" "mongodb.plt" 1 4)] @define[mongodb-manual (list 'planet "mongodb.scrbl" mongodb-plt)] @(require scribble/manual scribble/eval (planet cce/scheme:6:0/planet) (planet cce/scheme:6:0/scribble) ;scribble/srcdoc (for-label scheme (planet jaymccarthy/mongodb:1:4) (only-in "main.ss" mongo-connection? current-mongo-connection current-mongo-collection mongo-use-database mongo-use-db mongo-use-collection mongo-use connect-to-mongo mongo-connect mongo-save mongo-save** mongo-find-cursor mongo-find mongo-findOne mongo-update mongo-getCollectionNames mongo-collections mongo-dbs mongo-object-id mongo-find-by-id ))) @title[#:tag "mongodb-native"]{MongoDB-native} @author["DaBaG"] @;defmodule[mongodb-native] @defmodule/this-package[] Built on top of the @other-doc[mongodb-manual] package (also provides all its functions and plus @scheme[mongo-connection?] which is renamed duplicate of @scheme[mongo?]. This package contains functions created to facilitate interaction with database. @deftech{Usage}: @schemeblock[(connect-to-mongo) (mongo-dbs) (mongo-use-db "pltexample") (mongo-collections) (mongo-use-collection "first") (mongo-find) (mongo-save '((a . 123) (|b c d| . "asd"))) (mongo-save (list (cons 'a 123) (cons '|b c d| "asd"))) (mongo-save '((_id . 1))) (mongo-save (list (list '_id (cons 'a 1) (cons 'b 2)) (cons 'b 1))) ; btw in mongo _id cannot be array (mongo-find) (mongo-find '((a . 123))) (mongo-findOne '((a . 123))) (mongo-update '((_id . 1)) '((c . 3))) (mongo-find-by-id 1) (mongo-update '((_id . 1)) '()) (mongo-find-by-id 1) (let ((object (make-hash))) (hash-set! object 'b 2) (hash-set! object 'c 4) (mongo-save object)) (let ((object (make-hash))) (hash-set! object 'b 2) (mongo-findOne object)) (mongo-update '((b . 2)) '((|$inc| . ((c . 3))))) (mongo-findOne '((b . 2))) (let ((object (mongo-findOne '((b . 2))))) (hash-set! object 'c (add1 (hash-ref object 'c))) (mongo-save object)) (mongo-find) (mongo-connect #:dbname "pltexample" #:collection "first") (mongo-dbs) (mongo-collections)] @section[#:tag "environment"]{Accessing data} @defproc[(mongo-connection? [connection any/c]) boolean?]{ Renamed @scheme[mongo?] from @other-doc[mongodb-manual]. Returns true if @scheme[connection] is object with some inforamtion about connection to mongo server.} @defparam[current-mongo-connection connection (or/c mongo-connection? false?)]{ Stores mongo connection created by @scheme[connect-to-mongo] (@scheme[mongo-connect]). Used by @scheme[mongo-use], @scheme[mongo-use-database], @scheme[mongo-dbs], etc. If @scheme[connection] is @scheme[false] - connection to server is not established. If you want to use functions of this module with several mongo servers simultaneously - try @tech["parameterization"] (@seclink[#:doc '(lib "scribblings/guide/guide.scrbl") "parameterize"]).} @defparam[current-mongo-collection collection (or/c mongo-collection? false?)]{ Simplifies database operations while you using one mongo collection: functions like @scheme[mongo-save], @scheme[mongo-find], @scheme[mongo-update] use it as the default parameter. Used by functions @scheme[mongo-use-collection], @scheme[mongo-use], @scheme[connect-to-mongo]. If @scheme[collection] is @scheme[false] - default collection is not selected.} @defproc[(mongo-use-database [database (or/c mongo-db? string? false?)]) (or/c mongo-db? false?)]{ Stores @scheme[mongo-db] or @scheme[false] in @scheme[current-mongo-db]. Accepts @scheme[mongo-db] or name of the database as @scheme[string]. Automaticly switches @scheme[current-mongo-collection] to @scheme[false]. Result is @scheme[mongo-db] or @scheme[false].} @defproc[(mongo-use-db [database (or/c mongo-db? string? false?)]) (or/c mongo-db? false?)]{ Alias to @scheme[mongo-use-database].} @defproc[(mongo-use-collection [collection (or/c mongo-collection? string? false?)]) (or/c mongo-collection? false?)]{ Stores @scheme[mongo-collection], collection with given name or @scheme[false] in @scheme[current-mongo-collection]. Result is new value of @scheme[current-mongo-collection]. @bold{Attention:} look at @secref{namesissues} about issues with names in the native console.} @defproc[(mongo-use (#:collection collection (or/c mongo-collection? string? false? void?) (void)) (#:database database (or/c mongo-db? string? false? void?) (void))) (or/c false? mongo-collection? mongo-db?)]{ Combination of @scheme[mongo-use-database] and @scheme[mongo-use-collection] functions. If collection is specified, result will be value of @scheme[current-mongo-collection]], if database - value of @scheme[current-mongo-database].} @defproc[(connect-to-mongo [#:dbname dbname (or/c string? false?) #f] [#:host host string? "localhost"] [#:port port number? 27017] [#:collection collection (or/c string? false?) #f]) (or/c mongo-connection? mongo-collection? mongo-db?)]{ Creates mongo connection and stores it in @scheme[current-mongo-connection]. Optionally accepts names of mongo database and mongo collection in that database. Returns @scheme[current-mongo-connection] if database name and colection was not specified, @scheme[current-mongo-db] if database name was specified, @scheme[current-mongo-collection] if both was specified.} @defproc[(mongo-connect [#:dbname dbname (or/c string? false?) #f] [#:host host string? "localhost"] [#:port port number? 27017] [#:collection collection (or/c string? false?) #f]) (or/c mongo-connection? mongo-collection? mongo-db?)]{ Alias to @scheme[connect-to-mongo].} @;======================================================================== @section["Data manipulations"] All functions in this section accepts collection as parameter. It can be mongo-collection or string that represents name of the collection. If you will not specify this parameter (current-mongo-collection) value will be used. @defproc[(mongo-save [query (or/c list? hash?)] [#:collection collection (or/c mongo-collection? string?) (current-mongo-collection)]) void?]{ Same as @italic{db.collection.save()} in the native mongo shell. Look @secref["queries"] for @scheme[query] syntax.} @defproc[(mongo-save** [query (or/c list? hash?)] [#:collection collection (or/c mongo-collection? string?) (current-mongo-collection)]) bson-objectid?]{ Experimental function. As for now it seems there is no function like "last inserted id". This function works the same as @scheme[mongo-save] but returns id of the last inserted record. To achieve this random key with random value inserted in the @scheme[query] -> query sent to database -> record that contains that random key value is fetched -> id of the record saved and instruction to modify record in database to remove random key is sent to database. Maybe there exists easier way to do it so try not to use it straight.} @defproc[(mongo-find-cursor [query (or/c list? hash?) '()] [#:collection collection (or/c mongo-collection? string?) (current-mongo-collection)]) mongo-cursor?]{ Same as @italic{db.collection.find()} in the native mongo shell. Look @secref["queries"] for @scheme[query] syntax. Returns cursor.} @defproc[(mongo-find [query (or/c list? hash?) '()] [#:collection collection (or/c mongo-collection? string?) (current-mongo-collection)]) list?]{ Same as @italic{db.collection.find()} in the native mongo shell. Look @secref["queries"] for @scheme[query] syntax. Uses @scheme[mongo-find-cursor]. Returns list of hashes as records that match query. If none is found empty list returned.} @defproc[(mongo-findOne [query (or/c list? hash?) '()] [#:collection collection (or/c mongo-collection? string?) (current-mongo-collection)]) (or/c hash? false?)]{ Same as @italic{db.collection.findOne()} in the native mongo shell. Look @secref["queries"] for @scheme[query] syntax. Retreives cursor with @scheme[mongo-find-cursor] and fetches first record from it, then kills cursor. Returns @scheme[hash] represention of record. If none is found result is @scheme[false].} @defproc[(mongo-update [criteria (or/c list? hash?)] [objNew(or/c list? hash?)] [upsert boolean? #f] [multi boolean? #f] [#:collection collection mongo-collection? (current-mongo-collection)]) void?]{ Same as @italic{db.collection.update()} in the native mongo shell. Look @secref["queries"] for @scheme[query] syntax. Positive @scheme[upsert] means that object will be inserted in collection if nothing will match @scheme[criteria]. If @scheme[multi] is false update will be applied only at first found object, either if @scheme[multi] is true all objects matched @scheme[criteria] will be updated. Look @link["http://www.mongodb.org/display/DOCS/Updating"]{original manual} for additional explanations.} @defproc[(mongo-getCollectionNames [#:database database (or/c mongo-db? string?) (current-mongo-db)]) list?]{ Same as @italic{db.getCollectionNames()} in the native mongo shell. Enumerates collection names in @scheme[database].} @defproc[(mongo-collections [#:database database (or/c mongo-db? string?) (current-mongo-db)]) list?]{ Alias for @scheme[mongo-getCollectionNames].} @defproc[(mongo-dbs [#:connection connection mongo-connection? (current-mongo-connection)]) list?]{ Same as @italic{"show dbs"} in the native mongo shell. Enumerates database names at the server.} @defproc[(mongo-object-id [object (or/c hash? list?)]) (or/c bson-objectid? false?)]{ Combination of @scheme[(hash-ref object '_id)] for hash representation of record and @scheme[(cdr (assoc '_id object))] for list representation.} @defproc[(mongo-find-by-id [id bson-objectid?]) (or/c hash? false?)]{ Way to omit writing @scheme[(mongo-findOne (list (cons '_id id)))].} @;======================================================================== @section[#:tag "comments"]{Various comments} @subsection[#:tag "queries"]{Queries} @margin-note{You can find out about BSON @itemize{ @item{at @link["http://bsonspec.org/#/specification"]{specification offical site}} @item{at @link["http://www.mongodb.org/display/DOCS/BSON"]{BSON section} on MongoDB site} @item{or in @other-doc[mongodb-manual] manual.}}} Current query syntax is cons-format syntax used in MongoDB module. It looks neatly but has some @tech{restrictions}. So: BSON structures are documents/dictionaries/hashes and lists/arrays. Working with BSON objects/queries you can use @tech[#:doc '(lib "scribblings/reference/reference.scrbl") "hash"]es + lists or just lists to describe them. Writing BSON objects/queries in lists: @itemlist[ @item{lists: @scheme['(1 2 3)] or @scheme[(list 1 2 3)]} @item{hashes: @scheme['((a . 1) (b . 2))] or @scheme[(list (cons 'a 1) (cons 'b 2))]} ] bnf style: @schemegrammar*[#:literals (symbol?) (query dictionary) (dictionary code:blank (code:line [dictionary-key content] ...)) (dictionary-key symbol?) (list code:blank (code:line content ...)) (content list dictionary other-types-of-BSON-value) ] Therefor @scheme[((a 1 2 3))] is a hash that containes key @scheme[a] with value @scheme[(1 2 3)]. It is equal to result of @scheme[(list (cons 'a (list 1 2 3)))] or @scheme[(list (list 'a 1 2 3))] expression. @deftech{Restrictions} is that you can not describe list of lists properly with this rules. Query @scheme['((a . ((b c) (d e))))] is properly notation of {a : [["b", "c"], ["d", "e"]]} and {a : {b : "c", d : "e"}} objects following current rules. The only way to solve this - use hashes. @subsection[#:tag "keyssyntax"]{Keys in documents} Currently used as base, module MongoDB requires all keys in queries and objects to be @scheme[symbol?]. So if you need to specify sofisticated key use @tt{@litchar{|}expression@litchar{|}}. For example key @scheme["123 qwe"] can be refered as @scheme['|123 qwe|]. @subsection[#:tag "namesissues"]{Names issues} Mongo collection created in plt with name @italic{"something-something"} will be unreachable in MongoDB native console through default notation @tt{db.something-something.find()} because of @litchar{-} symbol. Use @tt{db["something-something"].find()} form instead. @subsection{Troubles} If you encounter error @schemeerror{make-hasheq: expects no arguments, given 1: ((1 . function) (2 . binary) (3 . uuid) (5 . md5) (128 . user-defined))} look for README.txt in package folder.