Nano Hash - криптовалюты, майнинг, программирование

Реализация запроса Array.Except (Array2) ›0 в фильтре elasticsearch?

Допустим, у меня проиндексированы следующие документы:

[
    {
        "Id": 1,
        "Numbers": [1, 2, 3]
    },
    {
        "Id": 2,
        "Numbers": [4, 5]
    }    
]

У меня есть параметр [1,2,4,5], который определяет, какие числа мне не разрешено видеть - я хочу найти документы, в которых массив «Numbers» содержит хотя бы один элемент НЕ во входном массиве (так что в данном случае первый документ должен быть возвращен).

Реальный сценарий предназначен для поиска групп, которые (или чьи дочерние группы) не содержат продуктов, принадлежащих к определенному типу продуктов. У меня есть рекурсивно проиндексированные идентификаторы типов продуктов (представленные в примере числами), и я хочу найти группы, которые содержат продукты, не принадлежащие моему входному параметру (мой входной параметр представляет собой массив идентификаторов типов продуктов, которые мне не разрешено видеть)

Какой запрос / фильтр мне следует использовать и как его построить? Я учел следующее:

        return desc.Bool(b => b
            .MustNot(mn => mn.Bool(mnb => mnb.Must(mnbm => mnbm.Terms(t => t.ItemGroups, permissions.RestrictedItemGroups) && mnbm.Term(t => t.ItemGroupCount, permissions.RestrictedItemGroups.Count())))));

но проблема в том, что у меня есть 6 ограниченных групп элементов, где, поскольку данная группа содержит 3 ограниченные группы, я не найду никаких совпадений, потому что счетчик не совпадет. Теперь в этом есть некоторый смысл. В качестве обходного пути я реализовал Results.Except (Restricted) в C #, чтобы отфильтровать ограниченные группы после поиска, но хотел бы реализовать его в elasticsearch.

13.12.2018

Ответы:


1

Новый ответ

Я оставляю старый ответ ниже, так как он может быть полезен другим людям. В вашем случае вы хотите отфильтровать несоответствующие документы, а не только пометить их. Итак, следующий запрос даст вам то, что вы ожидаете, то есть только первый документ:

POST test/_search
{
  "query": {
    "script": {
      "script": {
        "source": """
          // copy the doc values into a temporary list
          def tmp = new ArrayList(doc.Numbers.values);

          // remove all ids from the params
          tmp.removeIf(n -> params.ids.contains((int)n));

          // return true if the array still contains ids, false if not
          return tmp.size() > 0;
        """,
        "params": {
          "ids": [
            1,
            2,
            4,
            5
          ]
        }
      }
    }
  }
}

Предыдущий ответ

Один из способов решить эту проблему - использовать поле скрипта, которое будет возвращать истину или ложь в зависимости от вашего состояния:

POST test/_search
{
  "_source": true,
  "script_fields": {
    "not_present": {
      "script": {
        "source": """
      // copy the numbers array
      def tmp = params._source.Numbers;

      // remove all ids from the params
      tmp.removeIf(n -> params.ids.contains(n));

      // return true if the array still contains data, false if not
      return tmp.length > 0;
""",
        "params": {
          "ids": [ 1, 2, 4, 5 ]
        }
      }
    }
  }
}

Результат будет выглядеть так:

  "hits" : {
    "total" : 2,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "test",
        "_type" : "doc",
        "_id" : "2",
        "_score" : 1.0,
        "_source" : {
          "Id" : 2,
          "Numbers" : [
            4,
            5
          ]
        },
        "fields" : {
          "not_present" : [
            false                           <--- you don't want this doc
          ]
        }
      },
      {
        "_index" : "test",
        "_type" : "doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "Id" : 1,
          "Numbers" : [
            1,
            2,
            3
          ]
        },
        "fields" : {
          "not_present" : [
            true                            <--- you want this one, though
          ]
        }
      }
    ]
  }
}
19.12.2018
  • Хм, интересно, есть идеи, насколько это повлияет на производительность? Я полагаю, с миллионами документов ситуация может стать довольно экстремальной? 19.12.2018

  • 2

    terms_set query похоже, для этого подходит; он похож на запрос terms с тем отличием, что вы можете указать, сколько терминов должно соответствовать динамическому значению, полученному из входных данных или каждого документа.

    В вашем случае вы хотите получить инверсию документов, где все числа в массиве Numbers находятся во входных терминах, т.е. если массив Numbers содержит хотя бы одно значение, которого нет во входных терминах, тогда его следует рассматривать как совпадение .

    Что-то вроде следующего будет работать

    private static void Main()
    {
        var defaultIndex = "my_index";
        var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
    
        var settings = new ConnectionSettings(pool)
            .DefaultIndex(defaultIndex)
            .DefaultFieldNameInferrer(f => f);
    
        var client = new ElasticClient(settings);
    
        if (client.IndexExists(defaultIndex).Exists)
            client.DeleteIndex(defaultIndex);
    
        var createIndexResponse = client.CreateIndex(defaultIndex, c => c
            .Settings(s => s
                .NumberOfShards(1)
                .NumberOfReplicas(0)
            )
            .Mappings(m => m
                .Map<MyDocument>(mm => mm.AutoMap())
            )
        );
    
        var bulkResponse = client.Bulk(b => b
            .IndexMany(new []
            {
                new MyDocument { Id = 1, Numbers = new int[] { 1, 2, 3 }},
                new MyDocument { Id = 2, Numbers = new int[] { 4, 5 }},
                new MyDocument { Id = 3, Numbers = new int[] { }},
            })
            .Refresh(Refresh.WaitFor)
        );
    
        var searchResponse = client.Search<MyDocument>(s => s
            .Query(q => (!q
                .TermsSet(ts => ts
                    .Field(f => f.Numbers)
                    .Terms(1, 2, 4, 5)
                    .MinimumShouldMatchScript(sc => sc
                        .Source("doc['Numbers'].size()")
                    )
                )) && q
                .Exists(ex => ex
                    .Field(f => f.Numbers)
                )
            )
        );
    }
    
    public class MyDocument 
    {
        public int Id { get; set; }
        public int[] Numbers { get; set; }
    }
    

    Сгенерированный поисковый запрос выглядит так

    {
      "query": {
        "bool": {
          "must": [
            {
              "exists": {
                "field": "Numbers"
              }
            }
          ],
          "must_not": [
            {
              "terms_set": {
                "Numbers": {
                  "minimum_should_match_script": {
                    "source": "doc['Numbers'].size()"
                  },
                  "terms": [
                    1,
                    2,
                    4,
                    5
                  ]
                }
              }
            }
          ]
        }
      }
    }
    

    и результат

    {
      "took" : 3,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : 1,
        "max_score" : 1.0,
        "hits" : [
          {
            "_index" : "my_index",
            "_type" : "mydocument",
            "_id" : "1",
            "_score" : 1.0,
            "_source" : {
              "Id" : 1,
              "Numbers" : [
                1,
                2,
                3
              ]
            }
          }
        ]
      }
    }
    

    Запрос terms_set находится в предложении must_not для инвертирования совпадений, где все значения в Numbers входят во входные термины, и объединен с запросом exists на Numbers для исключения документов, у которых нет значений для Numbers, как в примере документа с идентификатором 3.

    Это можно было бы улучшить, если также проиндексировать длину массива Numbers в другом поле документа, а затем использовать MinimumShouldMatchField(...) вместо скрипта. Просто нужно убедиться, что два свойства затем остаются синхронизированными, что было бы довольно легко сделать в C # POCO с помощью средства получения свойств, которое возвращает значение длины массива Numbers.

    30.12.2018
  • Хм интересно, спасибо. Я должен взглянуть на это. Одна вещь, о которой я не упомянул, это то, что мы используем ES .17, так что termsSet пока нет. 04.01.2019
  • terms_set доступен только в Elasticsearch 6.1.x и более поздних версиях. Если вы используете версию 0.17, вы можете рассмотреть возможность обновления, чтобы воспользоваться множеством новых функций, улучшений производительности и исправлений ошибок. 09.01.2019
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..