MCPcopy Index your code
hub / github.com/e-oj/Fawn

github.com/e-oj/Fawn @2.1.5

Chat with this repo
repository ↗ · DeepWiki ↗ · release 2.1.5 ↗ · + Follow
35 symbols 61 edges 15 files 26 documented · 74%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Travis npm npm npm

Fawn

Promise based Library for transactions in MongoDB

Fawn provides the ability to carry out edits on a mongoDB database as a series of steps. If an error occurs on any of the steps, the database is returned to its initial state (its state before the transaction started). It's based on the two phase commit system described in the MongoDB docs. Check out this Medium article for a more detailed look.

View on GitHub

Getting Started:

Install node.js and mongoDB

Start mongoDB in a terminal: mongod

Then: npm install fawn

Usage:

var Fawn = require("fawn");

Fawn.init("mongodb://127.0.0.1:27017/testDB");

or

var mongoose = require("mongoose");
mongoose.connect("mongodb://127.0.0.1:27017/testDB");

Fawn.init(mongoose);

Examples

Say you have two bank accounts, one belongs to John Smith and the other belongs to Broke Ass. You would like to transfer $20 from John Smith to Broke Ass. Assuming all first name and last name pairs are unique, this might look like:

var task = Fawn.Task();

//assuming "Accounts" is the Accounts collection
task.update("Accounts", {firstName: "John", lastName: "Smith"}, {$inc: {balance: -20}})
  .update("Accounts", {firstName: "Broke", lastName: "Ass"}, {$inc: {balance: 20}})
  .run()
  .then(function(results){
    // task is complete 

    // result from first operation
    var firstUpdateResult = results[0];

    // result from second operation
    var secondUpdateResult = results[1];
  })
  .catch(function(err){
    // Everything has been rolled back.

    // log the error which caused the failure
    console.log(err);
  });

Files can be saved to and removed from GridFS. Here's how you might update a user's profile image:

var newImageId = someMongoDbId;

task.saveFile("/path/to/new/profile/img", {_id: newImageId, filename: "profile.png"})
  .removeFile({_id: oldImageId})
  .update("users", {_id: userId}, {profileImageId: newImageId})
  .run()
  .then(function(results){
    var newImgFile = results[0];

    console.log(newImgFile.filename) // profile.png
  })
  .catch(function(err){
    // Everything has been rolled back.

    // log the error which caused the failure
    console.log(err);
  });

By default, tasks run using the native driver but you can opt for mongoose. If you prefer not to chain function calls, you don't have to:

task.update("Accounts", {firstName: "Broke", lastName: "Ass"}, {$inc: {balance: -20}})
task.update("Accounts", {firstName: "The", lastName: "Plug"}, {$inc: {balance: 20}})
task.run({useMongoose: true})
  .then(function(){
    // update is complete
  })
  .catch(function(err){
    // Everything has been rolled back.

    // log the error which caused the failure
    console.log(err);
  });

The server could crash before a task is complete, You can use the Roller to rollback all incomplete transactions before starting your server:

// assuming Fawn has been initialized. See Fawn.init below
var roller = Fawn.Roller();

roller.roll()
  .then(function(){
    // start server
  });

API

Fawn.init(db, _collection, options): Initialize Fawn

db (required): mongoose instance or connection string

_collection (optional): Name of collection used internally by Fawn to store transactions

options (optional. lol): Connection options. Same as mongoose connection options

Note: if you're running multiple apps connected to the same db, provide a string value for _collection that's unique to each app. Do this to avoid a situation where one app rolls back the unfinished transaction(s) of another app.

If you're using mongoose in your project initialize Fawn with mongoose:

var mongoose = require("mongoose");

mongoose.connect("mongodb://127.0.0.1:27017/testDB");

// remember, _collection is optional
Fawn.init(mongoose, "Fawn_collection_name_if_you_want_to_specify");

Without mongoose, Initialize Fawn like so:

// options object (http://mongoosejs.com/docs/connections.html#options)
var options = {
  user: "teh_huose_kat",
  pass: "teh_Kitti_passwrod"
};

var collection = "Fawn_collection_name_if_you_want_to_specify";

// remember, _collection and options are optional
Fawn.init("mongodb://127.0.0.1:27017/testDB", collection || null, options || null);

Fawn.Task(): Create a Fawn task

returns: A new task

After intitializing Fawn, create a task like so:

var task = Fawn.Task();

task.initModel(modelName, schema): To initialize a model with a Schema.

modelName (required): Name of the collection associated with this model

schema (optional): Same as object passed to mongoose Schema. Also see validation

Note: For model validation to work, run task with useMongoose set to true

Initalizes a mongoose model with the provided schema. If you're using mongoose, define your models with mongoose wherever possible. If the model has been defined by mongoose before this function is called, mongoose will throw an OverwriteModelError and if it was defined by Fawn, Fawn will throw an Error. Models can be defined only once.

```javascript var schema = { name: {type: String, required: true} , specials: [{title: String, year: Number}] };

task.initModel("comedians", schema) .save("comedians", {name: "Kevin Hart", specials: [{title: "What Now", year: 2016}]}) .run({useMongoose: true}) .then(function(results){ console.log(results); }); ```

Save operations to the "comedians" model will validate against the schema;

task.save(model, doc): To save a document

model (required): Name of the collection we're saving to or a mongoose model or a mongoose document

doc (optional): Object to save or a mongoose document

these are all valid:

