npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@trioxis/roar-mongo

v0.3.0-alpha

Published

Observable interface for MongoDB

Downloads

1

Readme

Roar Mongo

PRE-ALPHA. BREAKING CHANGES LIKELY

A functional reactive interface for MongoDb, based on Observables.

  • Composable
  • Reactive
  • Batteries removable, but included

Usage

Batteries Included

To get up and running quickly, you can use CRUDRepository and MappedRepository.

CRUDRepository is exactly as it sounds and exposes all the standard sort of methods you would expect: insert, query, update and delete.

MappedRepository is effectively a CRUDRepository, with the addition of input and output mapping. At Trioxis, we use this as an ORM.

In the below example, we want to validate the stored object, as well as maintain a slightly different api

This can be useful when using a library like Joi for validation.

import {
  MappedRepository,
  MongoConnect,
  MongoDispose
} from 'roar-mongo';
import { Observable } from 'rxjs/Rx';
import assert from 'assert';

const InboundMap = (input:Object)=>{
  assert.ok(input.name,'Author should contain name');
  const _id = input.id ? new ObjectID(input.id) : new ObjectID()

  return {
    name:input.name,
    _id
  };
}
const OutboundMap = (input:Object)=>({
  name:input.name,
  id:input._id.toString()
})

const myRepo =
MappedRepository(
  'authors',
  ()=>MongoConnect(MONGO_URL),
  InboundMap,
  OutboundMap
);

const items = [
  {
    name: 'Arya Stark'
  }, {
    name: 'Rob Stark'
  }
]

const main = async () => {
  let res = await myRepo.insert(items)
  // Returns an Observable of items
  .toArray()
  .toPromise();

  // Close the connection to allow the program to exit
  await MongoDispose(MONGO_URL);
}

main();

// Stored in Mongo:
// [
//   {
//     _id:ObjectID(1234),
//     name: 'Arya Stark'
//   }, {
//     _id:ObjectID(5678),
//     name: 'Rob Stark'
//   }
// ]

// console.log(res) would result in
// [
//   {
//     id:'1234',
//     name: 'Arya Stark'
//   }, {
//     id:'5678',
//     name: 'Rob Stark'
//   }
// ]

// Note the difference in ID key and value

Observables

Observable is the main primitive throughout the library. This means it's the same API to play with large data sets. Lets say you want to migrate something...

import {
  CRUDRepository,
  MongoConnect,
  MongoDispose
} from 'roar-mongo';

const myRepo =
CRUDRepository(
  'stuff',
  ()=>MongoConnect(MONGO_URL)
);

// Lets say... you've got all this stuff...
const newItems = Observable.range(0,1000000).map(i=>({foo:i}));

// Insert will automatically batch the data into sane amounts
await myRepo.insert(newItems).toPromise();

// And now... for some reason, we want to update every doc where foo is odd
// First we'll make a mongo query
const query = { foo: { $mod: [ 2, 1 ] } };

// Now we'll perform the query and map the results...
const updateItems = myRepo
.query(query)
.map(item=>({
  ...item,
  foo:(item.foo*10)
}));

const main = async () => {
  // ... and perform the update
  // Each item will be streamed in via the observable and sanely updated
  await myRepo.update(updateItems).toPromise();

  // Profit. Then close
  await MongoDispose(MONGO_URL);
}

main();

Roll your own repository

You can easily build your own repository too. Take the following for example. This repository has Add and GetByName. Both of which are composed from higher order functions exposed by this library.

import {
  Query,
  Insert,
  HandleArrayArgument,
  MapObservableArgument,
  MapObservableResult,
  MongoConnect,
  GetCollection
} from 'roar-mongo';

const MONGO_URL = ...;

const GetCollectionFn = ()=>GetCollection('myItems',MongoConnect(MONGO_URL)));
const OutMap = item=>({
  id:item._id.toString(),
  name:item.name
});
const InMap = ({name})=>({
  name
});

class MyRepo {
  GetByName(name){
    const mongoQuery = { name }
    return MapObservableResult(OutMap,
      Query(
        GetCollectionFn()
      )
    )(mongoQuery)
  }
  Add(arrayOfNames){
    return HandleArrayArgument(
      MapObservableArgument(InMap,
        MapObservableResult(OutMap,
          Insert(GetCollectionFn())
        )
      )
    )(arrayOfNames)
  }
}

API

CRUDRepository

Construction

import {
  CRUDRepository,
  MongoConnect,
  MongoDispose
} from 'roar-mongo';

const myRepo =
CRUDRepository('tests',()=>MongoConnect(MONGO_URL));

// When you are done, dispose the connection
MongoDispose(MONGO_URL);

insert

let insertedObjects = await myRepo
.insert(Observable.of([{a:'b'},{a:'b'}]))
.toArray()
.toPromise();

// [
//   { a: 'b', _id: 58154e18d52c140979028144 },
//   { a: 'b', _id: 58154e18d52c140979028145 }
// ]

query

import { CRUDRepository } from 'roar-mongo';

await myRepo
.query({})
.toArray()
.toPromise();

// [
//   { a: 'b', _id: 58154e18d52c140979028144 },
//   { a: 'b', _id: 58154e18d52c140979028145 }
// ]

update

// Objects are updated using their `_id`
// Argument to .update() must be Observables
let objectsToUpdate = insertedObjects.map(o => Observable.of({...o, newField: 'c'}));

await myRepo
.update(objectsToUpdate)
.toArray()
.toPromise();

// [ 58154e18d52c140979028144, 58154e18d52c140979028145 ]

upsert

// Upsert is supported as its own method
let moreToUpdate = objectsToUpdate.merge(Observable.of({...o, newField: 'c'}));

await myRepo
.upsert(moreToUpdate)
.toArray()
.toPromise();

// [ 58154e18d52c140979028144, 58154e18d52c140979028145, 59b5e02023a61300d983146c ]

delete

let objectsToDelete = insertedObjects;

await myRepo
.delete(objectsToDelete)
.toArray()
.toPromise();

// [ 58154e18d52c140979028144, 58154e18d52c140979028145 ]