elastica with terms and facets - php

I am faily new to elasticsearch and try to get along with elastica. I try to find out what Items are often togehter in a set of items when one of them is 2 and/or 7. So the index contains a lot of nested sets of items. The mutual items will be extracted with facets. But I don't seem to get the following query converted to elastica:
curl -X POST "http://localhost:9200/ratings/rating/_search?pretty=true" -d '
{
"query": {
"terms": {
"bookid": [2, 7],
"minimum_match" : 1
}
},
"size": 0,
"facets": {
"bookid": {
"terms": {
"field": "bookid"
}
}
}
}'
I was trying this:
// Load index
$index = $client->getIndex('ratings');
$type = $index->getType('rating');
// We want a Terms query.
$query = new Elastica_Query_Terms();
// Setting Terms
$query->setTerms('bookid', $bookids);
// Facets
$facet = new Elastica_Facet_Query('matches');
$facet->setField( 'bookid' )
->setSize(100);
$facet->setQuery($query);
$resultSet = $type->search($query);
return $resultSet;
but no luck so far. How do I add the facet properties?

Elastica query classes have an addFacet method, so rather than set the query on the facet, it makes more sense to add the facet to the query.
$facet = new Elastica_Facet_Query('matches');
$facet->setField('bookid')
->setSize(100);
$query = new Elastica_Query_Terms();
$query->setTerms('bookid', $bookids);
$query->addFacet($facet);

Related

Get Elasticsearch Score in Symfony

Elasticsearch provides a score field if you do a get request via cURL.
{
"_index": "twitter",
"_type": "tweet",
"_id": "123",
"_score": 4.2,
"firstName": "Max"
"lastName": "Mustermann"
}
Is there a way to get this score inside symfony. I am wondering if FOSElasticaBundle provides a function similar to the one below to get the score.
$finder = $this->container->get('fos_elastica.finder.app.article');
$boolQuery = new \Elastica\Query\BoolQuery();
$fieldQuery = new \Elastica\Query\Match();
$fieldQuery->setFieldQuery('title', 'I am a title string');
$fieldQuery->setFieldParam('title', 'analyzer', 'my_analyzer');
$boolQuery->addShould($fieldQuery);
When searching with FOSElasticaBundle, you'll get a Elastica\ResultSet with Elastica\Result inside. You can iterate on those results, they have a getScore method to get what you need.
$resultSet = $this->store->search($query);
$results = $resultSet->getResults();
foreach ($results as $result) {
$score = $result->getScore();
}
Alternatively, you can get the score with this: $result->getParam('_score');
If trying to change this from within a class that extends FOS\ElasticaBundle\Repository, consider using $this->findHybrid(). That method returns an array containing HybridResult objects. Each HybridResult in turn contains both the transformed entity and the result data (including a score).

elasticsearch aggregations on substring

I have a field indexed as String in elasticsearch 5
For example 20090219 , 20100416 etc
I can make a aggregation this data, But I want to aggregate on substring.
that is on
2009,2010
I don't want to convert to date. I want to get first 4 characters and get the count.
This is my current code.Very new to Elasticsearch
$params['body']["aggs"]["Year"]["terms"]["field"] = "PublicationDate.keyword";
$params['body']["aggs"]["Year"]["terms"]["size"] = 10;
$params['body']["aggs"]["Year"]["terms"]["order"]["_count"] = "desc";
You can use elasticsearch script feature to achieve this.
GET my-index/_search
{
"aggs" : {
"my-agg" : {
"terms" : {
"script": {
"inline": "doc['PublicationDate.keyword'].getValue().substring(0,4)"
},
"size": 10,
"order" : { "_count" : "desc" }
}
}
}
}
I don't know equivalent php script for above command, but believe you will able to make it work in php.
this did the task
$params['body']["aggs"]["PublicationYear"]["terms"]["script"] = "_value.substring(0,4)";

Elasticsearch either or match query

I am trying to write a query to search for a products on two columns called category1 and category2. I am working using elastic search php client and tried with match should query but this giving me wrong results because of match of substring.
But i am looking for exact match with OR operation on two columns. I am new to this please guide me.
$params['index'] = 'furnit';
$params['type'] = 'products';
$params['body']['query']['bool']['should'] = array(
array('match' => array('category1' => $category->name)),
array('match' => array('category2' => $category->name)),
);
$results = $this->elasticsearch->search($params);
If you are not searching then using a bool query in this scenario is not the right way to do it in elasticsearch. Queries are used when you are searching something and relevancy of your search keyword and score of matching documents matters.
Here you can apply a bool filter of elasticsearch to filter out the desired results. Using filters with queries (filtered query) is right way to do it as it excludes all non-matching documents and then you can search for desired documents by using match queries.
here's an example of a bool filter
{
"from": 0,
"size": 50,
"sort": [
{
"name" : {
"order": "asc"
}
}
],
"query": {
"filtered": {
"query": {
"match_all" : {}
},
"filter": {
"bool": {
"should": [
{
"term": {
"category1" : "category1"
}
},
{
"term": {
"category2" : "category2"
}
}
]
}
}
}
}
}
you can refer to docs as well (https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-filter.html)
Maybe your problem is you have used default analyzer (which is standard analyzer).
could you give me your mapping ?
I suggest you to change to use not_analyzer when indexing and use term filter/query.
You could use put mapping here to setting for your analyzer: Put Mapping
Edit: I have created a gist for you, check it here:
Mappings & Terms Filter

