Your problem is you are using query_string and not defining a default_field, so it's searching against the _all field which is using your default analyzer (standard most likely).
To fix this, do this:
curl -XPOST "http://localhost:9200/testindex/_search" -d'
{
"query": {
"query_string": {
"default_field": "text",
"query": "grey"}
}
}
}'
{"took":7,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":3,"max_score":0.30685282,"hits":[{"_index":"testindex","_type":"article","_id":"4","_score":0.30685282, "_source" : {"text": "greying"}},{"_index":"testindex","_type":"article","_id":"1","_score":0.30685282, "_source" : {"text": "grey"}},{"_index":"testindex","_type":"article","_id":"3","_score":0.30685282, "_source" : {"text": "greyed"}}]}}
I try to stay away from query_string searching though, unless I really can't avoid it. Sometimes, people coming from solr like this method of searching over the search dsl. In this case, try using match:
curl -XPOST "http://localhost:9200/testindex/_search" -d'
> {
> "query": {
> "match": {
> "text": "grey"
> }
> }
> }'
{"took":5,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":3,"max_score":0.30685282,"hits":[{"_index":"testindex","_type":"article","_id":"4","_score":0.30685282, "_source" : {"text": "greying"}},{"_index":"testindex","_type":"article","_id":"1","_score":0.30685282, "_source" : {"text": "grey"}},{"_index":"testindex","_type":"article","_id":"3","_score":0.30685282, "_source" : {"text": "greyed"}}]}}
But either way yields the correct results.
See documentation here for the query_string: