api queries. complex queries. please explain

hi

i would like to use queries more frequently. problem is that i do not understand all that is written on wiki.
considder following examples:

i have a routerboard with 5 ethernet interfaces, 1 wlan, 1 bridge

print all interfaces that are not type=wlan and type=bridge

/interface/print
=.proplist=type,.id
?type=wlan
?#!
?type=bridge
?#|

above works well

but this one:

/interface/print
=.proplist=type,.id
?type=wlan
?#!
?type=bridge
?#!
?#|

should return all interfaces that are not in type= wlan,bridge. i should get all ethernet interfaces only. in return i get all interfaces. it doesn’t make sense to me.

/interface/print
=.proplist=type,.id
?type=wlan
?type=bridge
?#|!

this one returns all ethernet interfaces only.

/interface/print
=.proplist=type,.id
?type=wlan
?type=bridge
?#&!

this returns all interfaces.
how does it correspond to De Morgan’s law ?

! character replaces top value with the opposite.

from what is written you can understand that ! character always corresponds to the item at index = 0. if it does how to negate items at other indexes ?
what is a “top value” ?

on wiki in table where operations are described word ‘character’ is frequently used. what kind of character ? what characters are allowed ?

/interface/print
=.proplist=type,.id
?type=wlan
?#!
?type=bridge
?#!
?#|

in above example indexes in stack are:
?type=wlan has stack index = 0
?type=bridge has stack index = 1
am i correct ?

index that is followed by a character pushes copy of value at that index.

again the question. witch character ?

index that is followed by the end of word replaces all values with the value at that index.

if i write:

/interface/print
?type=wlan
?name=bridge
?#0

it will be transformed into:

/interface/print
?type=wlan
?name=wlan

am i right ?

sequence of decimal digits followed by any other character or end of word is interpreted as a stack index. top value has index 0.

what does it mean ?

000101023123!

“000101023123” is a sequence of decimal digits
what index is it ?

. after another character pushes copy of top value.

so it means that a “.” will always point in value at stack index = 0 ?

As the spec says, all items are placed on a “stack”. So, “top value” refers to the top of said stack at that point in time.

So… let’s look at this line by line:

?type=wlan
?#!
?type=bridge
?#!
?#|
?type=wlan

The scripting equivalent of

where type=wlan
?#!

pops a value, and pushes back the opposite, i.e. the scripting equivalent of

where !(type=wlan)
?type=bridge

Unless otherwise told later on, this ends up acting as an “or”, i.e. the scripting equivalent of

where !(type=wlan) || type=bridge

(keep in mind that now, “type=bridge” is at the top of the stack, while “!(type=wlan)” is after it)
4.

?#!

Pops the last value, negates it, and pushes the result back on the stack, i.e. the scripting equivalent of

where !(type=wlan) || !(type=bridge)
?#|

Pops two values, "or"s them, and pushes the result back on the stack. In this case, the query would match the same stuff with or without that line, i.e. we have

where (!(type=wlan) || !(type=bridge))

I don’t think this is what you’re after, since even in scripting, this would output all interfaces. That’s because “or” short circuits - if the left part is true, the right is never checked. So, when RouterOS is evaluating an interface of type “bridge” - that’s not “wlan”, so it goes in the result set. And when an interface of type “wlan” comes in - it’s first false, but then the “or” becomes true since “wlan” is not “bridge”.

You want to “and” those instead, i.e.

?type=wlan
?#!
?type=bridge
?#!
?#&

which is a scripting equivalent of

where (!(type=wlan) && !(type=bridge))

or, as you’ve already discovered yourself, you can compact that into

?type=wlan
?type=bridge
?#|!

which would be the scripting equivalent of

where !(type=wlan || type=bridge)

because in that case, the stack is initially made of “type=wlan” at the bottom, and “type=bridge” at the top - you take those two, or them, and push the result back on the stack. You have exactly one value in the stack now, which is then negated.



P.S. If you’re using PHP, with my client, it’s slightly easier, in that you can simply think of every next step as adding the operation and placing “()” around everything, e.g.

RouterOS\Query::where('type', 'wlan') // (type=wlan)
->orWhere('type', 'bridge') // ((type=wlan) || (type=bridge))
->not() // (!((type=wlan) || (type=bridge)))
->andWhere('mtu', 1500);// ((!((type=wlan) || (type=bridge))) && mtu=1500)   

I’m planning on eventually making a string parser so that people can write the scripting equivalents, but I haven’t made one yet.

thx for your reply. it did answer some questions.


/interface/print
?type=wlan
?#!
?type=bridge
?#!
?#&

is equivalent to

/interface/print
?type=wlan
?type=bridge
?#|!

ok got that.

above is the same as:

(!a && !b) == !(a || b)

but how to write this:

!(a && b) == (!a || !b)
!(a && b)

would be

?a
?b
?#&!

and

(!a || !b)

would be

?a
?#!
?b
?#!|

In their shortest (yet understandable…) form that is.



As a general rule… convert the expression to Reverse Polish Notation.

thx for reply

my goal is to write some wrapper for queries so when someone will use my api implementation (in python3) he/she will not have to learn query syntax.

for example:

query('/interface').where(query.type == 'ethernet').returning(query.id)

this will show all ethernet interfaces with =.proplist=.id

it is just an begining so above may change.

The functions that I use can be found at SQL abstraction APIs (“Zend Framework 1”'s Zend_Db_Select in particular), which is why I settled for them, and would recommend in case you don’t have anything better.

I don’t know Python enough to know about any SQL abstraction APIs, but if you know some, I’d highly recommend you make your API close to that, as this is what Python programmers are likely to already know.

Alternatively, you could try doing the very thing I haven’t yet done - an expression parser, so that the scripting equivalents can be written directly as strings, as opposed to indirectly with methods, which is what any abstraction API does by definition.

Also, side note: “.proplist” is an attribute word, not a query word. It could (if you let it…) go into the same place(s) where users would normally specify arguments (e.g. “detail”, “stats”, etc.).

i don’t want to distinguish normal print and print with querry. in a sense of different methods.

query('/interface').where(query.type == 'ethernet').returning(query.id)

you could also use

query('/interface').returning(query.id)

thus for second command

/interface/print
=.proplist=.id

will be executed

OK, but what if I want to specify additional arguments to the print (e.g. detail)?

An additional “fixed” method? e.g.

query('/interface').detail().returning()

This means you’ll need to release a new version of your client with new methods every time MikroTik adds a new argument to print (which, in fairness, is rarely, but still…).

An additional method accepting such arguments or an additional argument? e.g.

query('/interface', {'detail': '', 'stats': ''}).returning()

This means returning()'s argument becomes kind’a pointless, since one may as well do

query('/interface', {'detail': '', 'stats': '', '.proplist': '.id,tx-byte,rx-byte'}).returning()

It’s your decision in the end of course… just be aware of both choice’s consequences.

Very simple question

I want to disconnect a user from ppp active
so I send
/ppp/active/remove
?name=value

it returns done
but that user specified in the value is not disconnected.
any input?

http://wiki.mikrotik.com/wiki/API#Queries
Api queries are not supported in commands other than print/getall. What you have posted is clearly a bug/confusing behavior of api. if queries are not supported in commands other than print/getall this should fail with approprieate error. i have tried with =name and with =.id. effect is still the same.

i suggest posting this issue to mikrotik support.

Thansk
at the end I found the solution, print with appropriate query and get the .id and then execute the remove command to specific .id and it works

I agree that if I send remove with filter ?name=xx and it is not supported instead to return !done shoudl return something like ?name not supported in remove command.

Massimo

I see no mention of querying for “begins with x”, “ends with x”, or “contains x”. Is this simply not possibly?

CLI examples:

/interface print where name~"^ether"
/interface print where name~"gateway$"
/interface print where name~"master"

It’s not possible… yet (hopefully).

See this topic.

Thanks a lot , it works using the .id

I’m doing my winforms app to connect to Mikrotik router using tik4net right now. I’m setting up Wifi Marketing and have some trouble with convert to API command:
Example:
/interface wireless set [ find default-name=wlan1 ] disabled=no mode=ap-bridge ssid=“Testing Wifi Marketing”
My Convert:
/interface/wireless/set
?default-name=wlan1
=disabled=no
=mode=ap-bridge
=ssid=Testing Wifi Marketing

API does not support query in set operations. Split your action into two commands:

  • Print
  • Set

Print:

/interface/wireless/print
?default-name=wlan1
=.proplist=.id

Result:


!re
=.id=*C
!done

Set with id = *C (in this example):


/interface/wireless/set
=disabled=no
=mode=ap-bridge
=ssid=Testing Wifi Marketing
=.id=*C

Result:


!done



Now you’ve mentioned tik4net, this is much easier:


  using (var conn = tik4net.ConnectionFactory.OpenConnection(TikConnectionType.Api, "192.168.88.1", 8728, "admin", "password"))
  {
    var obj = conn.LoadList<InterfaceWireless>(conn.CreateParameter("default-name", "wlan1")).First();
    obj.Disabled = false;
    obj.Mode = tik4net.Objects.Interface.InterfaceWireless.WirelessMode.ApBridge;
    obj.Ssid = "Testing Wifi Marketing";
    conn.Save(obj);
  }

Hi
Help with compiling a Linux command for api mikrotik, displaying the MAC address of the pppoe interface with the specified “remote-address”.
Waiting for something similar to:
read ip int mac t <<< “$(send_mikrotik_cmd “$ip” “${telnet_login}” “${telnet_password}” /ip arp print .proplist=address,mac-address,interface ?address=${remote-address})”
That’s for IP interfaces, but i need pppoe, and “/ip arp” is not suitable.
Thanks in advance

Isn’t this incorrect? I believe it uses AND logic by default. So at these two steps it would be

where !(type=wlan) && type=bridge

and

where !(type=wlan) && !(type=bridge)

respectively?

Maybe it was different in 2013, but right now the manual says the following:

PS: Why are inline code boxes still broken on this forum after all these years? T_T