Базовый синтаксис и использование языка запросов ArangoDB (AQL)

SQL

Оригинальная ссылка

Персональный блог-Добро пожаловать в гости

Язык запросов ArangoDB (AQL) похож на язык структурированных запросов (SQL) по своему назначению. Оба поддерживают чтение и изменение данных коллекции, но AQL не поддерживает операции определения данных, такие как создание и удаление баз данных, коллекций и индексов.

Хотя некоторые ключевые слова перекрываются, синтаксис AQL отличается от SQL. Например, предложения SQL WHERE и AQL FILTER эквивалентны, поскольку они оба определяют условия, при которых возвращаются результаты. Однако SQL использует предопределенную последовательность, чтобы определить, где должен появиться оператор WHERE. В AQL предложения выполняются слева направо, поэтому позиция предложения в запросе FILTER определяет его приоритет.

Несмотря на эти различия, у любого, кто имеет опыт работы с SQL, не должно возникнуть трудностей с изучением AQL.

ArangoDB

1. Предварительный просмотр данных

На этот раз используется 43 фрагмента данных, и каждый фрагмент данных содержит шесть полей, таких как фамилия, возраст, статус активности и характеристики.

ArangoDB

Каждая из этих функций имеет случайную букву в качестве ключа документа. Метки признаков доступны на английском и немецком языках.

ArangoDB

Место состоит из названия места, широты и долготы:

ArangoDB

2. Базовый CRUD

Создать коллекцию:

Перед созданием документа необходимо создать коллекцию, в которую его поместить.Коллекции можно создавать через веб-интерфейс, арангош или драйвер. AQL не может создавать коллекции.

ArangoDB

Щелкните КОЛЛЕКЦИИ в веб-интерфейсе, затем щелкните Добавить коллекцию и введите Имя персонажа. Подтвердить с сохранением. Новая коллекция появится в списке.

Вставьте один объект:

Вставить документ с помощью AQL

ArangoDB

INSERT {
    "name": "Ned",
    "surname": "Stark",
    "alive": true,
    "age": 41,
    "traits": ["A","H","C","N","P"]
} INTO Characters

грамматика:

INSERT document INTO collectionName

Документ представляет собой объект, состоящий из пар ключ-значение свойства. Кавычки для ключей атрибутов необязательны в AQL. Ключи всегда являются строками, а значения свойств могут иметь разные типы:

  • null
  • boolean (true, false)
  • number (integer and floating point)
  • string
  • array
  • object

Массовая вставка объектов:

AQL не допускает нескольких операций INSERT для одной и той же коллекции в одном запросе. Однако можно вставить несколько документов, используя тело цикла FOR.

LET data = [
    { "name": "Robert", "surname": "Baratheon", "alive": false, "traits": ["A","H","C"] },
    { "name": "Jaime", "surname": "Lannister", "alive": true, "age": 36, "traits": ["A","F","B"] },
    { "name": "Catelyn", "surname": "Stark", "alive": false, "age": 40, "traits": ["D","H","C"] },
    { "name": "Cersei", "surname": "Lannister", "alive": true, "age": 36, "traits": ["H","E","F"] },
    { "name": "Daenerys", "surname": "Targaryen", "alive": true, "age": 16, "traits": ["D","H","C"] },
    { "name": "Jorah", "surname": "Mormont", "alive": false, "traits": ["A","B","C","F"] },
    { "name": "Petyr", "surname": "Baelish", "alive": false, "traits": ["E","G","F"] },
    { "name": "Viserys", "surname": "Targaryen", "alive": false, "traits": ["O","L","N"] },
    { "name": "Jon", "surname": "Snow", "alive": true, "age": 16, "traits": ["A","B","C","F"] },
    { "name": "Sansa", "surname": "Stark", "alive": true, "age": 13, "traits": ["D","I","J"] },
    { "name": "Arya", "surname": "Stark", "alive": true, "age": 11, "traits": ["C","K","L"] },
    { "name": "Robb", "surname": "Stark", "alive": false, "traits": ["A","B","C","K"] },
    { "name": "Theon", "surname": "Greyjoy", "alive": true, "age": 16, "traits": ["E","R","K"] },
    { "name": "Bran", "surname": "Stark", "alive": true, "age": 10, "traits": ["L","J"] },
    { "name": "Joffrey", "surname": "Baratheon", "alive": false, "age": 19, "traits": ["I","L","O"] },
    { "name": "Sandor", "surname": "Clegane", "alive": true, "traits": ["A","P","K","F"] },
    { "name": "Tyrion", "surname": "Lannister", "alive": true, "age": 32, "traits": ["F","K","M","N"] },
    { "name": "Khal", "surname": "Drogo", "alive": false, "traits": ["A","C","O","P"] },
    { "name": "Tywin", "surname": "Lannister", "alive": false, "traits": ["O","M","H","F"] },
    { "name": "Davos", "surname": "Seaworth", "alive": true, "age": 49, "traits": ["C","K","P","F"] },
    { "name": "Samwell", "surname": "Tarly", "alive": true, "age": 17, "traits": ["C","L","I"] },
    { "name": "Stannis", "surname": "Baratheon", "alive": false, "traits": ["H","O","P","M"] },
    { "name": "Melisandre", "alive": true, "traits": ["G","E","H"] },
    { "name": "Margaery", "surname": "Tyrell", "alive": false, "traits": ["M","D","B"] },
    { "name": "Jeor", "surname": "Mormont", "alive": false, "traits": ["C","H","M","P"] },
    { "name": "Bronn", "alive": true, "traits": ["K","E","C"] },
    { "name": "Varys", "alive": true, "traits": ["M","F","N","E"] },
    { "name": "Shae", "alive": false, "traits": ["M","D","G"] },
    { "name": "Talisa", "surname": "Maegyr", "alive": false, "traits": ["D","C","B"] },
    { "name": "Gendry", "alive": false, "traits": ["K","C","A"] },
    { "name": "Ygritte", "alive": false, "traits": ["A","P","K"] },
    { "name": "Tormund", "surname": "Giantsbane", "alive": true, "traits": ["C","P","A","I"] },
    { "name": "Gilly", "alive": true, "traits": ["L","J"] },
    { "name": "Brienne", "surname": "Tarth", "alive": true, "age": 32, "traits": ["P","C","A","K"] },
    { "name": "Ramsay", "surname": "Bolton", "alive": true, "traits": ["E","O","G","A"] },
    { "name": "Ellaria", "surname": "Sand", "alive": true, "traits": ["P","O","A","E"] },
    { "name": "Daario", "surname": "Naharis", "alive": true, "traits": ["K","P","A"] },
    { "name": "Missandei", "alive": true, "traits": ["D","L","C","M"] },
    { "name": "Tommen", "surname": "Baratheon", "alive": true, "traits": ["I","L","B"] },
    { "name": "Jaqen", "surname": "H'ghar", "alive": true, "traits": ["H","F","K"] },
    { "name": "Roose", "surname": "Bolton", "alive": true, "traits": ["H","E","F","A"] },
    { "name": "The High Sparrow", "alive": true, "traits": ["H","M","F","O"] }
]

FOR d IN data
    INSERT d INTO Characters

грамматика:

LET variableName = valueExpression

Ключевое слово LET определяет массив переменных данных и значений объекта с тем же именем в формате[ {...}, {...}, ... ]

FOR variableName IN expression

Используется для перебора каждого элемента массива данных. В каждом цикле назначьте элемент переменной d. Затем используйте эту переменную в операторе INSERT. Эквивалент следующего формата:

INSERT {
    "name": "Robert",
    "surname": "Baratheon",
    "alive": false,
    "traits": ["A","H","C"]
} INTO Characters

INSERT {
    "name": "Jaime",
    "surname": "Lannister",
    "alive": true,
    "age": 36,
    "traits": ["A","F","B"]
} INTO Characters

...

забрать

Получить все документы в коллекции:

FOR c IN Characters
    RETURN c

грамматика:

FOR variableName IN collectionName

Для каждого документа в коллекции поочередно присваивайте значение переменной c, а затем возвращайте этот документ в соответствии с телом цикла.

Выберите один из документов следующим образом:

 {
    "_key": "2861650",
    "_id": "Characters/2861650",
    "_rev": "_V1bzsXa---",
    "name": "Ned",
    "surname": "Stark",
    "alive": true,
    "age": 41,
    "traits": ["A","H","C","N","P"]
  },

Документ содержит четыре свойства, которые мы сохраняем, а также еще три свойства, добавленные системой базы данных:

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

  • _id: имя коллекции/ключ документа, только для чтения

  • _rev: Идентификатор версии, управляемой системой, только для чтения.

Чтобы получить конкретный документ:

RETURN DOCUMENT("Characters", "2861650")
// --- or ---
RETURN DOCUMENT("Characters/2861650")

возвращение:

[
  {
    "_key": "2861650",
    "_id": "Characters/2861650",
    "_rev": "_V1bzsXa---",
    "name": "Ned",
    "surname": "Stark",
    "alive": true,
    "age": 41,
    "traits": ["A","H","C","N","P"]
  }
] 

грамматика:

DOCUMENT()

Получить определенный документ, используя _key или _id, функция также позволяет получить несколько документов одновременно

RETURN DOCUMENT("Characters", ["2861650", "2861653"])
// --- or ---
RETURN DOCUMENT(["Characters/2861650", "Characters/2861653"])

Обновить документацию:

Изменить существующие файлы:

UPDATE "2861650" WITH { alive: false } IN Characters
语法:
UPDATE documentKey WITH object IN collectionName

Обновляет указанные документы с перечисленными свойствами (добавляя их, если они не существуют), но оставляет остальные без изменений. Чтобы заменить все содержимое документа, используйте функцию REPLACE:

REPLACE "2861650" WITH {
    name: "Ned",
    surname: "Stark",
    alive: false,
    age: 41,
    traits: ["A","H","C","N","P"]
} IN Characters 

Функция также работает для циклов, таких как добавление новых свойств ко всем документам:

FOR c IN Characters
    UPDATE c WITH { season: 1 } IN Characters

Удалить файлы:

грамматика:

REMOVE _key IN Collectiosname

Чтобы полностью удалить документ из коллекции, требуется операция REMOVE. Он работает так же, как и другие операции модификации, но без предложения WITH:

REMOVE "2861650" IN Characters 

3. Сопоставление файлов

грамматика:

FILTER

Найдите документы, которые удовлетворяют более сложным условиям, чем равенство _key, имея возможность формулировать произвольные условия для соответствия документов.

Равное условие:

FOR c IN Characters
    FILTER c.name == "Ned"
    RETURN c

Условие фильтрации следующее: «Имя атрибута символьного документа должно быть равно строке Ned». Возвращает символьный документ, если применяется условие.

Состояние области:

FOR c IN Characters
    FILTER c.age >= 13
    RETURN c.name

Различные условия:

FOR c IN Characters
    FILTER c.age < 13
    FILTER c.age != null
    RETURN { name: c.name, age: c.age }

//or

FOR c IN Characters
    FILTER c.age < 13 AND c.age != null
    RETURN { name: c.name, age: c.age } 

Альтернативные условия:

FOR c IN Characters
    FILTER c.name == "Jon" OR c.name == "Joffrey"
    RETURN { name: c.name, surname: c.surname } 

В-четвертых, сортировка и ограничения

Синтаксис ограничения:

LIMIT()

За LIMIT следует максимальное отображаемое число, которое ограничивает количество строк, отображаемых в результате.

FOR c IN Characters
    LIMIT 5
    RETURN c.name

Вы также можете использовать LIMIT, чтобы пропустить определенное количество записей и вернуться к следующим n документам:

FOR c IN Characters
    LIMIT 2, 5
    RETURN c.name

Синтаксис сортировки:

SORT()

DESC по убыванию, чтобы изменить порядок сортировки

FOR c IN Characters
    SORT c.name DESC
    LIMIT 10
    RETURN c.name

Сортировать по нескольким полям

FOR c IN Characters
    FILTER c.surname
    SORT c.surname, c.name
    LIMIT 10
    RETURN {
        surname: c.surname,
        name: c.name
    }

