SQL::Abstract::Limit failing at OR logic - perl

I'm trying to create an OR logic query using Class::DBI/Class::DBI::AbstractSearch. My code looks something like this:
my $results = Example::CDBI::Quote->search_where(
{ field_1 => {'like', $search_string},
field_2 => {'like', $search_string}},
{logic => 'or'}
);
According to the documentation this should work. It says that the information is passed to SQL::Abstract::Limit, which shows as taking the logic parameter. Unfortunately, MySQL shows the following in the query log (edited for brevity, and assuming a search of "123"):
SELECT * FROM quote WHERE ((field_1 LIKE '123' AND field_2 LIKE '123' ))
I have trying changing 'or' to 'OR' (silly, but worth a shot) which did not work. I also tried hunting down the logic in SQL::Abstract::Limit, but this operator is being passed to SQL::Abstract instead.
How do I get SQL::Abstract::Limit to accept OR logic from Class::DBI?
How Class::DBI calls SQL::Abstract::Limit
I was able to determine how SQL::Abstract::Limit is being constructed. I put values in instead of the variable names so it is easier to read.
my $sql = SQL::Abstract::Limit->new({'logic' => 'OR'});
my($phrase, #bind) = $sql->where(
{'field_1'=>{'like' => '123'},'field_2'=>{'like'=>'123'}},
undef, undef, undef);

You can apply OR locally like this:
use SQL::Abstract;
my $sql = SQL::Abstract->new;
my ($stmt, #bind) = $sql->where(
{ -or => [ { field_1 => { 'like', 'John' }},
{ field_2 => { 'like', 'John' }},
],
}, []);
gives in $stmt:
WHERE ( ( field_1 LIKE ? OR field_2 LIKE ? ) )
The logic property can be set in SQL::Abstract constructor, but I don't have idea how to propagate from Class::DBI.
Edit: I don't know if it is bug or feature, but it the operators changed by logic clause seems apply only when you define with arrayrefs. With hashrefs, you get always AND:
my $sql_and = SQL::Abstract::Limit->new(logic => 'AND');
my $sql_or = SQL::Abstract::Limit->new(logic => 'OR');
say $sql_and->where(['field_1'=>{'like' => '123'},'field_2'=>{'like'=>'123'}]);
# WHERE ( ( field_1 LIKE ? AND field_2 LIKE ? ) )
say $sql_or->where (['field_1'=>{'like' => '123'},'field_2'=>{'like'=>'123'}]);
# WHERE ( ( field_1 LIKE ? OR field_2 LIKE ? ) )
Or, to work with Class::DBI:
my $results = Example::CDBI::Quote->search_where(
[ field_1 => {'like', $search_string},
field_2 => {'like', $search_string}],
{logic => 'or'}
);

Related

Read a value from an array

I have an array defined as below.
I am able to fetch the value for email and uid as mentioned below. How do I get the user display name userdn?
my ($mesg) = $ldap->search(
scope => 'one',
base => $userdn,
filter => $filter,
attrs => [ 'email', 'pwdChangedTime', 'uid' ],
);
This is how I read the email ID and uid, but I'm unable to read the user display name.
foreach my $entry ( $mesg->all_entries ) {
$email = ${ $entry->get( 'email' ) }[0];
$uid = ${ $entry->get( 'uid' ) }[0];
$uName = ${ $entry->get( 'userdn' ) };
}
This is not really about an array, but rather about how to use Net::LDAP. There are no arrays in your example code.
You want to get the userdn property for the items returned from your LDAP query. In order to do that, you need to tell search to fetch those.
A list of attributes to be returned for each entry that matches the search filter.
This means, you need to include userdn in the call to search.
my ($mesg) = $ldap->search(
scope => 'one',
base => $userdn,
filter => $filter,
attrs => [ 'email', 'pwdChangedTime', 'uid', 'userdn' ],
);
Now you can get the userdn.
$uName = $entry->get('userdn')->[0];
I've used the arrow dereference syntax instead of ${ ... }[0] here because I find that easier to read. However, as ysth points out below in the comments, get is deprecated and not documented in the current version of Net::LDAP. It was deprecated in release 0.21 in September 2000, which is currently seventeen years ago.
Instead, you should probably use get_value, which returns the first value of that attribute.
$uName = $entry->get_value('userdn');

Cakephp table query

I have a members table. It has these columns:
ID
fname
lname
parent
spouse
I want 'parent' field to have select option in a form which uses 'fname' & 'lname' and store it in 'parent' field. I have tried to do this Members controller:
$results = $this->Members->find('list',
array('order'=>array('fname DESC')));
$parent = $results->toArray();
I am calling $parent variable as an input. However that method doesn't work. I don't know how to figure it out.
When calling list you can configure the fields used for the key and value with the keyField and valueField options respectively.
Doc: https://book.cakephp.org/3.0/en/orm/retrieving-data-and-resultsets.html#finding-key-value-pairs
Like this
$query = $articles->find('list', [
'keyField' => 'slug',
'valueField' => 'title'
]);
In your case it will be like this
$results = $this->Members->find('list', array(
'keyField' => 'ID',
'valueField' => 'parent'
'order'=>array('fname DESC'),
));
$parent = $results->toArray();

