Algolia Search and Nested Array - javascript

I am working on a search application that uses algolia for indexing. When the user types a search term into the text input box, we want to populate the autocompletion dropdown with events. Every event belongs to an event category as well.
Example:
{
"category": "Disney",
"events": [
{
"title": "Ice Skating"
},
{
"title": "Peter-Pan"
},
{
"title": "Roller Skating"
}
]
}
If someone searches for "skating", we want to pull back the parent category and the child events "Ice Skating" and "Roller Skating" but omit the "Peter-Pan" event.
Is this type of nested filtering possible with Algolia? If so, how would the filtering work? Would it need to be done with JS, will Algolia handle it for me or do we need to create separate indexes for Event Categories and then Individual Events?
Thanks!

Yes, Algolia will automatically filter out Peter Pan and return items with Skating.
As an example I've got some data setup like so:
announcements: [
{
id: ..,
..
author: {
id: ..,
name: 'Preston',
..
}
},
..
]
If I search Announcements for Preston it'll return any announcement that has Preston in the author attribute. This is the default for Algolia and will search the entire record for your search term. This can be slow.
You can go into the Algolia dashboard, or with the API, and under your index's Ranking tab define what you want to search by and ignore.

The first thing you need to do is adding events.title to the searchable attributes. This will make sure that when the end-user types skat, it will match one of the title.
Then you can check what parts of each result is matched using _highlightResult and more specifically filtering based on the matchLevel:
full is when you have a match and none is when you don't.
This filtering should be possible in JS, in the template you use to display the results.

Related

Deleting an object from a nested array in DynamoDB - AWS JavaScript SDK