Роль ФИЛЬТРА здесь состоит в том, чтобы вести записи только с непустой фамилией.

5. Комбинация

грамматика:MERGE()

Функция MERGE() заключается в объединении объектов вместе. Последние переопределяются при слиянии, потому что используются исходные свойства персонажа { черты: ... }.

FOR c IN Characters
    RETURN MERGE(c, { traits: DOCUMENT("Traits", c.traits)[*].en } )

6. Схема работы

Создайте график:

грамматика:

INSERT { _from: _id(A), _to: _id(B) } INTO ChildOf

Пример: Сначала создайте новую коллекцию и обязательно измените тип коллекции на Edge.

ArangoDB

Затем, запрашивая данные из нескольких коллекций, результаты сохраняются в пограничной коллекции.

LET data = [                                                        //关系数据
    {
        "parent": { "name": "Ned", "surname": "Stark" },
        "child": { "name": "Robb", "surname": "Stark" }
    }, {
        "parent": { "name": "Ned", "surname": "Stark" },
        "child": { "name": "Sansa", "surname": "Stark" }
    }, {
        "parent": { "name": "Ned", "surname": "Stark" },
        "child": { "name": "Arya", "surname": "Stark" }
    }, {
        "parent": { "name": "Ned", "surname": "Stark" },
        "child": { "name": "Bran", "surname": "Stark" }
    }, {
        "parent": { "name": "Catelyn", "surname": "Stark" },
        "child": { "name": "Robb", "surname": "Stark" }
    }, {
        "parent": { "name": "Catelyn", "surname": "Stark" },
        "child": { "name": "Sansa", "surname": "Stark" }
    }, {
        "parent": { "name": "Catelyn", "surname": "Stark" },
        "child": { "name": "Arya", "surname": "Stark" }
    }, {
        "parent": { "name": "Catelyn", "surname": "Stark" },
        "child": { "name": "Bran", "surname": "Stark" }
    }, {
        "parent": { "name": "Ned", "surname": "Stark" },
        "child": { "name": "Jon", "surname": "Snow" }
    }, {
        "parent": { "name": "Tywin", "surname": "Lannister" },
        "child": { "name": "Jaime", "surname": "Lannister" }
    }, {
        "parent": { "name": "Tywin", "surname": "Lannister" },
        "child": { "name": "Cersei", "surname": "Lannister" }
    }, {
        "parent": { "name": "Tywin", "surname": "Lannister" },
        "child": { "name": "Tyrion", "surname": "Lannister" }
    }, {
        "parent": { "name": "Cersei", "surname": "Lannister" },
        "child": { "name": "Joffrey", "surname": "Baratheon" }
    }, {
        "parent": { "name": "Jaime", "surname": "Lannister" },
        "child": { "name": "Joffrey", "surname": "Baratheon" }
    }
]

FOR rel in data
    LET parentId = FIRST(                                    //FIRST()提取第一个元素
        FOR c IN Characters
            FILTER c.name == rel.parent.name                    //筛选条件
            FILTER c.surname == rel.parent.surname
            LIMIT 1
            RETURN c._id                                                 //返回_id
    )
    LET childId = FIRST(
        FOR c IN Characters
            FILTER c.name == rel.child.name
            FILTER c.surname == rel.child.surname
            LIMIT 1
            RETURN c._id
    )
    FILTER parentId != null AND childId != null                        //剔除_id都为空的记录
    INSERT { _from: childId, _to: parentId } INTO ChildOf       //将数据插入边集合
    RETURN NEW                                                                   //返回数据 

Вы также можете создавать граничные данные напрямую:

INSERT { _from: "Characters/robb", _to: "Characters/ned" } INTO ChildOf

Пройдите по графику:

грамматика:

FOR v IN 1..1 OUTBOUND _id ChildOf

    RETURN v.name

其中1..1为遍历深度

Пример:

FOR c IN Characters
    FILTER c.name == "Bran"
    FOR v IN 1..1 OUTBOUND c ChildOf
        RETURN v.name

возвращение

