Как выполнить полнотекстовое индексирование и поиск в приведенном ниже документе json в ArangoDb?

{
"batters":
    {
    "batter":[
            { "id": "1001", "type": "Regular" },
            { "id": "1002", "type": "Chocolate" },
            { "id": "1003", "type": "Blueberry" },
            { "id": "1004", "type": "Devil's Food" }
    ]
    },
    "topping":[
            { "id": "5001", "type": "None" },
            { "id": "5002", "type": "Glazed" },
            { "id": "5005", "type": "Sugar" },
            { "id": "5007", "type": "Powdered Sugar" },
            { "id": "5006", "type": "Chocolate with Sprinkles" },
            { "id": "5003", "type": "Chocolate" },
            { "id": "5004", "type": "Maple" }
     ]
}

По сути, чтобы иметь здесь полнотекстовый поиск, мне нужно было бы выполнить индексацию по «batters.batter», а также по «batters.topping», то есть по двум атрибутам. Как справиться с таким полнотекстовым поиском. Пожалуйста, объясните метод, и я бы реализовал свой поиск через REST API. Заранее спасибо.


person Haseb Ansari    schedule 14.01.2016    source источник
comment
Удовлетворил ли ответ ваши потребности? Если да, можете ли вы отметить его как принятый? Если нет, то чего не хватает?   -  person dothebart    schedule 03.02.2016


Ответы (1)


Лучший способ решить эту проблему — немного изменить структуру данных, так как полнотекстовые индексы могут работать только с одним атрибутом, а двойной запрос индекса ни в коем случае не будет быстрым. Поэтому мы используем анонимный граф, чтобы связать строки с их объектом.

Итак, мы создаем две (вершинные) коллекции, одну коллекцию ребер, одну коллекцию вершин с полнотекстовым индексом:

db._create("dishStrings")
db._createEdgeCollection("dishEdges")
db._create("dish")

db.dishStrings.ensureIndex({type: "fulltext", fields: [ "name" ]});

И сохраните к ним документы со связывающими их отношениями. Мы используем атрибут _key, который используется для ссылки на вершины в отношениях ребер _from и _to:

db.dishStrings.save({"_key": "1001", "name": "Regular" , type: "Batter"});
db.dishStrings.save({"_key": "1002", "name": "Chocolate", type: "Batter" });
db.dishStrings.save({"_key": "1003", "name": "Blueberry", type: "Batter"});
db.dishStrings.save({"_key": "1004", "name": "Devil's Food", type: "Batter"});
db.dishStrings.save({"_key": "5001", "name": "None", type: "Topping"});
db.dishStrings.save({"_key": "5002", "name": "Glazed", type: "Topping"});
db.dishStrings.save({"_key": "5005", "name": "Sugar", type: "Topping"});
db.dishStrings.save({"_key": "5007", "name": "Powdered Sugar", type: "Topping"});
db.dishStrings.save({"_key": "5006", "name": "Chocolate with Sprinkles", type: "Topping"});
db.dishStrings.save({"_key": "5003", "name": "Chocolate", type: "Topping"});
db.dishStrings.save({"_key": "5004", "name": "Maple", type: "Topping"});

db.dishEdges.save("dishStrings/1001", "dish/batter", {tasty: true, type: "Batter"})
db.dishEdges.save("dishStrings/1002", "dish/batter", {tasty: true, type: "Batter"})
db.dishEdges.save("dishStrings/1003", "dish/batter", {tasty: true, type: "Batter"})
db.dishEdges.save("dishStrings/1004", "dish/batter", {tasty: true, type: "Batter"})
db.dishEdges.save("dishStrings/5001", "dish/batter", {tasty: true, type: "Topping"})
db.dishEdges.save("dishStrings/5002", "dish/batter", {tasty: true, type: "Topping"})
db.dishEdges.save("dishStrings/5003", "dish/batter", {tasty: true, type: "Topping"})
db.dishEdges.save("dishStrings/5004", "dish/batter", {tasty: true, type: "Topping"})
db.dishEdges.save("dishStrings/5005", "dish/batter", {tasty: true, type: "Topping"})
db.dishEdges.save("dishStrings/5006", "dish/batter", {tasty: true, type: "Topping"})
db.dishEdges.save("dishStrings/5007", "dish/batter", {tasty: true, type: "Topping"})

db.dish.save({_key: "batter", tasty: true})

Мы подтверждаем, что полнотекстовый индекс будет работать:

db._query("FOR oneDishStr IN FULLTEXT(dishStrings, 'name', 'Chocolate')" +
          " RETURN oneDishStr").toArray()

( .toArray() выведет нам результат на консоль) Получаем 3 попадания, одно тесто, две начинки. Поскольку строки поиска могут содержать непроверенные строки, мы скорее используем переменные связывания, чтобы обойти инъекции:

db._query("FOR oneDishStr IN FULLTEXT(dishStrings, 'name', @searchString) " + 
          " RETURN oneDishStr", 
          {searchString: "Chocolate"});

Теперь давайте воспользуемся краевым отношением, чтобы найти подключенную тарелку:

db._query("FOR oneDishStr IN FULLTEXT(dishStrings, 'name', @searchString) "+ 
          "RETURN {str: oneDishStr, " + 
                  "dishes: NEIGHBORS(dishStrings, dishEdges, oneDishStr," + 
                                     " 'outbound')}",
           {searchString: "Chocolate"})

Это был старый (до версии 2.7) способ использования графиков, так как мы хотим использовать быстрые фильтры, давайте переведем это в новый синтаксис 2.8:

db._query("FOR oneDishStr IN FULLTEXT(dishStrings, 'name', @searchString) " + 
          "  FOR v IN 1..1 OUTBOUND oneDishStr dishEdges RETURN " + 
          "    {str: oneDishStr, dish: v}",
         {searchString: "Chocolate"})

Мы видим, что в обоих случаях мы получаем один обход для каждого из 3 результатов полнотекстового поиска для Chocolate. Теперь нас интересуют только попадания Toppings, поэтому мы будем фильтровать все ребра, которые не относятся к типу Topping:

db._query("FOR oneDishStr IN FULLTEXT(dishStrings, 'name', @searchString) "+
          "   FOR v, e IN 1..1 OUTBOUND oneDishStr dishEdges " + 
          "      FILTER e.type == 'Topping' " +
          "         RETURN {str: oneDishStr, dish: v}", 
          {searchString: "Chocolate"})
person dothebart    schedule 15.01.2016