```javascript var Cars = mongoose.model("cars", new Schema({make: String, year: Number})); var toyota = new Cars({make: "Toyota", year: 2015});

task.save("cars", {make: "Toyota", year: 2015}); task.save(Cars, {make: "Toyota", year: 2015}); task.save("cars", toyota); task.save(Cars, toyota); task.save(toyota); ```

Note: No changes will be made to to your database until you call task.run()

task.update(model, condition, data): To update a document

model (required): Name of the collection we're updating or a mongoose model or a mongoose document

condition (required): Same as in mongoose and mongodb

data (optional): Data to update with same as in mongoose and mongodb

These are all valid

```javascript var Cars = mongoose.model("cars", new Schema({make: String, year: Number}));

task.update("cars", {make: "Toyota"}, {year: 2016}); task.update(Cars, {make: "Toyota"}, {year: 2016});

Cars.findOne({make: "Toyota"}, function(toyota){ task.update(toyota, {year: 2016}); }); ```

Note: No changes will be made to to your database until you call task.run()

task.options(options): Add options to an update task.

options (required): Update options = mongoose options + {viaSave: Boolean}

Attach to update call as shown

```javascript task.update("cars", {make: "Toyota"}, {year: 2016}) .options({multi: true});

// Also valid

task.update("cars", {make: "Ford"}, {year: 2016}); task.options({multi: true}); ```

The viaSave option allows you update a mongoose document using the save function. It's useful if you want to trigger mongoose pre save hooks. For this option to work you must run the task using mongoose

with mongoose:

  var doc = someMongooseDocument;

  doc.someProperty = newValue;
  doc.save().then(console.log);

with Fawn: ```javascript var doc = someMongooseDocument; var newDoc = doc.toObject();

newDoc.someProperty = newValue

task.update(doc, newDoc) .options({viaSave: true}) .run({useMongoose: true}) .then(console.log); ``` Note: No changes will be made to to your database until you call task.run()

task.remove(model, condition): Remove document(s) from a collection

model (required): Name of the collection we're deleting from or a mongoose model or a mongoose document

condition (optional): Same as in mongoose

These are all valid

```javascript var Cars = mongoose.model("cars", new Schema({make: String, year: Number}));

// removes all cars with year === 2015 task.remove("cars", {year: 2015}); task.remove(Cars, {year: 2015});

Cars.findOne({year: 2015}, function(car){ // remove just this car task.remove(car); }); ```

Note: No changes will be made to to your database until you call task.run()

task.saveFile(filePath, options): Save a file to the db via GridFS

filePath (required): Path to the file

options (optional): Same as GridStore options

Saves the file at "filePath" to the database using GridFS. The result of this operation is the saved file's object. See File object

```javascript task.saveFile("path/to/some/file", {filename: "a_string_filename.ext"}) .update("SomeCollection", updateConditions, updateData) .run() .then(function(results){ var file = results[0];

  console.log(file.filename); // a_string_filename.ext
}).catch(function(err){
  // Everything has been rolled back.

  //log the error which caused the failure
  console.log(err);
});

```

Note: No changes will be made to to your database until you call task.run()

task.removeFile(options): Remove a file from the db via GridFS

options (required): Same as GridStore options

Removes a file that matches "options" from the database using GridFS. The result of this operation is a GridStore instance (can be ignored). See GridStore

```javascript task.removeFile({_id: fileId}) .update("SomeCollection", updateConditions, updateData) .run() .then(function(results){ // if you need the gridStore instance var gridStore = results[0]; }) .catch(function(err){ // Everything has been rolled back.

  //log the error which caused the failure
  console.log(err);
});

```

Note: No changes will be made to to your database until you call task.run()

task.run(options): Run a task.

options: {useMongoose: Boolean}

returns: Promise

For the database changes to occur, you must call task.run(). This function returns a promise. On success, the promise is resolved with an array containing the node-mongodb-native or mongoose result of each operation in sequence. If an error occurs, the promise is rejected with the error that caused the failure.

```javascript task.update("Accounts", {firstName: "John", lastName: "Smith"}, {$inc: {balance: -20}}) .update("Accounts", {firstName: "Broke", lastName: "Ass"}, {$inc: {balance: 20}}) .run() // or run({useMongoose: true}); .then(function(results){ //task is complete

  //result from first operation
  var firstUpdateResult = results[0];

  //result from second operation
  var secondUpdateResult = results[1];
})
.catch(function(err){
  // Everything has been rolled back.

  //log the error which caused the

Core symbols most depended-on inside this repo

storeOldData
called by 4
lib/utils/db.utils.js
checkInitStatus
called by 3
lib/fawn.js
done
called by 3
lib/roller.js
getModel
called by 3
lib/utils/db.utils.js
getCollectionForStep
called by 3
lib/utils/db.utils.js
setModel
called by 2
lib/utils/db.utils.js
Task
called by 1
lib/task.js
getResolveFunc
called by 1
lib/task.js

Shape

Function 35

Languages

TypeScript100%

Modules by API surface

lib/utils/db.utils.js11 symbols
lib/task.js9 symbols
lib/roller.js8 symbols
lib/fawn.js3 symbols
lib/utils/gen.utils.js2 symbols
tests/task.tests.js1 symbols
tests/roller.tests.js1 symbols

Datastores touched

(mongodb)Database · 1 repos
testDBDatabase · 1 repos

For agents

$ claude mcp add Fawn \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact