Home

Awesome

Talking is like medicine, little of it benefits and lot of it harms. - Imam Ali

myCouch

CouchDB client wrtten in Nim.

currently it's based on CouchDB v3.1.1

note: deprecated & no-op APIs are not included

APIs

How can I know what proc for API should I use?

  1. you go to the CoudhDB documentation link
  2. copy a API link (eg: api/ddoc/render.html#db-design-design-doc-update-update-name)
  3. search that link in the couchdb/api.nim or [github-page]
  4. you found the corresponding proc!

Limitation

note: examples are placed in tests/tapi.nim

Features

Mango Query-Lang

  mango(
    query= PS(@name == "hamid" and @year notin [1399])
    fields= @["name", "stars"]
  )

converts to =>

  {
    "selector" : {
      "$and": {
        "name": {
          "$eq": "hamid"
        },
        "year": {
          "$nin": [1399]
        }
      }
    },
    "fields": ["name", "stars"]
  }

SQL-like Selector Parser

you can put the query im 2 ways: [PS is an alias for parseSelector]

PS:
  nil                        # {"_id": {"$gt": nil}}
  
  # field name variants
  # since nim doesn't support underline at the first character of an identifier, you can use -
  field == true             # {"<THE VALUE OF VAR 'field'>": {"$eq": true}}
  @field == true            # {"field"                     : {"$eq": true}}
  @-field                   #  "_field" 
  @"_field._sub"            #  "_field._sub"

  # comparisions < <= == != >= >
  @year < bad_year            # 'year' is a field / 'bad_year' is var

  @name =~ "ali"              # regex match | $regex
  @name =~ pat.pattern        # ""

  @year mod [4,2]             # modular | $mod

  @year in    [2020, 2021]    # in | $in
  @year notin [2020, 2021]    # not in | $nin

  ?= @genre or ?! @genre      # (?=): exists, (?!): not exists | $exists
  
  ? @genre or ! @genre        # (?): == true, (!): == false
  
  @year is myType             # is for type spesification | $type
  @year is number             # object, array, string, number, nil, bool

  @list.size(3)               # match array len | $size
  @list.all(["hamid", "ali"]) # all function are the same for elemMatch, allMatch, keyMapMatch functions | $all 

  # or not | $and $or $not
  not (@artist == "mohammadAli" and (@genre notin ["pop", "rock"] or @artist == "iman khodaee"))
  (@field == 3 and @date == 12).nor(@field == 4) # since nim doesnt have 'nor' operator | $nor

async + sync!

you can use all of APIs with your favourite runtime(did i use the right word?).

Qeury Server

Do you remember some of Erlang's built-in View functions? here were gonna do something like that [but in nim]

we have 5 entry points:

  1. mapfun -> map functions
  2. redfun -> reduce functions
  3. updatefun -> update
  4. filterfun -> filter
  5. validatefun -> validate

each one are name of a macro that must be associated with corresponding proc.

every proc must be matched with it's corresponding pattern [you can see patterns in mycouch/queryServer/designDocuments.nim] otherwise you'll get an error.

here's an exmaple of proc testMap as an map function

import json, tables
import mycouch/queryServer/[protocol, designDocuments]

proc testMap(doc: JsonNode): seq[JsonNode] {.mapfun.}= 
  # emit values like: [genre, movie_name]
  if ("title" in doc) and ("genres" in doc):
    for genre in doc["genres"]:
      emit(genre, doc["title"])

when isMainModule:
  run()

notes:

compile that file and config the query server doc

then you can create a design document with your query server: [design doc example for above code]

{
    "_id": "_design/temp",
    "language": "<your-language-server-name>",
    "views": {
        "myview": {
            "map": "testMap"
        }
    }
}

done! your query server is ready! [ examples with more details are placed in tests/queryServerInstance.nim]

TODOs

Notes

contributions are welcome :D