Repository: elastic/elasticsearch-dsl-py (language: python)

Issue:
Terms lookup feature no longer supported due to recent release
A recent release appears to have broken the ability to perform a "terms lookup" query which is a special subset of terms query which allows you to specify the index, id, and field so you can limit terms to those found in a specific document in another index:

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-terms-query.html#query-dsl-terms-lookup

I used to be able to create a "terms lookup" query like so:

```
Q('terms', **{'_id': {
    'index': 'my-other-index',
    'id': 'my-document-id',
    'path': 'my-field'
}})
```

Now, however, the query generated flattens the dictionary into a list of keys, so that what actually gets built is this:

```
"terms": {
    "_id": [
        "index",
        "id",
        "path"
    ]
}
```

This seems to be an unintended side-effect of a recent release. Please advise.


Discussion / hints:
I can confirm that reverting back to version 8.15.0 allows the query to be generated appropriately, i.e.:

```
"terms": {
    "_id": {
        "index": "my-other-index",
        "id": "my-document-id",
        "path": "my-field"
    }
}
```
Yes, this is another regression introduced by #1887 in 8.15.1. There are lots of variations that were not considered. I have addressed a couple already, but missed the passing of a dict as an argument.

Fix patch:
diff --git a/elasticsearch_dsl/query.py b/elasticsearch_dsl/query.py
index 652b3d57..bc07ac63 100644
--- a/elasticsearch_dsl/query.py
+++ b/elasticsearch_dsl/query.py
@@ -552,7 +552,7 @@ class Terms(Query):
 
     def _setattr(self, name: str, value: Any) -> None:
         # here we convert any iterables that are not strings to lists
-        if hasattr(value, "__iter__") and not isinstance(value, (str, list)):
+        if hasattr(value, "__iter__") and not isinstance(value, (str, list, dict)):
             value = list(value)
         super()._setattr(name, value)
 


Test patch:
diff --git a/tests/test_query.py b/tests/test_query.py
index 64d8ba2f..00db4804 100644
--- a/tests/test_query.py
+++ b/tests/test_query.py
@@ -84,6 +84,14 @@ def test_terms_to_dict() -> None:
     assert {"terms": {"_type": ["article", "section"], "boost": 1.1}} == query.Terms(
         _type=("article", "section"), boost=1.1
     ).to_dict()
+    assert {"terms": {"_type": "article", "boost": 1.1}} == query.Terms(
+        _type="article", boost=1.1
+    ).to_dict()
+    assert {
+        "terms": {"_id": {"index": "my-other-index", "id": "my-id"}, "boost": 1.1}
+    } == query.Terms(
+        _id={"index": "my-other-index", "id": "my-id"}, boost=1.1
+    ).to_dict()
 
 
 def test_bool_to_dict() -> None: