# Text query on multiple fields

**URL:** <https://discuss.elastic.co/t/text-query-on-multiple-fields/7002>\
**Category:** Elasticsearch\
**Created:** [March 14, 2012, 4:14pm UTC](https://discuss.elastic.co/t/text-query-on-multiple-fields/7002 "2012-03-14T16:14:36Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jan\_Fiedler](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/jan_fiedler/32/2518_2.png) [@Jan\_Fiedler](https://discuss.elastic.co/u/Jan_Fiedler)\
**Post date:** [March 14, 2012, 4:14pm UTC](https://discuss.elastic.co/t/text-query-on-multiple-fields/7002/1 "2012-03-14T16:14:36Z")

</div>

I used to use the 'query\_string' query type to run searches across multiple  
fields (via the build in dismax capability). However, the Lucene parsing of  
the query phrase causes more harm than good for me so I thought to move to  
the text query family (that would only analyze but not parse the search  
phrase).

This works fine as long as I only have one field to search. In case of  
multiple fields things become difficult as the text query family is  
strictly single fielded. I understand that I would have to construct a  
boolean (and) query per term with a dismax per field to achieve what  
'query\_string' is doing implicitly. However, this approach would require  
analyzing the search phrase (to get to its terms) _before_ I can construct  
the correct query. Doing this analyze would mean another roundtrip via the  
analyze API and I could not use the text query with the analyzed terms.  
This does not seem right.

So my question is whether I am missing something or whether there is a  
certain mismatch in the capabilities of the text vs. query\_string API?  
Could the text query family be extended to support multiple fields ?

---

<div class="post-metadata">

**Author:** ![Clinton\_Gormley](https://avatars.discourse-cdn.com/v4/letter/c/50afbb/32.png) [@Clinton\_Gormley](https://discuss.elastic.co/u/Clinton_Gormley)\
**Post date:** [March 14, 2012, 4:38pm UTC](https://discuss.elastic.co/t/text-query-on-multiple-fields/7002/2 "2012-03-14T16:38:14Z")

</div>

Hi Jan

On Wed, 2012-03-14 at 09:14 -0700, Jan Fiedler wrote:

> I used to use the 'query\_string' query type to run searches across  
> multiple fields (via the build in dismax capability). However, the  
> Lucene parsing of the query phrase causes more harm than good for me  
> so I thought to move to the text query family (that would only analyze  
> but not parse the search phrase).
> 
> This works fine as long as I only have one field to search. In case of  
> multiple fields things become difficult as the text query family is  
> strictly single fielded. I understand that I would have to construct a  
> boolean (and) query per term with a dismax per field to achieve what  
> 'query\_string' is doing implicitly. However, this approach would  
> require analyzing the search phrase (to get to its terms) before I can  
> construct the correct query. Doing this analyze would mean another  
> roundtrip via the analyze API and I could not use the text query with  
> the analyzed terms. This does not seem right.

Doesn't this do what you want?

curl -XGET '[http://127.0.0.1:9200/\_all/\_search?pretty=1](http://127.0.0.1:9200/_all/_search?pretty=1)' -d '  
{  
"query" : {  
"dis\_max" : {  
"queries" : [  
{  
"text" : {  
"name" : "foo bar"  
}  
},  
{  
"text" : {  
"title" : "foo bar"  
}  
}  
]  
}  
}  
}  
'

clint

---

<div class="post-metadata">

**Author:** ![Jan\_Fiedler](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/jan_fiedler/32/2518_2.png) [@Jan\_Fiedler](https://discuss.elastic.co/u/Jan_Fiedler)\
**Post date:** [March 14, 2012, 4:44pm UTC](https://discuss.elastic.co/t/text-query-on-multiple-fields/7002/3 "2012-03-14T16:44:40Z")

</div>

Well, not sure. This would use the default operator of the text query  
(which is 'OR'). I have not tested it but I would assume I would end up  
hitting documents that only have one of the terms (in either name or  
title). What I need is documents that have both terms (i.e. 'foo' and  
'bar') in either the 'name' or the 'title' field.

>

---

<div class="post-metadata">

**Author:** ![Clinton\_Gormley](https://avatars.discourse-cdn.com/v4/letter/c/50afbb/32.png) [@Clinton\_Gormley](https://discuss.elastic.co/u/Clinton_Gormley)\
**Post date:** [March 14, 2012, 5:07pm UTC](https://discuss.elastic.co/t/text-query-on-multiple-fields/7002/4 "2012-03-14T17:07:48Z")

</div>

On Wed, 2012-03-14 at 09:44 -0700, Jan Fiedler wrote:

> Well, not sure. This would use the default operator of the text query  
> (which is 'OR'). I have not tested it but I would assume I would end  
> up hitting documents that only have one of the terms (in either name  
> or title). What I need is documents that have both terms (i.e. 'foo'  
> and 'bar') in either the 'name' or the 'title' field.

😉

curl -XGET '[http://127.0.0.1:9200/\_all/\_search?pretty=1](http://127.0.0.1:9200/_all/_search?pretty=1)' -d '  
{  
"query" : {  
"dis\_max" : {  
"queries" : [  
{  
"text" : {  
"name" : {  
"operator" : "and",  
"query" : "foo bar"  
}  
}  
},  
{  
"text" : {  
"title" : {  
"operator" : "and",  
"query" : "foo bar"  
}  
}  
}  
]  
}  
}  
}  
'

>

---

<div class="post-metadata">

**Author:** ![Jan\_Fiedler](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/jan_fiedler/32/2518_2.png) [@Jan\_Fiedler](https://discuss.elastic.co/u/Jan_Fiedler)\
**Post date:** [March 14, 2012, 6:57pm UTC](https://discuss.elastic.co/t/text-query-on-multiple-fields/7002/5 "2012-03-14T18:57:17Z")

</div>

I am pretty sure this is still not what I need and what query\_string is  
providing. The query will now insist that both terms (i.e. 'foo' and 'bar')  
are present in a single field. It will not match documents that have 'foo'  
in the 'name' field and 'bar' in the 'title' field. This is what I tried to  
get at in my first post. You would need to parse the phrase 'foo bar' to  
get the terms such that you could build a bool (and) query per term with  
dis\_max queries over fields. I bet this is what the 'query\_string' is doing  
internally when mapping to Lucene. It is just missing for the text query  
(or the 'query\_string' should have a mode to disable parsing such that it  
only analyzes).

---

<div class="post-metadata">

**Author:** ![Clinton\_Gormley](https://avatars.discourse-cdn.com/v4/letter/c/50afbb/32.png) [@Clinton\_Gormley](https://discuss.elastic.co/u/Clinton_Gormley)\
**Post date:** [March 15, 2012, 7:20am UTC](https://discuss.elastic.co/t/text-query-on-multiple-fields/7002/6 "2012-03-15T07:20:50Z")

</div>

On Wed, 2012-03-14 at 11:57 -0700, Jan Fiedler wrote:

> I am pretty sure this is still not what I need and what query\_string  
> is providing. The query will now insist that both terms (i.e. 'foo'  
> and 'bar') are present in a single field. It will not match documents  
> that have 'foo' in the 'name' field and 'bar' in the 'title' field.  
> This is what I tried to get at in my first post. You would need to  
> parse the phrase 'foo bar' to get the terms such that you could build  
> a bool (and) query per term with dis\_max queries over fields. I bet  
> this is what the 'query\_string' is doing internally when mapping to  
> Lucene. It is just missing for the text query (or the 'query\_string'  
> should have a mode to disable parsing such that it only analyzes).

OK, I misunderstood your previous email.

It may just be easier to sanitise the user input and use the query  
string. This is what I do in my Perl module:

[https://metacpan.org/module/ElasticSearch::Util#filter\_keywords-](https://metacpan.org/module/ElasticSearch::Util#filter_keywords-)

#===================================  
sub filter\_keywords {  
#===================================  
local $\_ = shift;

```
s{[^[:alpha:][:digit:] \-+'"*@\._]+}{ }g;

return '' unless /[[:alpha:][:digit:]]/;

s/\s*\b(?:and|or|not)\b\s*/ /gi;

# remove '-' that don't have spaces before them
s/(?<! )-/\ /g;

# remove the spaces after a + or -
s/([+-])\s+/$1/g;

# remove + or - not followed by a letter, number or "
s/[+-](?![[:alpha:][:digit:]"])/ /g;

# remove * without 3 char prefix
s/(?<![[:alpha:][:digit:]\-@\._]{3})\*/ /g;

# ensure quotes are closed
my $quotes = (tr/"//);
if ( $quotes % 2 ) { $_ .= '"' }

s/^\s+//;
s/\s+$//;

return $_;

```

}

clint

---

<div class="post-metadata">

**Author:** ![kimchy](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/kimchy/32/44952_2.png) [@kimchy](https://discuss.elastic.co/u/kimchy)\
**Post date:** [March 16, 2012, 5:13pm UTC](https://discuss.elastic.co/t/text-query-on-multiple-fields/7002/7 "2012-03-16T17:13:17Z")

</div>

The reason why its simpler to do this with the query\_string with multiple  
fields is because the query parser for query\_string already breaks the  
words by whitespace (to parse the relevant syntax), so effectively, its  
building the dis max around queries generated by that query\_string  
whitepsace tokenization (each one is also further analyzed). The text query  
simply takes the text and analyzes it, generating the relevant query.

On Wed, Mar 14, 2012 at 8:57 PM, Jan Fiedler [fiedler.jan@gmail.com](mailto:fiedler.jan@gmail.com) wrote:

> I am pretty sure this is still not what I need and what query\_string is  
> providing. The query will now insist that both terms (i.e. 'foo' and 'bar')  
> are present in a single field. It will not match documents that have 'foo'  
> in the 'name' field and 'bar' in the 'title' field. This is what I tried to  
> get at in my first post. You would need to parse the phrase 'foo bar' to  
> get the terms such that you could build a bool (and) query per term with  
> dis\_max queries over fields. I bet this is what the 'query\_string' is doing  
> internally when mapping to Lucene. It is just missing for the text query  
> (or the 'query\_string' should have a mode to disable parsing such that it  
> only analyzes).

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/elastic/original/3X/1/a/1ac57faf039f6b580b3f104ef42a2a89e41014de.png) [@system](https://discuss.elastic.co/u/system)\
**Post date:** [July 6, 2017, 3:35am UTC](https://discuss.elastic.co/t/text-query-on-multiple-fields/7002/8 "2017-07-06T03:35:53Z")

</div>