I'm building an app where I need to delete items stored in the database. Here's a (shortened) example of user data I have in my DynamoDB table called 'registeredUsers':
{
"userId": "f3a0f858-57b4-4420-81fa-1f0acdec979d"
"aboutMe": "My name is Mary, and I just love jigsaw puzzles! My favourite jigsaw category is Architecture, but I also like ones with plants in them.",
"age": 27,
"email": "mary_smith#gmail.com",
"favourites": {
"imageLibrary": [
{
"id": "71ff8060-fcf2-4523-98e5-f48127d7d88b",
"name": "bird.jpg",
"rating": 5,
"url": "https://s3.eu-west-2.amazonaws.com/jigsaw-image-library/image-library/images/bird.jpg"
},
{
"id": "fea4fd2a-851b-411f-8dc2-1ae0e144188a",
"name": "porsche.jpg",
"rating": 3,
"url": "https://s3.eu-west-2.amazonaws.com/jigsaw-image-library/image-library/images/porsche.jpg"
},
{
"id": "328b913f-b364-47df-929d-925676156e97",
"name": "rose.jpg",
"rating": 0,
"url": "https://s3.eu-west-2.amazonaws.com/jigsaw-image-library/image-library/images/rose.jpg"
}
]
}
}
I want to be able to delete the item 'rose.jpg' in the user.favourites.imageLibrary array. In order to select the correct user, I can provide the userId as the primary key. Then, in order to select the correct image in the array, I can pass the AWS.DocumentClient the 'id' of the item in order to delete it. However, I'm having trouble understanding the AWS API Reference docs. The examples given in the developer guide do not describe how to delete an item by looking at one of it's attributes. I know I have to provide an UpdateExpression and an ExpressionAttributeValues object. When I wanted to change a user setting, I found it pretty easy to do:
const params = {
TableName: REGISTERED_USERS_TABLE,
Key: { userId },
UpdateExpression: "set userPreferences.difficulty.showGridOverlay = :d",
ExpressionAttributeValues: {
":d": !showGridOverlay
},
ReturnValues: "UPDATED_NEW"
};
To conclude, I need a suitable Key, UpdateExpression and ExpressionAttributeValues object to access the rose.jpg item in the favourites array.
Unfortunately, the UpdateExpression syntax is not as powerful as you would have liked. It supports entire nested documents inside the item, but not sophisticated expressions to search in them or to modify them. The only ability it gives you inside a list is to access or modify its Nth element. For example:
REMOVE #favorites.#imagelibrary[3]
Will remove the 3rd element of imagelibrary (note that the "#imagelibrary" will need to be defined in ExpressionAttributeNames), and you can also have a condition on #favorites.#imagelibrary[3].#id, for example, in ConditionExpression. But unfortunately, there is no way to specify more complex combinations of conditions and updates, such as "find me the i where #favorites.#imagelibrary[i].#id is equal something, and then REMOVE this specific element".
Your remaining option is to read the full value of the item (or with ProjectionExpression just the #favorties.#imagelibrary array), and then in your own code find which of the elements you want to remove (e.g., discover that it is the 3rd element), and then in a separate update, remove the 3rd element.
Note that if there's a possibility that some other parallel operation also changes the item, you must use a conditional update (both UpdateExpression and ConditionExpression) for the element removal, to ensure the element that you are removing still has the id you expected. If the condition fails, you need to repeat the whole operation again - read the modified item again, find the element again, and try to remove it again. This is an example of the so-called "optimistic locking" technique which is often used with DynamoDB.

Algolia search on nested objects in a record - multiple facetFilters in one object

I’m migrating from Mongo to Firebase with Algolia on top to provide the search. But hitting a snag coming up with a comparable way to search in individual elements of a record.
I have an object that stores when a room is available: from and to. Each record can have many individual from/to combos (see the sample below with 2). I want to be able to run a search something like:
roomavailable.from <= 1522195200 AND roomavailable.to >=1522900799
But only have the query search a match within each element, not any facet in all elements. An element query in Mongo works like that. But if I run that query on the record listed below, it will return the record, because the two roomavailable objects satisfy the .from and .to query. I think.
Is there a way to ensure the search is looking only at matching a pair of .from and .to in an individual object/element?
Below is the pertinent part of the record stored in Algolia so you can see the structure.
"roomavailable": [
{
"_id": "rJbdWvY9M",
"from": 1522195200,
"to": 1522799999
},
{
"_id": "r1H_-vKqz",
"from": 1523923200,
"to": 1524268799
}
],
And here is the Mongo (mongoose) equivalent where its searching inside individual elements (this works):
$elemMatch: {
from: {
$lte: moment(dateArray[0]).utc().startOf('day').format()
},
to: {
$gte: moment(dateArray[1]).utc().endOf('day').format()
}
}
I have also tried this query but it seems to still match either the .from AND .to but in any of the the individual roomavailable elements:
index.search({
query: '',
filters: filters,
facetFilters: [roomavailable.from: 1522195200, roomavailable.to: 1524268799],
attributesToRetrieve: [
"roomavailable",
],
restrictHighlightAndSnippetArrays: true
})
I found a couple posts on Algolia discussing using 1 bracket vs. 2 brackets in the facetFilters. I've tried both. Neither work.
Any suggestions would be awesome. Thanks!
Edit: See discussion on Algolia Discourse:
https://discourse.algolia.com/t/how-to-match-multiple-attributes-in-nested-object-with-numericfilters/4887/8
Hi #kanec, thanks for clarifying your question!
Indeed what #Alefort suggested (using roomavailable in a separate index) would be the easiest option since the query I mentioned above will definitely return the results you want. This will mean that you'll have to query the room availability index separately in order to get which IDs are available, so you'll have to use multiple-queries:
https://www.algolia.com/doc/api-reference/api-methods/multiple-queries/
That said, I asked our core API team to see if there's a more reasonable way to approach this issue, but I fear that this is a filter limit due to performance reasons with arrays. You could transform your data structure in the following and index your rooms as an object instead:
[
{
"roomavailable": {
"0": {
"_id": "rJbdWvY9M",
"from": 1522195200,
"to": 1522799999
},
"1": {
"_id": "r1H_-vKqz",
"from": 1523923200,
"to": 1524268799
}
}
}
]
So you can apply the following filter:
{
"filters": "roomavailable.0.from <= 1522195200 AND roomavailable.0.to >= 1522799999 AND roomavailable.1.from <= 1522195200 AND roomavailable.1.to >=1522900799"
}
The downside of this is that you'll need to know the length of roomavailable in order to build the search query on the front-end (you can do so at indexing time by adding a roomavailable_count property) and also this will probably will be less performant with a considerable number of rooms per item; in this case, switching to a dedicated index makes totally sense for the following reasons:
If in your backend you frequently update available rooms you won't impact the other indices' build time
Filters will perform better (as explained above)
Indexing strategy will be simpler to handle
Let me know what you think about this and if it helps you out.

Angular filter show found not found indicator

I'm bulding a datagrid based on AngularJS and i got stucked on filter function.
I need to build a filter that interacts with the json to tell me if the value filtered was found or not found. The filter must be applied to generic jsons
I have the following json
var json = [{
"id":"1",
"name":"Homer",
"Role":"Analyst",
"Found":"true"
},{
"id":"2",
"name":"Maggie",
"Role":"Manager",
"Found":"true"
},
{
"id":"3",
"name":"Lisa",
"Role":"CEO",
"Found":"true"
},]
If i type "Li" or "Mana" on a search text box, Lisa and Maggie object must be shown because "Li" is part of Lisa's name and "Mana" is part of Manager role name and found key on Homer object must be set to false
Use ng-repeat with |filter is not an option, since I got lot of things going on like submenus on each row, colspans, etc
Thanks
You can try to do the filter in the controller using $filter avoiding the ng-repeat.
$filter('filter' )(Json, textToFilterBy);
Here is the Docs for $filter
https://docs.angularjs.org/api/ng/filter/filter
Here is an example of it working in a controller.
https://jsfiddle.net/Michael_Warner/khLhLou8/6/

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