Sorting in FOSElasticaBundle - symfony

I use FOSElasticaBundle in my project to search on my Player entity. As i only want to search entities with the property isactive on a value 1, i followed the documentation on "Filtering Results and Executing a Default Query": FriendsOfSymfony/FOSElasticaBundle/README.md
$query = new \Elastica\Query\QueryString($searchterm);
$term = new \Elastica\Filter\Term(array('isactive' => true));
$filteredQuery = new \Elastica\Query\Filtered($query, $term);
$players = $this->get('fos_elastica.finder.xxx.player')->find($filteredQuery);
The configuration of my bundle looks like following:
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
serializer:
callback_class: FOS\ElasticaBundle\Serializer\Callback
serializer: serializer
indexes:
xxx:
client: default
types:
player:
mappings:
firstname: { boost: 3 }
lastname: { boost: 3 }
serializer:
groups: [elastica, Default]
persistence:
driver: orm
model: xxx\FrontendBundle\Entity\Player
listener: ~
provider: ~
finder: ~
Now i want to do some sorting and cut back the result with limit and offset. How can i achieve this?
I found a solution like
$finalQuery = new \Elastica\Query($boolQuery);
$finalQuery->setSort(array('price' => array('order' => 'asc')));
But i dont have an Elastica\Query object and the AbstractQuery didn't support this method. Same with
$elasticaQuery->addSort($sort);
What to do? Where to read?? ://
(in addition, if we are here already: what does {boost: 3} really do exactly?)

you have to create a generic Elastica\Query() object.
Then you can add sort to this query with ->addSort($sort)
And later you can assign a proper query with ->setQuery();
Your example should look like this
$query = new \Elastica\Query();
$query->addSort(array('price' => array('order' => 'asc')));
$q = new \Elastica\Query\QueryString($searchterm);
$term = new \Elastica\Filter\Term(array('isactive' => true));
$filteredQuery = new \Elastica\Query\Filtered($q, $term);
$query->setQuery($filteredQuery);
$players = $this->get('fos_elastica.finder.xxx.player')->find($query);
Boost allows you to make one field more\less important than other within a query.

Related

Empty relations when serializing with JMSSerializer

I am having troubles while writing a controller-action inside a Symfony project, that should return data (in this particular case orders of a web-shop). Yeah ... It's a kind of a REST-API. That route just get's called from some JavaScript. And the data has to be visualized on the client-side.
The Problem:
I cannot find out, why the serialization of related entities results in empty objects. In this example it is the user of an order, which is empty.
This is a sample output:
orders: [
{
id: 2,
created: '2016-05-04T11:40:27+00:00',
user: {},
}
]
When I do something like
$orders = $this->getDoctrine()->getRepository('AppBundle:Order')
->findAllCompleted();
$serializationContext->setSerializeNull(true);
$serializationContext->setGroups(['statistics']);
$json = $serializer->serialize($orders, 'json', $serializationContext);
$response = new Response($json, $statusCode, [
'Content-Type' => 'application/json',
]);
return $response;
... i get a nice JSON response from the server, but every related entity of each order, like let's say user is {} (empty).
Even if I dump the related entity before it gets serialized like so:
[...]
$myOrder = array_filter($orders, function($order) {
if ($order->getId() == 2) {
return true;
}
return false;
});
dump($myOrder[0]->getUser());
die();
... it results in an empty (unhydrated) entity.
But if I change this debugging code to:
$myOrder = array_filter($orders, function($order) {
if ($order->getId() == 2) {
return true;
}
return false;
});
dump($myOrder[0]->getUser()->getUsername());
die();
... I get a clear output (string) with the value of the username of that entity.
So I think the issue is about a non hydrated entity, and not the serializer or its wrong configuration.
How can I get the JMSSerializer to take care of the hydration of those related entities?
I didn't find any hint in the docs ...
BTW, this are the JMS entity configs of order and user.
AppBundle\Entity\User:
exclusion_policy: ALL
properties:
userMeta:
expose: true
address:
expose: true
username:
expose: true
email:
expose: true
isReseller:
expose: true
acceptPublicOrders:
expose: true
vatNumber:
expose: true
taxRate:
expose: true
AppBundle\Entity\Order:
exclusion_policy: NONE
properties:
id:
groups: ['statistics']
user:
groups: ['statistics']
configuration:
groups: ['statistics']
created:
groups: ['statistics']
invoiceItems:
groups: ['statistics']
exclude: true
I think your problem is caused by doctrine lazy loading maybe you can try to change the fetch mode of the User association to EAGER in your Order entity
#ManyToOne(targetEntity="Cart", cascade={"all"}, fetch="EAGER")
By default i think it doesn't fetch the associations unless you call it directly like you did here
dump($myOrder[0]->getUser()->getUsername());
https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html#annref-onetoone
Or this if you use DQL
14.7.6.6. Temporarily change fetch mode in DQL
http://doctrine-orm.readthedocs.io/en/latest/reference/dql-doctrine-query-language.html#temporarily-change-fetch-mode-in-dql
Edit : i was wrong
I made some tests everything worked fine with lazy loading or eager until i tried with groups, even if the fields are exposed you don't use the Default group so it only take the things with the 'statistics' group on it
Try to add the default group here
$serializationContext->setGroups(['Default','statistics']);
Or add the statistics group on your user fields both worked for me

Apply different filters on multiple index search

I'm using FOSElasticaBundle with Symfony 3. I want to search on different index in ES6.
I have 2 entities Dogs, Cats with a field name "owner" (entity User). Dogs and Cats have a field "name" (string), I want to search every Dogs and only Cats that have it owner set at userId.
Example:
User: #1 Bob
User: #2 Charle
Cat: #1 Ruf, owner #1
Cat: #2 Pat, owner #2
Dog: #1 Ruf
Dog: #2 Pat
If I'm Bob, and I write "Ruf". I want as result Cat#1, Dog#1 but if i write "Pat", I want as result Dog#2.
elastica.yml
fos_elastica:
clients:
default:
host: %elastic_host%
port: %elastic_port%
indexes:
dog:
finder: ~
client: default
types:
dog:
indexable_callback: 'getEnabled'
properties:
id:
type: integer
name: ~
persistence:
driver: orm
model: AppBundle\Entity\Dog
finder: ~
elastica_to_model_transformer:
ignore_missing: true
cat:
finder: ~
client: default
types:
cat:
indexable_callback: 'getEnabled'
properties:
name: ~
owner:
type: "object"
properties:
id: integer
persistence:
driver: orm
model: AppBundle\Entity\Cat
finder: ~
elastica_to_model_transformer:
ignore_missing: true
I'm searching in ES with the method:
public function search(User $user, $query)
{
$search = $this->indexManager->getIndex('dog')->createSearch();
$search->addIndex('cat');
$search->addType('dog');
$search->addType('cat');
$resultSet = $search->search($query);
return $this->formatResult($resultSet);
}
How can i do my search ? Should i use Filter on Cat ? Could i use one repository per indexes ?
You can search using multiple indexes using ruflin/elastica.
It would be something like:
$search = new Elastica\Search($client);
$search->addIndex('dog')->addIndex('cat');
try {
$searchResponse = $search->search($q);
return $searchResponse->getResults();
} catch (ResponseException $exception) {
return [];
}
I'm sorry, but I do not know how to do this with FOSElasticaBundle.
finder should search by all indexes:
#fos_elastica.finder.app
result query look like:
$boolQuery = new \Elastica\Query\BoolQuery();
/*****************DOG PART**********************/
$dogBoolQuery = new \Elastica\Query\BoolQuery();
$dogNameMatchQuery = new \Elastica\Query\Term();
$dogNameMatchQuery->setTerm('name', $query);
$dogTypeFilter = new \Elastica\Query\Type();
$dogTypeFilter->setType('dog');
$dogBoolQuery->addMust($dogNameMatchQuery);
$dogBoolQuery->addFilter($dogTypeFilter);
/***************************************/
/*****************CAT PART**********************/
$catBoolQuery = new \Elastica\Query\BoolQuery();
$catTypeFilter = new \Elastica\Query\Type();
$catTypeFilter->setType('cat');
$ownerNameTermQuery = new \Elastica\Query\Term();
$ownerNameTermQuery->setTerm('id', $user->getId());
$ownerQuery = new \Elastica\Query\HasParent($ownerNameTermQuery, 'owner');
$catBoolQuery->addFilter($catTypeFilter);
$catBoolQuery->addFilter($ownerQuery);
/***************************************/
$boolQuery->addShould($dogBoolQuery);
$boolQuery->addShould($catBoolQuery);
$searchQuery = new \Elastica\Query();
$searchQuery->setQuery($boolQuery);
$results = $this->finder->find($searchQuery);
I would do two separate searches, one for each index. If you need a single query for pagination purposes, then I would use the Elasticsearch api directly, as I'm not sure if the FosElasticaBundle supports this kind of searches.
You can find how to do multiple indices search in the Elasticsearch documentation using Curl calls to the ES api.

Boosting field in the mapping file not working with QueryString

I have an application where the users (clients) can activate different modules.
On the search, the user should be able to search a value in the entire index (for the modules he has activated).
The problem is that I also need to boost some field.
Currently I have this.
mapping.yml
indexes:
traveler:
client: default
finder: ~
types:
Country:
mappings:
name: { boost: 10 }
code:
# additional info (irrelevant)
persistence:
driver: orm
model: CoreBundle\Entity\Country
provider: ~
listener: ~
finder: ~
serializer:
groups: [Default, elastica]
Places:
mappings:
name:
address:
type: "object"
properties:
city: ~
region: ~
country: ~
persistence:
driver: orm
model: CoreBundle\Entity\Places
provider: ~
listener: ~
finder: ~
serializer:
groups: [Default, elastica]
And in my service I have this :
$index = $this->get('fos_elastica.finder.traveler');
$query = new \Elastica\Query\QueryString($search);
# if the client has the module activated
foreach ($this->getClient()->getServices() as $service) {
switch ($service->getService()->getTag()) {
case "countries":
$filters->addShould(
new Type('Country')
);
break;
case "places":
$filters->addShould(
new Type('Places')
);
break;
}
}
$query = new \Elastica\Query\Filtered($query, $filters);
// set result limit
$globalQuery = new Query();
$globalQuery->setQuery($query);
$globalQuery->setSize($limit);
// return result
return $index->find($query);
Now, for example, if I search "Germany", the "Country" result should be first in the result set, because I applied a boost to it in the yml mapping.
Instead, the first 5 or 6 results are for "Places" which have in their country address "Germany".
The boost is not applied (I tried with higher values, removing it ... results are not changed).
I found a way where it works, if I change my service like this, but I would prefer that it use the boost property defined in the mapping.
$query = new QueryString($search);
$query->setFields(array(
'_all',
'Country.name^10'
));
Am I doing something wrong ?
Is there another way to do this ?
Thanks !

creating custom repository fos elastic search

im trying to make a simple custom repository in order to understand how elastic search repository works. the documentation is pretty straight forward but i still dont understand how it works, im getting this error ´The service definition "fos_elastica.manager" does not exist.´. so far i think my problem is in the controller since i dont understand how to intialize them, also i would like to know if im in the right way in my configuration of the custom repository and the simple query i made.
im getting this error with this configuration whenever i try to make a search,
The service definition "fos_elastica.manager" does not exist.
this is my configuration so far:
//app/config.yml
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
sava:
client: default
types:
blog:
mappings:
id:
type: integer
body : ~
title : ~
tags: ~
persistence:
identifier: id
driver: orm
model: sava\BlogBundle\Entity\TblPost
finder: ~
provider: ~
listener: ~
repository: sava\BlogBundle\SearchRepository\TblPostRepository
this is my controller action:
namespace sava\BlogBundle\Controller;
//custom querys
use FOS\ElasticaBundle\Manager\RepositoryManager;
use FOS\ElasticaBundle\Repository;
//
use Symfony\Component\DependencyInjection\ContainerBuilder;
class TblPostController extends Controller
{
public function getPostAction(Request $request)
{
$container = new ContainerBuilder();
$repositoryManager = $container->get('fos_elastica.manager');
$repository = $repositoryManager->getRepository('BlogBundle:TblPost');
$items2 = $repository->matchExact($categoria,$searchQuery );
return $this->render('savaBlogBundle:TblPost:index.html.twig', array(
'results' => $items2, 'entities' => $items2
));
}
this is my post repository:
<?php
namespace sava\BlogBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\ElasticaBundle\Repository;
class TblPostRepository extends FOS\ElasticaBundle\Repository
{
public function matchExact($campo, $searchQuery) {
//$finder = $this->get('fos_elastica.finder.sava.blog');
$query = new Query();
if($searchQuery=='')
{
$innerQuery = new Query\MatchAll();
}
else{
$innerQuery = new Query\Match();
$innerQuery->setField( $campo , array('query' => $searchQuery));
}
$query->setQuery($innerQuery);
$query->setSize(1000000);
$query->setExplain(true);
return $this->find($query);
}
}
and since im using yml this is my tblpost.orm, i did generate my entities.
whenever i do the get postaction it throws me that it cant find the container, and i dont see an example in how to properly intiaze it, also is this is how you make a custom query?
EDIT 1:
so i changed this:
$container = new ContainerBuilder();
$repositoryManager = $container->get('fos_elastica.manager');
to this:
$elastica = $this->container->get('fos_elastica.manager');// single entry point, no fancy services
$SearchRepository = $elastica->getRepository('savaBlogBundle:TblPostRepository');// single type
and im getting this error:
No search finder configured for sava\BlogBundle\Entity\TblPostRepository
I've just had the same issue, the soultion is that instead of savaBlogBundle:TblPostRepository you should use your entity, for example:
$SearchRepository = $elastica->getRepository('savaBlogBundle:TblPost`)
According your fatal error in title of the issue enter link description here
The main problem why did you get that mistake is that, you assigned the same TblPostRepository in (doctrine config for entity) and in fos_elastica.

Simple Symfony2 / Doctrine Validation not working

This just doesn't make sense. I can't seem to get a simple Symfony2 validation working.
$insert = new MyEntity();
$insert->setTest1( 'test' );
$validator = $this->get('validator');
$errors = $validator->validate($insert);
...but $errors is always an object with an empty constraints array. It never fails the validation.
My configuration (Yaml):
MyBundle\Entity\MyEntity:
properties:
test1:
- MinLength: 10
- Email
type: entity
table: null
fields:
id:
type: integer
id: true
generator:
strategy: AUTO
test1:
type: string
length: 255
column: test_1
test2:
type: integer
column: test_2
lifecycleCallbacks: { }
You're mixing doctrine's mapping and symfony's validation in a single yml file.
The validation configuration in yml is loaded from the files:
Acme/YourBundle/Resources/config/validation.yml // YAML
Acme/YourBundle/Resources/config/validation.xml // XML
And the mapping information should be placed in one of:
Acme/YourBundle/Resources/config/doctrine/MyEntity.orm.yml // YAML
Acme/YourBundle/Resources/config/doctrine/MyEntity.orm.xml // XML
Acme/YourBundle/Resources/config/doctrine/orm/MyEntity.orm.yml // YAML
Acme/YourBundle/Resources/config/doctrine/orm/MyEntity.orm.xml // XML

Resources