Reputation: 393
I am trying to find an exact search for an url with ElasticSearch ("@elastic/elasticsearch": "^7.5.0"). I have configured my mapping like so:
const schema = {
userId: {
type: "keyword"
},
url: {
type: "keyword",
index: false,
analyzer: 'keyword'
},
pageTitle: {
type: 'text',
},
pageText: {
type: 'text',
}
};
await client.indices.putMapping({
index,
type,
include_type_name: true,
body: {
properties: schema
}
})
I have tried different queries, and they looks like this:
body: {
query: {
bool: {
must: {
match: {
query: 'test stack',
analyzer: 'keyword',
}
}
}
}
}
Or second attempt:
body: {
query: {
constant_score: {
filter: {
bool: {
must: {
term: {
url: 'test stack'
}
}
}
}
},
}
}
None of them work. I want to get only the results where the exact string 'test/stack' is found. Any help would be highly appreciated.
Example of data I'm trying to add:
[
{"url": "test stack",
"userId": "anotherTest",
"pageTitle": "not important",
"pageText": "not important",
"log": [1, 3, 7]
},
{"url": "test stack",
"userId": "anotherTest",
"pageTitle": "not important",
"pageText": "not important",
"log": [1, 3, 7]
},
{"url": "test stack",
"userId": "anotherTest",
"pageTitle": "not important",
"pageText": "not important",
"log": [1, 3, 7]
}
]
Thanks.
Upvotes: 0
Views: 485
Reputation: 393
I managed to make this work. Steps are: 1. Delete the index. 2. Delete the custom mapping function. 3. Create the index (with client.indices.create) 4. Index the first item (with client.index). 5. At this point, you can check in postman the dynamic mappings created by ElasticSearch (only visible after 1st item is indexed, by what I could tell). You can make a get request at http://localhost:9200/history/_mappings, and the response should look something like this:
{
"history": {
"mappings": {
"properties": {
"fullTitle": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"log": {
"properties": {
"startTime": {
"type": "long"
},
"timeSpent": {
"type": "long"
}
}
},
"protocol": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"text": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"totalTimeSpent": {
"type": "long"
},
"totalVisits": {
"type": "long"
},
"url": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"userId": {
"type": "long"
}
}
}
}
}
As you can see, any field indexed as text has attached another field, called keyword, which can be used for exact matches. 6. The query to get the exact matches looks like this:
const result = await esClient.search({
index: 'history',
body: {
query: {
term: {
'url.keyword': {
value: toInsert.url
}
}
}
}
})
At this point you should receive results only in case of exact match for the field "url" in my case. Hope this helps somebody else. Thanks @ibexit for trying to help me.
Upvotes: 2
Reputation: 3667
I see two problems:
The mapping defined for the url field says
url: { type: "keyword", index: false, analyzer: 'keyword' },
If you define index: false
, the field will not be searchable at all. Using the following mapping should work properly:
url: { type: "keyword" }
See https://www.elastic.co/guide/en/elasticsearch/reference/current/keyword.html for more detailed information
The keyword mapped fields will not match using the match
query which is designed to query text fields. Please use the term query instead for keyword fields. Please notice the example below using the Elasticseaech Query API:
GET /_search
{
"query": {
"term": {
"url": { <<= the field to search
"value": "test stack" <<= the searched value
}
}
}
}
Here is the according documentation: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-term-query.html
BTW: keep in mind that you need to reindex the data after a mapping change
Upvotes: 0