FOSElasticaBundle perform nested queries - symfony

I'm learning to use FOSElasticaBundle, but I can not run a query where there are nested fields.
The purpose is this: to look for a query_string in multiple fields, including fields nested.
My setup is:
structure:
mappings:
name: { boost: 9, type: string }
locality:
type: "nested"
properties:
locality: { boost: 8, type: string }
For now I run this code:
$mainQuery = new \Elastica\Query\Bool();
$searchQuery = new \Elastica\Query\QueryString();
$searchQuery->setParam('query', $query);
$searchQuery->setParam('fields', array(
'name'
));
$searchNested = new \Elastica\Query\QueryString();
$searchNested->setParam('query', $query);
$searchNested->setParam('fields', array(
'locality'
));
$nestedQuery = new \Elastica\Query\Nested();
$nestedQuery->setPath('locality');
$nestedQuery->setQuery($searchNested);
$mainQuery->addShould($searchQuery);
$mainQuery->addShould($nestedQuery);
$results = $index->search($mainQuery)->getResults();
But returns no result!
How do I create queries with nested fields?

Related

Dart how to add complex parameter

I need to get url with query parameters, but I dont know how.
I need this -> "entityTypeId=172&filter[id]=1&filter[id]=3&filter[id]=5".
In JS i can do like that
var httpBuildQuery = require('http-build-query');
var params = {
entityTypeId: 172,
filter: {
id: [1, 3, 5]
}};
const url = url + "?" + httpBuildQuery(params);
console.log(httpBuildQuery(params));
In PHP
$params = array(
'filter' => array ('ID' => array('1', '3', '5'),),
'entityTypeId' => 172,
);
http_build_query($params);
In dart I tried this
var uri = Uri(
scheme: 'http',
host: 'b24-ybr1v4.bitrix24.ru',
path: '/rest/1/token/crm.item.list.json',
queryParameters: {
'entityTypeId': '172',
'filter': [
{'id': '1'}
],
},
);
But in this case I get error:
The following TypeErrorImpl was thrown while handling a gesture:
Expected a value of type 'String', but got one of type 'IdentityMap<String, String>'
How to get parameter like "filter[id]"?
You could try moving the [id] part into the query parameter key.
var uri = Uri(
scheme: 'http',
host: 'b24-ybr1v4.bitrix24.ru',
path: '/rest/1/token/crm.item.list.json',
queryParameters: {
'entityTypeId': '172',
'filter[id]': ['1', '3', '5'],
},
);

elasticsearch doesn't returns all hits

