Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query builder #842

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Added
- Support for the analytics component
- Function builder
- Query builder
- Solarium\Component\FacetSet::setMatches()
- Solarium\Component\FacetSet::setExcludeTerms()
- Solarium\Component\Facet\Field::setMatches()
Expand Down
84 changes: 84 additions & 0 deletions docs/queries/query-helper/query-builder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
Query builder
-------------

The query builder is a simple helper class to help writing and maintaining (filter) queries using Solr's query language.
While the query will only accept a single (composite) expression, the addition of filter queries can consist of multiple expressions.

Query example
-------------
```php
<?php

use Solarium\Builder\Select\QueryBuilder;

// ...

$query = $client->createSelect();

$expr = QueryBuilder::expr();
$builder = QueryBuilder::create()
->where($expr->andX(
$expr->eq('foo', 'bar'),
$expr->eq('baz', 'qux')
))
;

$query->setQueryFromQueryBuilder($builder);

// which would be equal to
$query->setQuery('foo:"bar" AND baz:"qux"');
```

Filter Query example
-------------
```php
<?php

use Solarium\Builder\Select\QueryBuilder;

// ...

$query = $client->createSelect();

$expr = QueryBuilder::expr();
$builder = QueryBuilder::create()
->where($expr->eq('foo', 'bar')),
->andWhere($expr->neq('baz', 'qux')
);

$query->addFilterQueriesFromQueryBuilder($builder);

// which would be equal to
$value = 'foo:"bar"';
$query->addFilterQuery(['key' => sha1($value), 'query' => $value]);
$value = '-baz:"qux"';
$query->addFilterQuery(['key' => sha1($value), 'query' => $value]);
```

Complex filter queries
----------------------
While the ``addFilterQueriesFromQueryBuilder`` method only provides in setting the facet query key and actual query, the ``QueryBuilder`` can be used in the construction of more complex facet queries.
If one, for example, need to add a tag to the filter query the following method could be used.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
If one, for example, need to add a tag to the filter query the following method could be used.
If one, for example, needs to add a tag to the filter query the following method could be used.

```php
<?php

use Solarium\Builder\Select\QueryBuilder;
use Solarium\Builder\Select\QueryExpressionVisitor;

// ...

$query = $client->createSelect();

$expr = QueryBuilder::expr();
$visitor = new QueryExpressionVisitor();

$builder = QueryBuilder::create()
->where($expr->eq('foo', 'bar'))
);

$query->addFilterQuery([
'key' => 'my-key,
'query' => $visitor->dispatch($builder->getExpression()[0]),
'local_tag' => 'my-tag',
]);
```
2 changes: 0 additions & 2 deletions src/Builder/AbstractExpressionVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
/**
* Expression Visitor.
*
* @codeCoverageIgnore
*
* @author wicliff <[email protected]>
*/
abstract class AbstractExpressionVisitor
Expand Down
7 changes: 5 additions & 2 deletions src/Builder/Comparison.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
/**
* Comparison.
*
* @codeCoverageIgnore
*
* @author wicliff <[email protected]>
*/
class Comparison implements ExpressionInterface
Expand Down Expand Up @@ -80,6 +78,11 @@ class Comparison implements ExpressionInterface
*/
public const MATCH = 'MATCH';

/**
* Empty.
*/
public const EMPTY = 'EMPTY';

/**
* @var string
*/
Expand Down
2 changes: 0 additions & 2 deletions src/Builder/CompositeComparison.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
/**
* Composite Expression.
*
* @codeCoverageIgnore
*
* @author wicliff <[email protected]>
*/
class CompositeComparison implements ExpressionInterface
Expand Down
189 changes: 189 additions & 0 deletions src/Builder/Select/ExpressionBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Solarium package.
*
* For the full copyright and license information, please view the COPYING
* file that was distributed with this source code.
*/

namespace Solarium\Builder\Select;

use Solarium\Builder\Comparison;
use Solarium\Builder\CompositeComparison;

/**
* Expression Builder.
*
* @author wicliff <[email protected]>
*/
class ExpressionBuilder
{
/**
* @param null $x
*
* @throws \Solarium\Exception\RuntimeException
*
* @return \Solarium\Builder\CompositeComparison
*/
public function andX($x = null): CompositeComparison
{
return new CompositeComparison(CompositeComparison::TYPE_AND, \func_get_args());
}
Comment on lines +24 to +34
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ... operator makes this typehintable and thus more readable.

Suggested change
/**
* @param null $x
*
* @throws \Solarium\Exception\RuntimeException
*
* @return \Solarium\Builder\CompositeComparison
*/
public function andX($x = null): CompositeComparison
{
return new CompositeComparison(CompositeComparison::TYPE_AND, \func_get_args());
}
/**
* @param Comparison ...$comparison
*
* @throws \Solarium\Exception\RuntimeException
*
* @return \Solarium\Builder\CompositeComparison
*/
public function andX(Comparison ...$comparison): CompositeComparison
{
return new CompositeComparison(CompositeComparison::TYPE_AND, $comparison);
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i've been working quite a bit with the FunctionBuilder recently and one of the benefits of not typehinting the arguments is that you can pass a string as argument for the edge cases you run into.

for instance creating this function filter(mult(price,quantity),or(cats:equal(category,_))) would translate into

$expression = FunctionBuilder::create()
            ->where($expr->filter($expr->mult('price', 'quantity'), $expr->or('cats:equal(category,_)')))
        ;

as $expr->equals would render a different expression, it cannot be used to generate the desired result.

as this is a helper class, i'd personally prefer to make it not too strict; what do you think?


/**
* @param null $x
*
* @throws \Solarium\Exception\RuntimeException
*
* @return \Solarium\Builder\CompositeComparison
*/
public function orX($x = null): CompositeComparison
{
return new CompositeComparison(CompositeComparison::TYPE_OR, \func_get_args());
}
Comment on lines +36 to +46
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/**
* @param null $x
*
* @throws \Solarium\Exception\RuntimeException
*
* @return \Solarium\Builder\CompositeComparison
*/
public function orX($x = null): CompositeComparison
{
return new CompositeComparison(CompositeComparison::TYPE_OR, \func_get_args());
}
/**
* @param Comparison ...$comparison
*
* @throws \Solarium\Exception\RuntimeException
*
* @return \Solarium\Builder\CompositeComparison
*/
public function orX(Comparison ...$comparison): CompositeComparison
{
return new CompositeComparison(CompositeComparison::TYPE_OR, $comparison);
}


/**
* @param string $field
* @param mixed $value
*
* @return \Solarium\Builder\Comparison
*/
public function eq(string $field, $value): Comparison
{
return new Comparison($field, Comparison::EQ, $value);
}

/**
* @param string $field
* @param mixed $value
*
* @return \Solarium\Builder\Comparison
*/
public function neq(string $field, $value): Comparison
{
return new Comparison($field, Comparison::NEQ, $value);
}

/**
* @param string $field
* @param mixed $value
*
* @return \Solarium\Builder\Comparison
*/
public function lt(string $field, $value): Comparison
{
return new Comparison($field, Comparison::LT, $value);
}

/**
* @param string $field
* @param mixed $value
*
* @return \Solarium\Builder\Comparison
*/
public function gt(string $field, $value): Comparison
{
return new Comparison($field, Comparison::GT, $value);
}

/**
* @param string $field
* @param mixed $value
*
* @return \Solarium\Builder\Comparison
*/
public function lte(string $field, $value): Comparison
{
return new Comparison($field, Comparison::LTE, $value);
}

/**
* @param string $field
* @param mixed $value
*
* @return \Solarium\Builder\Comparison
*/
public function gte(string $field, $value): Comparison
{
return new Comparison($field, Comparison::GTE, $value);
}

/**
* @param string $field
* @param mixed $value
*
* @return \Solarium\Builder\Comparison
*/
public function in(string $field, $value): Comparison
{
return new Comparison($field, Comparison::IN, $value);
}

/**
* @param string $field
* @param mixed $value
*
* @return \Solarium\Builder\Comparison
*/
public function notIn(string $field, $value): Comparison
{
return new Comparison($field, Comparison::NIN, $value);
}

/**
* @param string $field
* @param mixed $value
*
* @return \Solarium\Builder\Comparison
*/
public function range(string $field, $value): Comparison
{
return new Comparison($field, Comparison::RANGE, $value);
}

/**
* @param string $field
* @param mixed $value
*
* @return \Solarium\Builder\Comparison
*/
public function regexp(string $field, $value): Comparison
{
return new Comparison($field, Comparison::REGEXP, $value);
}

/**
* @param string $field
* @param mixed $value
*
* @return \Solarium\Builder\Comparison
*/
public function like(string $field, $value): Comparison
{
return new Comparison($field, Comparison::LIKE, $value);
}

/**
* @param string $field
* @param mixed $value
*
* @return \Solarium\Builder\Comparison
*/
public function match(string $field, $value): Comparison
{
return new Comparison($field, Comparison::MATCH, $value);
}

/**
* @param string $field
*
* @return \Solarium\Builder\Comparison
*/
public function empty(string $field): Comparison
{
return new Comparison($field, Comparison::EMPTY, null);
}
}
Loading