How to display the matched words of an Algolia search query? - javascript

I'm using Algolia's algoliasearch-client-js and autocomplete.js to power searches on my website. That works.
But I also want to include the excerpt/snippet of the text with which the search query matches. How to do that?
My current code is:
autocomplete('#search-input', { hint: true, autoselect: true }, [
{
source: autocomplete.sources.hits(index, { hitsPerPage: 7 }),
displayKey: 'title',
templates: {
footer: '<span class="search-foot">Powered by <img src="/static/assets/algolia-logo.png" width="47" height="15"></span>',
suggestion: function(suggestion) {
return '<div class="search-lang">' +
suggestion._highlightResult.platform.value +
'</div>' +
suggestion._highlightResult.title.value;
}
}
}
]).on('autocomplete:selected', function(event, suggestion, dataset) {
window.location.href = suggestion.url;
});
To highlight the excerpt that caused the query to match with a record, their FAQ says:
The AttributesToSnippet setting is a way to shorten ("snippet") your
long chunks of text to display them in the search results. Just think
about the small pieces of text displayed below a Google result: it's
built from a subset of the sentences of the page content, includes
your matching keywords, and avoid flooding the search results page.
For example, if you limit the number of words of the "description"
attribute to 10, the "_snippetResult.description.value" attribute of
the JSON answer will only contain the 10 best words of this
description.
There's no example of AttributesToSnippet, however. On their Github documentation I find a bit more info:
attributesToHighlight
scope: settings, search
type: array of strings
default: null
Default list of attributes to highlight. If set to null, all indexed
attributes are highlighted.
A string that contains the list of attributes you want to highlight
according to the query. Attributes are separated by commas. You can
also use a string array encoding (for example ["name","address"]). If
an attribute has no match for the query, the raw value is returned. By
default, all indexed attributes are highlighted (as long as they are
strings). You can use * if you want to highlight all attributes.
I'm struggling with translating their abstract, scattered information into a coherent piece of code. Any suggestions?

attributesToIndex, attributesToHighlight and attributesToSnippet are the three main settings used for highlighting.
attributesToIndex is an index setting (you can set it in your dashboard or your back-end, but not in the front-end).
attributesToHighlight are, if not set, equal to the attributesToIndex. They can be set in your index settings, as attributesToIndex, but can also be overridden at query time (and can only contain attributes also in attributesToIndex)
attributesToSnippet are, if not set, equal to an empty array. Each attribute can have a modifier at the end like :10 to say how much words you want in your snippet. Other than that, they work the same way than attributesToHighlight.
Let's take an example:
Index settings
attributesToIndex: ['title', 'description']
attributesToHighlight: ['title']
attributesToSnippet: ['description:3']
Record
{
"title": "Test article",
"description": "A long long long test description long long long",
"link": "https://test.com/test-article"
}
For the query "test", here's basically the JSON of a suggestion you'd get:
{
"title": "Test article",
"description": "A long long long test description long long long",
"link": "https://test.com/test-article",
"_highlightResult": {
"title": {
"value": "<em>Test article</em>"
}
},
"_snippetResult": {
"description": {
"value": "... long <em>test</em> description ..."
}
}
}
Notice that neither description nor link are in the _highlightResult object.
link was ignored from the search since it's not in the attributesToIndex
description is not in the _highlightResult because it's not in attributesToHighlight.
You can also notice that in both _highlightResult and _snippetResult, the test word is wrapped in <em></em> tags. That's the tags you can use to show which words matched.
I've omitted some attributes of the answer that didn't help understand my answer. You can see them in your browser console by adding a small console.log(suggestion) at the beginning of your suggestion function.

I've fixed the problem myself, due to finding a setting in Algolia's dashboard by pure luck. To make the returned search results return the snippet too, I did two things:
1). There's an option in Algolia's dashboard that's named 'Attributes to snippet', which you can find in the 'Display' tab of the particular index that you're searching with.
In my case, I set that option to the record attribute that I wanted to highlight in my search queries like this:
2). After I configured that setting, I could access _snippetResult in the function for the autocomplete.js library. As you can see in the image above, the attribute that I added to the 'Attributes to snippet' option was 'content', and so I access the words that matched with the search query with suggestion._snippetResult.content.value.
My code now is:
autocomplete('#search-input', { hint: true, autoselect: false }, [
{
source: autocomplete.sources.hits(index, { hitsPerPage: 7 }),
displayKey: 'title',
templates: {
footer: '<span class="search-foot">Powered by <img src="/static/assets/algolia-logo.png" width="47" height="15"></span>',
suggestion: function(suggestion) {
return '<div class="search-lang">' +
suggestion._highlightResult.platform.value +
'</div><div class="search-title">' +
suggestion._highlightResult.title.value +
'</div>' + '<div class="search-snippet">' +
suggestion._snippetResult.content.value + '</div>';
}
}
}
]).on('autocomplete:selected', function(event, suggestion, dataset) {
window.location.href = suggestion.url;
});
So to summarise, there is simply a manual option to enable the return of search snippets instead of having to use attributesToSnippet somewhere in the code.

Related

Get text nodes separately (in the correct order) with fast-xml-parser?

No matter what options I try, I seem to get all text nodes clustered together, without any information on where the inner XML nodes were inserted. It's easier to explain with a simple example:
<a>left <b>middle</b> right</a>
I expect this to give me something like this:
{
tag: 'a',
children: [
'left ',
{ tag: 'b', children: ['middle'] },
' right',
]
}
The exact format doesn't matter, but middle is between left and right. The order of children elements is important to me. With fast-xml-parser, what I get instead is the following:
{
"a": {
"#text": "left right",
"b": "middle",
}
}
I don't mind the different format, but I lost the information about the position of the <b>middle</b> node. From the JavaScript tree version of the XML file, there's no way to differentiate between these files, as they all parse into the same structure.
<a>left right<b>middle</b></a>
<a><b>middle</b>left right</a>
<a>left <b>middle</b> right</a>
<a>le<b>middle</b>ft right</a>
Is there an option which will allow me to preserve the order of text nodes?
Unfortunately, it seems like the answer is that this option is simply not available in fast-xml-parser, as per this issue I found: https://github.com/NaturalIntelligence/fast-xml-parser/issues/175. Text nodes will get merged as one, and the team member explains that this is "not a bug but expected behavior".

Dialogflow composite entity parameter is undefined when it shouldn't be

Long story short, I'm making a real estate agent chatbot and I just implemented a filter allowing the user to search within a range of numbers (e.g. at least one bedroom, under $2500). In order to do this, I made an entity_range composite entity composed of the range type (e.g. at most, exactly) and the entity itself (unit-currency for price, plus some custom entities like the number of bedrooms). Prior to creating entity_range, the entities themselves worked fine. But now, it seems as though the entity part of entity_range is undefined. See a sample of my code below:
function get_count(req, res) {
console.log("price: " + req.queryResult.parameters["entity_range"]["unit-currency"])
var price, beds, baths, num_filter_funct
if(req.queryResult.parameters["entity_range"]["unit-currency"] != undefined) {
price = req.queryResult.parameters["entity_range"]
console.log("price: " + price)
} else {
console.log("could not find parameter")
}
Before creating entity_range, my code looked exactly the same, except without ["entity_range"] between parameters and ["unit-currency"]. Anyway, this code logs:
price: undefined
could not find parameter
after the input "How many for $2500," with the following diagnostic info:
...
"queryResult": {
"queryText": "how many for $2500",
"parameters": {
"entity_range": [
{
"unit-currency": {
"amount": 2500,
"currency": "USD"
}
}
]
}...
So the entity "unit-currency" is recognized by Dialogflow, but not by my program. entity_range does allow users to not specify a range, so that's not the issue:
see screenshot here.
I would greatly appreciate any advice you have to offer!
That JSON shows entity_range being an array instead of an object. an object.
parameters.entity_range[0][“unit-currency”] should work. Note the [0]. You’ll also want to add some checks before this to make sure enitiy_range exists and it’s length is > 0.
And this part is just a guess but perhaps you mistakenly clicked the “Is List” box for this parameter in dialogflow? I’m checking it would probably make it be an object instead of an array and your existing code would work.

mongodb - find and replace partial string across various fields

Say I have a URL: aaa.something.com/id that is found in several collections, in many different fields.
I would like to change it to bbb.something.com/id via regex (or similar) to find and replace only the prefix of the URL string.
The following:
db.tests.find({ "url": /^aaa\.something\.com\// }).forEach(function(doc) {
doc.url = doc.url.replace(/^aaa\.something\.com\//, "bbb.something.com/");
db.tests.update({ "_id": doc._id },{ "$set": { "url": doc.name } });
});
assumes that the field is always known to be url.
But in the database, The URL could be found in a number of locations such as:
content.photo
content.media
content.media[i].data
avatar
url
You can a wildcard text index and then use $text to find documents which match the specified regex. Once you get these docs you can write Javascript code for finding keys which match your regex and replacing them as needed.

Create a table with alphabetical sorting and search

I wants to create a table (with alphabetical filter and search) like this(example). In which by clicking on any alphabet table shows name that started with only that alphabet. And also have a search option.
I have searched a lot on google but couldnt find table like that. Any one have any idea how i can get that table.
On both letter click and search events, you should run a JS filter function on your data set and display only the relevant data.
A naive example of a filter function:
function filterByProperty(data, prop, searchString) {
return data.filter(function(singleObject) {
return singleObject[prop].indexOf(searchString) === 0;
});
}
var dataSet = [
{
name: "Amanda",
company: "Google"
},
{
name: "Johnny",
company: "Facebook"
},
{
name: "Max",
company: "Go Daddy"
}
];
// Will return a collection containing the first and third objects in dataSet.
var filteredData = filterByProperty(dataSet, "company", "G");
It used a plugin called: DataTable, you can follow the guide here to make your own alphabet search.
https://www.datatables.net/blog/2014-08-26
No enough reputation to comment, so...
From Chrome Developers(Press F12 -> click on Network tab & reload the page) tool
I see dynamic-table.js(not sure if this is library or custom file ) &
jquery.dataTables.jsis used. So you can take a look into these library
I have found solution of my problem through the link provided below.
SOLUTION IS HERE
Note : In this solution only alphabetical filter is available, search feature is not there. Search feature was not that important to me, so I am fine with only alphabet filter.

Elasticsearch multi_match query

I have a search text field in a web GUI for an Elasticsearch index which has two different types of fields that need to be searched on; fulltext (description) and an exact match (id).
Question 1 - How should I add the second exact match query for the id field? When I search for IDs, the exact ID is within the result "set," but it should be the only result.
The description search seems to be working correctly, just not the ID search.
"multi_match": {
"fields": ["id", "description"],
"query": query,
"description": {
"fuzziness": 1,
"operator": "and"
}
}
I think that you are looking for something like this. Try it.
{
"query": {
"bool": {
"must": [ {
"match": {
"description": {
"fuzziness": 1,
"query": "yourfuzzinessdescription"
}
}
},
{
"term" : {
"id" : 1
}
}
]
}
}
}
Dani's query structure is probably what you are looking for but perhaps you also need an alternative to the fuzziness aspect of the query. Or maybe not - can you please provide an example of an user input for the description field and what you expect that to match that up with?
Looking at Match Query documentation and Elasticsearch Common Options - fuzziness, that fuzziness is based on Levenshtein Distance. So, that query corresponds to allowing an edit distance of 1 and will allow minor misspellings and such. If you keep the and operator in the original query, then all terms in the query must get matched. Given you have a document with a description like "search server based on Lucene", you will not be able to retrieve that with a description query like "lucene based search server". Using an analyzer with the stop filter and a stemming filter in combination with a match phrase query with a slop would work? But again, it depends on what you are trying.

Categories