I'm using Symfony 2.3 and ElasticSearchBundle 3.0. I implemented two fields for the search. The search works correctly but it doesn't display all results. For example: when I search for the a keyword, the number of hits are 33 hits but it returns only 10 results.
config.php
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
serializer:
callback_class: FOS\ElasticaBundle\Serializer\Callback
serializer: serializer
indexes:
hortis:
finder: ~
client: default
settings:
index:
analysis:
analyzer:
custom_search_analyzer:
type: custom
tokenizer: standard
filter : [standard, lowercase, asciifolding]
custom_index_analyzer:
type: custom
tokenizer: standard
filter : [standard, lowercase, asciifolding, custom_filter]
filter:
custom_filter:
type: edgeNGram
side: front
min_gram: 3
max_gram: 100
types:
business:
mappings:
name: { search_analyzer: custom_search_analyzer, index_analyzer: custom_index_analyzer, type:string }
enabled: ~
gouvernaurat: ~
delegation: ~
postal_code: ~
# activities.principal: { search_analyzer: custom_search_analyzer, index_analyzer: custom_index_analyzer, type:string }
activities :
type : object
properties :
principal : ~
persistence:
driver: orm
model: Toto\AdminBundle\Entity\EntityName
provider: ~
listener: ~
finder: ~
controller.php
public function searchEngineAction(Request $request) {
$finder = $this->container->get('fos_elastica.index.hortis.business');
// get data from both fields
$querystring = strip_tags($request->get('name'));
$querystring2 = strip_tags($request->get('location'));
$boolQuery = new \Elastica\Query\Bool();
// if both fields are empty then display all businesses
if (empty($querystring) and empty($querystring2)) {
$query = new \Elastica\Query\MatchAll();
$boolQuery->addMust($query);
} else {
// create a boolean query
if (!empty($querystring)) {
$fieldQuery = new \Elastica\Query\QueryString();
$fieldQuery->setFields(array('name', 'activities.principal'));
$fieldQuery->setQuery($querystring);
$boolQuery->addMust($fieldQuery);
}
if (!empty($querystring2)) {
$fieldQuery2 = new \Elastica\Query\QueryString();
$fieldQuery2->setFields(array(
'gouvernaurat', 'delegation', 'postal_code'));
$fieldQuery2->setQuery($querystring2);
$boolQuery->addMust($fieldQuery2);
}
}
// select only enbaled business
$enabled = new \Elastica\Query\Term();
$enabled->setTerm('enabled', true);
$boolQuery->addMust($enabled);
$findAll = \Elastica\Query::create($boolQuery);
$findAll->setSize(27);
// trigger search function
$elasticaResultSet = $finder->search($findAll);
dump($elasticaResultSet);
// get results from
$findbusinesses = $elasticaResultSet->getResults();
$noresult = '';
if (!$findbusinesses) {
$noresult = 'no result';
}
$em = $this->getDoctrine()->getManager();
$FrontSettings = $em->getRepository('TotoAdminBundle:FrontSettings')->getFrontSettings();
if (!$FrontSettings) {
throw $this->createNotFoundException('Unable to find frontSettings entity');
}
// get all categories and activities
$categories = $em->getRepository('TotoAdminBundle:Category')
->findBy(array(), array('order' => 'ASC'));
if (!$categories) {
throw $this->createNotFoundException('unable to find categories and activities');
}
$paginator = $this->get('knp_paginator');
$businesses = $paginator->paginate(
$findbusinesses, $this->get('request')->query->get('page', 1)/* page number */, 9/* limit per page */
);
return $this->render('TotoFrontBundle:Front:search_result.html.twig', array(
'querystring' => $querystring, 'businesses' => $businesses,
'FrontSettings' => $FrontSettings, 'noresult' => $noresult,
'categories' => $categories,
));
}
How can I display the all the hits?
Elasticsearch by default only returns the first 10 results. This setting can be modified by specifing the from and size parameters. Note that it rarely makes sense to display all results on one page, instead use a pagination with a controllable amount of viewed items.
If you want all hits on one page also consider using the scroll api as deep pagination can get very inefficiently when having a high amount of results.

FOSElasticaBundle multiple nested query

I use FOSElasticaBundle to handle searching. All works great when I have one level of nesting. However, when I have two levels of nesting results which should match the innermost nest are not returned (e.g. searching for 'xx' category does produce results, but searching for 'yy' brand does not - and should).
Here's my fos_elastica configuration:
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
my_index:
client: default
types:
product:
mappings:
title: { boost: 1 }
articleNumber: ~
introductionDateSearch: { type: integer }
delistingDateSearch: { type: integer }
deleted: { type: boolean }
category:
type: "nested"
properties:
name: { boost: 1 }
brand:
type: "nested"
properties:
name: { boost: 1 }
persistence:
driver: orm
model: MyBundle\Entity\Product
provider: ~
finder: ~
listener: ~
And my query handler:
public function searchForKeyword($keyword, AbstractUser $user)
{
$this->setFilters($user);
$keyword = trim($keyword);
if ($keyword !== '') {
$mainQuery = new \Elastica\Query\Bool();
$mainProductQuery = new \Elastica\Query\Bool();
//searching in Product title
$productQuery = new \Elastica\Query\Text();
$productQuery->setFieldQuery('title', $keyword);
$productQuery->setFieldParam('title', 'boost', 5);
$productQuery->setFieldParam('title', 'type', 'phrase_prefix');
//searching in Product articleNumber
$articleNumberQuery = new \Elastica\Query\Text();
$articleNumberQuery->setFieldQuery('articleNumber', $keyword);
$articleNumberQuery->setFieldParam('articleNumber', 'boost', 5);
$articleNumberQuery->setFieldParam('articleNumber', 'type', 'phrase_prefix');
//searching in Category name
$categoryQuery = new \Elastica\Query\Text();
$categoryQuery->setFieldQuery('name', $keyword);
$categoryQuery->setFieldParam('name', 'boost', 3);
$categoryQuery->setFieldParam('name', 'type', 'phrase_prefix');
$nestedCategoryProductQuery = new \Elastica\Query\Nested();
$nestedCategoryProductQuery->setPath('category');
$nestedCategoryProductQuery->setQuery($categoryQuery);
//searching in Brand name
$brandQuery = new \Elastica\Query\Text();
$brandQuery->setFieldQuery('name', $keyword);
$brandQuery->setFieldParam('name', 'boost', 3);
$brandQuery->setFieldParam('name', 'type', 'phrase_prefix');
$nestedBrandCategoryQuery = new \Elastica\Query\Nested();
$nestedBrandCategoryQuery->setPath('category.brand');
$nestedBrandCategoryQuery->setQuery($brandQuery);
$mainProductQuery->addShould($productQuery);
$mainProductQuery->addShould($articleNumberQuery);
$mainProductQuery->addShould($nestedCategoryProductQuery);
$mainProductQuery->addShould($nestedBrandCategoryQuery);
$mainQuery->addMust($mainProductQuery);
$esFilteredQuery = new \Elastica\Query\Filtered($mainQuery, $this->filters);
} else {
$esFilteredQuery = new \Elastica\Query\Filtered(new \Elastica\Query\MatchAll(), $this->filters);
}
$this->query = new \Elastica\Query();
$this->query->setQuery($esFilteredQuery);
}
How is the $nestedBrandCategoryQuery added to the $mainProductQuery?
Thanks for your help!
gtb
FOSElasticaBundle uses the Elastica Library. So this should not be an issue of FOSElasticaBundle.
Have a Look at http://elastica.io/ for more Details about the Lib. In my experience,there is nothing you can not do with Elastica if it is supported by Elasticsearch. Even when there is no Mapper in Elastica, just use the Raw Array Query (http://elastica.io/example/raw-array-query.html) to build the desired Query.

Symfony add Avatar field to sfGuardUser model

I have a project in symfony that I would like to let my users upload an image for their "avatar" field. I have found many posts about how to "extend" the table which I have with the schema below:
Member:
inheritance:
type: column_aggregation
extends: sfGuardUser
columns:
idmember: { type: integer }
birthday: { type: date }
avatar: { type: string(255) }
bio: { type: string(255) }
The columns get added to the table just fine, but when I go to change the widget to a sfWidgetFormInputFileEditable it breaks. Here is the Form.class file:
$file_src = $this->getObject()->getAvatar();
if ($file_src == '')
{
$file_src = 'default_image.png';
}
$this->widgetSchema['avatar'] = new sfWidgetFormInputFileEditable(array(
'label' => ' ',
'file_src' => '/uploads/avatar/'.$file_src,
'is_image' => true,
'edit_mode' => true,
'template' => '<div>%file%<br />%input%</div>',
));
and "save" function of the form:
if($this->isModified())
{
$uploadDir = sfConfig::get('sf_upload_dir');
$thumbnail = new sfThumbnail(150, 150);
$thumbnail2 = new sfThumbnail(800, 800);
if($this->getAvatar())
{
$thumbnail->loadFile($uploadDir.'/avatar/'.$this->getAvatar());
$thumbnail->save($uploadDir.'/avatar/thumbnail/'. $this->getAvatar());
$thumbnail2->loadFile($uploadDir.'/avatar/'.$this->getAvatar());
$thumbnail2->save($uploadDir.'/avatar/big/'. $this->getAvatar());
}
}
When I submit the form, I get this error message:
This form is multipart, which means you need to supply a files array as the bind() method second argument.
In the action where you bind the form you should use something like this:
$form->bind($request->getParamater($form->getName()), $request->getFiles($form->getName()));
So you need to pass the uploaded files as the second parameter to the bind method.

How to use nested queries with FOQElasticaBundle for Symfony2

I have a problem with query building with FOQElasticaBundle
I have 3 entities
User
Hotel
Ambiance
Users can have 1 or more Hotels, and each Hotel has only 1 Ambiance.
In my config file, I have:
foq_elastica:
clients:
default: { host: %elasticsearch.host%, port: %elasticsearch.port% }
indexes:
MyBundle:
client: default
finder:
types:
user:
mappings:
id:
boost: 10
analyzer: fr_case_analyzer
name:
boost: 5
analyzer: fr_case_analyzer
hotels:
type: "nested"
properties:
name:
boost: 10
analyzer: fr_case_analyzer
ambiance:
boost: 1
I want to be able to search for User by typing his name or the name of his hotels, and possibly add a filter on the Ambiance type.
So the query should look like something like this :
$mainQuery = new \Elastica_Query_Bool();
$nameQuery = new \Elastica_Query_Bool();
$filtersQuery = new \Elastica_Query_Bool();
//searching in Users' names
$nameQuery = new \Elastica_Query_Text();
$nameQuery->setFieldQuery('name', $searchName);
$nameQuery->setFieldParam('name', 'boost', 5);
$nameQuery->setFieldParam('name', 'type', 'phrase_prefix');
//searching in Hotels' names
$hotelNameQuery = new \Elastica_Query_Text();
$hotelNameQuery->setFieldQuery('name', $searchName);
$hotelNameQuery->setFieldParam('name', 'boost', 3);
$hotelNameQuery->setFieldParam('name', 'type', 'phrase_prefix');
$nestedHotelNameQuery = new \Elastica_Query_Nested();
$nestedHotelNameQuery->setPath('hotels');
$nestedHotelNameQuery->setQuery($hotelNameQuery);
$nameQuery->addShould($nameQuery);
$nameQuery->addShould($nestedHotelNameQuery);
//if filter on ambiance
$ambianceQuery = new \Elastica_Query_Term();
$ambianceQuery->setTerm('ambiance', $arrFilters['ambiance']);
$nestedAmbianceQuery = new \Elastica_Query_Nested();
$nestedAmbianceQuery->setPath('hotels');
$nestedAmbianceQuery->setQuery($ambianceQuery);
$filtersQuery->addMust($nestedAmbianceQuery);
//adding the parameters to the main query
$mainQuery->addMust($nameQuery);
$mainQuery->addMust($filtersQuery);
Unfortunately this doesn't work and returns no result if the Ambiance filter is activated, but works perfectly if I only search with the name.
What do I do wrong ?
I found why it wouldn't work.
The bundle actually uses __toString() on the object.
So, instead of querying on the "id" of the ambiance, I modified my html inputs so the value is the ambiance's name.
Here's my own version of the solution :
According to the elasticsearch documentation we should implement a structure that's smilar to the json bellow :
{
"query": {
"bool": {
"must": [
{ "match": { "title": "eggs" }},
{
"nested": {
"path": "comments",
"query": {
"bool": {
"must": [
{ "match": { "comments.name": "john" }},
{ "match": { "comments.age": 28 }}
]
}}}}
]
}}}
So to do this with symfony 2 and according to the FOSElasticaBundle bundle we will make the following code lines :
//if filter on ambiance
$ambianceQuery = new \Elastica_Query_Term();
$ambianceQuery->setTerm('ambiance', $arrFilters['ambiance']);
// We will add the the term to the query bool
$filtersQuery->addMust($ambianceQuery)
$nestedAmbianceQuery = new \Elastica_Query_Nested();
$nestedAmbianceQuery->setPath('hotels');
// we will set the result as argument in the setQuery method.
$nestedAmbianceQuery->setQuery($filtersQuery);
// And finally we add the nested query to the main query bool through addMust.
$mainQuery->addMust($nestedAmbianceQuery);
Hope that will help others.
A+
Just had the same Problem. This works for me:
..
$ambianceQuery->setTerm('hotels.ambiance', $arrFilters['ambiance']);
..
Coudn't find any examples on this in FOQElasticaBundle (now FOSElasticaBundle) for Symfony2 but it should end in an elasticsearch query like this one here:
http://www.elasticsearch.org/guide/reference/query-dsl/nested-query/
One can do/test raw elasticsearch queries like this:
$queryString = '{..JSON..}';
$boolQuery = new \Elastica_Query_Builder($queryString);

Resources