[
  "Ned",
  "Catelyn"
]

Обход следующий:

ArangoDB

Для обратного обхода вам нужно использовать ключевое слово INBOUND:

FOR c IN Characters
    FILTER c.name == "Tywin"
    FOR v IN 2..2 INBOUND c ChildOf
        RETURN DISTINCT v.name
输出:

[
  "Joffrey"
] 

Обход следующий:

ArangoDB

Следует отметить, что «1..1» ограничивает глубину обхода до 1, «2..2» ограничивает глубину обхода до 2, а «1..2» ограничивает глубину обхода до 1 или 2.

7. Геопространственный запрос

данные о местоположении

Создайте коллекцию местоположений:

ArangoDB

Введите данные о местоположении:

LET places = [
    { "name": "Dragonstone", "coordinate": [ 55.167801, -6.815096 ] },
    { "name": "King's Landing", "coordinate": [ 42.639752, 18.110189 ] },
    { "name": "The Red Keep", "coordinate": [ 35.896447, 14.446442 ] },
    { "name": "Yunkai", "coordinate": [ 31.046642, -7.129532 ] },
    { "name": "Astapor", "coordinate": [ 31.50974, -9.774249 ] },
    { "name": "Winterfell", "coordinate": [ 54.368321, -5.581312 ] },
    { "name": "Vaes Dothrak", "coordinate": [ 54.16776, -6.096125 ] },
    { "name": "Beyond the wall", "coordinate": [ 64.265473, -21.094093 ] }
]

FOR place IN places
    INSERT place INTO Locations

Настройте геопространственный индекс:

В интерфейсе COLLECTIONS добавьте новый Indexes и установите его в качестве поля координат:

ArangoDB

ArangoDB

Найти близлежащие места

грамматика:

NEAR()

Найти ближайшую точку отсчета координат

FOR loc IN NEAR(Locations, 53.35, -6.26, 3)
    RETURN {
        name: loc.name,
        latitude: loc.coordinate[0],
        longitude: loc.coordinate[1]
    }

вывод:

[
  {
    "name": "Vaes Dothrak",
    "latitude": 54.16776,
    "longitude": -6.096125
  },
  {
    "name": "Winterfell",
    "latitude": 54.368321,
    "longitude": -5.581312
  },
  {
    "name": "Dragonstone",
    "latitude": 55.167801,
    "longitude": -6.815096
  }
]

Найти местоположение в радиусе

грамматика:

WITHIN()

Поиск местоположения в заданном радиусе от контрольной точки

FOR loc IN WITHIN(Locations, 53.35, -6.26, 200 * 1000)
    RETURN {
        name: loc.name,
        latitude: loc.coordinate[0],
        longitude: loc.coordinate[1]
    }

вывод

[
  {
    "name": "Vaes Dothrak",
    "latitude": 54.16776,
    "longitude": -6.096125
  },
  {
    "name": "Winterfell",
    "latitude": 54.368321,
    "longitude": -5.581312
  }
]

Найти местоположения по расстоянию:

грамматика:

NEAR()或WITHIN()

Возвращает расстояние до контрольной точки, добавляя необязательный пятый параметр. Должна быть строка:

FOR loc IN NEAR(Locations, 53.35, -6.26, 3, "distance")
    RETURN {
        name: loc.name,
        latitude: loc.coordinate[0],
        longitude: loc.coordinate[1],
        distance: loc.distance / 1000
    }

вывод:

[
  {
    "name": "Vaes Dothrak",
    "latitude": 54.16776,
    "longitude": -6.096125,
    "distance": 91.56658640314431
  },
  {
    "name": "Winterfell",
    "latitude": 54.368321,
    "longitude": -5.581312,
    "distance": 121.66399816395028
  },
  {
    "name": "Dragonstone",
    "latitude": 55.167801,
    "longitude": -6.815096,
    "distance": 205.31879386198324
  }
]

Проблемы с AQL:

Вопрос 1: Как подсчитать и вернуть результаты запроса?

  • Решение:
RETURN COUNT(FOR v IN visitors FILTER v.ip == "127.0.0.1" RETURN 1)
Категории