DBIx::Class: How can I sort on multiple substrings of a column?

I have a SQLite-database with table with a document number following this schema:
16-145-45
16-127-30
16-141-42
16-122-14
15-090-04
15-089-15
15-089-05
I'd like to sort the ResultSet on the first and last part of the number, like this. First, all documents starting with the highest two-digit prefix (16) sorted by the last 2 digits and then the same with the next block, and so on.
16-145-45
16-141-42
16-127-30
16-122-14
15-089-15
15-089-05
15-090-04
Is there a way to do this in DBIx::Class with some sort of custom order_by clause, or what would be the approach?
I have tried the following, which does not work, because the middle part of the number is also considered for sorting:
my #rs = $self->search(undef,
{
order_by => { -desc => 'me.number' }
}
);
If you want the database to sort the results, you have to use literal SQL.
Here's an example for Postgres (I added a space after the backslash to fix the syntax highlighting):
my #rs = $self->search(undef,
{
order_by => \ "split_part(number, '-', 1) || split_part(number, '-', 3) DESC",
}
);
Or, by creating an output column with the +select result set attribute:
my #rs = $self->search(undef,
{
'+select' => [
{ sort_key => \ "split_part(number, '-', 1) || split_part(number, '-', 3)" },
],
'+as' => [ qw(sort_key) ], # Make sort key accessible from DBIC.
order_by => { -desc => 'sort_key' },
}
);
Another approach is to retreive the whole unsorted result set, and sort it on the client side. DBIC doesn't have any specific features to help you with that, so simply use Perl's sort function.
Since the answer from #nwellnhof works like a charm, I just wanted to provide the corresponding syntax for SQLite, which does not know the split_part() function.
# SQL for filtering the doc number in SQLite
my #rs = $self->search(undef,
{
order_by => \ "SUBSTR(me.number, 1, 2) || SUBSTR(me.number, -2, 2) DESC"
}
);
You need to extract additional columns from the result set which are equal to the value of the function that you want to sort by. Then you can just put those columns in an order_by clause as normal
This assumes that your document number field is called docnum. It fetches all the columns from Table plus the two substrings of docnum called docnum1 and docnum3
my $rs = $schema->resultset('Table')->search(undef,
{
'+select' => [
{ substr => [ 'docnum', 1, 2 ], -as => 'docnum1' },
{ substr => [ 'docnum', -2 ], -as => 'docnum3' },
],
order_by => [ { -desc => 'docnum1' }, { -desc => 'docnum3' } ],
}
);

perl object and attribute

I am trying to create perl object (and its associated attributes), based on some conditions (for example the version of the object), a certain attribute is not needed (or invalid otherwise). How do I create the object, without having to specify the object twice (once for each condition).
my $obj = myobject->new (
attr1 => value1,
attr2 => value2,
attr3 => valuen,
)
Can I for example, define a variable and assign it different value based on the condition? Like this:
my $attribute = (condition)? "":'attr4 => value4';
and simply define the object like this:
my $obj = myobject->new (
attr1 => value1,
attr2 => value2,
attr3 => valuen,
$attribute,
)
This obviously will err out. But you get my idea. How do I make this work?
The arguments to new are just a list — just like the arguments to anything, in fact. You can do what you want with an array instead of a string.
my #attribute = $condition ? () : (attr4 => value4);
my $obj = MyObject->new(
attr1 => value1,
attr2 => value2,
attr3 => value3,
#attribute
);
You can also use a hash if order doesn't matter (which it usually doesn't for constructors) — whatever makes your code better.
I think my approach would be to build a hash of parameters, and pass it to the constructor once it was complete
It would look like this. I've wrapped the whole thing in a do block to limit the scope of %attr; it's not essential. of course condition has to be a compilable Perl expression
my $obj = do {
my %attr = (
attr1 => 'value1',
attr2 => 'value2',
attr3 => 'valuen',
);
$attr{attr4} = 'value4' if condition;
MyObject->new(%attr);
};

Find query with and operator in PHP

Hi i am working on backend of web application & want to find the documents from mongodb database that contain key active_status with value set to both 1 & 2. With mongodb PHP i am confused of how to find with both parameters in single query.
My query was this:
$mongoDb = MongoDbConnector::getCollection("endusers");
$endUserData = $mongoDb->find(array('active_status' => 1, '$and' => array('active_status' => 2)));
I have to fetch the users whose active_status should be 1 & 2. The above query doesnt seems to work. What is the right one for that?
Thanks on advance for quick response.
You have $and the wrong way around. Both arguments need to be included:
$endUserData = $mongoDb->find(array(
'$and' => array(
array( 'active_status' => 1 )
array( 'active_status' => 2 )
)
));
And since that would only make sense when looking for both elements within an array element, then you should instead use $all, which is shorter syntax:
$endUserData = $mongoDb->find(array(
'active_status' => array( '$all' => array(1,2) )
));
I should add that unless you intend to match a document like this:
{ "active_status" => [1,2] }
The you do not in fact want $and at all, but rather you want $or or better yet $in for multiple possible values on the same field:
$endUserData = $mongoDb->find(array(
'active_status' => array( '$in' => array(1,2) )
));
This matches documents like this:
{ "active_status": 1 },
{ "active_status": 2 }