Tire search return terms by first letter - ruby

I'm using Tire/ElasticSearch to create an alphabetical browse of all the tags in my database. However, the tire search returns the tag I want as well as all the other tags associated to the same item. So, for example, if my letter was "A" and an item had the tags 'aardvark' and 'biscuit', both 'aardvark' and 'biscuit' would show up as results for the 'A' query. How can I construct this so that I only get 'aardvark'?
def explore
#get alphabetical tire results with term and count only
my_letter = "A"
self.search_result = Tire.search index_name, 'tags' => 'count' do
query {string 'tags:' + my_letter + '*'}
facet 'tags' do
terms 'tags', :order => 'term'
end
end.results
end
Mapping:
{
items: {
item: {
properties: {
tags: {
type: "string",
index_name: "tag",
index: "not_analyzed",
omit_norms: true,
index_options: "docs"
},
}
}
}
}

Following things that you'll need to change:
Mapping
You need to map the tags properly in order to search through them. And as your tags, are inside you item document, you need to set the properties of tags as nested, so that you can apply your search query in the facets too. Here is the mapping that you need to set:
{
item: {
items: {
properties: {
tags: {
properties: {
type: "nested",
properties: {
value: {
type: "string",
analyzer: 'not_analyzed'
}
}
}
}
}
}
}
}
Query
Now, you can use prefix query to search through the tags that start with a certain letter and get the facets, Here is the complete query:
query: {
nested: {
path: "tags",
query: {
prefix: {
'tags.value' : 'A'
}
}
}
}
facets: {
words: {
terms: {field: 'tags.value'},
type: 'nested',
facet_filter: {prefix: {
'tags.value' : 'A'
}
}
}
}
Facet filter is applied while computing facets, so you'll only get the facets which will match your criteria. I preferred prefix query over regular exp. query because of performance issues. But I am not quite sure whether prefix query works for your problem. Let me know it it doesn't work.

Related

What do I do when my query string in an elasticsearch is too long and the search doesn't work?

