Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit db0ffcb47f0ce67de5a44b0df215080289213b9a @edsrzf committed Nov 5, 2010
Showing with 592 additions and 0 deletions.
  1. +6 −0 .gitignore
  2. +25 −0 LICENSE
  3. +16 −0 Makefile
  4. +3 −0 README.md
  5. +195 −0 collection.go
  6. +103 −0 conn.go
  7. +97 −0 cursor.go
  8. +69 −0 database.go
  9. +78 −0 query.go
@@ -0,0 +1,6 @@
+*~
+_*
+*.5
+*.6
+*.8
+*.out
25 LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2010, Evan Shaw
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
@@ -0,0 +1,16 @@
+# Copyright 2010, Evan Shaw. All rights reserved.
+# Use of this source code is governed by a BSD-style License
+# that can be found in the LICENSE file.
+
+include $(GOROOT)/src/Make.inc
+
+TARG=github.com/edsrzf/mongogo
+GOFILES=\
+ collection.go\
+ conn.go\
+ cursor.go\
+ database.go\
+ query.go\
+
+include $(GOROOT)/src/Make.pkg
+
@@ -0,0 +1,3 @@
+Mongogo is a MongoDB driver for Go.
+
+It's not well tested, but the basics seem to work well enough.
@@ -0,0 +1,195 @@
+// Copyright 2010, Evan Shaw. All rights reserved.
+// Use of this source code is governed by a BSD-style License
+// that can be found in the LICENSE file.
+
+package mongo
+
+import (
+ "bytes"
+ "encoding/binary"
+ "os"
+ "github.com/edsrzf/go-bson"
+)
+
+// common message header size
+// 16-byte header
+const headerSize = 16
+
+// A Collection represents a MongoDB collection.
+type Collection struct {
+ db *Database
+ name string
+ fullName []byte
+}
+
+// Drop deletes c from the database.
+func (c *Collection) Drop() os.Error {
+ cmd := Query{"drop": string(c.fullName)}
+ _, err := c.db.Command(cmd)
+ return err
+}
+
+// Update updates a single document selected by query, according to doc.
+func (c *Collection) Update(query, doc bson.Doc) os.Error {
+ return c.update(query, doc, false, false)
+}
+
+// Upsert updates or inserts a single document selected by query,
+// according to doc.
+func (c *Collection) Upsert(query, doc bson.Doc) os.Error {
+ return c.update(query, doc, true, false)
+}
+
+// Update updates multiple documents selected by query, according to doc.
+func (c *Collection) UpdateAll(query, doc bson.Doc) os.Error {
+ return c.update(query, doc, false, true)
+}
+
+// UpsertAll updates or inserts multiple documents selected by query,
+// according to doc.
+func (c *Collection) UpsertAll(query, doc bson.Doc) os.Error {
+ return c.update(query, doc, true, true)
+}
+
+func (c *Collection) update(query, doc bson.Doc, upsert, multi bool) os.Error {
+ selData, err := bson.Marshal(query)
+ if err != nil {
+ return err
+ }
+ docData, err := bson.Marshal(doc)
+ if err != nil {
+ return err
+ }
+ cap := headerSize + 4 + len(c.fullName) + 4 + len(selData) + len(docData)
+ payload := make([]byte, headerSize+4, cap)
+ buf := bytes.NewBuffer(payload)
+ buf.Write(c.fullName)
+ var flags int32
+ if upsert {
+ flags |= 1
+ }
+ if multi {
+ flags |= 2
+ }
+ binary.Write(buf, order, flags)
+ buf.Write(selData)
+ buf.Write(docData)
+ payload = payload[:cap]
+ return c.db.conn.sendMessage(2001, 0, payload)
+}
+
+// Insert creates a new document in c.
+func (c *Collection) Insert(doc bson.Doc) os.Error {
+ data, err := bson.Marshal(doc)
+ if err != nil {
+ return err
+ }
+ cap := headerSize + 4 + len(c.fullName) + len(data)
+ payload := make([]byte, headerSize+4, cap)
+ buf := bytes.NewBuffer(payload)
+ buf.Write(c.fullName)
+ buf.Write(data)
+ payload = payload[:cap]
+ return c.db.conn.sendMessage(2002, 0, payload)
+}
+
+// Query searches c for any documents matching a query. It skips the first skip
+// documents and limits the search to limit.
+func (c *Collection) Query(query Query, skip, limit int32) (*Cursor, os.Error) {
+ conn := c.db.conn
+ data, err := bson.Marshal(bson.Doc(query))
+ if err != nil {
+ return nil, err
+ }
+ cap := headerSize + 4 + len(c.fullName) + 8 + len(data)
+ payload := make([]byte, headerSize, cap)
+ buf := bytes.NewBuffer(payload[headerSize:])
+ // TODO(eds): Consider supporting flags
+ binary.Write(buf, order, int32(0))
+ buf.Write(c.fullName)
+ binary.Write(buf, order, skip)
+ binary.Write(buf, order, limit)
+ buf.Write(data)
+ payload = payload[:cap]
+ if err := conn.sendMessage(2004, 0, payload); err != nil {
+ return nil, err
+ }
+
+ reply, err := conn.readReply()
+ if err != nil {
+ return nil, err
+ }
+
+ return &Cursor{c, reply.cursorID, 0, reply.docs}, nil
+}
+
+// FindAll returns all documents in c matching a query.
+func (c *Collection) FindAll(query Query) (*Cursor, os.Error) {
+ return c.Query(query, 0, 0)
+}
+
+// FindOne returns the first document in c that matches a query.
+func (c *Collection) FindOne(query Query) (bson.Doc, os.Error) {
+ cursor, err := c.Query(query, 0, 1)
+ if err != nil {
+ return nil, err
+ }
+ defer cursor.Close()
+ return cursor.Next(), nil
+}
+
+// Count returns the number of documents in c that match a query.
+func (c *Collection) Count(query bson.Doc) (int64, os.Error) {
+ cmd := Query{"count": c.name, "query": query}
+ reply, err := c.db.Command(cmd)
+ if reply == nil || err != nil {
+ return -1, err
+ }
+
+ // NOTE(eds): Mongo returns count as a double? Really? That seems silly.
+ return int64(reply["n"].(float64)), nil
+}
+
+func (c *Collection) remove(query bson.Doc, singleRemove bool) os.Error {
+ data, err := bson.Marshal(query)
+ if err != nil {
+ return err
+ }
+ l := len(c.fullName)
+ payload := make([]byte, headerSize+4+l+4+len(data))
+ copy(payload[headerSize+4:], c.fullName)
+ if singleRemove {
+ payload[headerSize+4+l] |= 1
+ }
+ copy(payload[headerSize+4+l+4:], data)
+ return c.db.conn.sendMessage(2006, 0, payload)
+}
+
+// Remove removes all documents in c that match a query.
+func (c *Collection) Remove(query bson.Doc) os.Error {
+ return c.remove(query, false)
+}
+
+// RemoveFirst removes the first document in c that matches a query.
+func (c *Collection) RemoveFirst(query bson.Doc) os.Error {
+ return c.remove(query, true)
+}
+
+// EnsureIndex ensures that an index exists on this collection.
+func (c *Collection) EnsureIndex(name string, keys map[string]int32, unique bool) os.Error {
+ col := c.db.Collection("system.indexes")
+ id := bson.Doc{"name": name, "ns": string(c.fullName), "key": keys, "unique": unique}
+ return col.Insert(id)
+}
+
+// DropIndexes deletes all indexes on c.
+func (c *Collection) DropIndexes() os.Error {
+ return c.DropIndex("*")
+}
+
+// DropIndex deletes a single index.
+func (c *Collection) DropIndex(name string) os.Error {
+ cmd := Query{"deleteIndexes": string(c.fullName), "index": name}
+ _, err := c.db.Command(cmd)
+ return err
+}
103 conn.go
@@ -0,0 +1,103 @@
+// Copyright 2010, Evan Shaw. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The mongo package provides a MongoDB driver implementation.
+package mongo
+
+import (
+ "bytes"
+ "encoding/binary"
+ "os"
+ "net"
+ "rand"
+ "github.com/edsrzf/go-bson"
+)
+
+var order = binary.LittleEndian
+
+// A Conn represents a connection to a MongoDB server.
+type Conn struct {
+ conn net.Conn
+}
+
+type reply struct {
+ requestID int32
+ responseTo int32
+ responseFlags int32
+ cursorID int64
+ startingFrom int32
+ numberReturned int32
+ docs []bson.Doc
+}
+
+// Dial connects to a MongoDB server at the remote address addr.
+func Dial(addr string) (*Conn, os.Error) {
+ c, err := net.Dial("tcp", "", addr)
+ if err != nil {
+ return nil, err
+ }
+ return &Conn{c}, nil
+}
+
+// Close closes the connection.
+func (c *Conn) Close() os.Error {
+ return c.conn.Close()
+}
+
+// Database returns the Database object for a name.
+func (c *Conn) Database(name string) *Database {
+ return &Database{c, name}
+}
+
+func (c *Conn) sendMessage(opCode, responseId int32, message []byte) os.Error {
+ messageLength := int32(len(message))
+ message = message[:0]
+ buf := bytes.NewBuffer(message)
+ binary.Write(buf, order, messageLength)
+ // request ID
+ binary.Write(buf, order, rand.Int31())
+ // response to
+ binary.Write(buf, order, responseId)
+ binary.Write(buf, order, opCode)
+ message = message[:messageLength]
+ _, err := c.conn.Write(message)
+ return err
+}
+
+func (c *Conn) readReply() (*reply, os.Error) {
+ var size uint32
+ err := binary.Read(c.conn, order, &size)
+ if err != nil {
+ return nil, err
+ }
+ raw := make([]byte, size)
+ _, err = c.conn.Read(raw)
+ if err != nil {
+ return nil, err
+ }
+ buf := bytes.NewBuffer(raw)
+ r := new(reply)
+ binary.Read(buf, order, &r.requestID)
+ binary.Read(buf, order, &r.responseTo)
+ var opCode int32
+ binary.Read(buf, order, &opCode)
+ if opCode != 1 {
+ return nil, os.NewError("expected OP_REPLY opCode")
+ }
+ binary.Read(buf, order, &r.responseFlags)
+ binary.Read(buf, order, &r.cursorID)
+ binary.Read(buf, order, &r.startingFrom)
+ binary.Read(buf, order, &r.numberReturned)
+ r.docs = make([]bson.Doc, r.numberReturned)
+ for i := range r.docs {
+ raw := buf.Bytes()
+ size := order.Uint32(raw)
+ r.docs[i], err = bson.Unmarshal(raw)
+ if err != nil {
+ break
+ }
+ buf.Next(int(size))
+ }
+ return r, err
+}
Oops, something went wrong.

0 comments on commit db0ffcb

Please sign in to comment.