Algolia filtering not working - php

I have a search index with the following settings:
$index->setSettings([
'searchableAttributes' => ['title', 'datePublished'],
'attributesForFacetting' => ['filterOnly(tags)']
]);
I add objects to the index like so:
$index->addObject([
'objectID' => $object->getId(),
'title' => $object->getTitle(),
'tags' => $object->getTags(), // i.e. ['tag one (special)', 'tag two', 'tag three']
'datePublished' => $object->getDatePublished()->getTimestamp()
]);
I then conduct a search to get related items like so:
$index->search(
[
'filters' => $tags, // i.e. "tags:tag one (special)" OR "tags:tag two"
'hitsPerPage' => 12
]
);
However, this always returns 0 results, even though there are multiple records with the tags being searched for. I verified this in the Algolia dashboard. So what am I doing wrong here and how do I fix this?

Everything you're doing seems to be correct. The only issue is coming from the way you are escaping the filters in the final search() call.
Your $tags variable should be {"filters": "tags:'tag one (special)' OR tags:'tag two'"}.
The filters key should always be a string, with each filter delimited by OR/AND. When the value is a single word, you don't need any quotes, but if it has several words, you need to enclose them in '.

Related

Odoo PHP API and Laradoo - how to save many2many many2one and selection fields

Could someone please provide a simple example of the usage for dealing with Odoo's one2many, many2many and selection fields when using Laradoo (or ripcord)?
Specifically how one would use them with create() and update(). In Python, it seems as if these are dealt with using special tuple commands however for PHP documentation seems very hard to find for these types of things and it would be extremely helpful.
For illustrative purposes in my particular project, I haven't been able to figure out how to relate a CRM lead tag to a lead during the creation process using Laradoo:
$id = $odoo->create('crm.lead', [
'type' => 'lead',
'priority' => 0, <-- what do we pass here for this selection field?
'name' => 'Example',
'contact_name' => 'John Doe',
'phone' => '555-555-5555',
'email_from' => 'example#domain.com',
'description' => 'Just some text.',
'tag_ids' => [1], <-- What do we pass here for this one2many field?
]);
In the example above when trying to set the priority selection field to an int other than 0 fails and when trying to pass an array of tag_ids (1 is valid tag id in my project), the lead remains untagged.
First of all selection field values are just string values that need to be part of the field defined selection values.
The values for relational fields like Onetomany and Many2many are ruled by the command formated values that you could read at:
https://github.com/odoo/odoo/blob/11.0/odoo/models.py#L3020-L3055
For the php api usage with ripcord you could set the tag_ids field value like:
$id = $odoo->create('crm.lead', [
'type' => 'lead',
'priority' => '0',
'name' => 'Example',
'contact_name' => 'John Doe',
'phone' => '555-555-5555',
'email_from' => 'example#domain.com',
'description' => 'Just some text.',
'tag_ids' => array(array(4,1)),
]);
This translate as that 1 is the id of a known and already existing crm.lead.tag that you could link to the m2m tag_ids field using the command 4. This could also be expressed using command 6 to link multiple ids on the same command value:
'tag_ids' => array(array(6,0,array(1,2,3))),
where using command 4 it will be:
'tag_ids' => array(array(4,1), array(4,2), array(4,3)),

What are the specifics for Laravel Query Builder's updateOrInsert function?

This function is not in the Laravel documentation, I have found it in the source code however I am not completely sure how it should be used. For example, if I am working with products, I want to either insert or update a product in the database based on its UPC. If a product with the same UPC exists, update it with the new values. If not, insert the new values as a new product. How should this be done using this function?
Thank you!
Insert or update a record matching the attributes, and fill it with values.
updateOrInsert(array $attributes, array $values = [])
https://laravel.com/api/master/Illuminate/Database/Query/Builder.html#method_updateOrInsert
DB::table('products')->updateOrInsert(
[
'upc' => $request->get('upc'),
],
[
'upc' => $request->get('upc'),
'name' => $request->get('name'),
'vendor' => $request->get('vendor'),
'description' => $request->get('description')
]
);

How to update/replace a field in an ElasticSearch document using PHP?

I want to update my Elasticsearch indexed document's field. In my case its the tags field.
This is the code I currently have:
// Index tags in the page document
$es_client->update([
'index' => 'myappname',
'type' => 'page',
'id' => $page_id,
'body' => [
'doc' => [
'tags' => $tagsArray
]
]
]);
So, this would update my document by adding the tags array to it, but it won't remove the old tags.
How can I make sure that the old tags get removed when I add the new tags?
I did look in the documentation, but as we all know, the Elasticsearch docs can be very confusing and all-over-the-place. Hence I am asking here after days of searching.
Any help or advice would be greatly appreciated.
Standard update behavior is to merge array/object fields as explained in the update API documentation .
...objects are merged together, existing scalar fields are overwritten
and new fields are added.
So instead you would use a script to modify the document source directly. You can make it generic and thus cacheable, and pass in params for better performance. Php API documentation
// Index tags in the page document
$es_client->update([
'index' => 'myappname',
'type' => 'page',
'id' => $page_id,
'body' => [
'script' => 'ctx._source.tags=tags',
'params' => ['tags' => $tagsArray]
]
]);

understanding ElasticSearch routing

I am trying to use the elasticsearch routing mapping to speed up some queries, but I am not getting the expected result set (not worried about the query performance just yet)
I am using Elastic to set up my mapping:
$index->create(array('number_of_shards' => 4,
'number_of_replicas' => 1,
'mappings'=>array("country"=>array("_routing"=>array("path"=>"countrycode"))),
'analysis' => array(
'analyzer' => array(
'indexAnalyzer' => array(
'type' => 'keyword',
'tokenizer' => 'nGram',
'filter' => array('shingle')
),
'searchAnalyzer' => array(
'type' => 'keyword',
'tokenizer' => 'nGram',
'filter' => array('shingle')
)
)
) ), true);
If I understand correctly, what should happen is that each result should now have a field called "countrycode" with the value of "country" in it.
The results of _mapping look like this:
{"postcode":
{"postcode":
{"properties":
{
"area1":{"type":"string"},
"area2":{"type":"string"},
"city":{"type":"string",
"include_in_all":true},
"country":{"type":"string"},
"country_iso":{"type":"string"},
"country_name":{"type":"string"},
"id":{"type":"string"},
"lat":{"type":"string"},
"lng":{"type":"string"},
"location":{"type":"geo_point"},
"region1":{"type":"string"},
"region2":{"type":"string"},
"region3":{"type":"string"},
"region4":{"type":"string"},
"state_abr":{"type":"string"},
"zip":{"type":"string","include_in_all":true}}},
"country":{
"_routing":{"path":"countrycode"},
"properties":{}
}
}
}
Once all the data is in the index if I run this command:
http://localhost:9200/postcode/_search?pretty=true&q=country:au
it responds with 15740 total items
what I was expecting is that if I run the query like this:
http://localhost:9200/postcode/_search?routing=au&pretty=true
Then I was expecting it to respond with 15740 results
instead it returns 120617 results, which includes results where country is != au
I did note that the number of shards in the results went from 4 to 1, so something is working.
I was expecting that in the result set there would be an item called "countrycode" (from the rounting mapping) which there isn't
So I thought at this point that my understand of routing was wrong. Perhaps all the routing does is tell it which shard to look in but not what to look for? in other words if other country codes happen to also land in that particular shard, the way those queries are written will just bring back all records in that shard?
So I tried the query again, this time adding some info to it.
http://localhost:9200/postcode/_search?routing=AU&pretty=true&q=country:AU
I thought by doing this it would force the query into giving me just the AU place names, but this time it gave me only 3936 results
So I Am not quite sure what I have done wrong, the examples I have read show the queries changing from needing a filter, to just using match_all{} which I would have thought would only being back ones matching the au country code.
Thanks for your help in getting this to work correctly.
Almost have this working, it now gives me the correct number of results in a single shard, however the create index is not working quite right, it ignores my number_of_shards setting, and possibly other ones too
$index = $client->getIndex($indexname);
$index->create(array('mappings'=>array("$indexname"=>array("_routing"=>array("required"=>true))),'number_of_shards' => 6,
'number_of_replicas' => 1,
'analysis' => array(
'analyzer' => array(
'indexAnalyzer' => array(
'type' => 'keyword',
'tokenizer' => 'nGram',
'filter' => array('shingle')
),
'searchAnalyzer' => array(
'type' => 'keyword',
'tokenizer' => 'nGram',
'filter' => array('shingle')
)
)
) ), true);
I can at least help you with more info on where to look:
http://localhost:9200/postcode/_search?routing=au&pretty=true
That query does indeed translate into "give me all documents on the shard where documents for country:AU should be sent."
Routing is just that, routing ... it doesn't filter your results for you.
Also i noticed you're mixing your "au"s and your "AU"s .. that might mix things up too.
You should try setting required on your routing element to true, to make sure that your documents are actually stored with routing information when being indexed.
Actually to make sure your documents are indexed with proper routing explicitly set the route to lowercase(countrycode) when indexing documents. See if that helps any.
For more information try reading this blog post:
http://www.elasticsearch.org/blog/customizing-your-document-routing/
Hope this helps :)

Iterating through an array and replacing data within a template

I have an XML document with "tags" that are replaced based on data within an array. There are two types of tags, one is a parent tag to define a set, another is simply a tag that is replaced by a value. Here's an example of the data used to build and fill in the template:
$array = array(
'name' => 'name',
'city' => 'city',
'addresses' => array(
array(
'street' => '123',
'city' => 'main'
),
array(
'street' => '123',
'city' => 'main'
'phone' => array(
array(
'home' => '123456', 'work' => '1234567'
)
Here is an example template:
<name>%name%</name>
<city>%city%</city>
%%addresses%%
<street>%street%</street>
<city>%city%</city>
%%phone%%
<home>%%home%%</home>
<work>%%work%%</work>
%%/phone%%
%%/addresses%%
The key values of the array, match the tags within the template. If the key is an array itself, then it loops through the data contained within that key's tag (%%).
I've tried doing a recursive function but it only seems to work one level deep.
Does anyone have any suggestions? Thank you!
I suggest you use an existing simple template language that "just works"tm, like Mustache (there are plenty much others). I know that Mustache supports looping over arrays, used it, does the job, easy to integrate. Available for many languages.

Categories