Warpath v0.1.0 Warpath View Source

A implementation of Jsonpath expression proposal by Stefan Goessner for Elixir.

Warpath support dot–notation expression $.store.book[0].author or the bracket–notation $['store']['book'][0]['author'].

Operators

OperatorDescription
$The root element to query. This starts all path expressions.
@The current node being processed by a filter predicate.
*Wildcard. All objects/elements regardless their names.
..Deep scan, recursive descent.
.nameDot-notated child, it support string or atom as keys.
['name']Bracket-notated child, it support string or atom as keys.
[int (,int>)]Array index or indexes
[start:end:step]Array slice operator. Start index inclusive, end index exclusive.
[?(expression)]Filter expression. Expression must evaluate to a boolean value.

Filter operators

Filter are expression that must be evaluated to a boolean value, Warpath will use then to retain data when filter a list, a filter expression have the syntax like this [?( @.category == 'fiction' )].

All filter operator supported by Warpath have the same behavior of Elixir lang, it means that it's possible to compare different data types.

The down side of this approach is that filter a list with different data types could result in undesired output, for example:

  iex> data = [:atom, "string", 11]
  ...> Warpath.query(data, "$[?(@ > 10)]")
  {:ok, [:atom, "string", 11]}

The expression were evaluate and return the entirely input list as the output, this happen because the way elixir implement comparison, check the Elixir getting started page for more information.

To cover this case Warpath support some functions to check the underline data type that the filter is operate on, it could be combined with and operator to gain the strictness among comparison.

The above expression could be rewrite to retain only integer values that is greater than 10 like this.

iex> data = [:atom, "string", 11]
...> Warpath.query(data, "$[?(@ > 10 and is_integer(@))]")
{:ok, [11]}
OperatorDescription
==left is equal to right
===left is equal to right in strict mode
!=left is not equal to right
!==left is not equal to right in strict mode
<left is less than right
<=left is less or equal to right
>left is greater than right
>=left is greater than or equal to right
inleft exists in right [?(@.price in [10, 20, 30])]
andlogical and operator [?(@.price > 50 and @.price < 100)]
orlogical or operator [?(@.category == 'fiction' or @.price < 100)]
notlogical not operator [?(not @.category == 'fiction')]
FunctionDescription
is_atom/1check if the given expression argument is evaluate to atom
is_binary/1check if the given expression argument is evaluate to binary
is_boolean/1check if the given expression argument is evaluate to boolean
is_float/1check if the given expression argument is evaluate to float
is_integer/1check if the given expression argument is evaluate to integer
is_list/1check if the given expression argument is evaluate to list
is_map/1check if the given expression argument is evaluate to map
is_nil/1check if the given expression argument is evaluate to nil
is_number/1check if the given expression argument is evaluate to number
is_tuple/1check if the given expression argument is evaluate to tuple

Examples

#Dot-notated access
iex>Warpath.query(%{"category" => "fiction", "price" => 12.99}, "$.category")
{:ok, "fiction"}

#Bracket-notated access
iex>Warpath.query(%{"key with whitespace" => "some value"}, "$.['key with whitespace']")
{:ok, "some value"}

#atom based access
iex>Warpath.query(%{atom_key: "some value"}, "$.:atom_key")
{:ok, "some value"}

#quoted atom
iex>Warpath.query(%{"atom key": "some value"}, ~S{$.:"atom key"})
{:ok, "some value"}

#wildcard access
iex>document = %{"store" => %{"car" => %{"price" => 100_000}, "bicycle" => %{"price" => 500}}}
...>Warpath.query(document, "$.store.*.price")
{:ok, [500, 100_000]}

#scan operator
iex>document = %{"store" => %{"car" => %{"price" => 100_000}, "bicycle" => %{"price" => 500}}}
...>Warpath.query(document, "$..price")
{:ok, [500, 100_000]}

#filter operator
iex>document = %{"store" => %{"car" => %{"price" => 100_000}, "bicycle" => %{"price" => 500}}}
...> Warpath.query(document, "$..*[?( @.price > 500 and is_integer(@.price) )]")
{:ok, [%{"price" => 100000}]}

#contains filter operator
iex>document = %{"store" => %{"car" => %{"price" => 100_000}, "bicyle" => %{"price" => 500}}}
...> Warpath.query(document, "$..*[?(@.price)]")
{:ok, [%{"price" => 500}, %{"price" => 100_000}]}

#index access
iex>document = %{"integers" => [100, 200, 300]}
...> Warpath.query(document, "$.integers[0, 1]")
{:ok, [100, 200]}

#wildcard as index access
iex>document = %{"integers" => [100, 200, 300]}
...> Warpath.query(document, "$.integers[*]")
{:ok, [100, 200, 300]}

#slice operator
iex> document = [0, 1, 2, 3, 4]
...> Warpath.query(document, "$[0:2:1]")
{:ok, [0, 1]}

#optional start and step
iex> document = [0, 1, 2, 3, 4]
...> Warpath.query(document, "$[:2]")
{:ok, [0, 1]}

#Negative index
iex> document = [0, 1, 2, 3, 4]
...> Warpath.query(document, "$[-2:]")
{:ok, [3, 4]}

#options
iex>document = %{"integers" => [100, 200, 300]}
...> Warpath.query(document, "$.integers[0, 1]", result_type: :path)
{:ok, ["$['integers'][0]", "$['integers'][1]"]}

iex>document = %{"integers" => [100, 200, 300]}
...> Warpath.query(document, "$.integers[0, 1]", result_type: :value_path)
{:ok, [{100, "$['integers'][0]"}, {200, "$['integers'][1]"}]}

Link to this section Summary

Functions

Query data for the given expression.

The same as query/3, but rise exception if it fail.

Link to this section Functions

Link to this function

query(term, expression, opts \\ [])

View Source
query(term(), String.t(), [{:result_type, :value | :path | :value_path}]) ::
  any()

Query data for the given expression.

  #data structure
  iex> term = %{"name" => "Warpath"}
  ...> Warpath.query(term, "$.name")
  {:ok, "Warpath"}
  #raw json
  iex> term = ~s/{"name": "Warpath"}/
  ...> Warpath.query(term, "$.name")
  {:ok, "Warpath"}

Options:

result_type:

  • :path - return the path of evaluated expression instead of it's value
  • :value - return the value of evaluated expression - default
  • :value_path - return both path and value.
Link to this function

query!(term, expression, opts \\ [])

View Source

The same as query/3, but rise exception if it fail.