Wondering what’s next for npm?Check out our public roadmap! »

    relax-ql

    1.3.5 • Public • Published

    What is relax-ql?

    A simple query language for mongoodb. It base on mongoosejs. Everything is too easy to getting done your job. Write LESS, do MORE and Let's RELAX (after done :v)

    Models

    var ReviewSchema = Schema({
      author: ObjectId,
        localBiz: ObjectId,
        postedTime: Date,
        content: String,
        rating: Number,
        likes: [ObjectId]   //Array of users id liked this review
    });
    const LocalBizSchema = new Schema({
        name: String,
        categories: [String],
        address: String,
        rating: Number
    });
     
    const UserSchema = new Schema({
        email: String,
        displayName: String
    });
     
    const BrandSchema = new Schema({
        name: String,
        localBizs: [ObjectId] //Array of localbizs id of this brand
    });
     
    var Review = mongoose.model('Review', ReviewSchema);
    var LocalBiz = mongoose.model('LocalBiz', LocalBizSchema);
    var User = mongoose.model('User', UserSchema);
    var Brand = mongoose.model('Brand', BrandSchema);
     
    module.exports = {
      Review,
      LocalBiz,
      User,
      Brand
    }

    Setup relax-ql

    var ql = require('relax-ql');
    ql.add({
      models: require('./db')
    });

    Example 1

    You want get 10 reviews from database.

    With mongoosejs:

    Review
      .find()
      .limit(10)
      .lean()
      .exec()
      .then(reviews => console.log(reviews));

    With relax-ql:

    ql`*: Review().limit(10)`
      .exec()
      .then(result => console.log(result));

    Example 2

    The code above is a simple query. And now, we will do with a more complex query. We will get 10 reviews from database and each review contain information of reviewer.

    With mongoosejs:

    Review
      .find()
      .limit(10)
      .lean()
      .exec()
      .then(reviews => {
        return Promise.all(
          reviews.map(review => new Promise((resolve, reject) => {
            User.findOne({
              _id: review.author
            })
            .lean()
            .exec()
            .then(user => {
              review.authorDetail = user;
              resolve(review);
            })
            .catch(err => eject(err))
          }))
        )
      })
      .then(reviews => console.log(reviews));

    With relax-ql:

    ql`
      *: Review().limit(10)
        authorDetail: User[this.author]`
    .exec()
    .then(reviews => console.log(reviews));

    Example 3

    Select and projection data

    With mongoosejs:

    Review
      .find()
      .limit(10)
      .lean()
      .exec()
      .select({
        content: true,
        rating: true,
        author: true,
        likes: {
          $slice: 3
        }
      })
      .then(reviews => {
        return Promise.all(
          reviews.map(review => new Promise((resolve, reject) => {
            User.findOne({
              _id: review.author
            })
            .lean()
            .exec()
            .select({
              displayName: true,
              email: true
            })
            .then(user => {
              review.author = user;
              resolve(review);
            })
            .catch(err => eject(err))
          }))
        )
      })
      .then(reviews => console.log(reviews));

    With relax-ql:

    ql`
      *: Review().limit(10)
        content
        rating
        likes(slice: 3)
        author:= User[this.author]
          displayName
          email`
    .exec()
    .then(reviews => console.log(reviews));

    So, what is relax-ql can do?

    Begin with Find or FindOne

    Find 10 reviews with rating = 5

    ql`*: Review(rating == 5).limit(10)`
    .exec()
    .then(reviews => console.log(reviews));

    or

    ql`*: Review.find(rating == 5).limit(10)`

    FindOne review with rating >= 3

    ql`*: Review[rating == 5]`

    or

    ql`*: Review.findOne(rating == 5)`

    Support query selectors

    Comparison

    == : $eq
    != : $ne
    < : $lt
    > : $gt
    <= : $lte
    >= : $gte
    IN : $in
    NIN : $nin
    in : $in
    nin : $nin
    

    Logical

    && : $and
    

    Element

    EXISTS : $exists
    TYPE : $type
    exists : $exists
    type : $type
    

    Evaluation

    MOD : $mod
    REGEX : $regex
    TEXT : $text
    WHERE : $where
    mod : $mod
    regex : $regex
    text : $text
    where : $where
    

    Array

    ALL : $all
    MATCH : $elemMatch
    SIZE: $size
    all : $all
    match : $elemMatch
    size: $size
    

    Example query selectors

    Example 1

    likeNumber >= 5 && likeNumber < 100 && rating IN [2, 3] && comments EXISTS true
    

    parse to

    {
      likeNumber: {
        $gte: 5,
        $lt: 100
      },
      rating: {
        $in: [2, 3]
      },
      comments: {
        $exists: true
      }
    }

    How to write a query selector

    The bellow is rules for write query selector.

    1. Attribute always is left side of operator and value to compare must be right side of operator
    2. Currently, relax-ql not support OR logical operation. Only use and. But we absolutely write a complex query selector. I will show to you in next session.
    3. relax-ql support some type like: String, Number, Boolean, Array, Object, Regex. Example: "abc" or 'abc' is string, [1, 2, 3] is array of number, {a : 1} is an object, true or TRUE or False is booleam type and /abcd/ig is regex type.
    4. Can pass value by params, also pass a query selector by params. I'll show to you in next session.

    Advance Find and FindOne

    Find with complex query selector

    ql`reviews: Review(likeNumber >= 5 && likeNumber < 100 && rating IN [2, 3] && comments EXISTS true)`
    ql`reviews: Review(${{
        $or: [{
          likeNumber: 5
        }, {
          likeNumber: 4
        }]
      }})`

    or use param

    var reviewQuery = {
      $or: [{
        likeNumber: 5
      }, {
        likeNumber: 4
      }],
      rating: {
        $in: [1, 2]
      },
      comments: {
        $all: [
          { "$elemMatch" : { likeNumber: { $gt: 50, $lt: 100} } },
          { "$elemMatch" : { content : { $regex: /abcd/i } } }
        ]
      }
    }
    ql`reviews: Review(${reviewQuery})`

    Find with options

    relax-ql support limit, skip, sort (ASC, DESC)

    ql`reviews: Review(likeNumber >= 5).limit(10).skip(10).DESC('rating').ASC('createdAt')`

    Note: Sepecial support unlean optional. Because the query default call .lean() function. When we use unlean option, the query will not call .lean(). It very usefull when you defined some virtual atributes. Example:

    Model:

    var PersonSchema = new Schema({
      name: {
        first: String,
        last: String
      }
    }, {
      toObject: { virtuals: true },
      toJSON: { virtuals: true }
    });
    PersonSchema
      .virtual('displayName')
      .get(function () {
        return this.name.first + ' ' + this.name.last;
      });
     
    var Person = mongoose.model('Person', PersonSchema);

    Don't use unlean()

    ql`*: Person[]`
    .exec()
    .then(p => console.log(p));
    // display on screen
    /*
    {
      name: {
        first: 'abc',
        last: 'def'
      }
    }
    */

    Use unlean() optional

    ql`*: Person[].unlean()`
    .exec()
    .then(p => console.log(p));
    // display on screen
    /*
    {
      name: {
        first: 'abc',
        last: 'def'
      },
      displayName: 'abc def'
    }
    */

    Selection and projection

    Selection

    ql`
      reviews: Review(likeNumber >= 5)
        comments
        content
        rating
    `

    or

    ql`
      status: Status[]
        content
        user
          name
          gender
     
    `

    It'll be

    Status.findOne()
      .select({
        content: true,
        user: true,
        'user.name': true,
        'user.gender': true
      })

    Projection

    Support

    slice : $slice
    SLICE : $slice
    match : $elemMatch
    MATCH : $elemMatch
    meta : $meta
    META : $meta
    

    Example:

    ql`
      status: Status[]
        content
        likes(3)
        comments(slice: 3)
    `

    =>

    Status.findOne()
      .select({
        content: true,
        'likes.$': 3,
        comments: {
          $slice: 3
        }
      })

    Nested query

    Example:

    ql`
      reviews: Review(rating >= 3).limit(5)
        author
        authorDetail: User[this.author]
          displayName
        localBiz:= LocalBiz[this.localBiz]
          name
          address
        likes
          *: User[this.$value]
            displayName
        relatedReviews: Review(localBiz == this.localBiz).limit(3)
          content
          author:= User[this.author]
            displayName
    `

    Keyword:

    • this: The pointer to parent
    • this.$value: The value of parent
    • := is select this attribute and result of query will overide to old value.
    • '*' is result of child query will overide to parent value

    How to access data of parent

    Can you see the format of relax-ql like this:

    ql`
      key_name: Model.function(query_selector).optional().optional()
        attr_selected
        attr_selected
        key_name: Model.function(query_selector).optional().optional()
          attr_selected
          attr_selected
     
        attr_selected:= Model.function(query_selector).optional().optional()
          attr_selected
            key_name: Model.function(query_selector).optional().optional()
              attr_selected
              attr_selected
        attr_selected
          *: Model.function(query_selector).optional().optional()
            attr_selected
            attr_selected
     
    `

    We have two way to access to parent data.

    1. Use this
    • It will access to nearest parent data
    1. Use key_name
    • It will access to first parent match with key_name

    Roadmap

    • Support all logical: AND, OR, NOT
    • Optimize query findOne
    • Fix projection: likes({$slice: 3})

    Install

    npm i relax-ql

    DownloadsWeekly Downloads

    28

    Version

    1.3.5

    License

    ISC

    Last publish

    Collaborators

    • avatar