MongoDB Group by Key PHP

Ok, so I have a collection in my MongoDB
here is a sample my key here is
company
{
"_id": ObjectId("4fdfe7b536314b4147000000"),
"company": "hoyts",
"barcode": "236602253",
"name": "Gold Class",
"logoURL": "http: \/\/www.incard.com.au\/newsite\/template\/images\/movieticket\/4cinemas\/ticketpic1.png",
"store_name": "movies"
}
Now the issue here is I have 4 rows/collects with hoyts, I need away to group them.
my current code
public function getstore($storename)
{
// select a collection (analogous to a relational database's table)
$collection = $this->db->products;
// find everything in the collection
$cursor = $collection->find(array("store_name"=>$storename));
$test = array();
// iterate through the results
while( $cursor->hasNext() ) {
$test[] = ($cursor->getNext());
}
//Print Results
print json_encode($test);
}
I tried using
group($key)->find
however that did not work.
Could someone give me a hand thanks
look at this example: http://php.net/manual/en/mongocollection.group.php

MongoDB & PHP get only product that matches barcode

I have this JSON and you can see under products i have barcodes for each product what i want to do is only get the information that matches the product barcode
{
"company": "village",
"logo": "http:\/\/www.incard.com.au\/newsite\/template\/images\/movieticket\/4cinemas\/villagetop.png",
"products": [
{
"barcode": "236690091",
"name": "Weekday",
"logo-URL": "http: \/\/www.incard.com.au\/newsite\/template\/images\/movieticket\/4cinemas\/ticketpic1.png",
"price": "12.50",
"discount": "1.50"
},
{
"barcode": "236690092",
"name": "Weekend",
"logo-URL": "http: \/\/www.incard.com.au\/newsite\/template\/images\/movieticket\/4cinemas\/ticketpic1.png",
"price": "13.50",
"discount": "1.60"
},
{
"barcode": "236690093",
"name": "Gold Class",
"logo-URL": "http: \/\/www.incard.com.au\/newsite\/template\/images\/movieticket\/4cinemas\/ticketpic1.png",
"price": "13.50",
"discount": "1.60"
}
],
"store_name": "movies"
}
for example If i hit 236690091 I only what the database (MongoDB) to return
"barcode": "236690091",
"name": "Weekday",
"logo-URL": "http: \/\/www.incard.com.au\/newsite\/template\/images\/movieticket\/4cinemas\/ticketpic1.png",
"price": "12.50",
"discount": "1.50"
not every product.
This is what I have tried
public function getbarcode($barcode)
{
// select a collection (analogous to a relational database's table)
$collection = $this->db->movies->products;
// find everything in the collection
$cursor = $collection->find(array("barcode" =>"{$barcode}"));
$test = array();
// iterate through the results
while( $cursor->hasNext() ) {
$test[] = ($cursor->getNext());
}
//Print Results
print json_encode($test);
}
You can't do this. MongoDB will always return the full document and will not allow you to return only a nested part that you want to search against. I would suggest to split out the products into its own collection, and then add the company info to each product. This will also circumvent the 16MB document limit in case you have lots of products for each company.
Without changing your schema, the following code should work:
public function getbarcode($barcode)
{
$products = array();
$collection = $this->db->movies->products;
foreach( $collection->find( array( 'products.barcode' => $barcode ) ) as $item )
{
foreach( $item->products as $product )
{
if ( $product['barcode'] == $barcode )
{
$products[] = $item;
}
}
}
return $products;
}
You can't do this the way you want. MongoDB will return only whole documents or some fields from the documents (if you specify them in query). You can't return only values that are matched by your query.
You can create a separate collection that will only hold the products objects (with a reference to a collection that holds the company data) where you can directly query for the product data you want.
If you can't / won't create another collection you can find all documents that have the product with specified barcode and filter them out using PHP.
For this second approach your query should be:
$collection->find(array("products.barcode" =>"{$barcode}"),
array('products' => 1));
With this query you're reaching into objects and returning only documents that have the barcode you are looking for.
Also, in this query you will only return the products property from your document and not the whole document. The products property will contain all the child objects, not just the one you are trying to find.
In your while loop you should check the values and filter them out properly.

Categories