I'm querying Elastic in my GraphQL resolver like this below. The peopleArray under the second multi_match is dynamic and is filled with numbers (e.g. [101, 102, 103...]). This is for different users to get their messages. If the user's peopleArray has 165 items, it works fine. However, I have test users who have 1873 items in the peopleArray and the query doesn't work. And I may need to support users with 60,000 items. Basically, I'm creating a messaging system for employees in a company and all the numbers are all the people in their network of message-sending. If this is not a practical way of doing this, any suggestions would be appreciated. But if it is possible, how do I get my results? (Have not tried this in the Kibana UI yet.)
const esClient = new elasticsearch.Client({
host: config.elasticsearchRoot
});
const result = await esClient.search({
index: `${config.index.messageStream}`,
body: {
query: {
bool: {
must: [
{
multi_match: {
query: tenantId,
fields: [
"tenantId",
"discretionary.clientKey.systemTenantId",
"clientKey.systemTenantId",
"author.tenantId"
],
operator: "OR"
}
},
{
multi_match: {
query: peopleArray,
fields: ["author.peopleId", "details.recipient.peopleId"],
operator: "OR"
}
}
]
}
},
from,
size,
docvalue_fields: [
{
field: "sortDate",
format: "use_field_mapping"
}
],
sort: {
sortDate: {
missing: "_last",
unmapped_type: "long",
order: "desc"
}
}
}
});```

How to query from two elasticsearch indexes with one query

Newbie here. I have two indexes: /items and /categories that contain data similar to this:
#Items:
{ id:1, name: "banana", categories: [1] },
{ id:2, name: "coconut", categories: [1] },
{ id:3, name: "smoothie", categories: [1, 2] }
#Categories
{ id:1, name: "Apple" },
{ id:2, name: "Banana" }
I want to create a query with bana as a query string and I should return items 1 and 3. It should not return category objects. ID 1 because of the name match and ID 3 because it has a category that has a name match.
How could a query like this be constructed?
My current query does not return item 3 and simply like
GET /items/_search
{
"query": {
"bool":{
"must":{
"query_string":{
"query":"bana"
}
}
}
}
}
Is it necessary to create two indexes for this? maybe use name directly in Item index to replace id:
{ id:1, name: "banana", categories: ["Apple"] }
Since index will have its own dictionary, will not cause extra disk space.
if really need to join two indexes, maybe you want to try Parent-Child Relationship, but it will deprecated in future, and replaced by: join datatype, this way also will keep data in one index.

ElasticSearch query for items not in given array

I am trying to write a part of a query to filter out any items with a type as "group" and that have a group id that isn't in a given array of ids. I started writing a bool query with a must and must_not but I was getting tripped up on how to write "id not in the given array.
EDIT:
I am actually converting an outdated query using "and" and "not" to be ES 5.5 compatible. Here is the old query that worked.
:and => [
{
term: {
type: 'group'
}
},
{
:not => {
terms: {
group_id: group_ids
}
}
},
{
:not => {
terms: {
user_id: user_ids
}
}
}
]
group_ids and user_ids are arrays.
You probably have not analyzed the arrays with the IDs. You can use a Bool query with a filter clause, and then within that filter start a new bool query with a mustNot clause and within that clause add a terms query with your IDs.
bool: {
must: {
term: {
kind: 'group'
}
},
must_not: [
{
terms: {
group_id: group_ids
}
},
{
terms: {
user_id: user_ids
}
}
]
}

How to use ReQL filter and match command on arrays

I have a table in rethinkdb where each row has following structure -
{
'name':'clustername',
'services':[
{
'name':'service1'
},
{
'name':'service2'
}
]
}
I am running a query to filter service2 object like this
r.table('clusters').filter({"name": "clustername"})
.pluck('services').filter((service) => {
return service("name").match('service2')
})
But this is not returning anything: No results were returned for this query
Can anyone tell why this is happening?
pluck returns sequence, so this query:
r.table('clusters').filter({"name": "clustername"}).pluck('services')
will return:
{
"services": [
{
"name": "service1"
} ,
{
"name": "service2"
}
]
}
You need get services field from it, it will return array with services field of items found by filter.
And after that you need to use your second filter on each item by using map.
So, correct query:
r.table('clusters').filter({"name": "clustername"}).pluck('services')("services").map(item => {
return item.filter(service => {
return service("name").match("service2");
});
})

NEST (ElasticSearch) matching Highlights to documents

I'm using C# NEST with ElasticSearch. I'm able to query an index of Products and look in their Name and CategoryName fields for matches. I can also extend the query using Highlights.
Now in my IQueryResponse response I have two collections: (1) .Documents and (2) .Highlights.
e.g.: Consider the search for: "cat" which has 3 document results:
{
{ Name: "Cat product", CategoryName: "Category1" },
{ Name: "Some product", CategoryName: "Category2" },
{ Name: "Some product2", CategoryName: "Category3" }
}
But now I have 4 highlight results:
{
{ Field: "name", Highlights: ['"<u>Cat</u> product"'] },
{ Field: "categoryName", Highlights: ['"<u>Cat</u>egory1"'] },
{ Field: "categoryName", Highlights: ['"<u>Cat</u>egory2"'] },
{ Field: "categoryName", Highlights: ['"<u>Cat</u>egory3"'] }
}
They seem to be in no way related to each other. How do I know which Highlight item belongs to which Document item?
IQueryResponse also exposes .DocumentsWithMetaData of type IEnumerable<IHit<T>> where T is the type of your document
This is basically the unwrapped view of the results as return by elasticsearch IHit<T> has many useful properties such as the Highlights.
I've added a DocumentId result to the highlight class Highlight so that no matter how you get to the highlight you can relate it back easily to the hit.
So use .DocumentsWithMetaData for now, the next release will have a more logical API for highlights.
here is an updated answer for version 7.x. You receive two collections as before, .Documents and .Hits .
Within .Hits each one has an .Id that matches the _id of the index in elasticsearch. Note: if you request more than one highlighting .NumberofFragments in your query, you will just keep overwriting the result.title and result.content in the code below, so take this as a loose example to indicate how you can match the highlight result to the correct document result, and then overwrite the document field with the one containing the highlighting.
if (response.Documents.Count > 0)
{
foreach (MyResultClass result in response.Documents) //cycle through your results
{
foreach (var hit in response.Hits) // cycle through your hits to look for match
{
if (hit.Id == result.id) //you found the hit that matches your document
{
foreach (var highlightField in hit.Highlight)
{
if (highlightField.Key == "title")
{
foreach (var highlight in highlightField.Value)
{
result.title = highlight.ToString();
}
}
else if (highlightField.Key == "content")
{
foreach (var highlight in highlightField.Value)
{
result.content = highlight.ToString();
}
}
}
}
}
}

Resources