Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?

Generating complex LDAP queries with Perl

by grinder (Bishop)
on Jul 17, 2009 at 10:46 UTC ( #781021=CUFP: print w/replies, xml ) Need Help??

For the longest time, I've managed to limit my LDAP queries to the absolute minimum, (foo=bar), or heaven forfend, (| (foo=bar) (foo=rat)) (and it was only upon rereading that I realised I had left off the last closing parenthesis, which just goes to show how easy it is to get this wrong!)

Yesterday I had to write some really tangled queries to tease out a thousand or so records from a directory at $work. I ran into grief trying to keep my conditionals (and parentheses) nested, and concluded that it would be much better to a have a program generate the queries for me. Hence, given something like:

my $now = time; my $filter = AND( "(objectClass=dynCand)", "(candActive=TRUE)", "(dynActive=TRUE)", OR( NOT( "(candBase=EXTERNAL)" ), AND( "(candBase=EXTERNAL)", NOT( OR( "(dynProfile=ante)", "(dynProfile=catp)", "(dynProfile=fci)", "(dynProfile=mgn)", "(dynProfile=oppse)", "(dynProfile=pic)", "(dynProfile=ren)", ) ) ) ), "(candDateStart<=$now)", "(candDateEnd>=$now)", "(dynDateStart<=$now)", "(dynDateEnd>=$now)", );

The bit in the middle says "I want all the records that are internal, or if they are external, all except some profiles".

when I run the above code, it produces the fabulous:

(& (& (& (& (& (& (& (objectClass=dynCand) (candActive=TRUE)) (dynActive=TRUE)) (| (!(candBase=EXTERNAL)) (& (candBase=EXTERNAL) (!(| (| (| (| (| (| (dynProfile=ante) (dynProfile=catp)) (dynProfile=fci)) (dynProfile=mgn)) (dynProfile=oppse)) (dynProfile=pic)) (dynProfile=ren)))))) (candDateStart<=1247827075)) (candDateEnd>=1247827075)) (dynDateStart<=1247827075)) (dynDateEnd>=1247827075))

The implementation is trivial:

sub AND { return _joiner( '&', @_ ); } sub OR { return _joiner( '|', @_ ); } sub _joiner { my $op = shift; my $filter = shift; while (my $cond = shift) { $filter = "($op $filter $cond)" if defined $cond; } return $filter; } sub NOT { return "(!$_[0])"; } sub IGNORE { return; }

I threw in the IGNORE function to remove parts of the construct. Since Perl doesn't have a multi-line comment block, and you cannot embed POD within a function call, this was the easiest way to comment out a subconditional. So, if you ever need to build hairy LDAP queries, this might be for you.

• another intruder with the mooring in the heart of the Perl

Replies are listed 'Best First'.
Re: Generating complex LDAP queries with Perl
by jwkrahn (Monsignor) on Jul 17, 2009 at 21:41 UTC
    while (my $cond = shift) { $filter = "($op $filter $cond)" if defined $cond; }

    The while conditional will exit the loop if $cond is false or undefined so the subsequent test for defined is redundant, $cond will not be undefined inside the loop.   Note that if $cond also contains "0" or "" it will exit the loop.

      Just to situate the historical context, I added the if defined $cond much later on in the piece, when I added the IGNORE() function to change an AND() or an OR() into a no-op.

      The idea was to ensure that "valid1", "valid2", undef, "valid3" produces a syntactically correct query, without a fourth half-baked conditional creeping in there.

      But you know what? You're absolutely correct. Given the above and the following:

      sub _joiner { my $op = shift; my $filter = shift; while (my $cond = shift) { $filter = "($op $filter $cond)"; } return $filter; } my $filter = AND( "(a=1)", IGNORE( "(b=1)", "(b=2)", ), "(c=1)", );

      It does indeed produce (& (a=1) (c=1)). Thank-you very much for this insight, I appreciate it.

      • another intruder with the mooring in the heart of the Perl

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: CUFP [id://781021]
Approved by marto
Front-paged by moritz
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (8)
As of 2017-09-21 11:37 GMT
Find Nodes?
    Voting Booth?
    During the recent solar eclipse, I:

    Results (245 votes). Check out past polls.